OpenCV 4.8.0 setMouseCallback 实战:3种鼠标事件组合实现简易图像标注工具
OpenCV 4.8.0 setMouseCallback 实战3种鼠标事件组合实现简易图像标注工具在计算机视觉项目开发中图像标注是不可或缺的基础环节。虽然市面上有LabelImg等成熟标注工具但掌握自定义标注工具的开发能力能让我们根据特定需求灵活调整标注逻辑。本文将基于OpenCV 4.8.0的setMouseCallback函数构建一个支持多点、矩形和擦除功能的轻量级标注工具。1. 环境准备与基础概念1.1 安装OpenCV 4.8.0推荐使用conda创建独立环境conda create -n opencv-label python3.8 conda activate opencv-label pip install opencv-python4.8.0 numpy1.2 鼠标事件核心参数OpenCV的鼠标回调函数包含五个关键参数参数名类型描述eventint事件类型编码如左键按下1x, yint图像坐标系下的像素坐标flagsint组合状态标志如拖拽中1paramAny用户自定义数据常用事件类型速查表事件常量值触发条件EVENT_LBUTTONDOWN1左键按下EVENT_RBUTTONDOWN2右键按下EVENT_MBUTTONDOWN3中键按下EVENT_MOUSEMOVE0鼠标移动2. 标注工具架构设计2.1 功能逻辑分解我们的工具将实现三层交互逻辑绘制层实时显示当前标注状态事件层处理鼠标输入和键盘快捷键数据层存储标注坐标和类型信息import cv2 import numpy as np class ImageAnnotator: def __init__(self, image_path): self.img cv2.imread(image_path) self.clone self.img.copy() self.annotations [] self.current_action None self.start_coord None2.2 核心回调函数结构鼠标回调需要处理三种主要操作模式def mouse_callback(self, event, x, y, flags, param): if event cv2.EVENT_LBUTTONDOWN: self._handle_left_click(x, y) elif event cv2.EVENT_RBUTTONDOWN: self._handle_right_click(x, y) elif event cv2.EVENT_MBUTTONDOWN: self._handle_middle_click(x, y) elif event cv2.EVENT_MOUSEMOVE: self._handle_mouse_move(x, y, flags)3. 多模式标注实现3.1 左键点标注模式实现单击打点和拖拽连续打点两种方式def _handle_left_click(self, x, y): self.current_action point self.annotations.append((point, (x, y))) cv2.circle(self.clone, (x, y), 3, (0, 0, 255), -1) def _handle_mouse_move(self, x, y, flags): if flags cv2.EVENT_FLAG_LBUTTON: # 左键拖拽 self.annotations.append((point, (x, y))) cv2.circle(self.clone, (x, y), 2, (0, 255, 255), -1)3.2 右键矩形标注模式支持两种绘制方式点击确定对角点拖拽实时绘制def _handle_right_click(self, x, y): if not self.current_action: self.current_action rectangle self.start_coord (x, y) else: end_coord (x, y) self.annotations.append((rectangle, (self.start_coord, end_coord))) cv2.rectangle(self.clone, self.start_coord, end_coord, (255, 0, 0), 2) self.current_action None def _update_rectangle(self, x, y): temp_img self.clone.copy() cv2.rectangle(temp_img, self.start_coord, (x, y), (255, 0, 0), 1) return temp_img3.3 中键擦除功能实现采用邻近点检测算法实现智能擦除def _handle_middle_click(self, x, y): erase_radius 10 new_annotations [] for anno in self.annotations: if anno[0] point: dist np.sqrt((anno[1][0]-x)**2 (anno[1][1]-y)**2) if dist erase_radius: new_annotations.append(anno) else: # rectangle new_annotations.append(anno) self.annotations new_annotations self._redraw_annotations()4. 工程化增强功能4.1 标注持久化存储支持JSON格式的标注保存与加载def save_annotations(self, file_path): import json serializable [] for anno in self.annotations: if anno[0] point: serializable.append({type: point, coord: anno[1]}) else: serializable.append({ type: rectangle, coords: [anno[1][0], anno[1][1]] }) with open(file_path, w) as f: json.dump(serializable, f)4.2 可视化增强技巧添加状态提示和标注索引显示def _draw_status(self): status_bar np.zeros((30, self.clone.shape[1], 3), dtypenp.uint8) mode_text fMode: {self.current_action} if self.current_action else Ready cv2.putText(status_bar, mode_text, (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) count_text fAnnotations: {len(self.annotations)} cv2.putText(status_bar, count_text, (self.clone.shape[1]-150, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) return np.vstack([self.clone, status_bar])4.3 完整工具实现整合所有功能的完整类实现class ImageAnnotator: def __init__(self, image_path): self.img cv2.imread(image_path) self.clone self.img.copy() self.annotations [] self.current_action None self.start_coord None def run(self): cv2.namedWindow(Image Annotator) cv2.setMouseCallback(Image Annotator, self._mouse_callback) while True: display_img self._draw_status() cv2.imshow(Image Annotator, display_img) key cv2.waitKey(1) 0xFF if key ord(q): break elif key ord(s): self.save_annotations(annotations.json) print(Annotations saved!) cv2.destroyAllWindows() # 其他方法同上...在实际项目中这种自定义标注工具可以轻松扩展支持多边形标注、标签分类等高级功能。相比通用工具它的优势在于可以深度集成到特定业务流水线中比如直接对接模型训练接口或与数据增强流程联动。