使用Python和MediaPipe实现手势虚拟鼠标控制

概述

使用Python实现虚拟鼠标控制,利用手势识别来替代传统鼠标操作。这一实现依赖于计算机视觉库OpenCV、手势识别库MediaPipe以及其他辅助库如PyAutoGUI和Pynput。

环境配置

在开始之前,请确保已安装以下Python库:

pip install opencv-python mediapipe pynput pyautogui numpy pillow

模块介绍

1. utils.py

utils.py包含一个Utils类,主要提供在图像上添加中文文本的功能。这对于在实时视频流中显示信息非常有用。

代码解析
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFontclass Utils:def __init__(self):passdef cv2AddChineseText(self, img, text, position, textColor=(0, 255, 0), textSize=30):if isinstance(img, np.ndarray):  # 判断是否OpenCV图片类型img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(img)fontStyle = ImageFont.truetype("./fonts/simsun.ttc", textSize, encoding="utf-8")draw.text(position, text, textColor, font=fontStyle)return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)

2. handProcess.py

handProcess.py负责手势的识别和处理。该模块使用MediaPipe库来检测和跟踪手部的关键点,并根据手势的不同动作触发相应的鼠标操作。

代码解析
import cv2
import mediapipe as mp
import time
import math
import numpy as np
from utils import Utilsclass HandProcess:def __init__(self, static_image_mode=False, max_num_hands=2):self.mp_drawing = mp.solutions.drawing_utilsself.mp_hands = mp.solutions.handsself.hands = self.mp_hands.Hands(static_image_mode=static_image_mode,min_detection_confidence=0.7,min_tracking_confidence=0.5,max_num_hands=max_num_hands)self.landmark_list = []self.action_labels = {'none': '无','move': '鼠标移动','click_single_active': '触发单击','click_single_ready': '单击准备','click_right_active': '触发右击','click_right_ready': '右击准备','scroll_up': '向上滑页','scroll_down': '向下滑页','drag': '鼠标拖拽'}self.action_deteted = ''def checkHandsIndex(self, handedness):if len(handedness) == 1:handedness_list = [handedness[0].classification[0].label]else:handedness_list = [handedness[0].classification[0].label, handedness[1].classification[0].label]return handedness_listdef getDistance(self, pointA, pointB):return math.hypot((pointA[0] - pointB[0]), (pointA[1] - pointB[1]))def getFingerXY(self, index):return (self.landmark_list[index][1], self.landmark_list[index][2])def drawInfo(self, img, action):thumbXY, indexXY, middleXY = map(self.getFingerXY, [4, 8, 12])if action == 'move':img = cv2.circle(img, indexXY, 20, (255, 0, 255), -1)elif action == 'click_single_active':middle_point = int((indexXY[0] + thumbXY[0]) / 2), int((indexXY[1] + thumbXY[1]) / 2)img = cv2.circle(img, middle_point, 30, (0, 255, 0), -1)elif action == 'click_single_ready':img = cv2.circle(img, indexXY, 20, (255, 0, 255), -1)img = cv2.circle(img, thumbXY, 20, (255, 0, 255), -1)img = cv2.line(img, indexXY, thumbXY, (255, 0, 255), 2)elif action == 'click_right_active':middle_point = int((indexXY[0] + middleXY[0]) / 2), int((indexXY[1] + middleXY[1]) / 2)img = cv2.circle(img, middle_point, 30, (0, 255, 0), -1)elif action == 'click_right_ready':img = cv2.circle(img, indexXY, 20, (255, 0, 255), -1)img = cv2.circle(img, middleXY, 20, (255, 0, 255), -1)img = cv2.line(img, indexXY, middleXY, (255, 0, 255), 2)return imgdef checkHandAction(self, img, drawKeyFinger=True):upList = self.checkFingersUp()action = 'none'if len(upList) == 0:return img, action, Nonedete_dist = 100key_point = self.getFingerXY(8)if upList == [0, 1, 0, 0, 0]:action = 'move'if upList == [1, 1, 0, 0, 0]:l1 = self.getDistance(self.getFingerXY(4), self.getFingerXY(8))action = 'click_single_active' if l1 < dete_dist else 'click_single_ready'if upList == [0, 1, 1, 0, 0]:l1 = self.getDistance(self.getFingerXY(8), self.getFingerXY(12))action = 'click_right_active' if l1 < dete_dist else 'click_right_ready'if upList == [1, 1, 1, 1, 1]:action = 'scroll_up'if upList == [0, 1, 1, 1, 1]:action = 'scroll_down'if upList == [0, 0, 1, 1, 1]:key_point = self.getFingerXY(12)action = 'drag'img = self.drawInfo(img, action) if drawKeyFinger else imgself.action_deteted = self.action_labels[action]return img, action, key_pointdef checkFingersUp(self):fingerTipIndexs = [4, 8, 12, 16, 20]upList = []if len(self.landmark_list) == 0:return upListif self.landmark_list[fingerTipIndexs[0]][1] < self.landmark_list[fingerTipIndexs[0] - 1][1]:upList.append(1)else:upList.append(0)for i in range(1, 5):if self.landmark_list[fingerTipIndexs[i]][2] < self.landmark_list[fingerTipIndexs[i] - 2][2]:upList.append(1)else:upList.append(0)return upListdef processOneHand(self, img, drawBox=True, drawLandmarks=True):utils = Utils()results = self.hands.process(img)self.landmark_list = []if results.multi_hand_landmarks:for hand_index, hand_landmarks in enumerate(results.multi_hand_landmarks):if drawLandmarks:self.mp_drawing.draw_landmarks(img, hand_landmarks, self.mp_hands.HAND_CONNECTIONS,self.mp_drawing_styles.get_default_hand_landmarks_style(),self.mp_drawing_styles.get_default_hand_connections_style())for landmark_id, finger_axis in enumerate(hand_landmarks.landmark):h, w, c = img.shapep_x, p_y = math.ceil(finger_axis.x * w), math.ceil(finger_axis.y * h)self.landmark_list.append([landmark_id, p_x, p_y, finger_axis.z])if drawBox:x_min, x_max = min(self.landmark_list, key=lambda i: i[1])[1], max(self.landmark_list, key=lambda i: i[1])[1]y_min, y_max = min(self.landmark_list, key=lambda i: i[2])[2], max(self.landmark_list, key=lambda i: i[2])[2]img = cv2.rectangle(img, (x_min - 30, y_min - 30), (x_max + 30, y_max + 30), (0, 255, 0), 2)img = utils.cv2AddChineseText(img, self.action_deteted, (x_min - 20, y_min - 120), textColor=(255, 0, 255), textSize=60)return img

3. virtual_mouse.py

virtual_mouse.py是主程序模块,整合了手势识别和鼠标控制功能,实现了通过手势控制鼠标移动、点击和滚动的功能。

代码解析
import cv2
import handProcess
import time
import numpy as np
import pyautogui
from utils import Utils
from pynput.mouse import Button, Controllerclass VirtualMouse:def __init__(self):self.image = Noneself.mouse = Controller()def recognize(self):handprocess = handProcess.HandProcess(False, 1)utils = Utils()fpsTime = time.time()cap = cv2.VideoCapture(0)resize_w = 960resize_h = 720frameMargin = 100screenWidth, screenHeight = pyautogui.size()stepX, stepY = 0, 0finalX, finalY = 0, 0smoothening = 7action_trigger_time = {'single_click': 0,'double_click': 0,'right_click': 0}mouseDown = Falsewhile cap.isOpened():action_zh = ''success, self.image = cap.read()self.image = cv2.resize(self.image, (resize_w, resize_h))if not success:print("空帧")continueself.image.flags.writeable = Falseself.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)self.image = cv2.flip(self.image, 1)self.image = handprocess.processOneHand(self.image)cv2.rectangle(self.image, (frameMargin, frameMargin), (resize_w - frameMargin, resize_h - frameMargin), (255, 0, 255), 2)self.image, action, key_point = handprocess.checkHandAction(self.image, drawKeyFinger=True)action_zh = handprocess.action_labels[action]if key_point:x3 = np.interp(key_point[0], (frameMargin, resize_w - frameMargin), (0, screenWidth))y3 = np.interp(key_point[1], (frameMargin, resize_h - frameMargin), (0, screenHeight))finalX = stepX + (x3 - stepX) / smootheningfinalY = stepY + (y3 - stepY) / smootheningnow = time.time()if action_zh == '鼠标拖拽':if not mouseDown:self.mouse.press(Button.left)mouseDown = Trueself.mouse.position = (finalX, finalY)else:if mouseDown:self.mouse.release(Button.left)mouseDown = Falseif action_zh == '鼠标移动':self.mouse.position = (finalX, finalY)elif action_zh == '单击准备':passelif action_zh == '触发单击' and (now - action_trigger_time['single_click'] > 0.3):self.mouse.click(Button.left, 1)action_trigger_time['single_click'] = nowelif action_zh == '右击准备':passelif action_zh == '触发右击' and (now - action_trigger_time['right_click'] > 2):self.mouse.click(Button.right, 1)action_trigger_time['right_click'] = nowelif action_zh == '向上滑页':pyautogui.scroll(30)elif action_zh == '向下滑页':pyautogui.scroll(-30)stepX, stepY = finalX, finalYself.image.flags.writeable = Trueself.image = cv2.cvtColor(self.image, cv2.COLOR_RGB2BGR)cTime = time.time()fps_text = 1 / (cTime - fpsTime)fpsTime = cTimeself.image = utils.cv2AddChineseText(self.image, "帧率: " + str(int(fps_text)), (10, 30), textColor=(255, 0, 255), textSize=50)self.image = cv2.resize(self.image, (resize_w // 2, resize_h // 2))cv2.imshow('virtual mouse', self.image)if cv2.waitKey(5) & 0xFF == 27:breakcap.release()control = VirtualMouse()
control.recognize()

4. 功能列表

在这个虚拟鼠标控制项目中,通过识别不同的手势来触发相应的鼠标操作。以下是该项目中实现的主要功能及其对应的手势:

  1. 鼠标移动

    • 手势:食指竖起(其他手指收回)。
    • 描述:食指指尖的移动映射到屏幕上的鼠标光标移动。
  2. 单击准备

    • 手势:拇指和食指都竖起且未接触。
    • 描述:准备触发单击。
  3. 触发单击

    • 手势:拇指和食指接触(捏合)。
    • 描述:触发一次鼠标左键单击。
  4. 右击准备

    • 手势:食指和中指都竖起且未接触。
    • 描述:准备触发右击。
  5. 触发右击

    • 手势:食指和中指接触(捏合)。
    • 描述:触发一次鼠标右键单击。
  6. 鼠标拖拽

    • 手势:中指、无名指和小指竖起(拇指和食指收回)。
    • 描述:模拟鼠标左键按住并拖动。
  7. 向上滚动

    • 手势:五指全部竖起。
    • 描述:触发页面向上滚动。
  8. 向下滚动

    • 手势:除了拇指外,其他四指竖起。
    • 描述:触发页面向下滚动。

5.测试

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/376890.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SadTalker数字人服务器部署

一、单独SadTalker部署 git clone https://github.com/OpenTalker/SadTalker.gitcd SadTalker conda create -n sadtalker python3.8conda activate sadtalkerpip install torch1.12.1cu113 torchvision0.13.1cu113 torchaudio0.12.1 --extra-index-url https://download.pyto…

RuoYi-后端管理项目入门篇1

目录 前提准备 下载若依前后端 Gitee 地址 准备环境 后端数据库导入 1 克隆完成 若依后端管理后端 Gitte 地址 :若依/RuoYi-Vue 2.1 创建Data Source数据源 2.2 填写好对应的数据库User 和 Password 点击Apply 2.3 新建一个Schema 2.4 填写对应数据库名称 这边演示写的…

husky 和 lint-staged 构建代码项目规范

目录 前言 最简单的方法 过 scripts 来解决如果检测工具多&#xff0c;需要多次处理 通过 husky(哈士奇)来解决容易遗忘的问题 1. 安装 2. husky init 3. 试一试​ lint-stadge 只 lint 改动的 1. 安装 2. 修改 package.json 配置 3. 添加 npm 脚本: 4.使用 Husky…

缓存与分布式锁

一、缓存 1、缓存使用 为了系统性能的提升&#xff0c;我们一般都会将部分数据放入缓存中&#xff0c;加速访问。 适合放入缓存的数据有&#xff1a; 即时性、数据一致性要求不高的&#xff1b;访问量大且更新频率不高的数据。 在开发中&#xff0c;凡是放入缓存中的数据我们都…

语言主要是一种交流工具,而不是思维工具?GPT5何去何从?

引言 在人工智能领域&#xff0c;特别是大语言模型&#xff08;LLM&#xff09;的发展中&#xff0c;语言和思维的关系一直是一个备受关注的话题。近期&#xff0c;麻省理工学院&#xff08;MIT&#xff09;在《Nature》杂志上发表了一篇题为《Language is primarily a tool f…

【ChatGPT】深入解析Prompt提示词及如何高效使用ChatGPT

一、Prompt提示词是什么&#xff1f; 1.1 Prompt的定义 Prompt是人工智能领域中的一个关键概念&#xff0c;尤其在自然语言处理&#xff08;NLP&#xff09;和生成型AI模型中。简而言之&#xff0c;prompt是一段文本或指令&#xff0c;用于引导或启动AI模型的特定响应或操作。…

Linux - 基础开发工具(yum、vim、gcc、g++、make/Makefile、git)

目录 Linux软件包管理器 - yum Linux下安装软件的方式 认识yum 查找软件包 安装软件 如何实现本地机器和云服务器之间的文件互传 卸载软件 Linux编辑器 - vim vim的基本概念 vim下各模式的切换 vim命令模式各命令汇总 vim底行模式各命令汇总 vim的简单配置 Linux编译器 - gc…

Spring-Cache 缓存

1.简介 2.SpringCache 整合 简化缓存开发 1.导入依赖 <!-- spring cache --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency>2.redis 作为缓存…

Mac应用程序清理卸载工具:App Cleaner Uninstaller for Mac 中文版

App Cleaner Pro是一款Mac上非常好用的软件卸载工具&#xff0c;支持应用卸载、Widget卸载、浏览器插件卸载&#xff0c;支持拖拽卸载和列表卸载&#xff0c;能够非常干净的卸载应用&#xff0c;节省你的磁盘空间。App Cleaner Uninstaller Pro是一款深度清理和卸载的工具&…

什么是边缘计算?创造一个更快、更智慧、更互联的世界

前言 如今&#xff0c;数十亿物联网传感器广泛部署在零售商店、城市街道、仓库和医院等各种场所&#xff0c;正在生成大量数据。从这些数据中更快地获得洞察&#xff0c;意味着可以改善服务、简化运营&#xff0c;甚至挽救生命。但要做到这一点&#xff0c;企业需要实时做出决策…

Excel第30享:基于辅助列的条件求和

1、需求描述 如下图所示&#xff0c;现要统计2022年YTD&#xff08;Year To Date&#xff1a;年初至今日&#xff09;各个人员的“上班工时&#xff08;a2&#xff09;”。 下图为系统直接导出的工时数据明细样例。 2、解决思路 Step1&#xff1a;确定逻辑。“从日期中提取出…

virtualbox的ubuntu默认ipv4地址为10.0.2.15的修改以及xshell和xftp的连接

virtualbox安装Ubuntu后&#xff0c;默认的地址为10.0.2.15 我们查看virtualbox的设置发现是NAT 学过计算机网络的应该了解NAT技术&#xff0c;为了安全以及缓解ip使用&#xff0c;我们留了部分私有ip地址。 私有IP地址网段如下&#xff1a; A类&#xff1a;1个A类网段&…

jenkins系列-09.jpom构建java docker harbor

本地先启动jpom server agent: /Users/jelex/Documents/work/jpom-2.10.40/server-2.10.40-release/bin jelexjelexxudeMacBook-Pro bin % sh Server.sh start/Users/jelex/Documents/work/jpom-2.10.40/agent-2.10.40-release/bin jelexjelexxudeMacBook-Pro bin % ./Agent.…

SAP PP学习笔记26 - User Status(用户状态)的实例,订单分割中的重要概念 成本收集器,Confirmation(报工)的概述

上面两章讲了生产订单的创建以及生产订单的相关内容。 SAP PP学习笔记24 - 生产订单&#xff08;制造指图&#xff09;的创建_sap 工程外注-CSDN博客 SAP PP学习笔记25 - 生产订单的状态管理(System Status(系统状态)/User Status(用户状态)),物料的可用性检查&#xff0c;生…

Nginx -Web服务器/反向代理/负载均衡

文章目录 一、web服务1.1 nginx安装1.2 配置文件1.3 Nginx处理Web机制 二、反向代理三、负载均衡3.1 分类3.2 负载相关配置文件3.3 keepalive 提高吞吐量3.4 配置浏览器缓存 附、JMeter性能测试工具 以赛促学内容,大概率感觉会使用nginx做web服务,特对nginx做总结归纳. Nginx是…

去水印小程序源码修复版-前端后端内置接口+第三方接口

去水印小程序源码&#xff0c;前端后端&#xff0c;内置接口第三方接口&#xff0c; 修复数据库账号密码错误问题&#xff0c;内置接口支持替换第三方接口&#xff0c; 文件挺全的&#xff0c;可以添加流量主代码&#xff0c;搭建需要准备一台服务器&#xff0c;备案域名和http…

react的解构赋值

我最近在用react讨生活。我的感觉&#xff0c;react开发效率不高。这当然应该是我还不熟悉react的缘故。但是&#xff0c;在阅读react代码过程中&#xff0c;其中一个容易困惑的地方是它到处充斥着的解构赋值。当然了&#xff0c;解构赋值并不是React特有的功能&#xff0c;而是…

编译x-Wrt 全过程

参考自;​​​​​​c编译教程 | All about X-Wrt 需要详细了解的小伙伴还请参看原文 ^-^ 概念&#xff1a; x-wrt&#xff08;基于openwrt深度定制的发行版本&#xff09; 编译系统: ubuntu22.04 注意&#xff1a; 特别注意的是&#xff0c;整个编译过程&#xff0c;都是用 …

JavaWeb后端学习

Web&#xff1a;全球局域网&#xff0c;万维网&#xff0c;能通过浏览器访问的网站 Maven Apache旗下的一个开源项目&#xff0c;是一款用于管理和构建Java项目的工具 作用&#xff1a; 依赖管理&#xff1a;方便快捷的管理项目以来的资源&#xff08;jar包&#xff09;&am…

未来互联网的新篇章:深度解析Facebook的技术与战略

随着科技的飞速发展和社会的不断变迁&#xff0c;互联网作为全球信息交流的重要平台&#xff0c;正经历着前所未有的变革和演进。作为全球最大的社交媒体平台之一&#xff0c;Facebook不仅是人们沟通、分享和互动的重要场所&#xff0c;更是科技创新和数字化进程的推动者。本文…