使用Python和MediaPipe实现手势控制音量(Win/Mac)

1. 依赖库介绍

OpenCV

OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库。它包含了数百个计算机视觉算法。

MediaPipe

MediaPipe是一个跨平台的机器学习解决方案库,可以用于实时人类姿势估计、手势识别等任务。

PyCaw

PyCaw是一个Python库,用于控制Windows上的音频设备。

Python版本

本来在Python 3.11环境中进行测试,结果一直报错,似乎是mediapipe库的问题,换了Python 3.12环境后顺利解决

安装依赖

pip install mediapipe
pip install comtypes
pip install pycaw
pip install numpy
pip install opencv-python

2. 程序结构

程序主要分为以下几个部分:

  1. 初始化MediaPipe和音量控制接口。
  2. 从摄像头获取视频流。
  3. 处理视频帧以检测手部位置和姿态。
  4. 计算手指之间的距离,并将其映射到音量控制上。
  5. 显示处理后的图像,包括手部标志和音量指示。

3. 代码详解

3.1 初始化

首先,我们需要导入必要的库,并初始化MediaPipe和音量控制接口。

import cv2
import mediapipe as mp
from ctypes import cast, POINTER
from comtypes import CLSCTX_ALL
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume
import time
import math
import numpy as npclass HandControlVolume:def __init__(self):self.mp_drawing = mp.solutions.drawing_utilsself.mp_drawing_styles = mp.solutions.drawing_stylesself.mp_hands = mp.solutions.handsdevices = AudioUtilities.GetSpeakers()interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)self.volume = cast(interface, POINTER(IAudioEndpointVolume))self.volume.SetMute(0, None)self.volume_range = self.volume.GetVolumeRange()

3.2 主函数

recognize函数是程序的核心,负责处理视频流并进行手势识别和音量控制。

def recognize(self):fpsTime = time.time()cap = cv2.VideoCapture(0)resize_w = 640resize_h = 480rect_height = 0rect_percent_text = 0with self.mp_hands.Hands(min_detection_confidence=0.7,min_tracking_confidence=0.5,max_num_hands=2) as hands:while cap.isOpened():success, image = cap.read()image = cv2.resize(image, (resize_w, resize_h))if not success:print("空帧.")continueimage.flags.writeable = Falseimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)image = cv2.flip(image, 1)results = hands.process(image)image.flags.writeable = Trueimage = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)if results.multi_hand_landmarks:for hand_landmarks in results.multi_hand_landmarks:self.mp_drawing.draw_landmarks(image,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())landmark_list = []for landmark_id, finger_axis in enumerate(hand_landmarks.landmark):landmark_list.append([landmark_id, finger_axis.x, finger_axis.y, finger_axis.z])if landmark_list:thumb_finger_tip = landmark_list[4]thumb_finger_tip_x = math.ceil(thumb_finger_tip[1] * resize_w)thumb_finger_tip_y = math.ceil(thumb_finger_tip[2] * resize_h)index_finger_tip = landmark_list[8]index_finger_tip_x = math.ceil(index_finger_tip[1] * resize_w)index_finger_tip_y = math.ceil(index_finger_tip[2] * resize_h)finger_middle_point = (thumb_finger_tip_x + index_finger_tip_x) // 2, (thumb_finger_tip_y + index_finger_tip_y) // 2thumb_finger_point = (thumb_finger_tip_x, thumb_finger_tip_y)index_finger_point = (index_finger_tip_x, index_finger_tip_y)image = cv2.circle(image, thumb_finger_point, 10, (255, 0, 255), -1)image = cv2.circle(image, index_finger_point, 10, (255, 0, 255), -1)image = cv2.circle(image, finger_middle_point, 10, (255, 0, 255), -1)image = cv2.line(image, thumb_finger_point, index_finger_point, (255, 0, 255), 5)line_len = math.hypot((index_finger_tip_x - thumb_finger_tip_x),(index_finger_tip_y - thumb_finger_tip_y))min_volume = self.volume_range[0]max_volume = self.volume_range[1]vol = np.interp(line_len, [50, 300], [min_volume, max_volume])rect_height = np.interp(line_len, [50, 300], [0, 200])rect_percent_text = np.interp(line_len, [50, 300], [0, 100])self.volume.SetMasterVolumeLevel(vol, None)cv2.putText(image, str(math.ceil(rect_percent_text)) + "%", (10, 350),cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)image = cv2.rectangle(image, (30, 100), (70, 300), (255, 0, 0), 3)image = cv2.rectangle(image, (30, math.ceil(300 - rect_height)), (70, 300), (255, 0, 0), -1)cTime = time.time()fps_text = 1 / (cTime - fpsTime)fpsTime = cTimecv2.putText(image, "FPS: " + str(int(fps_text)), (10, 70),cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)cv2.imshow('MediaPipe Hands', image)if cv2.waitKey(5) & 0xFF == 27 or cv2.getWindowProperty('MediaPipe Hands', cv2.WND_PROP_VISIBLE) < 1:breakcap.release()

3.3 启动程序

最后,通过实例化HandControlVolume类并调用recognize方法来启动程序。

control = HandControlVolume()
control.recognize()

3.4 测试效果

在这里插入图片描述

4. Mac版本程序

主要功能

  • 使用MediaPipe检测手部姿态。
  • 通过计算手指之间的距离来调整系统音量。
  • 使用AppleScript来控制Mac系统的音量。

Mac版本所需依赖库

pip install mediapipe
pip install numpy
pip install opencv-python
pip install applescript

代码实现

import cv2
import mediapipe as mp
from ctypes import cast, POINTER
import applescript as al
import time
import math
import numpy as npclass HandControlVolume:def __init__(self):self.mp_drawing = mp.solutions.drawing_utilsself.mp_drawing_styles = mp.solutions.drawing_stylesself.mp_hands = mp.solutions.handsdef recognize(self):fpsTime = time.time()cap = cv2.VideoCapture(0)resize_w = 640resize_h = 480rect_height = 0rect_percent_text = 0with self.mp_hands.Hands(min_detection_confidence=0.7,min_tracking_confidence=0.5,max_num_hands=2) as hands:while cap.isOpened():success, image = cap.read()image = cv2.resize(image, (resize_w, resize_h))if not success:print("空帧.")continueimage.flags.writeable = Falseimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)image = cv2.flip(image, 1)results = hands.process(image)image.flags.writeable = Trueimage = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)if results.multi_hand_landmarks:for hand_landmarks in results.multi_hand_landmarks:self.mp_drawing.draw_landmarks(image,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())landmark_list = []for landmark_id, finger_axis in enumerate(hand_landmarks.landmark):landmark_list.append([landmark_id, finger_axis.x, finger_axis.y, finger_axis.z])if landmark_list:thumb_finger_tip = landmark_list[4]thumb_finger_tip_x = math.ceil(thumb_finger_tip[1] * resize_w)thumb_finger_tip_y = math.ceil(thumb_finger_tip[2] * resize_h)index_finger_tip = landmark_list[8]index_finger_tip_x = math.ceil(index_finger_tip[1] * resize_w)index_finger_tip_y = math.ceil(index_finger_tip[2] * resize_h)finger_middle_point = (thumb_finger_tip_x + index_finger_tip_x) // 2, (thumb_finger_tip_y + index_finger_tip_y) // 2thumb_finger_point = (thumb_finger_tip_x, thumb_finger_tip_y)index_finger_point = (index_finger_tip_x, index_finger_tip_y)image = cv2.circle(image, thumb_finger_point, 10, (255, 0, 255), -1)image = cv2.circle(image, index_finger_point, 10, (255, 0, 255), -1)image = cv2.circle(image, finger_middle_point, 10, (255, 0, 255), -1)image = cv2.line(image, thumb_finger_point, index_finger_point, (255, 0, 255), 5)line_len = math.hypot((index_finger_tip_x - thumb_finger_tip_x),(index_finger_tip_y - thumb_finger_tip_y))vol = np.interp(line_len, [50, 300], [0, 100])rect_height = np.interp(line_len, [50, 300], [0, 200])rect_percent_text = np.interp(line_len, [50, 300], [0, 100])al.run('set volume output volume ' + str(vol))cv2.putText(image, str(math.ceil(rect_percent_text)) + "%", (10, 350),cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)image = cv2.rectangle(image, (30, 100), (70, 300), (255, 0, 0), 3)image = cv2.rectangle(image, (30, math.ceil(300 - rect_height)), (70, 300), (255, 0, 0), -1)cTime = time.time()fps_text = 1 / (cTime - fpsTime)fpsTime = cTimecv2.putText(image, "FPS: " + str(int(fps_text)), (10, 70),cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)cv2.imshow('MediaPipe Hands', image)if cv2.waitKey(5) & 0xFF == 27:breakcap.release()

区别分析

  1. 音量控制方式

    • Windows版本:使用PyCaw库通过COM接口控制音量。
    • Mac版本:使用AppleScript控制音量。
  2. 依赖库

    • Windows版本:依赖PyCawcomtypes库。
    • Mac版本:依赖applescript库。
  3. 代码调整

    • Mac版本注释掉了与Windows音量控制相关的代码,并替换为AppleScript命令。
    • 音量计算部分的范围从Windows的音量范围映射变为0到100的映射。
  4. 平台适配

    • Windows程序利用PyCaw库与Windows系统进行交互,而Mac程序利用AppleScript与Mac系统进行交互。

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

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

相关文章

EXSI 实用指南 2024 -编译环境 Ubuntu 安装篇(二)

1. 引言 在当今的虚拟化领域&#xff0c;VMware ESXi 是备受推崇的虚拟化平台&#xff0c;广泛应用于企业和个人用户中。它以卓越的性能、稳定的运行环境和丰富的功能&#xff0c;为用户提供了高效的硬件资源管理和简化的 IT 基础设施维护。然而&#xff0c;如何在不同操作系统…

LeetCode Day8|● 344.反转字符串(原地) ● 541. 反转字符串II(i可以大步跨越) ● 卡码网:54.替换数字(ACM模式多熟悉熟悉)

字符串part01 day8-1 ● 344.反转字符串整体思路代码实现总结 day8-2 ● 541. 反转字符串II整体思路代码实现总结 day8-3 ● 卡码网&#xff1a;54.替换数字题目解题思路代码实现总结 day8-1 ● 344.反转字符串 整体思路 字符串和数组的思路差不多 原地操作 代码实现 class…

非常好的新版网盘系统,是一款PHP网盘与外链分享程序,支持文件预览

这是一款PHP网盘与外链分享程序&#xff0c;支持所有格式文件的上传&#xff0c; 可以生成文件外链、图片外链、音乐视频外链&#xff0c;生成外链同时自动生成相应的UBB代码和HTML代码&#xff0c; 还可支持文本、图片、音乐、视频在线预览&#xff0c;这不仅仅是一个网盘&a…

算法思想总结:字符串

一、最长公共前缀 . - 力扣&#xff08;LeetCode&#xff09; 思路1&#xff1a;两两比较 时间复杂度mn 实现findcomon返回两两比较后的公共前缀 class Solution { public:string longestCommonPrefix(vector<string>& strs) {//两两比较 string retstrs[0];size…

MySQL里的累计求和

在MySQL中&#xff0c;你可以使用SUM()函数来进行累计求和。如果你想要对一个列进行累计求和&#xff0c;可以使用OVER()子句与ORDER BY子句结合&#xff0c;进行窗口函数的操作。 以下是一个简单的例子&#xff0c;假设我们有一个名为sales的表&#xff0c;它有两个列&#x…

Android之间互传消息之ServerSocket,Android服务端接收Socket发送的TCP

Android之间在在局域网下互传消息&#xff0c;咱就不用走云服务器了吧&#xff0c;让俩安卓设备&#xff0c;自己传呗 方式1 通过在安卓设备上搭建Web服务器接收数据&#xff0c;可参考 Android使用AndServer在安卓设备上搭建服务端(Java)(Kotlin)两种写法 方式2 本文章&…

Open3d入门 点云中的重要概念

点云是由一组三维空间中的点组成的数据结构&#xff0c;常用于计算机视觉、3D建模和地形图生成等领域。每个点包含空间坐标&#xff08;x, y, z&#xff09;以及其他属性&#xff08;如颜色、密度等&#xff09;。以下是一些重要的点云概念&#xff1a; 1. 体素 (Voxel) 体素…

Vue3+Vite+TS+Axios整合详细教程

1. Vite 简介 Vite是新一代的前端构建工具&#xff0c;在尤雨溪开发Vue3.0的时候诞生。类似于Webpack Webpack-dev-server。其主要利用浏览器ESM特性导入组织代码&#xff0c;在服务器端按需编译返回&#xff0c;完全跳过了打包这个概念&#xff0c;服务器随起随用。生产中利用…

赛氪网荣获2024年中国高校计算机教育大会合作伙伴荣誉

2024年7月13日&#xff0c;在黑龙江哈尔滨召开的“2024年中国高校计算机教育大会&#xff08;CCEC2024&#xff09;”&#xff0c;环球赛乐&#xff08;北京&#xff09;科技有限公司(以下简称”赛氪网“)凭借其在高等教育与科技创新领域的卓越贡献&#xff0c;荣幸地获得了本次…

Python基础——函数

函数 和c语言函数意义相同&#xff0c;函数定义基础语法如下。 def 函数名&#xff08;传入参数&#xff09;:函数体return 返回值 展示自定义一个函数并对其进行封装。 # 自定义函数字符串长度计算str1 "itcast" count0 for x in str1:count 1 print(f"{c…

昇思25天学习打卡营第20天|CycleGAN图像风格迁移互换

模型简介 CycleGAN(Cycle Generative Adversarial Network) 即循环对抗生成网络&#xff0c;来自论文 Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks 。该模型实现了一种在没有配对示例的情况下学习将图像从源域 X 转换到目标域 Y 的方法。…

0基础学会在亚马逊云科技AWS上利用SageMaker、PEFT和LoRA高效微调AI大语言模型(含具体教程和代码)

项目简介&#xff1a; 小李哥今天将继续介绍亚马逊云科技AWS云计算平台上的前沿前沿AI技术解决方案&#xff0c;帮助大家快速了解国际上最热门的云计算平台亚马逊云科技AWS上的AI软甲开发最佳实践&#xff0c;并应用到自己的日常工作里。本次介绍的是如何在Amazon SageMaker上…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(九)-无人机服务区分离

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…

Solidworks工程图替换参考零件

1.用solidworks选择工程图文件&#xff0c;点击参考。 2.双击文件名 3.选择新的参考零件&#xff0c;点击确定。

[ruby on rails]部署时候产生ActiveRecord::PreparedStatementCacheExpired错误的原因及解决方法

一、问题&#xff1a; 有时在 Postgres 上部署 Rails 应用程序时&#xff0c;可能会看到 ActiveRecord::PreparedStatementCacheExpired 错误。仅当在部署中运行迁移时才会发生这种情况。发生这种情况是因为 Rails 利用 Postgres 的缓存准备语句(PreparedStatementCache)功能来…

力扣第406场周赛

力扣第406场周赛 100352. 交换后字典序最小的字符串 - 力扣&#xff08;LeetCode&#xff09; 贪心&#xff0c;从 0 0 0开始扫描到 n n n如果有一个可以交换的就立马交换 class Solution { public:string getSmallestString(string s) {for(int i1;i<s.size();i){if(s[i…

【PyQt】

PyQT5线程基础&#xff08;2&#xff09; 线程案例案例一案例二 线程案例 案例一 案例一代码通过线程实现点击按钮向线程传输地址&#xff0c;程序等待20秒后&#xff0c;返回结果。 通过QtDesigner创建如下图所示的界面ui&#xff0c;并用UIC工具转成对应的py文件。 Ui_tes…

C语言之指针的奥秘(三)

一、字符指针变量 在指针的类型中&#xff0c;有字符指针char*&#xff0c;一般使用&#xff1a; #include<stdio.h> int main() {char ch w;char* p &ch;*p w;return 0; } 还有一种方式&#xff1a; #include<stdio.h> int main() {const char* p &qu…

123456

截止2023年10月&#xff0c;目前已公开的官方矢量数据有3个网站&#xff0c;按照公开时间顺序分别是&#xff1a;1&#xff09;全国地理信息资源目录服务系统&#xff1b;2&#xff09;西藏自治区自然资源厅&#xff1b;3&#xff09;福建省标准地图服务。我们将持续更新公开的…

2024较火的软件宣传单页HTML源码

源码介绍 2024较火的软件宣传单页HTML源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果 效果截图 源码获取 2024较火的软件宣传单页HTML源码