十七、鼠标操作与响应

项目功能实现:通过移动鼠标,在图片上实现类似画板绘制矩形的功能,并把绘制的矩形区域单独显示出来
按照之前的博文结构来,这里就不在赘述了

一、头文件

mouse.h

#pragma once#include<opencv2/opencv.hpp>using namespace cv;class MOUSE {
public:void mouse(Mat& image);
};#pragma once

二、函数实现

mouse.cpp

Point start_point(-1, -1);
Point end_point(-1, -1);
首先对起始和结束的位置进行初始化

Mat picture;
因为要在原图上进行操作,且每次绘制完矩形之后要重新初始化,故通过picture进行克隆原图像操作

setMouseCallback("Mouse_control", on_draw, (void*)(&image));
想要实现鼠标响应事件,需要调用setMouseCallback这个函数
参数一:显示窗口名称
参数二:响应函数
参数三:传入的个人参数数据信息,这里是传入了一张图片

响应函数on_draw的实现
Mat image = *((Mat*)userdata);
因为传入的是一张图片,故这里就接收一下

鼠标事件包含很多种,这里使用到的是按下、移动、抬起
OpenCV官网API传送门

鼠标按下事件:event == EVENT_LBUTTONDOWN)
按下的位置是矩形的起始位置,通过start_point.x = x; start_point.y = y;得到矩形的起始位置信息

鼠标抬起事件:event == EVENT_LBUTTONUP
抬起的位置是矩形的结束位置,通过end_point.x = x; end_point.y = y;得到矩形的结束位置信息
结束位置与起始位置的差就是矩形的宽和高,通过dx = abs(end_point.x - start_point.x); dy = abs(end_point.y - start_point.y);得到dx和dy就是矩形的宽和高信息,这里加绝对值是防止用户从下往上绘制矩形情况无法绘制

Rect box(start_point.x, start_point.y, dx, dy);
通过起始位置和宽高信息可以将矩形绘制出来

rectangle(image, box, Scalar(255, 0, 0), 2, LINE_AA, 0);
因为要在原图上进行操作,故通过rectangle函数在原图上进行绘制矩形
详细参数使用可参考博文:十四、图像几何形状绘制
参数一:原图
参数二:在原图上要绘制的区域
参数三:绘制的颜色
参数四:线段粗细,-1表示填充,其他正整数表示线条粗细程度,越大越粗
参数五:消除锯齿,因为像素点是浮点数,在连线的时候会出现锯齿,LINE_AA也可以用16代替,表示通过周围16个像素点信息进行消除锯齿
参数六:偏置项类似微调的作用,这里默认0即可

Rect box(start_point.x, start_point.y, dx, dy);
imshow("ROI", image(box));
通过起始点坐标信息以及宽高信息进行绘制ROI区域,得到对象box
然后显示ROI区域即可

鼠标移动事件:event == EVENT_MOUSEMOVE
因为每次绘制矩形的时候,前面的矩形都得消失,说白了就是初始化了,通过picture.copyTo(image);每次调用原始图像进行操作即可,克隆一下原始图像,进行存储,移动的过程中实时的进行刷新,每次都对原图进行操作
ectangle(image, box, Scalar(255, 0, 0), 2, LINE_AA, 0);
绘制矩形区域

#include"mouse.h"
#include<iostream>
#include<opencv2/opencv.hpp>Point start_point(-1, -1);
Point end_point(-1, -1);
Mat picture;static void on_draw(int event, int x, int y, int flags, void* userdata) {Mat image = *((Mat*)userdata);if (event == EVENT_LBUTTONDOWN) {//鼠标按下start_point.x = x;start_point.y = y;std::cout << "start point is: " << start_point << std::endl;}else if (event == EVENT_LBUTTONUP) {end_point.x = x;end_point.y = y;int dx, dy;dx = abs(end_point.x - start_point.x);dy = abs(end_point.y - start_point.y);if (dx > 0 && dy > 0) {//只有进行了移动,有距离差才算开始绘制矩形std::cout << "end point is: " << end_point << std::endl;Rect box(start_point.x, start_point.y, dx, dy);picture.copyTo(image);imshow("ROI", image(box));rectangle(image, box, Scalar(255, 0, 0), 2, LINE_AA, 0);imshow("Mouse_control", image);//重新初始化起点坐标,为下一次绘制做准备start_point.x = -1;start_point.y = -1;}}else if (event == EVENT_MOUSEMOVE) {if (start_point.x > 0 && start_point.y > 0) {//只有起始位置不为0才进行操作,否则操作就没啥意义end_point.x = x;end_point.y = y;int dx, dy;dx = abs(end_point.x - start_point.x);dy = abs(end_point.y - start_point.y);if (dx > 0 && dy > 0) {//起始位置和结束位置必须有距离Rect box(start_point.x, start_point.y, dx, dy);picture.copyTo(image);rectangle(image, box, Scalar(255, 0, 0), 2, LINE_AA, 0);imshow("Mouse_control", image);}}}
}void MOUSE::mouse(Mat& image) {namedWindow("Mouse_control", WINDOW_FREERATIO);setMouseCallback("Mouse_control", on_draw, (void*)(&image));imshow("Mouse_control", image);picture = image.clone();
}

三、主函数

yy_main.cpp

#include <opencv2/opencv.hpp>
#include <iostream>
#include "mouse.h"using namespace cv;
using namespace std;int main(int argc, char** argv) {Mat src = cv::imread("E:/C++_workspace/beyond.jpg", IMREAD_COLOR);if (src.empty()) {printf("load image is false...\n");return -1;}namedWindow("yanyu", WINDOW_FREERATIO);imshow("yanyu", src);MOUSE yy;yy.mouse(src);waitKey(0);destroyAllWindows();return 0;
}

项目结构如下:
在这里插入图片描述

效果图如下:
对图片进行移动鼠标绘制矩形,并单独显示该区域
在这里插入图片描述

四、课后作业

项目功能实现:通过移动鼠标,在图片上实现类似画板绘制的功能,并把绘制的圆形区域单独显示出来
mouse.cpp

核心小技巧为:
circle(mask, start_point, r, Scalar(255, 255, 255), -1);通过圆心和半径绘制填充的白色圆形区域
picture.copyTo(result, mask);利用copyTo函数的特性, 将picture所对应的mask中的非零区域拷贝到result中
imshow("ROI", result);显示即可

#include"mouse.h"
#include<iostream>
#include<opencv2/opencv.hpp>Point start_point(-1, -1);
Point end_point(-1, -1);
Mat picture;static void on_draw(int event, int x, int y, int flags, void* userdata) {Mat image = *((Mat*)userdata);Mat mask, result;mask = Mat::zeros(image.size(), CV_8UC3);if (event == EVENT_LBUTTONDOWN) {//鼠标按下start_point.x = x;start_point.y = y;std::cout << "start point is: " << start_point << std::endl;}else if (event == EVENT_LBUTTONUP) {//鼠标抬起end_point.x = x;end_point.y = y;int dx, dy;int r;dx = abs(end_point.x - start_point.x);dy = abs(end_point.y - start_point.y);r = sqrt(abs(pow(dx, 2)) + abs(pow(dy, 2)));if (dx > 0 && dy > 0) {std::cout << "end point is: " << end_point << std::endl;std::cout << "r is: " << r << std::endl;circle(image, start_point, r, Scalar(0, 255, 0), 1);imshow("Mouse_control", image);circle(mask, start_point, r, Scalar(255, 255, 255), -1);picture.copyTo(result, mask);imshow("ROI", result);//重新初始化起点坐标,为下一次绘制做准备start_point.x = -1;start_point.y = -1;}}else if (event == EVENT_MOUSEMOVE) {if (start_point.x > 0 && start_point.y > 0) {end_point.x = x;end_point.y = y;int dx, dy;double r;dx = abs(end_point.x - start_point.x);dy = abs(end_point.y - start_point.y);r = sqrt(pow(dx, 2) + pow(dy, 2));if (dx > 0 && dy > 0) {picture.copyTo(image);circle(image, start_point, r, Scalar(0, 255, 0), 2, 8, 0);imshow("Mouse_control", image);}}}
}void MOUSE::mouse(Mat& image) {namedWindow("Mouse_control", WINDOW_FREERATIO);setMouseCallback("Mouse_control", on_draw, (void*)(&image));imshow("Mouse_control", image);picture = image.clone();
}

效果图如下:
在这里插入图片描述

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

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

相关文章

C/C++的内存管理(1)

内存管理 C与C的内存分布C语言中动态内存管理方式回顾C内存管理的方式 C与C的内存分布 我们学习C语言时就知道&#xff0c;储存不同的变量计算机会相应分配不同区块的内存。那为什么要把内存化为不同的区域呢&#xff1f;实质上是为了方便管理 下面我们来看看下面一道例题&…

JAVAEE初阶 网络编程(十二)

HTTP协议 一. 状态码1.200 OK2. 404 NOT FOUND3.403 forbidden4. 405 Method Not Allowed5. 500 Interval Server Error6.504 Gateway Timeout7.302 Move temporarily 二. 如何构造HTTP请求1.通过第三方工具构造. 二.HTTPS三. 密码学中的重要概念1.明文2.密文3.密钥4.对称加密5…

普中51单片机学习(EEPROM)

EEPROM IIC串行总线的组成及工作原理 I2C总线的数据传送 数据位的有效性规定 I2C总线进行数据传送时&#xff0c;时钟信号为高电平期间&#xff0c;数据线上的数据必须保持稳定&#xff0c;只有在时钟线上的信号为低电平期间&#xff0c;数据线上的高电平或低电平状态才允许…

串的相关题目

于是他错误的点名开始了 我发现有关hash得题目有些是可以通过map数组来完成的&#xff1a;何为map数组&#xff0c;我们先思考一下最简单的桶的排序&#xff0c;桶排序是将我们需要数字最为下标输进数组中&#xff0c;而数组是存放的数字是这个数字出现的次数&#xff0c;但是由…

基于ORB-SLAM2与YOLOv8剔除动态特征点(三种方法)

基于ORB-SLAM2与YOLOv8剔除动态特征点(三种方法) 写上篇文章时测试过程比较乱&#xff0c;写的时候有些地方有点失误&#xff0c;所以重新写了这篇 本文内容均在RGB-D环境下进行程序测试 本文涉及到的动态特征点剔除速度均是以https://cvg.cit.tum.de/data/datasets/rgbd-dat…

Kubernetes 卷存储 NFS | nfs搭建配置 原理介绍 nfs作为存储卷使用

目录 1、NFS介绍2、NFS服务部署2.1安装nfs服务 (服务端配置)2.2启动NFS服务2.3 服务检查2.4 客户端配置 3、nfs作为存储卷使用3.1 nfs作为volume3.2 nfs存储的缺点3.3 nfs作为PersistentVolum 4、nfs作为动态存储提供5、总结 1、NFS介绍 NFS&#xff08;Network File System&a…

模糊的照片怎么变清晰?这些方法让你快速完成

在我们日常生活中&#xff0c;拍照已经成为记录生活点滴的重要方式。然而&#xff0c;有时候由于各种原因&#xff0c;拍摄出来的照片会显得模糊不清&#xff0c;这让我们感到十分苦恼。尤其是当我们希望珍藏那些对我们有着特殊意义的照片时&#xff0c;却因为清晰度不足而无法…

智慧公厕是什么?智慧公厕是构建智慧城市的环境卫生基石

随着城市化进程的不断加速&#xff0c;城市人口密度和流动性也逐渐增大&#xff0c;对城市公共设施的需求与日俱增。而在这些公共设施中&#xff0c;公厕作为城市基础设施中不可或缺的一环&#xff0c;对城市的环境卫生和市民生活质量起着举足轻重的作用。如何提高公厕的管理效…

思考:如何写出让同事难以维护的代码?

本文从【程序命名&注释】【数据类型&类&对象】【控制执行流程】和【程序/结构设计】四个方面梳理了一些真实案例&#xff0c;相信通过这些案例你能迅速get技能&#xff1a;如何写出让同事难以维护的代码doge。 比起什么程序员删库跑路&#xff0c;我更喜欢「写出让…

基于springboot+vue的电影评论网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

OPPO公布全新AI战略;苹果iPad新品细节曝光;周鸿祎称Sora工作原理像人做梦

OPPO 公布全新 AI 战略&#xff0c;AI 手机时代再提速 昨日&#xff0c;OPPO 举办 AI 战略发布会&#xff0c;分享新一代 AI 手机的四大能力特征&#xff1a; AI 手机首先要能够高效地利用计算资源&#xff0c;以满足 AI 时代下生成式 AI 的计算需要&#xff1b;AI 手机要能敏…

SwiftUI 集合视图(Grid)拖放交换 Cell 的极简实现

概览 自从 SwiftUI 横空出世那天起&#xff0c;小伙伴们都感受到了它惊人的简单与便捷。而在本课中&#xff0c;我们将会用一个小“栗子”更直观的让大家体验到它无与伦比简洁的描述性特质&#xff1a; 如上图所示&#xff0c;我们在 SwiftUI 中实现了 Grid 中拖放交换 Cell 的…