第四部分、键盘+鼠标响应操作
- 第一节、键盘响应操作
- 1.键盘响应事件
- 2.键盘响应
- 3.代码练习与测试
- 第二节、鼠标操作与响应
- 1.鼠标事件与回调
- 2.鼠标操作
- 3.代码练习与测试
- 学习参考
第一节、键盘响应操作
键盘响应中有一个函数叫做waitKey
,所有的获取键盘键值都是通过waitKey函数实现的。
1.键盘响应事件
- cv.waitKey( [, delay] ) --> retval
- delay如果没有声明或者delay=0,表示一直阻塞
- delay大于0,表示阻塞指定毫秒数
- retval返回的对应键盘键值,注意:在不同的操作系统中可能会有差异!
- 典型的retval = 27是ESC按键(windows环境下)
用户通过敲击键盘,操作系统会返回键值给各个应用程序,当返回键值给OpenCV的时候,如果我们有自己定义的接收返回值的方法,就会按照我们定义的方法对返回值进行处理。
键盘属于外部设备,由操作系统负责监听响应,当键盘发出响应被操作系统接收后,操作系统根据发出注册的应用程序返回接收到的键盘值。
2.键盘响应
- 检查返回键值,根据不同键值完成不同操作
- 推荐使用if-elif-else, switch-case方式python3.10支持
# 例如借助if-elif-else来处理我们需要的键值
if <expr>:
<statement(s)>
elif <expr>:
<statement(s)>
elif <expr>:
<statement(s)>
...
else:
<statement(s)>
3.代码练习与测试
例如我们可以按照如下设定:
- 按ESC退出程序
- 按1显示HSV图像
- 按2显示YCrCb图像
- 按3显示RGB图像
- 按0恢复原图BGR显示
def keyboard_demo():
image = cv.imread(r"F:\python\opencv-4.x\samples\data\butterfly.jpg")
cv.namedWindow("keyboard_demo", cv.WINDOW_AUTOSIZE)
cv.imshow("keyboard_demo", image)
while True:
c = cv.waitKey(10) # 停顿10ms
# ESC
if c == 27:
break
# key = 0
elif c == 48:
cv.imshow("keyboard_demo", image)
# key = 1
elif c == 49:
hsv = cv.cvtColor(image, cv.COLOR_BGR2HSV)
cv.imshow("keyboard_demo", hsv)
# key = 2
elif c == 50:
ycrcb = cv.cvtColor(image, cv.COLOR_BGR2YCrCb)
cv.imshow("keyboard_demo", ycrcb)
# key = 3
elif c == 51:
rgb = cv.cvtColor(image, cv.COLOR_BGR2RGB)
cv.imshow("keyboard_demo", rgb)
else:
if c != -1:
print("Key: ", c, "is not define.")
cv.waitKey(0)
cv.destroyAllWindows()
结果示例:
从左往右依次是BGR/HSV/YCrCb/RGB:
如果键入了没有定义的键值就会按照else中写的返回一个结果如下图所示:
第二节、鼠标操作与响应
1.鼠标事件与回调
和键盘一样,鼠标属于外部设备,由操作系统负责监听响应,当鼠标发出响应被操作系统接收后,操作系统根据发出注册的应用程序返回接收到的鼠标值。
注册与回调过程非常类似滚动条的操作。
鼠标支持的事件:
- 左键操作:
- 右键操作:
- 中键+滚轮操作:
1.EVENT_LBUTTONDOWN
2.EVENT_MOUSEMOVE
3.EVENT_LBUTTONUP
# 以上是三个非常重要的鼠标事件,这三个动作要在一套动作中完成
鼠标左键按下、鼠标移动、鼠标左键弹起
2.鼠标操作
# 鼠标回调函数
1. cv2.setMouseCallback(windowName, onMouse, param=None)
# windowName:窗口名称
# onMouse:鼠标响应函数
# param:响应函数传递的的参数
# 鼠标响应函数
2. onMouse(event, x, y, flags, param)
# event:鼠标事件,可用参数对应值代替
# x:鼠标x坐标
# y:鼠标y坐标
# flags:鼠标状态,可用参数对应值代替
# param:param是用户定义的传递到setMouseCallback函数调用的参数
3.代码练习与测试
绘制矩形:
- 鼠标绘制第一步,reset之前的绘制
- 记录鼠标按下位置(EVENT_LBUTTONDOWN)
- 记录鼠标移动位置,并绘制矩形(EVENT_MOUSEMOVE)
- 抬起,记录位置,绘制最终矩形大小(EVENT_LBUTTONUP)
鼠标绘制矩形:
# 鼠标操作绘制矩形
b1 = cv.imread(r"F:\python\opencv-4.x\samples\data\starry_night.jpg")
img = np.copy(b1)
# (x1, y1)表示左上角,(x2, y2)表示右下角点
x1 = -1
x2 = -1
y1 = -1
y2 = -1
# 定义绘制矩形的注册函数
def mouse_drawing_rectangle(event, x, y, flags, parm):
# 全局参数
global x1, y1, x2, y2
# 鼠标放下,赋值左上角点给x1,y1
if event == cv.EVENT_LBUTTONDOWN:
x1 = x
y1 = y
# 鼠标移动
if event == cv.EVENT_MOUSEMOVE:
# x1,y1初始值都是-1,如果移动过程<0说明鼠标没有摁下
if x1 < 0 or y1 < 0:
return
x2 = x
y2 = y
dx = x2 - x1
dy = y2 - y1
# 移动有一定距离才会绘制
if dx > 0 and dy > 0:
# 矩形绘制到b1(读入的图片)上
# img是原图
b1[:, :, :] = img[:, :, :] # 用原图覆盖擦除之前的绘制结果
cv.putText(b1, "searching...", (x1, y1-5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 2)
cv.rectangle(b1, (x1, y1), (x2, y2), (255, 0, 255), 2, 8, 0) # 移动过程中用紫色线
if event == cv.EVENT_LBUTTONUP:
x2 = x
y2 = y
dx = x2 - x1
dy = y2 - y1
if dx > 0 and dy > 0:
# 矩形绘制到b1(读入的图片)上
# img是原图
b1[:, :, :] = img[:, :, :] # 用原图覆盖擦除之前的绘制结果
cv.putText(b1, "Moon", (x1, y1-5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv.rectangle(b1, (x1, y1), (x2, y2), (0, 0, 255), 2, 8, 0) # 鼠标抬起之后用红色线
# 重新赋值为下一次绘制做准备
x1 = -1
y1 = -1
x2 = -1
y2 = -1
def mouse_demo():
cv.namedWindow("mouse_demo", cv.WINDOW_AUTOSIZE)
# 实时关注mouse_demo画布上的响应,如果发生mouse_drawing中定义的事件,就返回响应
cv.setMouseCallback("mouse_demo", mouse_drawing_rectangle) # 绘制矩形
while True:
cv.imshow("mouse_demo", b1) # 绘制矩形
# 每过10ms就获取一次键盘键值,默认是-1,ESC键是27
c = cv.waitKey(10)
if c == 27:
break
cv.destroyAllWindows()
结果示例:
鼠标绘制圆形:
# 鼠标操作绘制圆形
b2 = cv.imread(r"F:\python\opencv-4.x\samples\data\starry_night.jpg")
img2 = np.copy(b2)
# (c1, c2)表示圆心坐标,r1表示半径
c1 = -1
c2 = -1
# 定义圆形的注册函数
def mouse_drawing_circle(event, x, y, flags, parm):
# 全局参数
global c1, c2, r1
# 鼠标放下,赋值左上角点给x1,y1
if event == cv.EVENT_LBUTTONDOWN:
c1 = x
c2 = y
# 鼠标移动
if event == cv.EVENT_MOUSEMOVE:
# c1,c2初始值都是-1,如果移动过程<0说明鼠标没有摁下
if c1 < 0 or c2 < 0:
return
dr = int(math.sqrt(pow((x-c1), 2) + pow((y-c2), 2)))
# 移动有一定距离才会绘制
if dr > 0:
# 圆形绘制到b1(读入的图片)上
# img是原图
b2[:, :, :] = img2[:, :, :] # 用原图覆盖擦除之前的绘制结果
cv.circle(b2, (c1, c2), dr, (255, 0, 255), 2, cv.LINE_8) # 移动过程中用紫色线
if event == cv.EVENT_LBUTTONUP:
dr = int(math.sqrt(pow((x - c1), 2) + pow((y - c2), 2)))
if dr > 0:
# 圆形绘制到b1(读入的图片)上
# img是原图
b2[:, :, :] = img2[:, :, :] # 用原图覆盖擦除之前的绘制结果
cv.circle(b2, (c1, c2), dr, (0, 0, 255), 2, cv.LINE_8) # 移动过程中用红色线
# 重新赋值为下一次绘制做准备
c1 = -1
c2 = -1
def mouse_demo():
cv.namedWindow("mouse_demo", cv.WINDOW_AUTOSIZE)
# 实时关注mouse_demo画布上的响应,如果发生mouse_drawing中定义的事件,就返回响应
cv.setMouseCallback("mouse_demo", mouse_drawing_circle) # 绘制圆形
while True:
cv.imshow("mouse_demo", b2) # 绘制圆形
# 每过10ms就获取一次键盘键值,默认是-1,ESC键是27
c = cv.waitKey(10)
if c == 27:
break
cv.destroyAllWindows()
结果示例:
其他有趣的实现
同时还可以实现一些有意思的功能比如实现实时响应截取
以及类似qq截图的功能
的,只有截图部分是亮的,其余部分是暗的。
下面代码我是单独写在一个包里面的,如果需要的话可以把他们在上面的类中调用自己试一试也是很有趣的。
import cv2 as cv
import numpy as np
# 图片区域显示
def rectangle_space(img, x1, x2, y1, y2):
# img = cv.imread(r"F:\python\opencv-4.x\samples\data\starry_night.jpg")
# cv.imshow("img", img)
# img2 = img[0:256, 256:512, 1:2] # 第一个0:256表示高度所在位置,第二个0:256表示宽度所在位置,第三个1:2表示输出通道数
# cv.imshow("rectangle_space", img2)
cv.imshow("rectangle_space", img[y1:y2, x1:x2, 0:3])
cv.waitKey(1)
# 除了截图部分其余均变暗
def rectangle_dark(img, x1, x2, y1, y2):
# img = cv.imread(r"F:\python\opencv-4.x\samples\data\starry_night.jpg")
# cv.imshow("img", img)
# img2 = np.zeros_like(img)
# img2[:, :, :] = (np.uint8(60), np.uint8(60), np.uint8(60))
# img2[0:256, 256:512, :] = 0
# result = cv.subtract(img, img2)
# cv.imshow("result", result)
img2 = np.zeros_like(img)
img2[:, :, :] = (np.uint8(60), np.uint8(60), np.uint8(60))
img2[y1:y2, x1:x2, :] = 0
result = cv.subtract(img, img2)
cv.imshow("mouse_demo", result)
cv.waitKey(1000)
# 当前程序执行部分
if __name__ == '__main__':
print("Hello world.")
rectangle_dark()
结果示例:
左图为实时截取,右图为类似qq截图的实现
学习参考
本系列所有OpenCv相关的代码示例和内容均来自博主学习的网站:opencv_course