基于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()
热门推荐
保释要带什么证件?了解流程及注意事项
辣椒的种植技术与管理方法
租房合同押金纠纷怎么处理?提前终止合同协议怎么写?
Cocos Creator 游戏性能优化指南
这种长得像灯笼一样的北方水果,我打赌南方人没见过
电商淘宝选品类类目有哪些要点和技巧?
硫磺熏蒸对黄芩饮片化学成分含量影响的实验研究
茅山上清派字辈顺序:道教重要派别的人才培养体系
四川统招专升本:揭开公办与民办本科院校的神秘面纱
探索历史人物:瞿式耜的读音与生平
焦虑症西医治疗好还是中医好
健康追踪与预防,现代健康管理平台的核心功能
古代男子的冠礼仪:三次加冠与三顶帽子的不同含义
白血病是白细胞增多还是减少?
5G/6G毫米波与太赫兹通信:迈向6G的技术前沿与挑战
烤生蚝的热量有多少
解锁金融领域的通行证,你需要了解的热门考证及深度学习路径
正念练习:识别生活和养育中的边界感
秦二世胡亥的母亲是谁?秦二世胡亥身世大揭秘
精算师考试介绍:报考条件及考试内容详解
肝硬化患者寿命有多久?做好这些患者生存期大大延长!
足部检查:外观、主动运动和被动运动检查
新能源电池的能量密度怎样提升?
滇红工夫茶的泡法:详细大全与冲泡技巧及功效解析
古人言:天道酬勤,地道酬善,人道酬诚,商道酬信,业道酬精!
金星:从宜居到“炼狱”的演变
无锡居住环境全解析:自然、设施与文化的完美融合
联合用药一般不超过几种 盲目联用药 越用越危险
总是耳鸣怎么快速消除
换机油,5W-30 还是 5W-40?别纠结啦!