OpenCV鼠标操作
在计算机视觉和图像处理的开发中,我们经常需要与图像窗口进行交互,例如选取目标区域、标注图像或者动态绘图。OpenCV 提供了基本的鼠标操作接口,使得开发者可以捕获鼠标点击、移动、拖拽等事件,从而实现丰富的交互功能。本文将介绍 OpenCV 鼠标操作的基本原理、如何设置鼠标事件回调函数以及结合示例代码.
鼠标常用场景
- 图像标注:通过鼠标选取区域进行目标标注或区域分割。
- 交互式绘图:例如绘制直线、曲线、矩形或多边形,用于数据标注和调试。
- 图像裁剪:实现用户自定义裁剪区域,快速获得感兴趣区域。
- 增强交互体验:开发交互式图像编辑工具或可视化应用时,通过鼠标操作实现实时反馈。
1. 鼠标事件简介
OpenCV 通过 cv::setMouseCallback 函数为窗口绑定鼠标事件回调函数,该函数会在鼠标发生特定事件时被自动调用。常见的鼠标事件包括:
EVENT_LBUTTONDOWN:鼠标左键按下EVENT_LBUTTONUP:鼠标左键释放EVENT_RBUTTONDOWN:鼠标右键按下EVENT_RBUTTONUP:鼠标右键释放EVENT_MBUTTONDOWN:鼠标中键按下EVENT_MOUSEMOVE:鼠标移动EVENT_LBUTTONDBLCLK:鼠标左键双击
每个事件都有不同的用途,例如在图像上拖拽绘制矩形、捕获点击位置或者进行目标追踪,都可以依靠这些事件来实现交互。
2. 回调函数实现
在设置鼠标回调函数时,我们需要定义一个特定格式的函数,该函数会在鼠标事件触发时被调用。函数原型如下:
void onMouse(int event, int x, int y, int flags, void* userdata);
- event:表示鼠标事件类型,如
EVENT_LBUTTONDOWN - x, y:鼠标事件发生时的图像坐标
- flags:表示鼠标事件的附加信息(如 Ctrl、Shift 键状态等)
- userdata:用户自定义数据,可以传递需要在回调中使用的其他信息
在回调函数中,我们可以根据事件类型实现不同的操作,例如在鼠标左键按下时记录起点,移动时动态显示绘图效果,左键释放时完成绘制等。
3. 示例代码:绘制矩形
以下示例代码演示了如何利用鼠标事件在图像窗口中动态绘制矩形。程序在鼠标左键按下时记录起始点,拖拽过程中显示动态矩形,左键释放后完成绘制。
#include "stdio.h"
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace cv;
using namespace std;
// 全局变量,用于存储绘制状态和起点坐标
bool drawing = false;
Point startPoint;
Mat image, tempImage;
/**
* \brief 鼠标回调.
*
* \param event:表示鼠标事件类型,如 EVENT_LBUTTONDOWN
* \param x:鼠标事件发生时的图像坐标
* \param y:鼠标事件发生时的图像坐标
* \param flags:表示鼠标事件的附加信息(如 Ctrl、Shift 键状态等)
* \param userdata:用户自定义数据,可以传递需要在回调中使用的其他信息
*/
void onMouse(int event, int x, int y, int flags, void* userdata)
{
if (event == EVENT_LBUTTONDOWN) {
drawing = true;
startPoint = Point(x, y);
tempImage = image.clone(); // 保存当前图像状态
}
else if (event == EVENT_MOUSEMOVE && drawing) {
// 拖拽时动态绘制矩形
image = tempImage.clone();
rectangle(image, startPoint, Point(x, y), Scalar(0, 255, 0), 2);
imshow("Mouse Drawing", image);
}
else if (event == EVENT_LBUTTONUP && drawing) {
drawing = false;
Point endPoint(x, y);
// 保证左上和右下坐标正确
int x_min = min(startPoint.x, endPoint.x);
int y_min = min(startPoint.y, endPoint.y);
int x_max = max(startPoint.x, endPoint.x);
int y_max = max(startPoint.y, endPoint.y);
int rect_width = x_max - x_min;
int rect_height = y_max - y_min;
// 绘制最终的矩形
rectangle(image, Point(x_min, y_min), Point(x_max, y_max), Scalar(0, 255, 0), 2);
// 生成矩形信息文字,例如:"x: 50 y: 50 w: 200 h: 150"
string info = "x:" + to_string(x_min) + " y:" + to_string(y_min) +
" w:" + to_string(rect_width) + " h:" + to_string(rect_height);
// 设置文字背景区域:在矩形下方显示
int margin = 5;
int textRectX = x_min;
int textRectY = y_max + margin;
int textRectWidth = rect_width;
int textRectHeight = 30; // 固定高度
// 若超出图像下边界,则调整位置
if (textRectY + textRectHeight > image.rows) {
textRectY = image.rows - textRectHeight;
}
// 绘制填充背景(黑色)
rectangle(image, Point(textRectX, textRectY),
Point(textRectX + textRectWidth, textRectY + textRectHeight),
Scalar(0, 0, 0), FILLED);
// 设置文字样式
int fontFace = FONT_HERSHEY_SIMPLEX;
double fontScale = 0.5;
int thickness = 1;
int baseline = 0;
Size textSize = getTextSize(info, fontFace, fontScale, thickness, &baseline);
// 计算文字在背景矩形中的居中位置
Point textOrg(textRectX + (textRectWidth - textSize.width) / 2,
textRectY + (textRectHeight + textSize.height) / 2);
// 在背景上绘制白色文字
putText(image, info, textOrg, fontFace, fontScale, Scalar(255, 255, 255), thickness);
imshow("Mouse Drawing", image);
}
}
int main()
{
image = imread("E:/image/lena.png");
namedWindow("Mouse Drawing", WINDOW_AUTOSIZE);
//设置鼠标操作回调函数
setMouseCallback("Mouse Drawing", onMouse, NULL);
imshow("Mouse Drawing", image);
cout << "请在图像窗口中按住鼠标左键拖拽绘制矩形..." << endl;
waitKey();
cv::destroyAllWindows();
return 0;
}
3.1代码解析
-
全局变量
drawing用于记录是否正在绘图。startPoint保存鼠标左键按下时的坐标。image为当前显示图像,tempImage用于在绘图过程中保存原图状态。
-
onMouse 回调函数
根据鼠标事件类型分别处理:
- 按下:开始绘图并记录起点。
- 移动:实时更新绘制效果,显示动态矩形。
- 释放:最终绘制矩形并结束操作。
-
主函数
创建窗口、绑定回调函数并显示初始图像,等待用户操作。
-
最终效果如下图:


957

被折叠的 条评论
为什么被折叠?



