基于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()
热门推荐
三月八日必做5道菜!婆婆吃了直夸孝顺,健康长寿全在碗里
如何正确拆卸焊接部件?
何种情况下需要切除肺结节
国债购买指南:储蓄国债和记账式国债的详细购买流程
红薯的热量是多少大卡?红薯的热量和减肥功效一览
平钢化玻璃与普通玻璃的区别在哪里?
国家确定“徐州”,为江苏省域副中心城市,为什么选中徐州
产品经理如何设计高效的分享功能
一文读懂禧玛诺公路车套件,新手不迷路!
目前毕节境内发现的史前人类最早的用火遗存
优化建筑设计:BIM参数化建模技术
BIM建模:如何利用先进技术实现高效建筑设计?
架构师工作业绩如何写
掌握显卡设置技巧,提升电脑性能与游戏体验的实用指南
旅行的意义与价值:拓宽视野、放松身心与文化交融
一个月降低低密度脂蛋白,从饮食、运动到药物治疗的全方位指南
拨开叶酸的"致癌"疑云
西洋参片适合哪些人群使用?
拉布拉多绝育后的表现及护理要点
不动明王菩萨寓意及其象征意义
中科院心理所揭示大脑中表征认知功能的核心脑网络
中医教你,不同体质如何喝茶
房地产税收变化对市场影响的分析
上海房产税新标准出炉,未来政策走向引关注
脸上红色痘印怎么消除快一点
宋高宗与岳飞:君臣情深下的信仰悲歌
如何和孩子的新老师打招呼 高情商家长与班主任沟通话术
新车临牌办理指南:方法与注意事项全解析
低GI饮食可以稳定血糖?低GI食物是什么、低GI好处、饮食清单一次了解!
为太阳能板选择合适的汇流箱:需要考虑的五个因素