昊虹AI笔记网

 找回密码
 立即注册
搜索
查看: 1238|回复: 0
收起左侧

详解OpenCV的窗口鼠标事件设置函数setMouseCallback()和鼠标事件回调函数onMouse(),并利用它们实现显示鼠标坐标、截取图像区域等功能

[复制链接]

239

主题

241

帖子

931

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
931
昊虹君 发表于 2022-10-28 11:07 | 显示全部楼层 |阅读模式
详解OpenCV的窗口鼠标事件设置函数setMouseCallback()和鼠标事件回调函数onMouse(),并利用它们实现显示鼠标坐标、截取图像区域等功能

说明:本文提供的窗口鼠标事件设置的示例代码是C++的代码,如需Python的示例代码,请访问下面这个页面
https://www.hhai.cc/thread-129-1-1.html

OpenCV的函数setMouseCallback()的功能是设置窗口的鼠标事件回调函数,它本质上是对窗口的设置。

函数setMouseCallback()的原型如下:
  1. void cv::setMouseCallback(const String &winname,
  2.                           MouseCallbackonMouse,
  3.                           void *userdata = 0)
复制代码

其参数意义如下:
winname---要设置鼠标事件的窗口名。

onMouse---鼠标事件的回调函数名,当窗口产生鼠标事件时,调用的函数名。

userdata---如果用户想传一些自定义的数据给回调函数,可以放在userdata中。

鼠标事件的回调函数的原型是有固定格式的,回调函数的原型如下:
  1. void onMouse(int event, int x, int y, int flags, void *userdata)
复制代码

其参数意义如下:
event---其值代表鼠标基础事件序号,具体来说有以下基础事件:
  1. enum
  2. {
  3.     CV_EVENT_MOUSEMOVE      =0,   //鼠标移动
  4.     CV_EVENT_LBUTTONDOWN    =1,   //按下左键
  5.     CV_EVENT_RBUTTONDOWN    =2,   //按下右键
  6.     CV_EVENT_MBUTTONDOWN    =3,   //按下中键
  7.     CV_EVENT_LBUTTONUP      =4,   //放开左键
  8.     CV_EVENT_RBUTTONUP      =5,   //放开右键
  9.     CV_EVENT_MBUTTONUP      =6,   //放开中键
  10.     CV_EVENT_LBUTTONDBLCLK  =7,   //左键双击
  11.     CV_EVENT_RBUTTONDBLCLK  =8,   //右键双击
  12.     CV_EVENT_MBUTTONDBLCLK  =9,   //中键双击
  13.     CV_EVENT_MOUSEWHEEL     =10,  //滚轮滚动
  14.     CV_EVENT_MOUSEHWHEEL    =11   //横向滚轮滚动
  15. };
复制代码


x---鼠标在x轴方向上的坐标值,窗口左上角为原点(0,0)

y---鼠标在y轴方向上的坐标值,窗口左上角为原点(0,0)

flags---flags的值代表鼠标拖拽事件和Ctrl、Shift、Alt按键事件的代号,具体如下:
  1. enum
  2. {
  3.     CV_EVENT_FLAG_LBUTTON   =1,   //左键拖拽
  4.     CV_EVENT_FLAG_RBUTTON   =2,   //右键拖拽
  5.     CV_EVENT_FLAG_MBUTTON   =4,   //中键拖拽
  6.     CV_EVENT_FLAG_CTRLKEY   =8,   //按住CTRL拖拽
  7.     CV_EVENT_FLAG_SHIFTKEY  =16,  //按住Shift拖拽
  8.     CV_EVENT_FLAG_ALTKEY    =32   //按住ALT拖拽
  9. };
复制代码


提示:为什么不把事件全放在event中?
因为分别放在event和flag中会产生组合事件的效果,有时候我们要进行组合事件判断,比如鼠标移动时,有可能是按住左键在拖曳,有可能并没有按住左键(只是单纯的移动鼠标),这个时候我们就要结合flag来判断。

userdata---用户传来的自定义数据。

窗口鼠标事件函数setMouseCallback()具体怎么用?

举个简单例子,大家就知道怎用了。
下面的例子实现当在窗口中点击鼠标左键时在原图上绘制一个绿色的实心圆。
原图是一张全蓝色的图,这张图的下载链接如下:
链接:https://pan.baidu.com/s/1_JQQS9qRos74GxtSU9-gqg?pwd=i87b
代码如下:
[C++] 纯文本查看 复制代码
//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术

//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询

//OpenCV版本 OpenCV3.0

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;

Mat org;

void on_mouse(int event, int x, int y, int flags, void *ustc)//event鼠标基础事件代号;
//x,y代表这次鼠标事件的坐标;
//flags为鼠标拖拽事件和Ctrl、Shift、Alt按键事件的代号.
{
	if (event == CV_EVENT_LBUTTONDOWN)  //检测到鼠标左键按下时的响应,咱们这个例子中是画一个绿色的实心圆
	{
		Point center_point = Point(x, y);
		circle(org, center_point, 50, Scalar(0, 220, 0), CV_FILLED, CV_AA, 0);
		imshow("img", org);
	}
}


void main()
{
	org = imread("F:/material/images/P0039-all_blue.bmp");

	namedWindow("img");
	setMouseCallback("img", on_mouse, 0);//设置窗口鼠标事件回调函数

	imshow("img", org);
	cv::waitKey(0);
}

截图不方便体现上面代码的运行效果,所以我录制了视频,视频的百度网盘下载链接如下:
https://pan.baidu.com/s/1SCXxFWGYiidL2OkpRsPYQg?pwd=4k4f

上面的代码中用到了函数circle() ,关于函数circle() 的详细介绍,可参考链接 https://www.hhai.cc/thread-96-1-1.html

接下来,我们应用OpenCV窗口鼠标事件设置函数setMouseCallback()实现显示当前鼠标坐标、截取指定区域图像等功能。
[C++] 纯文本查看 复制代码
//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术

//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询

//OpenCV版本 OpenCV3.0

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;


Mat org, img, tmp;

void on_mouse(int event, int x, int y, int flags, void *ustc)//event鼠标基础事件代号;
//x,y代表这次鼠标事件的坐标;
//flags为鼠标拖拽事件和Ctrl、Shift、Alt按键事件的代号.
{
	static Point pre_pt = (-1, -1);//初始坐标,注意这里应该设为static类型,这是因为下一次调用要用到上一次调用的pre_pt值  
	static Point cur_pt = (-1, -1);//实时坐标,注意这里应该设为static类型,这是因为下一次调用要用到上一次调用的cur_pt值    
	char temp[16];
	if (event == CV_EVENT_LBUTTONDOWN)//左键按下,显示按下点的坐标,并在按下点绘制一个实心圆  
	{
		org.copyTo(img);
		sprintf(temp, "(%d,%d)", x, y);//把要显示的格式化的字符串放到字符串数组temp中
		pre_pt = Point(x, y);//此时pre_pt中存储的是鼠标左键按下时的坐标
		putText(img, temp, pre_pt, FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 0, 220), 1, 8);//把表示鼠标左键按下时坐标的文字写到图像img上
		circle(img, pre_pt, 2, Scalar(0, 0, 0), CV_FILLED, CV_AA, 0);//在按下点绘制一个一个小的实心圆(相当于一个实心点) 
		imshow("img", img);
	}
	else if (event == CV_EVENT_MOUSEMOVE && !(flags & CV_EVENT_FLAG_LBUTTON))//鼠标移动时(没有进行左键拖曳的移动)显示鼠标所在位置的坐标
	{
		img.copyTo(tmp);
		sprintf(temp, "(%d,%d)", x, y);//把要显示的格式化的字符串放到字符串数组temp中
		cur_pt = Point(x, y);//把当前鼠标坐标存放于cur_pt中
		putText(tmp, temp, cur_pt, FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 0, 220));// 将表示鼠标当前坐标的文字写到图像tmp上
		imshow("img", tmp);
	}
	else if (event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON))//鼠标左键拖曳时,在图像上绘制矩形并显示当前鼠标的坐标。  
		//这里要注意:左键拖曳操作必然有个点击鼠标左键的事件在先,
		//但是点击鼠标左键的事件并不包含于鼠标左键拖曳事件中。
		//即执行到这个条件分支时,必然是执行了这个代码中的第一个条件分支了的。
	{
		img.copyTo(tmp);
		sprintf(temp, "(%d,%d)", x, y);//把要显示的格式化的字符串放到字符串数组temp中
		cur_pt = Point(x, y);//把当前鼠标坐标存放于cur_pt中
		putText(tmp, temp, cur_pt, FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 0, 220));;// 将表示鼠标当前坐标的文字写到图像tmp上
		rectangle(tmp, pre_pt, cur_pt, Scalar(0, 255, 0), 1, 8, 0);//在图像tmp上实时绘制鼠标左键拖动时形成的矩形  
		imshow("img", tmp);
	}
	else if (event == CV_EVENT_LBUTTONUP)//左键拖曳释放后在图像上绘制矩形并截取矩形区域的图像显示到新的窗口中  
	{
		sprintf(temp, "(%d,%d)", x, y);;//把要显示的格式化的字符串放到字符串数组temp中
		cur_pt = Point(x, y); //把当前鼠标坐标存放于cur_pt中
		putText(img, temp, cur_pt, FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 0, 220));// 将表示鼠标当前坐标的文字写到图像img上
		circle(img, pre_pt, 2, Scalar(0, 0, 0), CV_FILLED, CV_AA, 0);//在鼠标释放点绘制一个小的实心圆(相当于一个实心点)
		rectangle(img, pre_pt, cur_pt, Scalar(0, 255, 0), 1, 8, 0);//根据初始点和结束点,将矩形画到img上  
		imshow("img", img);
		img.copyTo(tmp);
		//截取矩形包围的图像,并保存到dst中  
		int width = abs(pre_pt.x - cur_pt.x);
		int height = abs(pre_pt.y - cur_pt.y);
		if (width == 0 || height == 0)//这里我们要排除掉只点一下鼠标左键而没有进行拖曳操作的情况
		{
			return;
		}
		Mat dst = org(Rect(min(cur_pt.x, pre_pt.x), min(cur_pt.y, pre_pt.y), width, height));

		destroyWindow("dst");//这一句代码的作用是避免上一次截取图像的显示对这一次截取图像的显示的影响
		namedWindow("dst");
		imshow("dst", dst);
		waitKey(0);
	}
}
void main()
{
	org = imread("F:/material/images/P0005-BaoXiaofeng-face.jpg");
	org.copyTo(img);
	org.copyTo(tmp);
	namedWindow("img");
	setMouseCallback("img", on_mouse, 0);//设置窗口鼠标事件回调函数
	imshow("img", img);
	cv::waitKey(0);
}

代码中用到的图像的下载链接如下:
https://pan.baidu.com/s/1GQrl1ATIdarzXm3EdLJRzg?pwd=avsa

代码运行结果截图不好体现,所以我录了个视频来向大家展示运行结果:
视频的百度网盘下载链接:https://pan.baidu.com/s/1durs2nHwTtL21dGyrtl8Uw?pwd=n39c&#160;
这个视频有声音哦,有我的讲解,建议大家带上耳机观看哦。

上面的代码基本上每一句我都写了注释,所以也就不用对上面的代码多做解释了。

上面的代码中用到了圆形绘制函数circle() 、文本绘制函数putText()、矩形绘制函数rectangle()。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|昊虹AI笔记网 ( 蜀ICP备2024076726 )

GMT+8, 2024-5-18 12:41 , Processed in 0.026906 second(s), 23 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表