学习OpenCV 06 鼠标事件

学习OpenCV 06 鼠标事件

与键盘事件不同,鼠标事件是通过更加传统的回调(callback)函数机制完成的,即我们必须先写好一个回调程序,使OpenCV在发送任何鼠标事件时都可以调用这个回调程序。当我们完成回调程序后,需要在OpenCV中注册(register)这个函数,即告诉OpenCV这是一个正常的回调程序,用户使用鼠标在特定窗口完成一些操作后可以得到一定的响应。

回调callback

回调其实就是任何以一个参数集(the correct set of arguments)并返回一个正确的类型(the correct type)的函数。需要明确告诉程序使用的回调函数发生的事件类型和发生地点。若用户按下鼠标同时还按下Shift键或Alt键,也需明确告诉回调函数。指向这样的函数的指针是cv::MouseCallback。以下是定义回调函数必须匹配的接口协议:

void your_mouse_callback( 
    int    event,    //事件类型
    int    x,        //鼠标事件的X坐标
    int    y,        //鼠标事件的Y坐标
    int    flags,    //事件的更多细节
    void*  param,    //cv::setMouseCallback()里的参数
)

其中,第一个参数会取下表的一个值

鼠标事件类型
事件 数值
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

 

 

 

 

 

 

 

 

 

 

 

 

第二和第三个参数将被设置为鼠标事件的X坐标和Y坐标。!!!需要注意:这里的坐标是图像上的像素坐标,独立于窗口其他细节的。

第四个参数为标志值,是一个bit位,每个bit位都代表在事件发生时的不同条件。如cv::EVENT_FLAG_SHIFTKEYD的数值是16(即第四个bit,也就是10000即1<<4[1左移4位]);因此,我们若想测试Shift键是否按下,可以计算按位与(AND)flags&cv:: EVENT_FLAG_SHIFTKEYD。下表为完整的标志列表

鼠标事件标志
标志 数值
cv::EVENT_FLAG_LBUTTON 1
cv::EVENT_FLAG_RBUTTON 2
cv::EVENT_FLAG_MBUTTON 4
cv::EVENT_FLAG_CTRLKEY 8
cv::EVENT_FLAG_SHIFTKEY 16
cv::EVENT_FLAG_ALTKEY 32

 

 

 

 

 

 

 

 

最后一个参数是一个void类型指针,基于这种类型指针,OpenCV可以用来传递额外信息到任何类型的结构。

注册回调函数

接下来,就需要一个函数来注册这个回调函数,即cv::setMouseCallback(),它需要三个参数

void cv::setMouseCallback(
    const string&        windowName,    //标志窗口的句柄
    cv::MouseCallbacn    on_mouse,      //回调函数
    void*                param=NULL     //回调函数的附加参数
)

第一个参数是回调函数作用的窗口名称,即只有事件在这个特定窗口中时才能出发回调。 

第二个参数是要注册的回调函数。

第三个即刚才提到的回调函数接口协议的定义里面的param,可以让我们在回调函数执行时给回调函数传递特定的信息。

实例:使用鼠标回调完成绘制方框的小程序

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

using namespace std;
using namespace cv;

void my_mouse_callback(int event, int x, int y, int flags, void* param);//回调函数定义

Rect box;
bool drawing_box = false;

void draw_box(Mat &img, Rect box) {
	rectangle(img, box.tl(), box.br(), Scalar(0, 0, 255));//tl()提取左上角,br()提取右下角,Scalar(0,0,255)为红色。即在img上绘图一个红色的从左上角到右下角的矩形
}

void help() {
	cout << "show how to use a mouse to draw regions in an image." << endl;
}

int main(int argc, char**argv) {

	help();
	box = Rect(-1, -1, 0, 0);//Rect(x,y,w,h),(x,y)为左上角,第3、4参数为width和height
	Mat image(200, 200, CV_8UC3), temp;
	image.copyTo(temp);

	box = Rect(-1, -1, 0, 0);
	image = Scalar::all(0);

	namedWindow("Box Example");

	setMouseCallback("Box Example", my_mouse_callback, (void*)&image);

	for (;;) {

		image.copyTo(temp);
		if (drawing_box)	draw_box(temp, box);
		imshow("Box Example", temp);

		if (waitKey(15)==27) break;
	}

	return 0;
}

void my_mouse_callback(int event, int x, int y, int flags, void* param) {

	Mat& image = *(Mat *)param;

	switch (event)
	{
		case EVENT_MOUSEMOVE: {
			if (drawing_box) {
				box.width = x - box.x;
				box.height = y - box.y;
			}
		}
								break;

		case EVENT_LBUTTONDOWN: {
			drawing_box = true;
			box = Rect(x, y, 0, 0);
		}
								break;

		case EVENT_LBUTTONUP: {
			drawing_box = false;
			if (box.width < 0) {
				box.x += box.width;
				box.width *= -1;
			}
			if (box.height < 0) {
				box.y += box.height;
				box.height *= -1;
			}
			draw_box(image, box);
		}
							  break;
	}
}

程序结果: 

 学习OpenCV 06 鼠标事件

关于cv::waitKey()函数的说明

waitKey()除了设定自动返回之前等待按键的事件量(ms为单位)外,还有个功能是让任何OpenCV窗口进行更新。这意味着如果不调用waitKey(),图像可能永远不会在窗口中绘制,或者窗口在移动、调整大小、从覆盖中显示出来等时回很奇怪甚至严重异常。