番外4. Python OpenCV 中鼠标事件相关处理与常见问题解决方案
【摘要】 本系列专栏写作方式本系列专栏写作将采用首创的问答式写作形式,快速让你学习到 OpenCV 的初级、中级、高级知识。 4. Python OpenCV 中鼠标事件相关处理与常见问题解决方案本篇博客主要分析 cv2.setMouseCallback 函数,以及该函数在日常编码中出现问题是如何进行解决。本函数主要是 OpenCV 中用来处理鼠标相关事件的函数,通过它可以捕获到数据触发的事件,并对...
本系列专栏写作方式
本系列专栏写作将采用首创的问答式写作形式,快速让你学习到 OpenCV 的初级、中级、高级知识。
4. Python OpenCV 中鼠标事件相关处理与常见问题解决方案
本篇博客主要分析 cv2.setMouseCallback 函数,以及该函数在日常编码中出现问题是如何进行解决。
本函数主要是 OpenCV 中用来处理鼠标相关事件的函数,通过它可以捕获到数据触发的事件,并对其进行处理。
使用该函数前,可以先通过 help 函数查阅基本用法。
该函数原型如下:
setMouseCallback(windowName, onMouse [, param]) -> None
可以看到该函数有两个参数,其一是窗口的名称,其二是回调函数,窗口名称与cv2.imshow 中的名称保持一致即可。
通过函数原型,可以看出 cv2.setMouseCallback 函数是在给窗口设置一个回调函数。
OpenCV 中鼠标都有哪些事件?
查看事件的代码如下,通过内置函数 dir 可以进行查阅。
import cv2
def show_event():
events = [i for i in dir(cv2) if 'EVENT' in i]
print(events)
if __name__ == "__main__":
show_event()
运行结果如下,所有与 event(事件相关的函数,都罗列了出来)
['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']
以上事件中,最常用的为 EVENT_LBUTTONDOWN,EVENT_LBUTTONUP,我们接下来就重点掌握。
EVENT_LBUTTONDOWN 鼠标左键按下事件。
先通过以下代码呈现一个窗体,测试一下鼠标左键按下。
import cv2
def show_event():
events = [i for i in dir(cv2) if 'EVENT' in i]
print(events)
def mouse_handler(event, x, y, flags, userdata):
if event == 1: # cv2.EVENT_LBUTTONDOWN
print("鼠标左键按下")
if __name__ == "__main__":
image = cv2.imread("./tt.jpg")
cv2.namedWindow("mouse_event")
cv2.imshow("mouse_event", image)
cv2.setMouseCallback("mouse_event", mouse_handler)
cv2.waitKey()
代码运行效果如下,在图片上点击鼠标左键,会在控制台进行数据的输入,输出内容如截图红框位置所示。
此时需要注意的问题是,即使你没有加载任何图片,只是使用 nameWindow 命名了一下窗体,对应的 setMouseCallback 函数也会绑定成功,具体测试代码如下。
# image = cv2.imread("./tt.jpg")
cv2.namedWindow("mouse_event")
# cv2.imshow("mouse_event", image)
cv2.setMouseCallback("mouse_event", mouse_handler)
由上面的案例,我们还能得到下述推论,cv2.setMouseCallback 函数中第二个参数回调函数onMouse ,具备某种格式,因为在上述代码中出现了这样一段内容:
def mouse_handler(event, x, y, flags, userdata):
这里其实对于所有鼠标事件,回调函数格式都是统一的,只是函数内部的具体实现不同。
参数说明如下:
- event:鼠标事件名称,通过该值可以获取鼠标进行的何种事件操作;
- x, y:鼠标进行事件操作一瞬间,所在的坐标位置;
- flags:指的是与 event 相关的实践中包含 FLAG 的事件;
- userdata:鼠标回调函数触发时传递进来的参数。
以上参数都非常容易理解,但是目前网络上很多内容对 flags 参数都一带而过,没有进行说明。
该参数其实就是我们在上文获取到的所有事件的一个子集,在看一下之前获取到的所有事件。
['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']
在其中你重点寻找带 flag 的值,检索如下:
'EVENT_FLAG_ALTKEY',
'EVENT_FLAG_CTRLKEY',
'EVENT_FLAG_LBUTTON',
'EVENT_FLAG_MBUTTON',
'EVENT_FLAG_RBUTTON',
'EVENT_FLAG_SHIFTKEY'
稍微对英文进行一下翻译,就能了解 flags 参数,例如我们想要实现按住鼠标左键的同时进行拖动,那核心代码为:
event == cv2.EVENT_MOUSEMOVE and flags == cv2.EVENT_FLAG_LBUTTON
按住键盘 CTRL 的同时,按下鼠标左键,代码如下
event == cv2.EVENT_LBUTTONUP and flags == cv2.EVENT_FLAG_CTRLKEY
接下来我们就实现一下如何按住鼠标左键并进行拖动,绘制一个矩形。
import cv2
image = cv2.imread("./tt.jpg")
cv2.namedWindow("mouse_event")
x1, y1 = 0, 0
def show_event():
events = [i for i in dir(cv2) if 'EVENT' in i]
print(events)
def mouse_handler(event, x, y, flags, userdata):
global x1, y1
if event == cv2.EVENT_LBUTTONDOWN:
print("左键点击")
x1, y1 = x, y
if event == cv2.EVENT_MOUSEMOVE and flags == cv2.EVENT_FLAG_LBUTTON:
# print("鼠标左键按下拖动")
cv2.rectangle(image, (x1, y1), (x, y), (0, 255, 0), -1)
if __name__ == "__main__":
cv2.setMouseCallback("mouse_event", mouse_handler)
while True:
cv2.imshow("mouse_event", image)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
上述代码,并未用到最后一个参数 userdata,接下来我们通过传参的方式应用一下该参数。
核心修改一个地方即可,在 cv2.setMouseCallback 函数部分将读取的图像 image
传递到回调的函数中去,具体如下
cv2.setMouseCallback("mouse_event", mouse_handler, image)
OpenCV 在视频中捕获鼠标事件的解决方案
上文已经实现了在图片中捕获鼠标事件,接下来我们看一下如何去在视频中进行相同的操作。
由以前的知识已经知道,视频处理就是对视频的每一帧进行相应的操作,那可以按照下述代码进行。
import cv2
def mouse_handler(event, x, y, flags, frame):
if frame is not None:
# 获取坐标,测试用
# print(x, y)
if event == cv2.EVENT_MOUSEMOVE:
cv2.putText(frame, "Hello OpenCV", (x, y),
cv2.FONT_HERSHEY_COMPLEX, 1, (255, 0, 0))
cv2.imshow("video", frame)
if __name__ == "__main__":
cap = cv2.VideoCapture("./test.mp4")
while cap.isOpened():
ret, frame = cap.read()
if ret:
cv2.imshow("video", frame)
cv2.setMouseCallback("video", mouse_handler, frame)
if cv2.waitKey(25) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
以上代码因为绑定在 EVENT_MOUSEMOVE 事件上,所以当鼠标移动的时候,会出现一个 Hello OpenCV
的字样,但是该方式会导致视频不断重复渲染,效率不好,频繁刷新几次之后,页面就会崩溃掉。
如果你单纯为了测试,可以将 cv2.waitKey(25) 中的数字设置到 1000,这样视频播放速度就会变慢,即可抓取到最终效果。
对于鼠标回调函数的学习,重点要掌握的依旧是各种事件,还有一个需要注意的是组合按键与鼠标位置的计算,你可以基于此实现一个简单的 OpenCV 画板,当然前提是你对之前学的图形绘制函数已经十分熟悉。
补充知识,OpenCV 绘制多边形
在 上一篇博客 中,我们缺少了一个函数,绘制多边形,这里进行一下补充,该函数为 cv2.polylines,函数原型如下:
polylines(img, pts, isClosed, color[, thickness[, lineType[, shift]]]) -> img
其中最重要的参数 pts,该参数表示待绘制多边形的折线数组,也可以理解为多边形的顶点顺序坐标。
例如下述代码:
image = np.zeros((400, 400, 3), np.uint8)
points = np.array(
[[50, 50], [170, 100], [200, 150], [300, 320]], np.int32)
cv2.polylines(image, [points], True, (255, 0, 0))
cv2.imshow('image',image)
cv2.waitKey()
绘制的多边形如下:
最后,你可以结合本文学到的 cv2.setMouseCallback 函数,加上绘制直线函数,实现一个多边形手动绘制工具。
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
作者其他文章
评论(0)