如何用cv2.setMouseCallback提取摄像头图像区域?裁剪图像异常求助
问题修复方案
核心错误原因
- 鼠标拖动绘制矩形时,直接在目标图像上反复修改(反转像素+画框),导致图像残留大量拖动痕迹
- 裁剪ROI时使用了被修改的图像而非原始副本,最终保存的裁剪图带有叠加框
- 裁剪后保存的文件名与原始帧重复,覆盖了原始图像
修改后的代码
import cv2 # 打开摄像头 cap = cv2.VideoCapture(0) i = 0 training_images = [] # 初始化全局变量 drawing = False x_pt, y_pt = -1, -1 top_left_point, bottom_right_point = (-1, -1), (-1, -1) def draw_bounding_box(click, x, y, flag_param, parameters): global x_pt, y_pt, drawing, top_left_point, bottom_right_point, image, copy if click == cv2.EVENT_LBUTTONDOWN: drawing = True x_pt, y_pt = x, y top_left_point = (x_pt, y_pt) elif click == cv2.EVENT_MOUSEMOVE: if drawing: # 每次移动时,先从原始副本恢复图像,再绘制当前矩形 image = copy.copy() bottom_right_point = (x, y) cv2.rectangle(image, top_left_point, bottom_right_point, (0, 255, 0), 2) elif click == cv2.EVENT_LBUTTONUP: drawing = False bottom_right_point = (x, y) # 最终绘制一次矩形到显示图像 image = copy.copy() cv2.rectangle(image, top_left_point, bottom_right_point, (0, 255, 0), 2) # 计算ROI坐标,确保坐标合法(避免x/y顺序颠倒) x1, y1 = min(x_pt, x), min(y_pt, y) x2, y2 = max(x_pt, x), max(y_pt, y) # 从原始副本裁剪ROI,避免使用被修改的显示图像 if x2 > x1 and y2 > y1: cropped_im = copy[y1:y2, x1:x2] training_images.append(cropped_im) cv2.imshow("cropped", cropped_im) # 用不同文件名保存裁剪图,避免覆盖原始帧 cv2.imwrite(f'C:/Downloads/testingPictures/Frame{i}_cropped.jpg', cropped_im) cv2.waitKey(3000) cv2.destroyWindow("cropped") else: print("无效的ROI区域,请重新选择") if __name__ == '__main__': # 步骤1:从摄像头捕获并保存原始图像 while cap.isOpened() and i < 10: ret, frame = cap.read() if not ret: break cv2.imwrite(f'C:/Downloads/testingPictures/Frame{i}.jpg', frame) i += 1 cap.release() print(f"已保存{i}张原始图像") # 步骤2:逐个处理图像,选择ROI并裁剪 while i > 0: i -= 1 # 读取原始图像,创建副本 copy = cv2.imread(f'C:/Downloads/testingPictures/Frame{i}.jpg') if copy is None: print(f"无法读取Frame{i}.jpg") continue image = copy.copy() cv2.namedWindow('Frame') cv2.setMouseCallback('Frame', draw_bounding_box) # 等待用户操作,直到按下ESC或完成选择 while True: cv2.imshow('Frame', image) k = cv2.waitKey(1) & 0xFF if k == 27: # ESC键退出 cv2.destroyAllWindows() exit() # 当完成ROI选择后,等待用户按下任意键进入下一张 if not drawing and top_left_point != (-1, -1) and bottom_right_point != (-1, -1): cv2.waitKey(0) # 重置ROI坐标,准备处理下一张 top_left_point, bottom_right_point = (-1, -1), (-1, -1) break cv2.destroyWindow('Frame') print(f"已完成{len(training_images)}张ROI裁剪") cv2.destroyAllWindows()
关键改动说明
- 避免图像污染:鼠标拖动时,每次都从原始副本
copy重新复制到image再绘制矩形,彻底消除拖动痕迹的叠加 - 正确裁剪ROI:从原始图像副本
copy中裁剪区域,而非被修改的显示图像,确保裁剪结果是原始内容 - 合法坐标处理:添加了坐标顺序判断(取min和max),避免因拖动方向颠倒导致的无效裁剪
- 文件名区分:裁剪后的图像添加
_cropped后缀,避免覆盖原始捕获的帧 - 流程优化:修改了等待逻辑,确保用户完成ROI选择后再进入下一张图像,避免超时自动跳转
内容的提问来源于stack exchange,提问作者Shruti Chakraborty
相关产品推荐
相关产品推荐

