开发一个小功能,要求在图上手动找出一些特殊点,并将配对的点用直线连起来。这个功能需要响应鼠标操作,OpenCV里通过一个中介函数配合一个鼠标回调函数来实现,这个中介函数为setMouseCallback,原型如下:
/** @brief Sets mouse handler for the specified window
@param winname Name of the window.
@param onMouse Callback function for mouse events. See OpenCV samples on how to specify and use the callback.
@param userdata The optional parameter passed to the callback.
*/
CV_EXPORTS void setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0);
第一个参数,const String &类型的winname,用户指定的窗口名称;
第二个参数MouseCallback类型的onMouse,为用户定义的回调函数,指定窗口里每次鼠标事件发生时,被调用的函数指针。
第三个参数为void*类型的userdata,用户定义的传递到回调函数中的参数,默认值为0。
回调函数形式为void onMouse(int event, int x, int y, int flags, void *param),函数名称可自定义。
其中,event为鼠标事件,可能的取值如下:
//! Mouse Events see cv::MouseCallback
enum MouseEventTypes {
EVENT_MOUSEMOVE = 0, //!< indicates that the mouse pointer has moved over the window.
EVENT_LBUTTONDOWN = 1, //!< indicates that the left mouse button is pressed.
EVENT_RBUTTONDOWN = 2, //!< indicates that the right mouse button is pressed.
EVENT_MBUTTONDOWN = 3, //!< indicates that the middle mouse button is pressed.
EVENT_LBUTTONUP = 4, //!< indicates that left mouse button is released.
EVENT_RBUTTONUP = 5, //!< indicates that right mouse button is released.
EVENT_MBUTTONUP = 6, //!< indicates that middle mouse button is released.
EVENT_LBUTTONDBLCLK = 7, //!< indicates that left mouse button is double clicked.
EVENT_RBUTTONDBLCLK = 8, //!< indicates that right mouse button is double clicked.
EVENT_MBUTTONDBLCLK = 9, //!< indicates that middle mouse button is double clicked.
EVENT_MOUSEWHEEL = 10,//!< positive and negative values mean forward and backward scrolling, respectively.
EVENT_MOUSEHWHEEL = 11 //!< positive and negative values mean right and left scrolling, respectively.
};
x,y为鼠标在图像坐标系中的坐标值,注意不是窗口坐标系;
flags是EVENT_FLAG的组合,可能的取值为,
//! Mouse Event Flags see cv::MouseCallback
enum MouseEventFlags {
EVENT_FLAG_LBUTTON = 1, //!< indicates that the left mouse button is down.
EVENT_FLAG_RBUTTON = 2, //!< indicates that the right mouse button is down.
EVENT_FLAG_MBUTTON = 4, //!< indicates that the middle mouse button is down.
EVENT_FLAG_CTRLKEY = 8, //!< indicates that CTRL Key is pressed.
EVENT_FLAG_SHIFTKEY = 16,//!< indicates that SHIFT Key is pressed.
EVENT_FLAG_ALTKEY = 32 //!< indicates that ALT Key is pressed.
};
param是用户定义的传递到SetMouseCallback中的参数。
写了一个简单的demo,参考如下:
/* Get mouse point */
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
#define max(a,b) ((a) > (b)) ? (a) : (b)
#define min(a,b) ((a) < (b)) ? (a) : (b)
bool g_b_start = false;
Point pt_start(-1,-1);
Point pt_end(-1,-1);
Point pt_curr(-1,-1);
/* Limit the value to a given range */
int bound(int a, int min_bound, int max_bound)
{
int tmp = max(a, min_bound);
int out = min(tmp, max_bound);
return out;
}
/* Define a callback function for mouse response */
void on_mouse(int event, int x, int y, int flags, void* userdata)
{
Mat &img = *(Mat *)userdata;
switch(event)
{
case EVENT_LBUTTONDOWN:
{
/* left button down, set the start flag */
if(!g_b_start)
{
g_b_start = true;
}
break;
}
case EVENT_LBUTTONUP:
{
if(g_b_start)
{
if(pt_start.x >= 0) /* pt_start exist*/
{
pt_end.x = bound(x, 0, img.cols - 1);
pt_end.y = bound(y, 0, img.rows - 1);
}
else /* pt_start not exist */
{
pt_start.x = bound(x, 0, img.cols - 1);
pt_start.y = bound(y, 0, img.rows - 1);
}
/* When there are pairs of start and end point, draw the line */
if((pt_start.x >= 0) && (pt_end.x >= 0))
{
line(img, pt_start, pt_end, Scalar(0,0,255), 2, CV_AA);
g_b_start = false;
/* Reset pt_start and pt_end */
pt_start.x = -1;
pt_start.y = -1;
pt_end.x = -1;
pt_end.y = -1;
}
}
break;
}
case EVENT_MOUSEMOVE:
{
/* Get current mouse point, just for displaying */
pt_curr.x = bound(x, 0, img.cols - 1);
pt_curr.y = bound(y, 0, img.rows - 1);
break;
}
case EVENT_RBUTTONDOWN:
{
/* Add some codes to erase the drawn lines */
break;
}
default:
{
break;
}
}
}
int main(int argc, char** argv)
{
const char* win_name = "mywindow";
Mat frame = imread("white.jpg");
Mat tmpImg;
namedWindow(win_name);
/* Set mouse response callback function */
setMouseCallback(win_name, on_mouse, (void*)&frame);
/* Dor displaying */
while(1)
{
frame.copyTo(tmpImg);
if(g_b_start)
{
if(pt_start.x >= 0)
{
/* Temporarily display for final line drawing */
if(pt_curr.x >= 0)
{
line(tmpImg, pt_start, pt_curr, Scalar(0,255,0), 2, CV_AA);
}
/* Display for final line drawing */
if(pt_end.x >= 0)
{
line(tmpImg, pt_start, pt_end, Scalar(0,0,255), 2, CV_AA);
}
}
}
imshow(win_name, tmpImg);
/* Wait for exit signal ESC */
if(waitKey(1) == 27)
{
break;
}
}
imwrite("out.bmp", frame);
return 0;
}
一个测试结果:
ps: 示例中OpenCV版本为3.4.7。