用鼠标绘制图形是很有用的,我们在后面的目标跟踪中会使用鼠标将需要跟踪的目标标注出来,之后对目标进行实时跟踪,所以今天我们来了解一下OpenCV中的一些基础知识—鼠标画笔。
首先需要了解一个函数:
cv2.setMouseCallback(windowName, MouseCallback , param=None)
其各个参数为:
windowName:窗口名称
MouseCallback:鼠标响应回调函数
param:响应函数传递的的参数
在这里,我们创建一个简单的应用程序,无论我们在哪里双击它,都可以在图像上绘制一个圆。
首先,我们创建一个鼠标回调函数,该函数在发生鼠标事件时执行。该函数原型为(此函数为我们自己所定义,OpenCV库中是没有此函数的,名称可以自己任意定义):
MouseCallback (int event, int x, int y, int flags, void userdata)
其各个部分参数为:
event:一个MouseEventTypes 常量
x:鼠标的x坐标
y:鼠标的y坐标
flags:一个MouseEventFlags常量
userdata:可选参数
鼠标事件可以是与鼠标相关的任何东西,例如左键按下,右键按下,左键双击等。它为我们提供了每个鼠标事件的坐标(x,y)。 通过此活动和地点,我们可以做任何我们喜欢的事情。 要列出所有可用的可用参数,我们在pycharm终端中运行以下代码:
view plaincopy to clipboardprint?
1.import cv2 as cv
2.
3.events = [i for i in dir(cv) if 'EVENT' in i]
4.print( events )
我们查看输出,会发现一大堆参数名称:
view plaincopy to clipboardprint?
1.['EVENT_FLAG_ALTKEY', 'EVENT_FLAG_CTRLKEY', 'EVENT_FLAG_LBUTTON', 'EVENT_FLAG_MBUTTON', 'EVENT_FLAG_RBUTTON', 'EVENT_FLAG_SHIFTKEY', 'EVENT_LBUTTONDBLCLK', 'EVENT_LBUTTONDOWN', 'EVENT_LBUTTONUP', 'EVENT_MBUTTONDBLCLK', 'EVENT_MBUTTONDOWN', 'EVENT_MBUTTONUP', 'EVENT_MOUSEHWHEEL', 'EVENT_MOUSEMOVE', 'EVENT_MOUSEWHEEL', 'EVENT_RBUTTONDBLCLK', 'EVENT_RBUTTONDOWN', 'EVENT_RBUTTONUP']
现在我们来看一下MouseCallback 函数中的event参数,其代表一个MouseEventFlags常量:
MouseEventFlags :
cv.EVENT_FLAG_LBUTTON = 1, 左键拖拽
cv.EVENT_FLAG_RBUTTON = 2, 右键拖拽
cv.EVENT_FLAG_MBUTTON = 4, 中键不放
cv.EVENT_FLAG_CTRLKEY = 8,按住ctrl不放
cv.EVENT_FLAG_SHIFTKEY = 16, 按住shift不放
cv.EVENT_FLAG_ALTKEY = 32 ,按住alt不放
继续看MouseCallback 函数中的flags参数,其代表一个MouseEventFlags常量:
cv.MouseEventTypes :
cv.EVENT_MOUSEMOVE = 0, 鼠标移动
cv.EVENT_LBUTTONDOWN = 1, 左键按下
cv.EVENT_RBUTTONDOWN = 2, 右键按下
cv.EVENT_MBUTTONDOWN = 3, 中键按下
cv.EVENT_LBUTTONUP = 4, 左键释放
cv.EVENT_RBUTTONUP = 5, 右键释放
cv.EVENT_MBUTTONUP = 6, 中键释放
cv.EVENT_LBUTTONDBLCLK = 7, 左键双击
cv.EVENT_RBUTTONDBLCLK = 8, 右键双击
cv.EVENT_MBUTTONDBLCLK = 9, 中健双击
cv.EVENT_MOUSEWHEEL = 10, 滚轮滑动
cv.EVENT_MOUSEHWHEEL = 11 横向滚轮滑动
接下来我们可以开始进行实验了,创建鼠标回调函数具有特定的格式,该格式在所有地方都是相同的。它仅在功能上有所不同。因此,现在我们用鼠标回调函数做一件事,在我们双击的地方绘制一个圆圈:
view plaincopy to clipboardprint?
1.import numpy as np
2.import cv2 as cv
3.
4.
5.def draw_circle(event, x, y, flags, param):
6. if event == cv.EVENT_LBUTTONDBLCLK:
7. cv.circle(img, (x, y), 100, (255, 0, 0), -1)
8.
9.
10.img = np.zeros((512, 512, 3), np.uint8)
11.cv.namedWindow('image')
12.cv.setMouseCallback('image', draw_circle)
13.while (1):
14. cv.imshow('image', img)
15. if cv.waitKey(20) & 0xFF == 27:
16. break
17.cv.destroyAllWindows()
这里面我们调用的参数是EVENT_LBUTTONDBLCLK,意思就是鼠标双击触发。我们双击任意一个区域,都会以此为圆心创建一个圆形出来:
鼠标画图形
现在我们需要进行更高级的操作, 在这种情况下,我们像在“画图”应用程序中一样,通过拖动鼠标来绘制矩形或圆形(取决于我们选择的模式)。 因此,我们的鼠标回调函数有两个部分,一个用于绘制矩形,另一个用于绘制圆形。这个将会非常有帮助,后期进行手动目标跟踪的时候,我们就会用鼠标自己手动标注这些图形,从而完成实时的目标跟踪。
现在我们来实现一个综合的例子,这个实例会帮助你理解图像交互的一些思想:
在图像上用鼠标画图,可以画圆或矩形,按q键在两种模式下切换。左键按下时开始画图,移动到哪儿画到哪儿,左键释放时结束画图。听上去很复杂,是吗?一步步分析下:
· 用鼠标画图:需要定义鼠标的回调函数mouse_event
· 画圆或矩形:需要定义一个画图的模式mode
· 左键单击、移动、释放:需要捕获三个不同的事件
· 开始画图,结束画图:需要定义一个画图的标记位drawing
好,开始代码:
```python
view plaincopy to clipboardprint?
1. import numpy as np
2. import cv2 as cv
3.
4. drawing = False # true if mouse is pressed
5. mode = True # if True, draw rectangle. Press 'm' to toggle to curve
6. ix, iy = -1, -1
7.
8. mouse callback function
9.
10.def draw_circle(event, x, y, flags, param):
11. global ix, iy, drawing, mode
12. if event == cv.EVENT_LBUTTONDOWN:
13. drawing = True
14. ix, iy = x, y
15. elif event == cv.EVENT_MOUSEMOVE:
16. if drawing:
17. if mode:
18. cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), -1)
19. else:
20. cv.circle(img, (x, y), 5, (0, 0, 255), -1)
21. elif event == cv.EVENT_LBUTTONUP:
22. drawing = False
23. if mode:
24. cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), -1)
25. else:
26. cv.circle(img, (x, y), 5, (0, 0, 255), -1)
27.
28.
29.img = np.zeros((512, 512, 3), np.uint8)
30.cv.namedWindow('image')
31.cv.setMouseCallback('image', draw_circle)
32.while 1:
33. cv.imshow('image', img)
34. k = cv.waitKey(1) & 0xFF
35. if k == ord('q'):
36. mode = not mode
37. elif k == 27:
38. break
39.cv.destroyAllWindows()
效果:
ok,搞定。
但是假如我们想创建一个未填充的矩形怎么办呢?
那么我们需要修改一下代码,在上一节中就已经讲过,画矩形函数以及画圆函数等等函数,它们的最后一个参数当为-1时,代表为实心,当大于0时,即为空心,假如我们定义为1,那么画出的图形的轮廓的粗细程度就为1,我们来看代码:
view plaincopy to clipboardprint?
1. import numpy as np
2. import cv2 as cv
3.
4. drawing = False # true if mouse is pressed
5. mode = True # if True, draw rectangle. Press 'm' to toggle to curve
6. ix, iy = -1, -1
7.
8.
9. # mouse callback function
10.def draw_circle(event, x, y, flags, param):
11.global ix, iy, drawing, mode
12.if event == cv.EVENT_LBUTTONDOWN:
13. drawing = True
14. ix, iy = x, y
15. elif event == cv.EVENT_MOUSEMOVE:
16. if drawing:
17. if mode:
18. tmp = img.copy()
19. cv.rectangle(tmp, (ix, iy), (x, y), (0, 255, 0), 1)
20. cv.imshow('image', tmp)
21. elif event == cv.EVENT_LBUTTONUP:
22. drawing = False
23. if mode:
24. cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), 1)
25.
26.
27.img = np.zeros((512, 512, 3), np.uint8)
28.cv.namedWindow('image')
29.cv.setMouseCallback('image', draw_circle)
30.cv.imshow("image",img)
31.while 1:
32. k = cv.waitKey(1) & 0xFF
33. if k == 27:
34. break
35.cv.destroyAllWindows()
我们看一下效果:
现在我们还可以修改一下代码,我们将EVENT_LBUTTONUP的参数语句执行的功能取消,这样的话,我们在画下一个矩形时上一个就会被自动清除:
view plaincopy to clipboardprint?
1. import numpy as np
2. import cv2 as cv
3.
4. drawing = False # true if mouse is pressed
5. mode = True # if True, draw rectangle. Press 'm' to toggle to curve
6. ix, iy = -1, -1
7.
8.
9. # mouse callback function
10.def draw_circle(event, x, y, flags, param):
11. global ix, iy, drawing, mode
12. if event == cv.EVENT_LBUTTONDOWN:
13. drawing = True
14. ix, iy = x, y
15. elif event == cv.EVENT_MOUSEMOVE:
16. if drawing:
17. if mode:
18. tmp = img.copy()
19. cv.rectangle(tmp, (ix, iy), (x, y), (0, 255, 0), 1)
20. cv.imshow('image', tmp)
21. elif event == cv.EVENT_LBUTTONUP:
22. drawing = False
23. # if mode:
24. # cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), 1)
25.
26.
27.img = np.zeros((512, 512, 3), np.uint8)
28.cv.namedWindow('image')
29.cv.setMouseCallback('image', draw_circle)
30.cv.imshow("image",img)
31.while 1:
32. k = cv.waitKey(1) & 0xFF
33. if k == 27:
34. break
35.cv.destroyAllWindows()
效果:
至此,OpenCV的鼠标绘图部分就算结束了,大家要想熟练掌握,还是自己多多练习。