问题描述:
设计一段程序完成的任务:根据我们选择的模式在拖动鼠标时绘制矩形或者是圆圈(就像画图程序中一样)。要求回调函数包含两部分,一部分画矩形,一部分画圆圈。
import cv2
import numpy as np
# 当鼠标按下时变为 True
drawing = False
# 如果 mode 为 true 绘制矩形。按下'm' 变成绘制曲线。
mode = True
ix, iy = -1, -1
img = np.zeros((512, 512, 3), np.uint8)
img_copy = img.copy()
# 创建回调函数
def draw_circle(event, x, y, flags, param):
global ix, iy, drawing, mode,img,img_copy
# 只单纯的按下鼠标:只返回位置坐标
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix, iy = x, y
print(x, y)
# 按下鼠标并且移动:表示绘制图形。event 可以查看移动,flag 查看是否按下
elif event == cv2.EVENT_MOUSEMOVE and flags == cv2.EVENT_FLAG_LBUTTON:
if drawing == True: # 鼠标左键被按下
if mode == True: # 画矩形
cv2.rectangle(img, (ix, iy), (x, y), (0, 255, 0), -1)
else: # 画圆
# 小圆点连在一起就成了线,3 代表了笔画的粗细
cv2.circle(img, (x, y), 30, (0, 0, 255), -1)
# 下面注释掉的代码是起始点为圆心,起点到终点为半径的
# 当鼠标松开停止绘画。
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
elif event == cv2.EVENT_LBUTTONDBLCLK: # 双击表示清除所画的图像
img = img_copy.copy()
print('double')
cv2.namedWindow('image')
cv2.setMouseCallback('image', draw_circle)
while (1):
cv2.imshow('image', img)
k = cv2.waitKey(1) & 0xFF
if k == ord('m'):
mode = not mode
elif k == 27:
break
逐行分析上述代码的逻辑结构。
问题解答:
这段代码的逻辑结构如下:
-
导入库:
import cv2 import numpy as np
导入 OpenCV 和 NumPy 库,前者用于图像处理,后者用于数组和矩阵操作。
-
初始化全局变量:
drawing = False # 标记是否正在绘图 mode = True # 默认为绘制矩形,按下 'm' 切换到绘制圆 ix, iy = -1, -1 # 初始坐标
drawing
:用于判断鼠标是否正在绘图。mode
:控制绘制形状的类型,默认为矩形。ix
和iy
:用于存储鼠标按下时的坐标。
-
创建图像:
img = np.zeros((512, 512, 3), np.uint8) # 创建一个全黑图像 img_copy = img.copy() # 复制图像,以便清空绘图时使用
-
定义鼠标回调函数:
def draw_circle(event, x, y, flags, param):
这个函数在鼠标事件发生时被调用,包含五个参数:事件类型、鼠标位置、标志和其他参数。
-
处理鼠标事件:
-
左键按下:
if event == cv2.EVENT_LBUTTONDOWN: drawing = True ix, iy = x, y print(x, y)
当鼠标左键按下时,设置
drawing
为True
,并记录起始坐标。 -
鼠标移动并按下左键:
elif event == cv2.EVENT_MOUSEMOVE and flags == cv2.EVENT_FLAG_LBUTTON: if drawing: if mode: cv2.rectangle(img, (ix, iy), (x, y), (0, 255, 0), -1) # 绘制矩形 else: cv2.circle(img, (x, y), 30, (0, 0, 255), -1) # 绘制圆
当鼠标移动且左键被按下时,根据当前模式绘制矩形或圆。
-
左键松开:
elif event == cv2.EVENT_LBUTTONUP: drawing = False
当鼠标左键松开时,设置
drawing
为False
,停止绘图。 -
双击左键:
elif event == cv2.EVENT_LBUTTONDBLCLK: img = img_copy.copy() # 清空图像 print('double')
双击左键时,图像恢复为初始状态(全黑)。
-
-
创建窗口并设置回调:
cv2.namedWindow('image') # 创建一个窗口 cv2.setMouseCallback('image', draw_circle) # 将鼠标事件与回调函数绑定
-
主循环:
while (1): cv2.imshow('image', img) # 显示当前图像 k = cv2.waitKey(1) & 0xFF # 等待键盘输入 if k == ord('m'): mode = not mode # 切换绘图模式 elif k == 27: break # 按下 ESC 键退出
- 不断显示更新的图像。
- 检查是否按下 'm' 键来切换绘图模式。
- 检查是否按下 ESC 键来退出程序。
总结
整体逻辑是通过鼠标事件在图像上绘制矩形或圆,使用全局变量跟踪绘图状态和坐标,回调函数处理鼠标事件,最后在主循环中不断更新显示。
############################################################
这段代码只点击鼠标左键,不拖动鼠标也会打印光标所在的位置吗?
是的,代码中的这段逻辑会在鼠标左键按下时打印光标的位置,即使你不拖动鼠标:
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix, iy = x, y
print(x, y)
这里的 EVENT_LBUTTONDOWN
事件会在鼠标左键按下时触发,因此每次按下左键时,都会打印出当前光标的位置 (x, y)
。如果你希望仅在拖动时打印位置,可以修改逻辑,将打印位置的语句放在鼠标移动的条件分支中。