OpenCV用鼠标获取坐标位置

这篇博客介绍如何利用OpenCV的setMouseCallback函数和鼠标回调函数实现在图像上手动选择点并连接成直线的功能。代码示例中详细解释了各个参数的作用,包括窗口名称、回调函数、鼠标事件类型等,并展示了从鼠标按下到释放过程中如何记录和绘制点的逻辑。最终,实现了在图像上动态显示和保存所画直线的效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开发一个小功能,要求在图上手动找出一些特殊点,并将配对的点用直线连起来。这个功能需要响应鼠标操作,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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值