Qt下使用OpenCV的鼠标回调函数进行圆形/矩形/多边形的绘制

avatar
作者
猴君
阅读量:1

文章目录


前言

本文主要讲述了在Qt下使用OpenCV的鼠标回调在OpenCV的namedWindow和imshow函数显示出来的界面上进行一些图形的绘制,并最终将绘制好的图形显示在QLabel上。示例代码见文章内容,大家可以参考学习,如有错误之处,欢迎大家批评指正。

项目效果
请添加图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、设置imshow显示窗口

这里对imshow出来的窗口进行了初始化,在namedWindow函数中设置了WINDOW_NORMAL标志,用来允许用户调整窗口大小。

//开始绘制区域 void Widget::startDrawArea(Mat imageMat) {     if(imageMat.empty())     {         QMessageBox::warning(this,"警告","请先选择显示图像!");         return;     }      //显示绘图窗口     QString title = "绘制区域";     string strTitle = title.toLocal8Bit().toStdString();     namedWindow(strTitle,WINDOW_NORMAL);      //设置窗口的初始位置和大小     moveWindow(strTitle,690,290);     resizeWindow(strTitle,400,400);      //设置鼠标回调函数     m_drawMat = getDrawAreaMat();     setMouseCallback(strTitle,mouseHandler,&imageMat);      //显示图像并等待用户操作     imshow(strTitle,m_drawMat);     waitKey(0); } 

二、绘制圆形

绘制圆形的时候,鼠标左键按下记录当前点位为圆心,按下并移动时记录移动的距离为半径,并作判断确保绘制的圆不会超出图形边界,这里会实时显示绘制的圆形,左键松开时结束圆的绘制。

//绘制圆形 if(m_drawCircleFlag) {     m_drawCircleFlag = false;     Point pt = Point(x,y);     m_radius = norm(pt - m_center);     int radiusMax = std::min(m_center.x,resultMat.cols - m_center.x);     radiusMax = std::min(radiusMax,m_center.y);     radiusMax = std::min(radiusMax,resultMat.rows - m_center.y);     if(m_radius > radiusMax)     {         m_radius = radiusMax;     }     circle(resultMat, m_center, m_radius, Scalar(0, 0, 255), 1, LINE_AA);     m_drawMat = resultMat.clone();     imshow(strTitle, m_drawMat); } 

三、绘制矩形

绘制矩形的时候,鼠标左键按下记录当前点位为矩形的起点,按下并移动时记录当前点位为矩形的终点,在移动过程中也会实时显示绘制出来的矩形,左键松开时结束矩形的绘制。

//绘制矩形 if(m_drawRectFlag) {     m_drawRectFlag = false;     m_endPoint = Point(x, y);     rectangle(resultMat, Rect(m_startPoint,m_endPoint), Scalar(0, 255, 0), 1, LINE_AA);     m_drawMat = resultMat.clone();     imshow(strTitle, m_drawMat); } 

四、绘制多边形

多边形需要获取各个顶点,所以这里在每次鼠标左键松开时将当前点保存到多边形顶点容器中,并在获取下一个顶点时将其与前一点相连起来,直到鼠标右键按下,将最后一点与第一点相连,结束多边形的绘制。这里每两点之间的连线使用了line函数,在结束绘制时还可以使用polylines函数,下面代码中有编写。

//绘制多边形 m_drawPolygonFlag = false; if(m_vPolygon.size() > 2) {     //如果已经绘制了多个点,将最后一个点与第一个点连接起来     line(resultMat, m_vPolygon[0], m_vPolygon.back(), Scalar(255, 0, 0), 1, LINE_AA);      //或者使用polylines绘制     //vector<vector<cv::Point>> polygons;     //polygons.push_back(m_vPolygon);     //polylines(resultMat, polygons, 1, Scalar(255, 0, 0), 1, LINE_AA, 0);      imshow(strTitle, resultMat); } 

五、示例完整代码

1.MyTest.pro
在这里我将OpenCV库的头文件和库文件打包到了OpenCV文件夹,并放在项目源程序的目录下,然后在pro文件中包含这个路径,代码如下:

#OpenCV INCLUDEPATH += $$PWD/OpenCV/Includes DEPENDPATH += $$PWD/OpenCV/Includes LIBS += -L$$PWD/OpenCV/Lib/ -lopencv_world455 

2.widget.h

#ifndef WIDGET_H #define WIDGET_H  #include <QWidget> #include <QMessageBox> #include <QFileDialog> #include <QDebug>  #include "opencv2/opencv.hpp"  using namespace cv; using namespace std;  QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE  class Widget : public QWidget {     Q_OBJECT  public:     Widget(QWidget *parent = nullptr);     ~Widget();      Mat getDrawAreaMat();     QPixmap cvMatToPixmap(const Mat imageMat);      void startDrawArea(Mat imageMat);  private:     static void mouseHandler(int event, int x, int y, int flags, void* param);  private slots:     void on_pb_selectMat_clicked();     void on_pb_drawCircle_clicked();     void on_pb_drawRect_clicked();     void on_pb_drawPolygon_clicked();  private:     Ui::Widget *ui;      Mat m_showMat;   //界面显示图像  }; #endif // WIDGET_H 

3.widget.cpp

#include "widget.h" #include "ui_widget.h"  //全局变量 Mat m_drawMat;   //绘制的图像  bool m_drawCircleFlag = false;    //绘制圆形标志 bool m_drawRectFlag = false;      //绘制矩形标志 bool m_drawPolygonFlag = false;   //绘制多边形标志  int m_radius = 0;               //绘制圆的半径 Point m_center = Point();       //绘制圆的圆心 Point m_startPoint = Point();   //绘制矩形的起点 Point m_endPoint = Point();     //绘制矩形的终点 vector<Point> m_vPolygon;       //多边形的顶点容器  Widget::Widget(QWidget *parent)     : QWidget(parent)     , ui(new Ui::Widget) {     ui->setupUi(this);     this->setFixedSize(650,420); }  Widget::~Widget() {     delete ui; }  //显示绘制的区域 Mat Widget::getDrawAreaMat() {     //绘制圆     Mat showMat = m_showMat.clone();     if((m_radius != 0) && (m_center != Point(0,0)))     {         circle(showMat, m_center, m_radius, Scalar(0, 0, 255), 1, LINE_AA);     }      //绘制矩形     if((m_startPoint != Point(0,0)) && (m_endPoint != Point(0,0)))     {         rectangle(showMat, Rect(m_startPoint,m_endPoint), Scalar(0, 255, 0), 1, LINE_AA);     }      //绘制多边形     int vNum = m_vPolygon.size();     for(int i=1;i<vNum;i++)     {         line(showMat, m_vPolygon[i-1], m_vPolygon[i], Scalar(255, 0, 0), 1, LINE_AA);         if(i == vNum-1)         {             line(showMat, m_vPolygon[0], m_vPolygon.back(), Scalar(255, 0, 0), 1, LINE_AA);         }     }     return showMat; }  //Mat转QPixmap QPixmap Widget::cvMatToPixmap(const Mat imageMat) {     QImage showImage;     if(imageMat.channels() > 1)     {         showImage = QImage((const unsigned char*)(imageMat.data),imageMat.cols,imageMat.rows,imageMat.step,QImage::Format_RGB888);   //彩色图     }     else     {         showImage = QImage((const unsigned char*)(imageMat.data),imageMat.cols,imageMat.rows,imageMat.step,QImage::Format_Indexed8);   //灰度图     }      //OpenCV使用BGR顺序,而Qt使用RGB顺序,因此需要交换颜色通道     return QPixmap::fromImage(showImage.rgbSwapped()); }  //开始绘制区域 void Widget::startDrawArea(Mat imageMat) {     if(imageMat.empty())     {         QMessageBox::warning(this,"警告","请先选择显示图像!");         return;     }      //显示绘图窗口     QString title = "绘制区域";     string strTitle = title.toLocal8Bit().toStdString();     namedWindow(strTitle,WINDOW_NORMAL);      //设置窗口的初始位置和大小     moveWindow(strTitle,690,290);     resizeWindow(strTitle,400,400);      //设置鼠标回调函数     m_drawMat = getDrawAreaMat();     setMouseCallback(strTitle,mouseHandler,&imageMat);      //显示图像并等待用户操作     imshow(strTitle,m_drawMat);     waitKey(0); }  //鼠标回调函数 void Widget::mouseHandler(int event, int x, int y, int flags, void* param) {     QString title = "绘制区域";     string strTitle = title.toLocal8Bit().toStdString();     Mat resultMat = *(Mat*)param;     if(event == EVENT_LBUTTONDOWN)   //鼠标左键按下     {         //绘制圆,确定圆心         if(m_drawCircleFlag)         {             m_center = Point(x,y);         }          //绘制矩形,确定起点         if(m_drawRectFlag)         {             m_startPoint = Point(x,y);         }     }     else if(event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))   //左键按下并移动     {         //实时显示绘制的圆         if(m_drawCircleFlag)         {             //计算半径             Point pt = Point(x,y);             m_radius = norm(pt - m_center);              //确保圆不会超出图像边界             int radiusMax = std::min(m_center.x,resultMat.cols - m_center.x);             radiusMax = std::min(radiusMax,m_center.y);             radiusMax = std::min(radiusMax,resultMat.rows - m_center.y);             if(m_radius > radiusMax)             {                 m_radius = radiusMax;             }             m_drawMat = resultMat.clone();             circle(m_drawMat, m_center, m_radius, Scalar(0, 0, 255), 1, LINE_AA);             imshow(strTitle, m_drawMat);         }          //实时显示绘制的矩形         if(m_drawRectFlag)         {             m_endPoint = Point(x, y);             m_drawMat = resultMat.clone();             rectangle(m_drawMat, Rect(m_startPoint,m_endPoint), Scalar(0, 255, 0), 1, LINE_AA);             imshow(strTitle, m_drawMat);         }     }     else if(event == EVENT_LBUTTONUP)   //鼠标左键抬起     {         //结束绘制圆         if(m_drawCircleFlag)         {             m_drawCircleFlag = false;             Point pt = Point(x,y);             m_radius = norm(pt - m_center);             int radiusMax = std::min(m_center.x,resultMat.cols - m_center.x);             radiusMax = std::min(radiusMax,m_center.y);             radiusMax = std::min(radiusMax,resultMat.rows - m_center.y);             if(m_radius > radiusMax)             {                 m_radius = radiusMax;             }             circle(resultMat, m_center, m_radius, Scalar(0, 0, 255), 1, LINE_AA);             m_drawMat = resultMat.clone();             imshow(strTitle, m_drawMat);         }          //结束绘制矩形         if(m_drawRectFlag)         {             m_drawRectFlag = false;             m_endPoint = Point(x, y);             rectangle(resultMat, Rect(m_startPoint,m_endPoint), Scalar(0, 255, 0), 1, LINE_AA);             m_drawMat = resultMat.clone();             imshow(strTitle, m_drawMat);         }          //绘制多边形的各顶点         if(m_drawPolygonFlag)         {             m_vPolygon.push_back(Point(x, y));             for(size_t i = 1; i < m_vPolygon.size(); i++)             {                 line(resultMat, m_vPolygon[i-1], m_vPolygon[i], Scalar(255, 0, 0), 1, LINE_AA);             }             imshow(strTitle, resultMat);         }     }     else if(event == EVENT_RBUTTONDOWN)   //鼠标右键按下     {         //结束绘制多边形         m_drawPolygonFlag = false;         if(m_vPolygon.size() > 2)         {             //如果已经绘制了多个点,将最后一个点与第一个点连接起来             line(resultMat, m_vPolygon[0], m_vPolygon.back(), Scalar(255, 0, 0), 1, LINE_AA);              //或者使用polylines绘制             //vector<vector<cv::Point>> polygons;             //polygons.push_back(m_vPolygon);             //polylines(resultMat, polygons, 1, Scalar(255, 0, 0), 1, LINE_AA, 0);              imshow(strTitle, resultMat);         }     } }  //选择图像 void Widget::on_pb_selectMat_clicked() {     QString fileName = QFileDialog::getOpenFileName(this,"选择图像文件","E:/PhotoTest/myPhoto","Image Files(*.png *.jpg *.bmp)");     if(!fileName.isEmpty())     {         m_showMat = imread(fileName.toLocal8Bit().toStdString(),1);          //更新界面显示         QPixmap showPixmap = cvMatToPixmap(m_showMat);         if(!showPixmap.isNull())         {             ui->lb_showMat->setPixmap(showPixmap.scaled(ui->lb_showMat->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));   //保持比例         }         else         {             QMessageBox::warning(this,"警告","图像文件打开失败!");         }     } }  //绘制圆形 void Widget::on_pb_drawCircle_clicked() {     setEnabled(false);     m_drawCircleFlag = true;     m_drawRectFlag = false;     m_drawPolygonFlag = false;      m_radius = 0;     m_center = Point(0,0);     startDrawArea(m_showMat.clone());      //更新界面显示     QPixmap showPixmap = cvMatToPixmap(getDrawAreaMat());     if(!showPixmap.isNull())     {         ui->lb_showMat->setPixmap(showPixmap.scaled(ui->lb_showMat->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));     }     setEnabled(true); }  //绘制矩形 void Widget::on_pb_drawRect_clicked() {     //点击后禁用窗口交互     setEnabled(false);     m_drawCircleFlag = false;     m_drawRectFlag = true;     m_drawPolygonFlag = false;      m_startPoint = Point(0,0);     m_endPoint = Point(0,0);     startDrawArea(m_showMat.clone());     QPixmap showPixmap = cvMatToPixmap(getDrawAreaMat());     if(!showPixmap.isNull())     {         ui->lb_showMat->setPixmap(showPixmap.scaled(ui->lb_showMat->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));     }      //绘制结束后开启交互     setEnabled(true); }  //绘制多边形 void Widget::on_pb_drawPolygon_clicked() {     setEnabled(false);     m_drawCircleFlag = false;     m_drawRectFlag = false;     m_drawPolygonFlag = true;      m_vPolygon.clear();     startDrawArea(m_showMat.clone());     QPixmap showPixmap = cvMatToPixmap(getDrawAreaMat());     if(!showPixmap.isNull())     {         ui->lb_showMat->setPixmap(showPixmap.scaled(ui->lb_showMat->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));     }     setEnabled(true); } 

4.widget.ui
请添加图片描述

总结

在Qt下使用OpenCV务必要配置好环境,这样才能正常使用OpenCV的函数。可以看到在这个示例中使用的都是一些基本的函数,但是很好的实现了本文标题所写的功能。在本人之前的文章中,也有使用Qt的事件过滤器来实现这个功能,见下文参考博客。


hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。

参考博客:Qt实现在QLabel上显示图片并进行线条/矩形框/多边形的绘制

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!