OpenCV中滑动条和鼠标事件响应操作的使用小结

前言

  既然在上一篇中提到了回调函数,Shaun 就干脆把 OpenCV 中较常使用的两个使用回调函数的函数使用方法也一并记录下来吧。

说明篇

OpenCV 中使用回调函数的两个函数为:

  1. 鼠标事件响应操作函数:void cv::setMouseCallback(const string& winname, MouseCallback onMouse, void* userdata = 0);

    参数浅解:

    const string& winname:窗口名称,对名为winname的窗口执行鼠标事件响应操作;

    MouseCallback onMouse:鼠标响应事件回调函数,监听鼠标的点击,移动,松开,判断鼠标的操作类型并做出相应处理;

    void* userdata:对应回调函数的可选参数,若使用全局变量可以忽略该参数。

    对应的回调函数声明为:typedef void (*MouseCallback)(int event, int x, int y, int flags, void* userdata);

    参数浅解:

    int event:鼠标滑动(CV_EVENT_MOUSEMOVE)、左键单击(CV_EVENT_LBUTTONDOWN)、右键单击(CV_EVENT_RBUTTONDOWN )等10种鼠标点击事件的int型代号;

    int x, int y:鼠标位于窗口的(x,y)坐标位置,窗口左上角默认为原点,向右为x正轴,向下为y正轴;

    int flags:鼠标左键拖拽(CV_EVENT_FLAG_LBUTTON)、右键拖拽(CV_EVENT_FLAG_RBUTTON)等6种鼠标拖拽事件的int型代号;

    void* userdata:回调函数的参数,若使用全局变量可以忽略该参数。

  2. 创建滑动条函数:int cv::createTrackbar(const string& trackbarname, const string& winname, int* value, int count, TrackbarCallback onChange=0, void* userdata=0);

    参数浅解:

    const string& trackbarname:创建的滑动条名称;

    const string& winname:所在窗口名称,对名为winname的窗口添加滑动条;

    int* value:滑块的位置,其初始值对应滑块的初始位置;

    int count:滑块可达到的最大位置的值,滑块最小位置的值总为0;

    TrackbarCallback onChange:滑动条事件回调函数,当滑动条上位置改变的时,则执行该回调函数;

    void* userdata:对应回调函数的可选参数,若使用全局变量可以忽略该参数。

    对应的回调函数声明为:typedef void (CV_CDECL *TrackbarCallback)(int pos, void* userdata);

    参数浅解:

    int pos:滑动条的位置对应的值;

    void* userdata:回调函数的参数,若使用全局变量可以忽略该参数。

※注:本文的函数说明采用的是 opencv-2.4.11 的函数声明,与 opencv-3.2.0 的函数声明区别在于 string 类型,opencv-3.2.0 采用的是其自己实现的一个 String 类。

实例篇

Show u the code,具体 C++ 实现代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <opencv2/opencv.hpp> 

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

// ---------------- 鼠标事件回调函数 ---------------------------------
static cv::Mat src_img; // 原始图像全局变量
static void mouseCallback(int event, int x, int y, int flags, void *)
{
bool selected = false;
static cv::Point left_top_vertex, right_down_vertex; // 左上角顶点和右下角顶点
// When the left mouse button is pressed, record its position and save it in corner1
if (event == CV_EVENT_LBUTTONDOWN) // 左键按下
{
left_top_vertex.x = x;
left_top_vertex.y = y;
std::cout << "Corner 1 recorded at " << left_top_vertex << std::endl;
}
// When the left mouse button is released, record its position and save it in corner2
if (event == cv::EVENT_LBUTTONUP) // 左键弹起
{
// Also check if user selection is bigger than 20 pixels (jut for fun!)
if (abs(x - left_top_vertex.x) > 10 && abs(y - left_top_vertex.y) > 10)
{
right_down_vertex.x = x;
right_down_vertex.y = y;
std::cout << "Corner 2 recorded at " << right_down_vertex << std::endl << std::endl;
selected = true;
}
else
{
std::cout << "Please select a bigger region" << std::endl;
}
}
// Update the box showing the selected region as the user drags the mouse
if (flags == CV_EVENT_FLAG_LBUTTON) // 左键拖拽
{
cv::Point pt;
pt.x = x;
pt.y = y;
cv::Mat local_img = src_img.clone();
rectangle(local_img, left_top_vertex, pt, cv::Scalar(0, 0, 255));
imshow("Cropping app", local_img);
}
// Define ROI and crop it out when both corners have been selected
if (selected)
{
cv::Rect box;
box.width = abs(left_top_vertex.x - right_down_vertex.x);
box.height = abs(left_top_vertex.y - right_down_vertex.y);
box.x = cv::min(left_top_vertex.x, right_down_vertex.x);
box.y = cv::min(left_top_vertex.y, right_down_vertex.y);
// Make an image out of just the selected ROI and display it in a new window
cv::Mat crop(src_img, box);
cv::namedWindow("Crop");
imshow("Crop", crop);
}
}

// ---------- 响应鼠标事件 ------------------------------------
void setMouseCallbackTest()
{
src_img = cv::imread("../data/lena.jpg", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
cv::namedWindow("Cropping app");
imshow("Cropping app", src_img);
// Set the mouse event callback function
cv::setMouseCallback("Cropping app", mouseCallback);

while (char(cv::waitKey(30)) != 'q') {}
}



// -------------- 滑动条回调函数 ------------------------
static void thresholdCallback(int slider_value, void* gray)
{
//static_cast<>用于安全转换指针
cv::Mat *tmp_gray = static_cast<cv::Mat *>(gray);
cv::Mat tmp = *tmp_gray;
cv::Mat dst;
threshold(tmp, dst, slider_value, 255, CV_THRESH_BINARY);

//显示效果图
cv::imshow("Trackbar Demo", dst);
}

// ------------ 创建滑动条 ----------------------------------
void createTrackbarTest()
{
cv::Mat src_gray = cv::imread("../data/lena.jpg", 0);

const int max_value = 255; //滑动条的最大值
int slider_value = 0; // 滑动条的初始值

char *window_name = "Trackbar Demo";
char *trackbar_name = "Value:";

// 创建一个窗口显示图片
cv::namedWindow(window_name, CV_WINDOW_AUTOSIZE);
imshow(window_name, src_gray);

// 创建滑动条来控制阈值
createTrackbar(trackbar_name, window_name, &slider_value, max_value, thresholdCallback, &src_gray);

while (char(cv::waitKey(30)) != 'q') {}
}

// ------- 将两个函数在同一个窗口执行 ------------
void callbackTest()
{
src_img = cv::imread("../data/lena.jpg", 0);
const int max_value = 255; //滑动条的最大值
int slider_value = 0; // 滑动条的初始值

char *window_name = "Callback Demo";
char *trackbar_name = "Value:";

// 创建一个窗口显示图片
cv::namedWindow(window_name, CV_WINDOW_AUTOSIZE);
imshow(window_name, src_img);

// 创建滑动条来控制阈值
createTrackbar(trackbar_name, window_name, &slider_value, max_value, thresholdCallback, &src_img);

// 鼠标事件响应
cv::setMouseCallback(window_name, mouseCallback);

while (char(cv::waitKey(30)) != 'q') {}
}


int main(int argc, char *argv[])
{
//setMouseCallbackTest();
//createTrackbarTest();
callbackTest();

while (char(cv::waitKey(30)) != 'q') {}

return 0;
}

  经 Shaun 测试,上面示例程序在 Win10 的 VS2013 中 opencv-2.4.11 和 opencv-3.2.0 下都能完美运行。

后记

  本来这两个函数都已经写(chao)好了,但为了更好的体现示例程序,又稍作了修改:添加鼠标左键拖拽事件及不使用全局变量等。

参考资料

[1] opencv2 使用鼠标绘制矩形并截取和保存矩形区域图像http://www.cnblogs.com/lidabo/category/516776.html

[2] Opencv中添加进度条及回调函数http://blog.csdn.net/weixin_35738542/article/category/6337413

[3] OpenCV2中滑动条(Trackbar)回调函数的小发现http://blog.csdn.net/u014291399/article/category/3097955

[4] OpenCV GUI基本操作,回调函数,进度条,裁剪图像等http://blog.csdn.net/wangyaninglm/article/category/1653815