Qt 结合 OpenCV 的 Mat 对象绘制矩形区域,拖到矩形区域可用于 ROI 区域的截取

文章详细描述了如何在Qt中创建TB_DrawRectangle类,实现在QLabel上基于GRectangle参数和OpenCVMat对象进行图像的矩形绘制,以及GRectangle类的拖动和尺寸调整功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TB_DrawRectangle 表示绘制矩形类,在 QLabel 上实现绘制功能;GRectangle 表示矩形参数类; Mat 为 OpenCV 的图像类。

在这里插入图片描述

// ui->framePicture 表示显示区域;
// qScrollArea 实现图像的放大缩小显示
QScrollArea* qScrollArea=new QScrollArea(ui->framePicture);
QHBoxLayout* layout=new QHBoxLayout(ui->framePicture);
layout->addWidget(qScrollArea);
//qLabelView 表示图像的显示容器
QLabel* qLabelView=new QLabel(qScrollArea);
qLabelView->setScaledContents(true);
qLabelView->setBackgroundRole(QPalette::Background);
qLabelView->setFrameStyle(QFrame::StyledPanel);
qLabelView->setFrameShadow(QFrame::Sunken);
qLabelView->setGeometry(0, 0, qScrollArea->width(), qScrollArea->height());
qScrollArea->setWidget(qLabelView);
//制矩形的参数类
GRectangle rect; 
//Opencv 的图像类
Mat image; 
//qDrawView 表示矩形绘制的对象
TB_DrawRectangle* qDrawView=new TB_DrawRectangle(qLabelView, &rect, &image);
qDrawView->setParent(qLabelView);
qDrawView->setWindowFlags(Qt::WindowStaysOnTopHint);
qDrawView->setAttribute(Qt::WA_TranslucentBackground,true);
qDrawView->setStyleSheet("background-color:transparent");

GRectangle 类对象

GRectangle 类对象表示矩形的参数,里面包含了 P0、P1、P2 三个点的坐标参数,拖到 P0 可实现整个矩形的移动;拖到 P1、P2 可实现矩形的长宽大小变化。

#define MOUSE_LENGTH_LEN 10
#define MOUSE_RADIU_LEN  5

// GRectangle 类对象
class GRectangle
{
public:
    GRectangle();
    ~GRectangle(){}
private:
    Point2f p0;
    Point2f p1;
    Point2f p2;
    void UpdataPoint2fApi();
public:
    void SetPosLimit(Size imageSize);
    void DragPointApi(QPointF point, Size imageSize, Size labelSize);
    void DrawApi(QPainter* painter, QColor color, Size imageSize, Size labelSize);
public:
    Point2f p1_;
    Point2f p2_;
};

// GRectangle 类对象初始化
GRectangle::GRectangle()
{
    p1_=Point2f(100, 100);
    p2_=Point2f(300, 300);
    p1=p1_;
    p2=p2_;
}

//更新点状态
void GRectangle::UpdataPoint2fApi()
{
    p0=Point2f((p1.x + p2.x)/2, (p1.y + p2.y)/2);
}

//设置限制
void GRectangle::SetPosLimit(Size imageSize)
{
    int w=imageSize.width;
    int h=imageSize.height;
    if(w==0 || h==0)
        return;
    p1.x = p1.x<0 ? 0 : (p1.x>=w ? w-1 : p1.x);
    p2.x = p2.x<0 ? 0 : (p2.x>=w ? w-1 : p2.x);
    p1.y = p1.y<0 ? 0 : (p1.y>=h ? h-1 : p1.y);
    p2.y = p2.y<0 ? 0 : (p2.y>=h ? h-1 : p2.y);
    if(p1.x==p2.x && p1.y==p2.y && (p1.x==0 || p1.x==w-1) && (p1.y==0 || p1.y==h-1))
    {
        p1=Point2f(w/2-10, h/2);
        p2=Point2f(w/2+10, h/2);
    }
}

//点距离
float cvPointDistance(Point2f p1, Point2f p2)
{
    return sqrt(pow(p2.x-p1.x, 2) + pow(p2.y-p1.y, 2));
}

// 拖动点位置, sX表示图像宽度/控件宽度, sY表示图像高度/控件高度
void GRectangle::DragPointApi(QPointF point, Size imageSize, Size labelSize)
{
    float sX=(float)imageSize.width/labelSize.width;
    float sY=(float)imageSize.height/labelSize.height;
    float sZ=(sX + sY)/2;
    Point2f p(point.x()*sX, point.y()*sY);
    vector<Point2f> sorts;
    sorts.push_back(p0);
    sorts.push_back(p1);
    sorts.push_back(p2);
    float length=10e9;
    int   index=-1;
    for(size_t i=0;i<sorts.size();i++)
    {
        float l=cvPointDistance(p, sorts[i]);
        if(l<=length)
        {
            length=l;
            index=(int)i;
        }
    }
    if(length<=(MOUSE_LENGTH_LEN*sZ))
    {
        switch(index)
        {
        //P0
        case 0:
        {
            int x=p.x-p0.x;
            int y=p.y-p0.y;
            p1=Point2f(p1.x + x, p1.y + y);
            p2=Point2f(p2.x + x, p2.y + y);
            break;
        }
        //P1
        case 1:
        {
            if(!(p.x<p2.x && p.y<p2.y))
                return;
            p1=Point2f(p.x, p.y);
            break;
        }
        //P2
        case 2:
        {
            if(!(p.x>p1.x && p.y>p1.y))
                return;
            p2=Point2f(p.x, p.y);
            break;
        }
        default:
            break;
        }
    }
    //限制位置尺寸大小
    this->SetPosLimit(imageSize);
}

//绘图
void GRectangle::DrawApi(QPainter* painter, QColor color, Size imageSize, Size labelSize)
{
    if(!(labelSize.width>0 && imageSize.width>0))
        return;
    float sX=(float)imageSize.width/labelSize.width;
    float sY=(float)imageSize.height/labelSize.height;
    //更新位置点
    this->UpdataPoint2fApi();
    //实际点装换为灰度点
    QPointF P0(p0.x/sX, p0.y/sY);
    QPointF P1(p1.x/sX, p1.y/sY);
    QPointF P2(p2.x/sX, p2.y/sY);
    //初始化绘图工具
    painter->setRenderHint(QPainter::Antialiasing, true);
    painter->setPen(color);
    QRectF rect1(P0.x()-MOUSE_RADIU_LEN, P0.y()-MOUSE_RADIU_LEN, 2*MOUSE_RADIU_LEN, 2*MOUSE_RADIU_LEN);
    painter->drawEllipse(rect1);
    QRectF rect2(P1.x()-MOUSE_RADIU_LEN, P1.y()-MOUSE_RADIU_LEN, 2*MOUSE_RADIU_LEN, 2*MOUSE_RADIU_LEN);
    painter->drawEllipse(rect2);
    QRectF rect3(P2.x()-MOUSE_RADIU_LEN, P2.y()-MOUSE_RADIU_LEN, 2*MOUSE_RADIU_LEN, 2*MOUSE_RADIU_LEN);
    painter->drawEllipse(rect3);
    QPoint l2=QPoint(P2.x(), P1.y());
    QPoint l4=QPoint(P1.x(), P2.y());
    painter->drawLine(P1, l2);
    painter->drawLine(l2, P2);
    painter->drawLine(P2, l4);
    painter->drawLine(l4, P1);
}

TB_DrawRectangle 类实现拖到、绘制等功能

class TB_DrawRectangle : public QWidget
{
    Q_OBJECT
public:
    explicit TB_DrawRectangle(QWidget *parent, GRectangle *rect, Mat *image);
    void mouseMoveEvent(QMouseEvent *e);
    void mouseReleaseEvent(QMouseEvent *e);
    void paintEvent(QPaintEvent *event);
    ~TB_DrawRectangle(){}
public:
    GRectangle* graphic;  
    Mat* imageSize;
    QColor color;
};

//初始化
TB_DrawRectangle::TB_DrawRectangle(QWidget *parent, GRectangle *rect, Mat *image) :
    QWidget(parent), graphic(rect), imageSize(image), color(Qt::red)
{}

//鼠标移动事件
void TB_DrawRectangle::mouseMoveEvent(QMouseEvent *e)
{
    if(!(e->buttons() & Qt::LeftButton))
        return;
    int x=e->x();
    int y=e->y();
    if(x<0 || y<0 || x>this->width() || y>this->height())
        return;
    if(!(graphic && imageSize))
        return;
    graphic->DragPointApi(QPointF(x, y), Size(imageSize->cols, imageSize->rows), Size(this->width(), this->height()));
}

//鼠标释放事件
void TB_DrawRectangle::mouseReleaseEvent(QMouseEvent *e)
{
    e->accept();
}

//鼠标绘制事件
void TB_DrawRectangle::paintEvent(QPaintEvent* event)
{
    Q_UNUSED(event);
    QPainter painter(this);
    if(!(graphic && imageSize))
        return;
    graphic->DrawApi(&painter, color, Size(imageSize->cols, imageSize->rows), Size(this->width(), this->height()));
    this->update();
}
<think>好的,我现在需要帮助用户解决一个使用QtOpenCV在C++中实现图片区域选择并进行OCR识别的问题。用户之前已经询问过实时绘制方框的实现,现在进一步要求整合图片打开、区域选择和显示OCR结果。 首先,我要确认用户的需求:他们想要一个完整的应用程序,能够打开图片,用鼠标选择区域,调用OCR识别,并显示结果。技术栈是C++QtOpenCV。需要注意的是,用户可能希望整个过程高效且用户界面友好。 接下来,我需要考虑如何将之前的实时绘制方框代码与OpenCV和OCR功能结合起来。之前的回答已经用Qt实现了方框绘制,现在需要加入OpenCV处理图像的部分,以及OCR识别。可能的问题包括如何将Qt的图像与OpenCVMat转换,如何截取选定区域,以及调用OCR库。 然后,用户提到使用C语言Qt开发,但Qt本身是C++的,所以可能需要确认用户是否真的需要纯C,但更可能是用C++,所以按Qt的C++来处理。另外,OCR部分需要选择适合的库,比如Tesseract,用户可能没有明确说明,所以需要建议一个常用的开源库。 接下来,结构上应该分为几个部分:界面设计(打开图片、显示区域)、事件处理(鼠标绘制)、图像处理(OpenCV读取和区域截取)、OCR识别(Tesseract集成)、结果显示。需要确保各部分之间的数据流正确,比如从Qt的Graphics View获取选区坐标,转换成OpenCVROI,进行OCR处理,再返回结果显示在Qt的界面上。 可能遇到的挑战包括坐标系的转换,尤其是在有缩放或滚动的情况下,需要正确映射鼠标位置到图像的实际像素坐标。此外,内存管理需要注意,特别是在频繁打开和关闭图片时,避免内存泄漏。 还要考虑如何整合OpenCVQt,比如使用QPixmap或QImage来显示OpenCVMat对象,可能需要颜色空间的转换,因为OpenCV默认使用BGR,而Qt使用RGB。 关于OCR部分,需要指导用户安装和配置Tesseract库,并在项目中正确链接。可能需要处理不同语言的支持,以及如何提高识别准确率,比如预处理图像(灰度化、二值化等)。 最后,代码结构需要清晰,方便用户理解和扩展。可能需要提供完整的示例代码,包括头文件、源文件和必要的配置说明。同时,提醒用户注意常见的错误,比如路径问题、库的版本兼容性等。 总结,整个思考过程需要从需求分析开始,拆解各个功能模块,考虑技术实现细节,确保各部分协同工作,并提供足够的错误处理和用户反馈,比如状态栏显示信息。同时,代码示例需要准确,注释清晰,帮助用户顺利实现功能。</think>以下是基于QtOpenCV实现的完整图片选区OCR解决方案(C++实现): --- ### **完整实现步骤** #### 1. 环境准备 ```bash # 所需库 Qt 5.15+ (Core, Gui, Widgets) OpenCV 4.5+ Tesseract OCR 5.0+ (需训练数据) ``` #### 2. 核心类设计 ```mermaid classDiagram class MainWindow { -cv::Mat cvImage -QGraphicsPixmapItem* imageItem -QGraphicsRectItem* rubberBand -QPoint origin +openImage() +processOCR(QRect) +mousePressEvent() +mouseMoveEvent() +mouseReleaseEvent() } ``` #### 3. 完整代码实现 **mainwindow.h** ```cpp #include <QMainWindow> #include <opencv2/opencv.hpp> #include <tesseract/baseapi.h> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); protected: void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; private slots: void openImage(); private: Ui::MainWindow *ui; cv::Mat cvImage; QGraphicsPixmapItem* imageItem = nullptr; QGraphicsRectItem* rubberBand = nullptr; QPoint origin; void processOCR(const QRect& area); void showImage(const cv::Mat& image); cv::Mat qImageToMat(const QImage& qImage); }; ``` **mainwindow.cpp** ```cpp #include "mainwindow.h" #include "ui_mainwindow.h" #include <QFileDialog> #include <QGraphicsScene> #include <QMessageBox> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); ui->graphicsView->setScene(new QGraphicsScene(this)); connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::openImage); } void MainWindow::openImage() { QString path = QFileDialog::getOpenFileName(this, "打开图片", "", "Images (*.png *.jpg *.bmp)"); if (path.isEmpty()) return; cvImage = cv::imread(path.toStdString()); if (cvImage.empty()) { QMessageBox::critical(this, "错误", "无法加载图片"); return; } showImage(cvImage); } void MainWindow::showImage(const cv::Mat& image) { QImage qImg(image.data, image.cols, image.rows, image.step, QImage::Format_BGR888); QGraphicsScene* scene = ui->graphicsView->scene(); scene->clear(); imageItem = scene->addPixmap(QPixmap::fromImage(qImg)); ui->graphicsView->setSceneRect(qImg.rect()); } void MainWindow::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton && !cvImage.empty()) { origin = e->pos(); rubberBand = new QGraphicsRectItem; rubberBand->setPen(QPen(Qt::red, 2)); ui->graphicsView->scene()->addItem(rubberBand); } } void MainWindow::mouseMoveEvent(QMouseEvent *e) { if (rubberBand) { QRect rect(origin, e->pos()); rubberBand->setRect(rect.normalized()); ui->statusBar->showMessage( QString("选区: %1×%2").arg(rect.width()).arg(rect.height())); } } void MainWindow::mouseReleaseEvent(QMouseEvent *e) { if (rubberBand) { QRect selected = rubberBand->rect().toRect(); ui->graphicsView->scene()->removeItem(rubberBand); delete rubberBand; rubberBand = nullptr; if (selected.width() > 10 && selected.height() > 10) { processOCR(selected); } } } void MainWindow::processOCR(const QRect& area) { try { // 转换为OpenCV坐标系 cv::Rect roi(area.x(), area.y(), area.width(), area.height()); cv::Mat target = cvImage(roi).clone(); // 图像预处理 cv::cvtColor(target, target, cv::COLOR_BGR2GRAY); cv::threshold(target, target, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); // OCR识别 tesseract::TessBaseAPI tess; tess.Init(NULL, "chi_sim", tesseract::OEM_LSTM_ONLY); // 使用中文训练数据 tess.SetImage(target.data, target.cols, target.rows, 1, target.step); char* text = tess.GetUTF8Text(); ui->textEdit->setPlainText(QString::fromUtf8(text)); delete[] text; } catch (cv::Exception& e) { QMessageBox::critical(this, "错误", QString("OpenCV错误: %1").arg(e.what())); } } // QImage转Mat工具函数 cv::Mat MainWindow::qImageToMat(const QImage& qImage) { return cv::Mat(qImage.height(), qImage.width(), CV_8UC3, const_cast<uchar*>(qImage.bits()), static_cast<size_t>(qImage.bytesPerLine())); } ``` #### 4. 关键配置说明 1. **OpenCV配置** ```cmake find_package(OpenCV REQUIRED) target_link_libraries(${PROJECT_NAME} PRIVATE ${OpenCV_LIBS}) ``` 2. **Tesseract配置** ```cmake find_package(Tesseract REQUIRED) target_link_libraries(${PROJECT_NAME} PRIVATE ${Tesseract_LIBRARIES}) ``` 3. **中文语言包** - 下载chi_sim.traineddata - 放置到tessdata目录(需设置环境变量TESSDATA_PREFIX) #### 5. 使用流程 1. 点击菜单"文件->打开"选择图片 2. 按住鼠标左键拖动画出选区 3. 释放鼠标自动显示识别结果 4. 支持功能: - 选区实时显示 - 中英文混合识别 - 自动图像二值化处理 #### 6. 常见问题解决 **问题1:OpenCVQt坐标偏差** ```cpp // 坐标转换修正(考虑视图缩放) QPointF scenePos = ui->graphicsView->mapToScene(e->pos()); QRectF sceneRect = QRectF(originScenePos, scenePos).normalized(); ``` **问题2:OCR识别率低** ```cpp // 增强图像预处理 cv::GaussianBlur(target, target, cv::Size(3,3), 0); cv::resize(target, target, cv::Size(), 2, 2, cv::INTER_CUBIC); // 放大2倍 ``` **问题3:内存泄漏检测** ```bash valgrind --leak-check=full ./OCRApp ``` --- ### **扩展功能建议** 1. **多语言切换** ```cpp // 动态切换语言 tess.Init(NULL, "eng+chi_sim", tesseract::OEM_LSTM_ONLY); ``` 2. **识别结果编辑** ```cpp // 添加结果校对窗口 QDialog* editDialog = new QDialog; QTextEdit* editArea = new QTextEdit(editDialog); ``` 3. **批量处理功能** ```cpp QDirIterator it(inputDir, {"*.jpg","*.png"}, QDir::Files); while (it.hasNext()) { processBatch(it.next()); } ``` --- 该方案实现了从图像选择到OCR识别的完整流程,结合Qt的优秀交互能力和OpenCV的高效图像处理能力,适合需要精确区域识别的应用场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刚出道的菜鸟@丢丢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值