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.mzph.cn/news/871380.shtml

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

相关文章

UE4 解决创建布料报错:三角形退化

**【问题】**创建创建布料时报错&#xff1a;三角形退化 【方法】 1.要重新绑定&#xff1a;导入到ue4为静态网格体&#xff0c;勾选“移除退化”&#xff0c;再导出fbx&#xff0c;再重新绑定 2.不用重新绑定&#xff1a;使用排除法&#xff08;费时&#xff09;&#xff0c…

全栈物联网项目:结合 C/C++、Python、Node.js 和 React 开发智能温控系统(附代码示例)

1. 项目概述 本文详细介绍了一个基于STM32微控制器和AWS IoT云平台的智能温控器项目。该项目旨在实现远程温度监控和控制,具有以下主要特点: 使用STM32F103微控制器作为主控芯片,负责数据采集、处理和控制逻辑采用DHT22数字温湿度传感器,精确采集环境温湿度数据通过ESP8266 W…

Mac和VirtualBox Ubuntu共享文件夹

1、VirtualBox中点击设置->共享文件夹 2、设置共享文件夹路径和名称&#xff08;重点来了&#xff1a;共享文件夹名称&#xff09; 3、保存设置后重启虚拟机&#xff0c;执行下面的命令 sudo mkdir /mnt/share sudo mount -t vboxsf share /mnt/share/ 注&#xff1a;shar…

Leetcode-203-移除链表元素-临时变量作用域-c++

题目详见https://leetcode.cn/problems/remove-linked-list-elements/ 题解代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullpt…

你需要知道的笔记本养护的几个小知识

笔记本充电器可以长期不拔吗&#xff1f; 可以&#xff0c;但建议偶尔使用电池激发电池活性并且修正电量计精度&#xff1b; 当充电器接入后&#xff0c;笔记本会优先使用充电器进行供电而不会使用电池供电&#xff0c;同时充电器也会为缺电的电池补充电量。 现在笔记本电脑普遍…

大数据开发中的数据驱动决策:关键问题与实践指南

目录 决策前的准备工作1. 我已经掌握了哪些信息&#xff1f;2. 我们已经做出决定了吗&#xff1f;3. 我们需要哪些额外信息以及何时需要&#xff1f; 决策过程中的关键问题1. 我们需要做这个决定吗&#xff1f;2. 错误地做出这个决定的代价是什么&#xff1f; 决策后的反思1. 我…

《Python零基础入门》——关于PyCharm使用技巧及python基本概念

从本次文章开始&#xff0c;我们将学习一门新的编程语言——Python。作为最热门的编程语言&#xff0c;Python相对比较清晰、简单。 python主要的编译工具就是pycharm&#xff0c;关于pycharm的安装及python配置环境&#xff0c;大家可自行参考网络上的教程&#xff0c;本文不…

# Redis 入门到精通(一)数据类型(4)

Redis 入门到精通&#xff08;一&#xff09;数据类型&#xff08;4&#xff09; 一、redis 数据类型–sorted_set实现时效性任务管理 1、sorted_set 类型数据操作的注意事项 score 保存的数据存储空间是64位&#xff0c;如果是整数范围是-9007199254740992~9007199254740992…

QT creator与VS2019 QT加载模块方法

QT creator与VS2019加载模块方法 QT creator&#xff0c;pro文件添加 VS2019 QT

酷克数据亮相第13届PostgreSQL中国技术大会,获数据库杰出贡献奖

7 月 12 日&#xff0c;第 13 届 PostgreSQL 中国技术大会在杭州盛大开幕。本次大会以“聚焦云端创新&#xff0c;汇聚智慧共享”为主题&#xff0c;邀请了国内外 PG 领域众多行业大咖、学术精英及技术专家&#xff0c;共同探讨数据库领域的发展趋势、技术创新和实践经验。酷克…

[Vulnhub] Tr0ll3 aircrack-ng+lynx

信息收集 IP AddressOpening Ports192.168.101.147TCP:22 $ ssh start192.168.101.147 用户:Start startTr0ll3:~$ find / -type f -perm 0777 2>/tmp/1 startTr0ll3:~$ cat /var/log/.dist-manage/wytshadow.cap | nc 192.168.101.128 10035 WIFI握手包 aircrack-ng 破…

记一次 .NET某上位视觉程序 离奇崩溃分析

一&#xff1a;背景 1. 讲故事 前段时间有位朋友找到我&#xff0c;说他们有一个崩溃的dump让我帮忙看下怎么回事&#xff0c;确实有太多的人在网上找各种故障分析最后联系到了我&#xff0c;还好我一直都是免费分析&#xff0c;不收取任何费用&#xff0c;造福社区。 话不多…

[译] Rust标准库有些特殊,让我们改它

本篇是对 RustConf 2023中的The standard library is special. Let’s change that.这一视频的翻译与整理, 过程中为符合中文惯用表达有适当删改, 版权归原作者所有. 今天我将讨论Rust的标准库,更具体地说,是关于标准库有何特殊之处,以及为什么我们应该改变这一点。首先声明一下…

大模型高效参数微调技术

文章目录 一、Fine-Tuning&#xff1a;微调二、Prompt-Tuning&#xff1a;提示调优2.1 工作原理2.2 PET (Pattern-Exploiting Training)2.3 Prompt-Tuning集成2.4 模板构建方式 三、Prefix Tuning&#xff1a;连续提示模板3.1 提出动机3.2 工作原理 四、P-Tuning V1/V24.1 P-Tu…

MQTT——Mosquitto使用(Linux订阅者+Win发布者)

前提&#xff1a;WSL&#xff08;Ubuntu22&#xff09;作为订阅者&#xff0c;本机Win10作为发布者。 1、Linux安装Mosquitto 命令行安装。 sudo apt-get install mosquitto 以上默认只安装了mosquitto的服务&#xff0c;不带测试客户端工具mosquitto_sub和mosquitto_pub。如…

楼栋管理助你打造智慧校园寝室新时代

在聚焦于智慧校园寝室管理的楼栋管理功能上&#xff0c;核心目标是实现对宿舍楼本身的高效、精细化运营。这一功能围绕楼栋信息维护、空间优化、安全监管等方面展开&#xff0c;旨在为学生创造一个安全、舒适的生活环境&#xff0c;同时提升管理效率。 楼栋管理功能首先建立在全…

Run LoongArch64 Alpine VM on x86_64

一、Build from source(build on x86_64) Obtain the latest libvirt, virt-manager, and qemu source code, compile and install them. 1.1 Build libvirt from source sudo apt-get update sudo apt-get install augeas-tools bash-completion debhelper-compat dh-apparm…

防火墙NAT实验(接上一个用认证实验)

目录 一、拓扑图 二、实验需求 三、实验步骤 需求1&#xff1a;办公区设备可以通过电信链路和移动链路上网(多对多的NAT&#xff0c;并且需要保留一个公网IP不能用来转换) 策略1&#xff1a;电信链路&#xff0c;多对多NAT&#xff0c;保留IP地址 测试策略1 策略2&#x…

2024年上半年信息系统项目管理师——综合知识真题题目及答案(第1批次)(4)

2024年上半年信息系统项目管理师 ——综合知识真题题目及答案&#xff08;第1批次&#xff09;&#xff08;4&#xff09; 第61题&#xff1a;The project manager should use &#xff08;tool for the purpose to report on the work remaining for projects. A. cumulativ…

内容协商源码解析与自定义 MessageConverter

目录 内容协商 1、引入xml依赖 2、postman分别测试返回json和xml 3、开启浏览器参数方式内容协商功能 4、内容协商原理 5、自定义 MessageConverter 综上 内容协商 根据客户端接收能力不同&#xff0c;返回不同媒体类型的数据。 若客户端无法解析服务端返回的内容&#…