基于OpenCV的手势鼠标开发指南(包含完整程序)
创作时间:
作者:
@小白创作中心
基于OpenCV的手势鼠标开发指南(包含完整程序)
引用
CSDN
1.
https://blog.csdn.net/weixin_52233146/article/details/146181755
使用Python和OpenCV实现隔空控制鼠标的炫酷功能!本文将手把手教你用Python开发一个基于OpenCV的手势鼠标程序。无需硬件基础,只需普通摄像头+50行代码,即可实现《钢铁侠》般的科技体验!
技术栈全景
核心框架介绍
框架名称 | 作用说明 | 官方文档 |
|---|---|---|
OpenCV | 实时视频处理/图像渲染 | opencv.org |
MediaPipe | 高精度手部关键点检测 | mediapipe.dev |
PyAutoGUI | 跨平台鼠标控制 | pyautogui.readthedocs.io |
NumPy | 三维空间坐标计算 | numpy.org |
OpenCV核心功能应用
# 摄像头初始化
cap = cv2.VideoCapture(0) # 0代表默认摄像头
# 图像处理三件套
frame = cv2.flip(frame, 1) # 镜像翻转(重要!)
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 颜色空间转换
cv2.imshow('Hand Tracking', frame) # 实时显示画面
环境配置
安装必备库
pip install opencv-python mediapipe pyautogui numpy
硬件检查
需要确认摄像头可用
开发环境配置
- Python 3.6+(建议3.8)
- IDE选择:PyCharm
手势功能
- 保持手掌与摄像头距离30-50cm
- 确保中指伸直用于光标控制
- 练习OK手势(拇指与食指轻触)
核心实现原理
手部关键点拓扑
关键点索引说明:
这个程序中主要使用了下面三个点位
- 4号点:拇指指尖
- 8号点:食指指尖
- 12号点:中指指尖
坐标映射算法
# 将摄像头坐标转换为屏幕坐标
h, w = frame.shape[:2] # 获取摄像头分辨率
screen_x = np.interp(curr_x, [0, w], [0, screen_w]) # X轴映射
screen_y = np.interp(curr_y, [0, h], [0, screen_h]) # Y轴映射
手势识别逻辑
def is_ok_gesture(landmarks):
# 获取三维坐标
thumb = landmarks[4] # 拇指指尖
index = landmarks[8] # 食指指尖
# 计算3D欧氏距离
distance = np.sqrt(
(thumb.x - index.x)**2 +
(thumb.y - index.y)**2 +
(thumb.z - index.z)**2
)
return distance < 0.03 # 经验阈值
初始化模块
pyautogui.FAILSAFE = False
# 配置MediaPipe检测参数
hands = mp_hands.Hands(
max_num_hands=1, # 单手势检测
min_detection_confidence=0.7, # 识别置信度
min_tracking_confidence=0.5 # 跟踪灵敏度
)
平滑滤波算法
# 移动平均滤波(有效消除抖动)
smoothening = 3 # 滤波系数(越大越平滑)
curr_x = prev_x + (new_x - prev_x)/smoothening
curr_y = prev_y + (new_y - prev_y)/smoothening
完整程序 main.py
import cv2
import mediapipe as mp
import pyautogui
import numpy as np
# 禁用 PyAutoGUI 的 fail-safe 功能
pyautogui.FAILSAFE = False
# 初始化MediaPipe手部检测模块
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
max_num_hands=1, # 只检测一只手
min_detection_confidence=0.7,
min_tracking_confidence=0.5
)
# 获取屏幕分辨率
screen_w, screen_h = pyautogui.size()
# 鼠标灵敏度系数 (根据实际需要调整)
sensitivity = 1.0 # 调整灵敏度,减少这个值以增大控制区域
# 初始化摄像头
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
# 平滑滤波参数
smoothening = 3
prev_x, prev_y = 0, 0
curr_x, curr_y = 0, 0
def is_ok_gesture(hand_landmarks):
"""判断是否为OK手势(拇指指尖与食指指尖距离小于阈值,且中指指尖与食指第二关节距离大于阈值)"""
thumb_tip = hand_landmarks.landmark[4]
index_tip = hand_landmarks.landmark[8]
middle_tip = hand_landmarks.landmark[12]
index_pip = hand_landmarks.landmark[6]
# 计算3D距离
thumb_index_distance = np.sqrt(
(thumb_tip.x - index_tip.x) ** 2 +
(thumb_tip.y - index_tip.y) ** 2 +
(thumb_tip.z - index_tip.z) ** 2
)
middle_index_distance = np.sqrt(
(middle_tip.x - index_pip.x) ** 2 +
(middle_tip.y - index_pip.y) ** 2 +
(middle_tip.z - index_pip.z) ** 2
)
return thumb_index_distance < 0.03 and middle_index_distance > 0.05
while cap.isOpened():
success, frame = cap.read()
if not success:
continue
# 镜像翻转
frame = cv2.flip(frame, 1)
# 转换为RGB格式
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 手部检测
results = hands.process(rgb_frame)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
# 获取中指指尖关键点(控制鼠标移动)
middle_finger_tip = hand_landmarks.landmark[mp_hands.HandLandmark.MIDDLE_FINGER_TIP]
# 坐标转换(摄像头坐标 -> 屏幕坐标)
h, w, _ = frame.shape
curr_x = middle_finger_tip.x * w
curr_y = middle_finger_tip.y * h
# 应用平滑滤波
curr_x = prev_x + (curr_x - prev_x) / smoothening
curr_y = prev_y + (curr_y - prev_y) / smoothening
# 计算屏幕坐标
screen_x = np.interp(curr_x, (0, w), (0, screen_w))
screen_y = np.interp(curr_y, (0, h), (0, screen_h))
# 移动鼠标
pyautogui.moveTo(screen_x, screen_y)
# 显示鼠标位置
cv2.putText(frame, f'Mouse Pos: ({int(screen_x)}, {int(screen_y)})',
(10, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
prev_x, prev_y = curr_x, curr_y
# 检测OK手势
if is_ok_gesture(hand_landmarks):
pyautogui.click()
cv2.putText(frame, "OK Gesture Detected", (10, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# 绘制手部关键点
mp.solutions.drawing_utils.draw_landmarks(
frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
# 显示调试画面
cv2.imshow('Hand Tracking', frame)
# 退出条件
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
热门推荐
内存条频率不一样能一起用?内存混用注意事项
春运购票防诈骗指南:识别常见骗局,保护个人信息
5G PA(功率放大器)Trigger 原理及应用解析
如何检测陈醋成分?陈醋检测流程及检测机构介绍
黄豆有多少种不同的品种?这些品种的特点分别是什么?
如何挑选优质羊肉?吃货必备指南!
李世民的继承者:李治的继位之路
如何理解股票成本的构成?这种成本构成有哪些影响因素?
解密“亚健康”:如何通过养生远离慢性疲劳
谷爱凌:任何状态下,都会一直代表中国
腿肿的治疗方法
虚拟现实引擎课程中的小球移动与小球控制:技术解析与实践意义
Unity 中简单的小球移动实现
水胆琥珀的十万个为什么
EDF研究|立即行动:基于自然的解决方案具有巨大的减排增汇潜力
创伤后压力综合征(PTSD)的症状、预防,医生完整说明
抖音转化率如何提高?提高转化率的方法有哪些?
土耳其全球影响力有多大?深度分析其国际地位与贡献
哪些食物是发物不利于伤口愈合
如何查找国外公司的组织结构
离职证明中的表现不良与公司解雇:HR需知的法律与道德边界
如何检查电脑CPU温度?
英雄联盟诺手背景故事介绍 德莱厄斯经历了什么
如何表扬团队合作精神
厉害的教师:花样表扬学生的35句话
家养小鸡要放在哪里?养家小鸡的绝佳安家指南:温馨小窝大揭秘!
科学减肥计划
科学减肥:28天健康调整,轻松瘦身不反弹
巴黎奥运会 | 40金!浇铸中国荣耀——巴黎奥运会中国体育代表团综述
买房产权到期后续费标准及房产证家庭成员身份认定