利用OpenCV窗口鼠标事件设置函数setMouseCallback()实现显示当前鼠标坐标截取指定区域图像等功能;顺便介绍函数putText()circle()rectangle()
Posted 昊虹算法
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用OpenCV窗口鼠标事件设置函数setMouseCallback()实现显示当前鼠标坐标截取指定区域图像等功能;顺便介绍函数putText()circle()rectangle()相关的知识,希望对你有一定的参考价值。
图像处理开发需求、图像处理接私活挣零花钱,请加微信/QQ 2487872782
图像处理开发资料、图像处理技术交流请加QQ群,群号 271891601
OpenCV的函数setMouseCallback()的功能是设置窗口的鼠标事件回调函数,它本质上是对窗口的设置。
函数setMouseCallback()的原型如下:
void cv::setMouseCallback(const String & winname,
MouseCallback onMouse,
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
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/imgproc/imgproc.hpp>
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
下面介绍上面代码中用到的OpenCV的圆形绘制函数circle()
函数circle()的原形如下:
void cv::circle ( InputOutputArray img,
Point center,
int radius,
const Scalar & color,
int thickness = 1,
int lineType = LINE_8,
int shift = 0
)
其参数意义如下:
img---在img上绘制圆形
center---圆心坐标
radius---圆半径
color---圆的颜色
thickness---圆的线条宽度,如果这个值为负,表示绘制的圆是实心圆。
lineType---圆的线条的样式,默认值是LINE_8(八连通直线),上面的代码中选择的是CV_AA(反走样直线),反走样直线的算法不单纯使用像素值绘图,因而显得平滑。
shift---中心坐标和半径值中的小数位数。这个参数让人很纳闷,既然center和radius的数据类型为int,那这个小数位数的设置有何意义??有知道答案的同学请在下面留言回复吧。有同学要问为什么你说center的数据类型为int。请看我的另一篇博文,链接https://blog.csdn.net/wenhao_ir/article/details/124312709
接下来,我们应用OpenCV窗口鼠标事件设置函数setMouseCallback()实现显示当前鼠标坐标、截取指定区域图像等功能。
代码如下:
代码中用到的图像的下载链接如下:
链接:https://pan.baidu.com/s/1GQrl1ATIdarzXm3EdLJRzg?pwd=avsa
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
//OpenCV版本:3.0
//VS版本:2012
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/imgproc/imgproc.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.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
这个视频有声音哦,有我的讲解,建议大家带上耳机观看哦。
上面的代码基本上每一句我都写了注释,所以也就不用对上面的代码多做解释了。
这里再说下上面的代码用到的为图形添加文本的函数putText()和绘制矩形的函数rectangle()
先说为图形添加文本的函数putText(),其原型如下:
void cv::putText( InputOutputArray img,
const String & text,
Point org,
int fontFace,
double fontScale,
Scalar color,
int thickness = 1,
int lineType = LINE_8,
bool bottomLeftOrigin = false
)
参数意义如下:
img---在img上添加文本
text---存储待添加文本的字符数组
org---文本添加在图像中的坐标
fontFace---文本的字体
fontScale---文本的大小
color---文本的颜色
thickness---文本线条的粗细,默认值为1
lineType---文本线条的类型,默认值为LINE_8
bottomLeftOrigin---如果这个值为true,则表示把图像的左下角视为图像的原点;如果值为False,则表示把图像的左上角视为图像的原点。
再说绘制矩形的函数rectangle(),其原型如下:
void cv::rectangle( InputOutputArray img,
Point pt1,
Point pt2,
const Scalar & color,
int thickness = 1,
int lineType = LINE_8,
int shift = 0
)
参数意义如下:
img---在img上绘制矩形
pt1---矩形的左上顶点坐标
pt2-矩形在右下顶点坐标
color---矩形的颜色
thickness---矩形的线宽。如果这个值为负,表示绘制的是实心矩形。
shift---坐标点的小数位数。这里同样存在和圆形绘制函数circle()中的参数shift类似的问题。坐标点的数据类型明明是整型,这里还设个小数位数干嘛?是为了将来向上兼容么?
这篇博文就先写到这里,感谢大家阅读。
图像处理开发需求、图像处理接私活挣零花钱,请加微信/QQ 2487872782
图像处理开发资料、图像处理技术交流请加QQ群,群号 271891601
以上是关于利用OpenCV窗口鼠标事件设置函数setMouseCallback()实现显示当前鼠标坐标截取指定区域图像等功能;顺便介绍函数putText()circle()rectangle()的主要内容,如果未能解决你的问题,请参考以下文章
OpenCV-Python实战(番外篇)——OpenCV中利用鼠标事件动态绘制图形