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

文章目录

  • 前言
  • 一、设置imshow显示窗口
  • 二、绘制圆形
  • 三、绘制矩形
  • 四、绘制多边形
  • 五、示例完整代码
  • 总结


前言

本文主要讲述了在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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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上显示图片并进行线条/矩形框/多边形的绘制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/375950.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python开发prometheus exporter--用于hadoop-yarn监控

首先写python的exporter需要知道Prometheus提供4种类型Metrics 分别是&#xff1a;Counter, Gauge, Summary和Histogram * Counter可以增长&#xff0c;并且在程序重启的时候会被重设为0&#xff0c;常被用于任务个数&#xff0c;总处理时间&#xff0c;错误个数等只增不减的指…

GAMMA软件适配航天宏图一号多星干涉数据

文章目录 1.航天宏图一号 X-频段 多基雷达星座2.航天宏图算法人员小结3.双基成像与单基成像干涉处理区别 GAMMA软件是世界著名的瑞士GAMMA遥感公司开发的专门用于干涉雷达数据处理的全功能商业软件。作为业内标杆软件&#xff0c;被全球范围内的研究人员、公司和公共机构广泛使…

Vim使用教程

目录 引言1. Vim的基本概念1.1 模式1.2 启动和退出 2. 基础操作2.1 导航2.2 插入文本2.3 删除和复制2.4 查找和替换 3. 高级功能3.1 多文件编辑3.2 宏录制和执行3.3 使用插件3.4 自定义快捷键 4. Vim脚本和自定义配置4.1 基本配置4.2 编写Vim脚本 5. 实用技巧5.1 快速移动5.2 批…

MT6985(天玑9200)芯片性能参数_MTK联发科旗舰5G移动平台处理器

MT6985天玑 9200 旗舰移动平台拥有专业级影像、沉浸式游戏和先进移动显示技术&#xff0c;以及更快捷、覆盖更广的 5G 和 支持 Wi-Fi 7 连接&#xff0c;具有高性能、高能效、低功耗表现。率先采用 Armv9 性能核&#xff0c;全部支持纯 64 位应用&#xff0c;开启高能效架构设计…

IC后端设计中的shrink系数设置方法

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 在一些成熟的工艺节点通过shrink的方式(光照过程中缩小特征尺寸比例)得到了半节点,比如40nm从45nm shrink得到,28nm从32nm shrink得到,由于半节点的性能更优异,成本又低,漏电等不利因素也可以…

nginx正向代理和反向代理

nginx正向代理和反向代理 正向代理以及缓存配置 代理&#xff1a;客户端不再是直接访问服务器&#xff0c;通过代理服务器访问服务端。 正向代理&#xff1a;面向客户端&#xff0c;我们通过代理服务器的IP地址访问目标服务端。 服务端只知道代理服务器的地址&#xff0c;真…

RMAN备份与还原

进入 rman 工具 rman target / 查看 rman 配置 rman> show all; 修改rman 配置 数据库全备 rman> run {allocate channel c1 type disk;allocate channel c2 type disk;backup incremental level 0 database format /home/oracle/backup/full_%d_%s_%t.bak;sql alte…

三个锦囊妙计助效率提升

前言 本文列出了3个常用的配置&#xff0c;可以帮助我们从繁琐重复的任务中解脱出来、实现自动化操作。日积月累&#xff0c;一定有助于提升效率。 1. gvim配置自动插入字符串 在.vimrc中加入以下一行代码&#xff0c;可以帮助你在gvim文本编辑器中快速插入一个带有日期或自定…

iPhone数据恢复篇:在 iPhone 上恢复找回短信的 5 种方法

方法 1&#xff1a;检查最近删除的文件夹 iOS 允许您在 30 天内恢复已删除的短信。您需要先从“设置”菜单启用“过滤器”。让我们来实际检查一下。 步骤 1&#xff1a;打开“设置” > “信息”。 步骤 2&#xff1a;选择“未知和垃圾邮件”&#xff0c;然后切换到“过滤…

SpringCloud第二篇(如何将大型项目拆分成微服务项目)

文章目录 一、认识微服务二、微服务拆分原则三、模块拆分1.根据不同功能创建模块2.修改配置文件3.搬运包 四、远程调用 这一章我们从单体架构的优缺点来分析&#xff0c;看看开发大型项目采用单体架构存在哪些问题&#xff0c;而微服务架构又是如何解决这些问题的 一、认识微服…

科技创新引领水利行业升级:深入分析智慧水利解决方案的核心价值,展望其在未来水资源管理中的重要地位与作用

目录 引言 一、智慧水利的概念与内涵 二、智慧水利解决方案的核心价值 1. 精准监测与预警 2. 优化资源配置 3. 智能运维管理 4. 公众参与与决策支持 三、智慧水利在未来水资源管理中的重要地位与作用 1. 推动水利行业转型升级 2. 保障国家水安全 3. 促进生态文明建设…

5G中的RedCap

5G中的RedCap&#xff1a;降低能力的重要性和实现方式 随着5G技术的推广和普及&#xff0c;设备和终端的多样化使得网络能力的管理变得更加复杂和关键。RedCap&#xff08;Reduced Capability&#xff09;作为一个重要的概念&#xff0c;旨在解决设备能力差异对网络服务和用户…

什么是STM32?嵌入式和STM32简单介绍

1、嵌入式和STM32 1.1.什么是嵌入式 除了桌面PC之外&#xff0c;所有的控制类设备都是嵌入式 嵌入式系统的定义&#xff1a;“用于控制、监视或者辅助操作机器和设备的装置”。 嵌入式系统是一个控制程序存储在ROM中的嵌入式处理器控制板&#xff0c;是一种专用的计算机系统。…

mybatis 延迟加载

MyBatis的延迟加载&#xff08;Lazy Loading&#xff09;是一种优化技术&#xff0c;用于在需要时才加载关联对象或集合&#xff0c;从而提高性能和效率。以下是对MyBatis延迟加载的详细介绍&#xff1a; 延迟加载的基本概念 延迟加载是指在第一次访问对象的属性时才加载该对象…

嵌入式面试准备

兆易创新 Linux中使用mkdir命令创建新的目录时&#xff0c;在其父目录不在时先创建父目录的选项&#xff1a; -m &#xff1a;–mode模式&#xff0c;建立目录的时候同时设置目录的权限。-p&#xff1a;–parents若所建立的上层目录目前尚未建立&#xff0c;则会一并建立上层…

SCI一区级 | Matlab实现NGO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测

SCI一区级 | Matlab实现NGO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测 目录 SCI一区级 | Matlab实现NGO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现NGO-CNN-LSTM-Mutilhead-Attention北方苍鹰算…

用Speedtest-Tracker跟踪上网速度(续)

什么是 Speedtest Tracker ? Speedtest Tracker 是一款自托管互联网性能跟踪应用程序&#xff0c;可针对 Ookla 的 Speedtest 服务运行速度测试检查。 之前老苏介绍的另一个 https://github.com/henrywhitaker3/Speedtest-Tracker 已被放弃。现在这个是积极维护的替代品&#…

192.168.1.1路由器管理系统使用教程

节选自&#xff1a;192.168.1.1路由器管理系统-厂商有哪些-如何使用-无法登录原因-苏州稳联 什么是 192.168.1.1 路由器管理系统&#xff1f; 192.168.1.1 是大多数家庭路由器的默认 IP 地址&#xff0c;用于访问路由器的管理控制台。通过这个管理系统&#xff0c;用户可以配…

彻底搞懂JVM垃圾回收

哈喽&#xff0c;大家好&#x1f389;&#xff0c;我是世杰。 欢迎大家关注我的公众号『程序员世杰』获取更多后端技术干货&#x1f389;&#x1f389;! 本文我为大家介绍「JVM垃圾回收那些事」 面试连环call 如何判断对象是否应被回收?finalize方法的实现机制是什么?如何判…

在Windows环境下安装pycharm

Python环境搭建 第一步下载安装python 等待安装完成 验证python是否安装成功 Python开发工具安装部署 JetBrains: Essential tools for software developers and teams PyCharm: the Python IDE for data science and web development 下载社区版本的PyCharm 双击打开下载好的…