简介
这个小的Python程序旨在通过手势识别实现鼠标控制,其中使用OpenCV进行视频处理,MediaPipe进行手部关键点检测,以及PyAutoGUI实现鼠标操作。
应用领域
-
辅助技术:
对于那些因身体残障无法使用传统鼠标和键盘的用户,手势控制可以提供一种更自由和自主的交互方式。 -
虚拟现实和增强现实:
在VR和AR环境中,手势控制可以为用户提供更自然的交互方式,使其能够直观地操控虚拟对象或界面元素。 -
医疗保健:
在医疗领域,手势控制可以用于手术室内的触摸无感操作,也可以用于康复和运动疗法。 -
展示和演示:
在展示和演示中,手势控制可以为演讲者或演员提供更生动和引人入胜的方式来交流和互动。 -
娱乐:
在游戏和娱乐产业中,手势控制可以提高游戏的交互性,使玩家能够更深度地参与到游戏体验中。 -
手势操控HUD:
头盔显示器(HUD)是一种将信息投射到驾驶员视野中的技术。手势控制可以用于操控HUD上显示的信息,例如接听电话、切换显示信息等。
涉及到的Python库
首先我们需要下载一下Python库
pyautogui
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ pyautogui
mediapipe
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ mediapipe
OpenCV
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ opencv-python
下载就完事啦,兄弟们~~~
代码解析
(1)初始化
初始化所需的库和变量。其中包括MediaPipe Hands模块、视频捕捉对象、帧率计算变量、屏幕尺寸和鼠标移动的阈值
其中鼠标移动的阈值的设定是为了平衡对手势变化的敏感度和对手部微小运动的抵抗力,从而使鼠标的移动更加稳定、准确。
import cv2
import mediapipe as mp
import time
import pyautogui
mp_hands = mp.solutions.hands
hands = mp_hands.Hands()
mp_drawing = mp.solutions.drawing_utils
cap = cv2.VideoCapture(0)
fps_time = time.time()
screen_width, screen_height = pyautogui.size()
threshold = 10
smooth_duration = 0.25
hand_detected = False
(2)主循环
通过OpenCV捕获实时视频流,然后在每个迭代中进行处理。使用MediaPipe Hands模块检测手部关键点,然后根据手指位置更新鼠标光标
while cap.isOpened():
ret, frame = cap.read()
# ...(帧处理的代码)
cv2.imshow('Hand grasping', frame)
if cv2.waitKey(1) & 0xFF == 27:
break
(3)手部检测和鼠标控制
在每个帧中,使用MediaPipe Hands模块检测手部关键点,提取食指位置,并将其映射到屏幕坐标。根据阈值检查手指移动,并使用PyAutoGUI平滑移动鼠标光标
results = hands.process(frame_rgb)
if results.multi_hand_landmarks:
hand_landmarks = results.multi_hand_landmarks[0]
index_finger = hand_landmarks.landmark[8]
finger_x, finger_y = int((1 - index_finger.x) * screen_width), int(index_finger.y * screen_height)
if abs(finger_x - pyautogui.position().x) > threshold or abs(finger_y - pyautogui.position().y) > threshold:
pyautogui.moveTo(finger_x, finger_y, duration=smooth_duration)
hand_detected = True
else:
hand_detected = False
(4)手指骨骼可视化
如果检测到手,脚本将手指关键点之间的骨骼可视化为线条,并显示在屏幕上。
当然了,这里我只是为了观察演示的效果,实际部署到项目之中并不需要显示这些内容从而可以减免不必要的硬件算力损耗。
if results.multi_hand_landmarks:
finger_points = [hand_landmarks.landmark[i] for i in [4, 8, 12, 16, 20]]
for i in range(4):
start_point = (int(finger_points[i].x * frame.shape[1]), int(finger_points[i].y * frame.shape[0]))
end_point = (int(finger_points[i + 1].x * frame.shape[1]), int(finger_points[i + 1].y * frame.shape[0]))
cv2.line(frame, start_point, end_point, (173, 186, 97), 2)
(5)结束和资源释放
最后,当我们按下ESC键时,循环退出,并释放使用的资源。
其实这里我还有一个想法就是通过判断是否保持握拳姿势5s来达到直接关闭程序的效果,但是我没有成功,如果大家可以研究出来的话,欢迎发到评论区讨论呀!~~
cap.release()
cv2.destroyAllWindows()
总体代码
那么总体可以运行的代码就如下所示啦
import cv2
import mediapipe as mp
import time
import pyautogui
# 初始化MediaPipe Hand模块
mp_hands = mp.solutions.hands
hands = mp_hands.Hands()
mp_drawing = mp.solutions.drawing_utils
# 打开摄像头
cap = cv2.VideoCapture(0)
# 初始化计时器
fps_time = time.time()
# 屏幕宽度和高度
screen_width, screen_height = pyautogui.size()
# 阈值和平滑移动参数
threshold = 10
smooth_duration = 0.25
# 是否已经检测到手
hand_detected = False
while cap.isOpened():
# 读取视频流中的一帧
ret, frame = cap.read()
if not ret:
break
# 将图像转换为RGB格式(MediaPipe要求输入为RGB格式)
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 使用MediaPipe Hands模块处理图像
results = hands.process(frame_rgb)
# 绘制手部关键点及骨骼线
if results.multi_hand_landmarks:
# 获取第一个检测到的手的信息
hand_landmarks = results.multi_hand_landmarks[0]
mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
# 获取食指关键点
index_finger = hand_landmarks.landmark[8]
# 将食指位置映射到屏幕坐标,添加阈值
finger_x, finger_y = int((1 - index_finger.x) * screen_width), int(index_finger.y * screen_height)
if abs(finger_x - pyautogui.position().x) > threshold or abs(finger_y - pyautogui.position().y) > threshold:
# 控制鼠标平滑移动
pyautogui.moveTo(finger_x, finger_y, duration=smooth_duration)
# 获取手指关键点
finger_points = [hand_landmarks.landmark[i] for i in [4, 8, 12, 16, 20]]
# 绘制手指骨骼线,修改颜色为#61BAAD
for i in range(4):
start_point = (int(finger_points[i].x * frame.shape[1]), int(finger_points[i].y * frame.shape[0]))
end_point = (int(finger_points[i + 1].x * frame.shape[1]), int(finger_points[i + 1].y * frame.shape[0]))
cv2.line(frame, start_point, end_point, (173, 186, 97), 2) # 修改颜色为#61BAAD
# 设置 hand_detected 为 True
hand_detected = True
else:
# 如果没有检测到手,重置hand_detected
hand_detected = False
# 计算帧率
current_time = time.time()
fps = 1 / (current_time - fps_time)
fps_time = current_time
# 显示帧率
cv2.putText(frame, f'FPS: {int(fps)}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# 显示处理后的图像
cv2.imshow('Hand grasping', frame)
# 检测按键,按下ESC键退出循环
if cv2.waitKey(1) & 0xFF == 27:
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
总结
由于这里需要一个视频来展示效果,我就不展示效果图了,大家可以自行复制原始代码来研究。