【从零学习OpenCV 4】鼠标响应
重磅干货,第一时间送达
经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍《从零学习OpenCV 4》。为了更让小伙伴更早的了解最新版的OpenCV 4,小白与出版社沟通,提前在公众号上连载部分内容,请持续关注小白。
有时我们需要在图像中标记出重要的区域,这时通过鼠标可以很好的完成这项任务,因此OpenCV 4中也提供了鼠标响应相关函数setMouseCallback(),该函数的函数原型在代码清单3-56中给出。
代码清单3-56 setMouseCallback()函数原型
1. void cv::setMouseCallback(const String & winname,
2. MouseCallback onMouse,
3. void * userdata = 0
4. )
winname:添加鼠标响应的窗口的名字
onMouse:鼠标响应的回调函数。
userdata:传递给回调函数的可选参数。
该函数能够为指定的图像窗口创建鼠标响应。函数第一个参数是需要创建鼠标响应的图像窗口的名字。第二个参数为鼠标响应的回调函数,该函数在鼠标状态发生改变时被调用,是一个MouseCallback类型的函数。最后一个参数是传递给回调函数的可选参数,一般情况下使用默认值0即可。
接下来将介绍MouseCallback类型的回调函数,该类型函数的原型在代码清单3-57中给出。
代码清单3-57 MouseCallback类型原型
1. typedef void(* cv::MouseCallback)(int event,
2. int x,
3. int y,
4. int flags,
5. void *userdata
6. )
event:鼠标响应事件标志,参数为EVENT_*形式,具体可选参数及含义在表3-9给出。
x:鼠标指针在图像坐标系中的x坐标
y:鼠标指针在图像坐标系中的y坐标
flags:鼠标响应标志,参数为EVENT_FLAG_*形式,具体可选参数及含义在表3-10给出。
userdata:传递给回调函数的可选参数
MouseCallback类型的回调函数是一个无返回值的函数,函数名可以任意设置,有五个参数,在鼠标状态发生改变的时候被调用。函数第一个参数是鼠标响应事件标志,参数为EVENT_*形式,具体可选参数及含义在表3-9给出。第二个和第三个参数分别是鼠标当前位置在图像坐标系中的x坐标和y坐标。第四个参数是鼠标响应标志,参数为EVENT_FLAG_*形式,具体可选参数及含义在表3-10给出。最后一个参数是传递给回调函数的可选参数,一般情况下用void*缺省即可。
表3-9 MouseCallback类型回调函数鼠标响应事件标志可选参数及含义
标志参数 |
简记 |
含义 |
EVENT_MOUSEMOVE |
0 |
表示鼠标指针在窗口上移动 |
EVENT_LBUTTONDOWN |
1 |
表示按下鼠标左键 |
EVENT_RBUTTONDOWN |
2 |
表示按下鼠标右键 |
EVENT_MBUTTONDOWN |
3 |
表示按下鼠标中键 |
EVENT_LBUTTONUP |
4 |
表示释放鼠标左键 |
EVENT_RBUTTONUP |
5 |
表示释放鼠标右键 |
EVENT_MBUTTONUP |
6 |
表示释放鼠标中键 |
EVENT_LBUTTONDBLCLK |
7 |
表示双击鼠标左键 |
EVENT_RBUTTONDBLCLK |
8 |
表示双击鼠标右键 |
EVENT_MBUTTONDBLCLK |
9 |
表示双击鼠标中间 |
EVENT_MOUSEWHEEL |
10 |
正值表示向前滚动,负值表示向后滚动 |
EVENT_MOUSEHWHEEL |
11 |
正值表示向左滚动,负值表示向右滚动 |
表3-10 MouseCallback类型回调函数鼠标响应标志及含义
标志参数 |
简记 |
含义 |
EVENT_FLAG_LBUTTON |
1 |
按住左键拖拽 |
EVENT_FLAG_RBUTTON |
2 |
按住右键拖拽 |
EVENT_FLAG_MBUTTON |
4 |
按住中键拖拽 |
EVENT_FLAG_CTRLKEY |
8 |
按下CTRL键 |
EVENT_FLAG_SHIFTKEY |
16 |
按下SHIFT键 |
EVENT_FLAG_ALTKEY |
32 |
按下ALT键 |
鼠标响应简单来说就是当鼠标位于对应的图像窗口内时,时刻检测鼠标状态,当鼠标状态发生改变时调用回调函数,根据回调函数中的判断逻辑选择执行相应的操作。例如回调函数中只处理鼠标左键按下的事件,即判断event标志是否为EVENT_LBUTTONDOWN,只有当event==EVENT_LBUTTONDOWN时才有相应的逻辑操作,否则将不会执行任何操作。
为了了解鼠标响应的使用方法,在代码清单3-58中给出了绘制鼠标移动轨迹的示例程序。程序中如果鼠标右键被按下,则会提示“点击鼠标左键才可以绘制轨迹”,点击左键会输出当前鼠标的坐标,并将该点坐标定义为某段轨迹的起始位置。之后按住左键移动鼠标,会进入到第三个逻辑判断,绘制鼠标的移动轨迹。示例程序中提供了两种绘制轨迹的方法,第一种是每次调用回调函数获得鼠标位置时更改周围的图像像素值,这种方式比较直观,但是由于回调函数有一定的执行时间,因此当鼠标移动较快时绘制的图像轨迹会出现断点。第二种绘制轨迹的方式是在前一时刻和当前时刻鼠标位置间绘制直线,这种方式可以避免因鼠标移动过快而带来的轨迹出现断点的问题。程序运行结果在图3-35给出。
代码清单3-58 myMouse.cpp绘制鼠标移动轨迹
1. #include <opencv2/opencv.hpp>
2. #include <iostream>
3.
4. using namespace std;
5. using namespace cv;
6.
7. Mat img,imgPoint; //全局的图像
8. Point prePoint; //前一时刻鼠标的坐标,用于绘制直线
9. void mouse(int event, int x, int y, int flags, void*);
10.
11. int main()
12. {
13. img = imread("lena.png");
14. if (!img.data)
15. {
16. cout << "请确认输入图像名称是否正确!" << endl;
17. return -1;
18. }
19. img.copyTo(imgPoint);
20. imshow("图像窗口1", img);
21. imshow("图像窗口2", imgPoint);
22. setMouseCallback("图像窗口1", mouse,0 ); //鼠标影响
23. waitKey(0);
24. return 0;
25. }
26.
27. void mouse(int event, int x, int y, int flags, void*)
28. {
29. if (event == EVENT_RBUTTONDOWN) //单击右键
30. {
31. cout << "点击鼠标左键才可以绘制轨迹" << endl;
32. }
33. if (event == EVENT_LBUTTONDOWN) //单击左键,输出坐标
34. {
35. prePoint = Point(x, y);
36. cout << "轨迹起使坐标" << prePoint << endl;
37.
38. }
39. if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)) //鼠标按住左键移动
40. {
41. //通过改变图像像素显示鼠标移动轨迹
42. imgPoint.at<Vec3b>(y, x) = Vec3b(0, 0, 255);
43. imgPoint.at<Vec3b>(y, x-1) = Vec3b(0, 0, 255);
44. imgPoint.at<Vec3b>(y, x+1) = Vec3b(0, 0, 255);
45. imgPoint.at<Vec3b>(y+1, x) = Vec3b(0, 0, 255);
46. imgPoint.at<Vec3b>(y+1, x) = Vec3b(0, 0, 255);
47. imshow("图像窗口2", imgPoint);
48.
49. //通过绘制直线显示鼠标移动轨迹
50. Point pt(x, y);
51. line(img, prePoint, pt, Scalar(0, 0, 255), 2, 5, 0);
52. prePoint = pt;
53. imshow("图像窗口1", img);
54. }
55. }
图3-35 myMouse.cpp程序中绘制的鼠标移动轨迹