一、学习目标
- 了解事件
- 编写一个简易绘画板
二、了解如何制作简易绘画板
2.1 了解鼠标多种事件
上一节我们简单的使用opencv的图形绘制方法,用鼠标绘制了一些内容。上一节所响应的是简单的双击事件EVENT_LBUTTONDBLCLK,在OpenCV的鼠标事件中还有很多。以下将列举出来:
- EVENT_MOUSEMOVE:鼠标滑动
- EVENT_LBUTTONDOWN:左键单击
- EVENT_RBUTTONDOWN:右键单击
- EVENT_MBUTTONDOWN:中间单击
- EVENT_LBUTTONUP:左键释放
- EVENT_RBUTTONUP:右键释放
- EVENT_MBUTTONUP:中键释放
- EVENT_LBUTTONDBLCLK:左键双击
- EVENT_RBUTTONDBLCLK:右键双击
- EVENT_MBUTTONDBLCLK:中键双击
以上事件在setMouseCallback函数回调后将会传到所执行的函数中,并且以event参数进行对应,取值通过event参数进行取值。
首先我们对一些事件进行监测,先尝试检测CV_EVENT_MOUSEMOVE鼠标滑动事件。代码如下:
import cv2
import numpy as np
def listing(event,x,y,flags,param):if event==cv2.EVENT_MOUSEMOVE:print('EVENT_MOUSEMOVE',' x:',x,' y:',y)
img=np.zeros((600,600,3),np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',listening)
while(1):cv2.imshow('image',img)if cv2.waitKey(20)&0xFF==27:break
cv2.destroyAllWindows()
以上代码使用listing作为回调后的处理,并且当事件为EVENT_MOUSEMOVE时将会输出事件名以及当前鼠标所在的x和y坐标的位置。
结果如下:
这时我们可以对所有的事件都进行监听,这时候修改listen函数就可以了:
def listening(event,x,y,flags,param):if event==cv2.EVENT_MOUSEMOVE:print('EVENT_MOUSEMOVE 鼠标滑动',' x:',x,' y:',y)elif event==cv2.EVENT_LBUTTONDOWN:print('EVENT_LBUTTONDOWN 左键单击',' x:',x,' y:',y)elif event==cv2.EVENT_RBUTTONDOWN:print('EVENT_RBUTTONDOWN 右键单击',' x:',x,' y:',y)elif event==cv2.EVENT_MBUTTONDOWN:print('EVENT_MBUTTONDOWN 中间单击',' x:',x,' y:',y)elif event==cv2.EVENT_LBUTTONUP:print('EVENT_LBUTTONUP 左键释放',' x:',x,' y:',y)elif event==cv2.EVENT_RBUTTONUP:print('EVENT_RBUTTONUP 右键释放',' x:',x,' y:',y)elif event==cv2.EVENT_MBUTTONUP:print('EVENT_MBUTTONUP 中键释放',' x:',x,' y:',y)elif event==cv2.EVENT_LBUTTONDBLCLK:print('EVENT_LBUTTONDBLCLK 左键双击',' x:',x,' y:',y)elif event==cv2.EVENT_RBUTTONDBLCLK:print('EVENT_RBUTTONDBLCLK 右键双击',' x:',x,' y:',y)elif event==cv2.EVENT_MBUTTONDBLCLK:print('EVENT_MBUTTONDBLCLK 中键双击',' x:',x,' y:',y)
结果如下:
2.2 制作一个简单的绘画板
首先我们对绘制一个拖拽绘制板功能做一个行为分析。我们正常进行拖拽画矩形,一般是按下左键,并且不放手,移动鼠标进行矩形的绘制,直到拖拽至我们觉得合适的位置后,我们开始松开鼠标。
在以上的绘制行为中,一共有几个鼠标事件。有按下鼠标左键事件EVENT_LBUTTONDOWN、鼠标移动事件EVENT_MOUSEMOVE、鼠标左键释放事件EVENT_LBUTTONUP。我们在按下鼠标左键的时候,从当前鼠标的x与y坐标开始绘制矩形,直到我们松开鼠标后停止绘制。
我们写一个函数作为回调的处理:
def draw(event,x,y,flags,param):
随后我们在鼠标按下左键的时候需要记住x与y的坐标位置,并且表示开始绘制,需要一个变量表示绘制状态开启:
if event==cv2.EVENT_LBUTTONDOWN:drawing=Truesx,sy=x,y
注意,由于回调函数每次循环时都会进行调用,若drawing此次为True后下一次不能直接进入绘制,这个时候需要把drawing、sx、sy都设置成全局变量:
drawing=False
sx,sy=0,0
并且在回调处理的函数中需要加入关键字进行声明是全局变量:
global sx,sy,drawing
接下来我们应该判断当前是否已经是按下鼠标左键并且进行了拖拽移动,这两个状态是并列的,所以写个elif语句进行判断:
elif event==cv2.EVENT_MOUSEMOVE and flags==cv2.EVENT_FLAG_LBUTTON:
这个时候在该判断中,使用if语句判断是否已经按下左键后开启了绘制,防止bug的出现,若已经开启了绘制则进行绘制矩形:
cv2.rectangle(img,(sx,sy),(x,y),(0,255,0),-1)
如上代码中为什么起始绘制点是sx与sy呢?那是因为我们按下了鼠标左键后的那个点是绘制起始点,从那个点开始绘制矩形到当前鼠标移动到的x和y坐标处,这样由于每次都覆盖掉原来的图像造成一种错觉,就是在拖拽进行绘制图像,并且进行填充,颜色为(0,255,0)。由于绘制状态不能一直开启,若直接进入了按下左脚与移动时由于保留了上次绘制的绘制开启,那么会造成初始绘制点的丢失,所以我们还需要判断当鼠标左键释放弹起后把绘制状态改为Fasle。代码如下:
elif event==cv2.EVENT_LBUTTONUP:drawing==False
所有完整的代码如下:
import cv2
import numpy as npdrawing=False
sx,sy=0,0def draw(event,x,y,flags,param):global sx,sy,drawingif event==cv2.EVENT_LBUTTONDOWN:drawing=Truesx,sy=x,yelif event==cv2.EVENT_MOUSEMOVE and flags==cv2.EVENT_FLAG_LBUTTON:if drawing==True:cv2.rectangle(img,(sx,sy),(x,y),(0,255,0),-1)elif event==cv2.EVENT_LBUTTONUP:drawing==False
img=np.zeros((600,600,3),np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw)
while(1):cv2.imshow('image',img)if cv2.waitKey(20)&0xFF==27:break
cv2.destroyAllWindows()
结果如下:
该系列首发于ebaina
三、总结
- 了解了多个鼠标事件
- 通过事件以及灵活运用绘图函数制作了一个简易的绘画板