# 一、鼠标事件的响应
当鼠标在窗口上进行操作(如点击、移动、滚轮滚动)时,会触发一个鼠标事件。OpenCV允许我们通过设置一个回调函数来响应这些事件。
# 1. 常见鼠标事件
事件常量 (event) | 描述 |
---|---|
EVENT_MOUSEMOVE |
鼠标移动 |
EVENT_LBUTTONDOWN |
左键按下 |
EVENT_RBUTTONDOWN |
右键按下 |
EVENT_MBUTTONDOWN |
中键按下 |
EVENT_LBUTTONUP |
左键松开 |
EVENT_RBUTTONUP |
右键松开 |
EVENT_MBUTTONUP |
中键松开 |
EVENT_LBUTTONDBLCLK |
左键双击 |
EVENT_RBUTTONDBLCLK |
右键双击 |
EVENT_MBUTTONDBLCLK |
中键双击 |
EVENT_MOUSEWHEEL |
滚轮向前滚动 |
EVENT_MOUSEHWHEEL |
滚轮向后滚动 |
这些事件通常与标志位(flags)结合使用,以判断组合键状态。
标志常量 (flags) | 描述 |
---|---|
EVENT_FLAG_LBUTTON |
左键被按下 |
EVENT_FLAG_RBUTTON |
右键被按下 |
EVENT_FLAG_MBUTTON |
中键被按下 |
EVENT_FLAG_CTRLKEY |
Ctrl 键被按下 |
EVENT_FLAG_SHIFTKEY |
Shift 键被按下 |
EVENT_FLAG_ALTKEY |
Alt 键被按下 |
# 2. 设置回调函数
通过 cv::setMouseCallback
函数为指定窗口注册一个回调函数。当该窗口发生鼠标事件时,注册的函数将被自动调用。
// 注册鼠标事件回调函数
// 第一个参数是窗口名称
// 第二个参数是回调函数的指针
// 第三个参数是传递给回调函数的用户自定义数据指针(void*),默认为0
cv::setMouseCallback(windows_name, on_mouse, &image);
# 3. 回调函数实现
下面是一个回调函数的示例,它实现在图像上响应鼠标拖拽并画出红色的线条。
/**
* @brief 鼠标事件回调函数,event和flags共同决定了鼠标具体是什么操作。
* @param event 鼠标事件类型 (如 EVENT_LBUTTONDOWN)
* @param x 鼠标在窗口中的x坐标
* @param y 鼠标在窗口中的y坐标
* @param flags 鼠标事件标志 (如 EVENT_FLAG_CTRLKEY)
* @param userdata 用户自定义数据指针
*/
void on_mouse(int event, int x, int y, int flags, void *userdata) {
using namespace cv;
static Point per_point;
static Point cur_point;
// 将void*类型的userdata转换为cv::Mat类型的引用
Mat& img = *static_cast<Mat*>(userdata);
if (event == EVENT_LBUTTONDOWN) { // 左键被按下
per_point = {x, y}; // 记录按下时的坐标
} else if (event == EVENT_MOUSEMOVE && flags == EVENT_FLAG_LBUTTON) { // 鼠标移动且左键被按下(拖拽)
cur_point = {x, y};
// 在图像上画线
// Scalar(0, 0, 255) 代表 BGR 红色; thickness 表示线的粗细
line(img, per_point, cur_point, Scalar(0, 0, 255), 4);
// 刷新显示
imshow(windows_name, img);
// 更新上一个点的位置为当前点
per_point = cur_point;
}
}
# 二、键盘事件的响应
键盘事件主要通过 cv::waitKey()
函数捕获。该函数在指定的毫秒数内等待按键事件。
cv::waitKey(delay)
:delay > 0
: 等待delay
毫秒。如果在等待期间有按键按下,函数返回该按键的ASCII码,否则返回 -1。delay <= 0
: 无限期等待,直到有按键按下,然后返回按键的ASCII码。
下面的代码实现了播放视频的功能,当按下 ESC
键时视频暂停(循环结束),再按任意键则程序终止。
#include <iostream>
#include <opencv2/opencv.hpp>
// 假设视频路径已定义
const std::string video_path = "your_video_file.mp4";
int main() {
cv::VideoCapture cap(video_path);
if (!cap.isOpened()) {
std::cerr << "Error: Could not open video file." << std::endl;
return -1;
}
cv::namedWindow("show video", cv::WINDOW_NORMAL);
cv::resizeWindow("show video", 800, 600);
cv::Mat frame; // 视频帧
while (true) {
cap >> frame;
if (frame.empty()) { // 如果视频播放完毕,退出循环
break;
}
imshow("show video", frame);
// 等待16ms,如果期间有按键,则捕获其ASCII码
// 27 是 ESC 键对应的ASCII码
if (cv::waitKey(16) == 27) {
break; // 如果按下ESC键,退出循环,视频暂停
}
}
// 循环结束后,无限期等待下一次按键,然后程序结束
cv::waitKey(0);
return 0;
}