说明:本文提供的窗口鼠标事件设置的示例代码是C++的代码,如需Python的示例代码,请访问下面这个页面
https://www.hhai.cc/thread-129-1-1.html
OpenCV的函数setMouseCallback()的功能是设置窗口的鼠标事件回调函数,它本质上是对窗口的设置。
函数setMouseCallback()的原型如下:
void cv::setMouseCallback(const String &winname,
MouseCallbackonMouse,
void *userdata = 0)
其参数意义如下:
winname---要设置鼠标事件的窗口名。
onMouse---鼠标事件的回调函数名,当窗口产生鼠标事件时,调用的函数名。
userdata---如果用户想传一些自定义的数据给回调函数,可以放在userdata中。
鼠标事件的回调函数的原型是有固定格式的,回调函数的原型如下:
void onMouse(int event, int x, int y, int flags, void *userdata)
其参数意义如下:
event---其值代表鼠标基础事件,具体来说有以下基础事件:
enum
{
CV_EVENT_MOUSEMOVE =0, //鼠标移动
CV_EVENT_LBUTTONDOWN =1, //按下左键
CV_EVENT_RBUTTONDOWN =2, //按下右键
CV_EVENT_MBUTTONDOWN =3, //按下中键
CV_EVENT_LBUTTONUP =4, //放开左键
CV_EVENT_RBUTTONUP =5, //放开右键
CV_EVENT_MBUTTONUP =6, //放开中键
CV_EVENT_LBUTTONDBLCLK =7, //左键双击
CV_EVENT_RBUTTONDBLCLK =8, //右键双击
CV_EVENT_MBUTTONDBLCLK =9, //中键双击
CV_EVENT_MOUSEWHEEL =10, //滚轮滚动
CV_EVENT_MOUSEHWHEEL =11 //横向滚轮滚动
};
x---鼠标在x轴方向上的坐标值,窗口左上角为原点(0,0)
y---鼠标在y轴方向上的坐标值,窗口左上角为原点(0,0)
flags---flags的值代表鼠标拖拽事件和Ctrl、Shift、Alt按键事件的代号,具体如下:
enum
{
CV_EVENT_FLAG_LBUTTON =1, //左键拖拽
CV_EVENT_FLAG_RBUTTON =2, //右键拖拽
CV_EVENT_FLAG_MBUTTON =4, //中键拖拽
CV_EVENT_FLAG_CTRLKEY =8, //按住CTRL拖拽
CV_EVENT_FLAG_SHIFTKEY =16, //按住Shift拖拽
CV_EVENT_FLAG_ALTKEY =32 //按住ALT拖拽
};
提示:为什么不把事件全放在event中?因为分别放在event和flag中会产生组合事件的效果,有时候我们要进行组合事件判断哦。比如鼠标移动时,有可能是按住左键在拖曳,有可能并没有按住左键,只是单纯的移动,这个时候我们就要结合flag来判断。
userdata---用户传来的自定义数据。
那么窗口鼠标事件函数setMouseCallback()怎么用呢?
举个简单例子,大家就知道怎用了。
下面的例子实现当在窗口中点击鼠标左键时在原图上绘制一个绿色的实心圆。
原图是一张全蓝色的图,这张图的下载链接如下:
链接:https://pan.baidu.com/s/1_JQQS9qRos74GxtSU9-gqg?pwd=i87b
//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术
//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询
//OpenCV版本 OpenCV3.0
#include
#include
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()实现显示当前鼠标坐标、截取指定区域图像等功能。
代码如下:
代码中用到的图像的下载链接如下:
链接:https://pan.baidu.com/s/1GQrl1ATIdarzXm3EdLJRzg?pwd=avsa
//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术
//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询
//OpenCV版本 OpenCV3.0
#include
#include
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/1durs2nHwTtL21dGyrtl8Uw?pwd=n39c
这个视频有声音哦,有我的讲解,建议大家带上耳机观看哦。
上面的代码基本上每一句我都写了注释,所以也就不用对上面的代码多做解释了。
另外,上面的代码中用到了圆形绘制函数circle() 、文本绘制函数putText()、矩形绘制函数rectangle()
关于圆形绘制函数circle()的详细介绍,可参考页面 https://www.hhai.cc/thread-96-1-1.html
关于文本绘制函数putText()的详细介绍,可参考页面 https://www.hhai.cc/thread-97-1-1.html
关于矩形绘制函数rectangle()的详细介绍,可参考页面 https://www.hhai.cc/thread-98-1-1.html