当前位置:   article > 正文

Qt下使用OpenCV的鼠标回调函数进行圆形/矩形/多边形的绘制_qt opencv找出矩形并画圆

qt opencv找出矩形并画圆


前言

本文主要讲述了在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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

二、绘制圆形

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

//绘制圆形
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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

三、绘制矩形

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

//绘制矩形
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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

四、绘制多边形

多边形需要获取各个顶点,所以这里在每次鼠标左键松开时将当前点保存到多边形顶点容器中,并在获取下一个顶点时将其与前一点相连起来,直到鼠标右键按下,将最后一点与第一点相连,结束多边形的绘制。这里每两点之间的连线使用了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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

五、示例完整代码

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

#OpenCV
INCLUDEPATH += $$PWD/OpenCV/Includes
DEPENDPATH += $$PWD/OpenCV/Includes
LIBS += -L$$PWD/OpenCV/Lib/ -lopencv_world455
  • 1
  • 2
  • 3
  • 4

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294

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

总结

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


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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/993815
推荐阅读
相关标签
  

闽ICP备14008679号