当前位置:首页 » 《关于电脑》 » 正文

Python从0到100(七十二):Python OpenCV-OpenCV实现手势音量控制(文末送书)

2 人参与  2024年11月21日 18:02  分类 : 《关于电脑》  评论

点击全文阅读


在这里插入图片描述

前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、 计算机视觉、机器学习、神经网络以及人工智能相关知识,成为学习学习和学业的先行者!
欢迎大家订阅专栏:零基础学Python:Python从0到100最新最全教程!

今天来学习一下如何使用OpenCV实现手势音量控制,欢迎大家一起前来探讨学习~

一、需要的库及功能介绍

本次实验需要使用OpenCV和mediapipe库进行手势识别,并利用手势距离控制电脑音量。

导入库:

cv2:OpenCV库,用于读取摄像头视频流和图像处理。mediapipe:mediapipe库,用于手部关键点检测和手势识别。ctypes和comtypes:用于与操作系统的音频接口进行交互。pycaw:pycaw库,用于控制电脑音量。

功能:

初始化mediapipe和音量控制模块,获取音量范围。打开摄像头,读取视频流。对每一帧图像进行处理: 转换图像为RGB格式。使用mediapipe检测手部关键点。如果检测到手部关键点: 在图像中标注手指关键点和手势连线。解析手指关键点坐标。根据拇指和食指指尖的坐标,计算手势距离。将手势距离转换为音量大小,并控制电脑音量。 显示处理后的图像。 循环执行前述步骤,直到手动停止程序或关闭摄像头。

注意事项:

在运行代码之前,需要安装相关库(opencv、mediapipe、pycaw)。需要连接音频设备并使其可访问。检测到多个手部时,只处理第一个检测到的手部。检测到手指关键点时,将索引指为0的关键点作为拇指的指尖,索引指为1的关键点作为食指的指尖。

cv2.VideoCapture()函数参数问题

在这里插入图片描述
这并没有错。但在树莓派上调用时需要更改参数,改为:

cap = cv2.VideoCapture(1)

调用电脑摄像头时:
电脑在用cv2.VideoCapture(0)时,程序结束后会有报错:

[ WARN:0] SourceReaderCB::~SourceReaderCB terminating async callback

需要改为:

cv2.VideoCapture(0,cv2.CAP_DSHOW)

二、导入所需要的模块

# 导入OpenCVimport cv2# 导入mediapipeimport mediapipe as mp# 导入电脑音量控制模块from ctypes import cast, POINTERfrom comtypes import CLSCTX_ALLfrom pycaw.pycaw import AudioUtilities, IAudioEndpointVolume# 导入其他依赖包import timeimport mathimport numpy as np

三、初始化 HandControlVolume 类

class HandControlVolume:    def __init__(self):        """        初始化 HandControlVolume 类的实例        初始化 mediapipe 对象,用于手部关键点检测和手势识别。        获取电脑音量接口,并获取音量范围。        """        # 初始化 medialpipe        self.mp_drawing = mp.solutions.drawing_utils        self.mp_drawing_styles = mp.solutions.drawing_styles        self.mp_hands = mp.solutions.hands        # 获取电脑音量范围        devices = 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()
初始化 mediapipe 对象,用于手部关键点检测和手势识别。获取电脑音量接口,并获取音量范围。

四、主函数

1.计算刷新率

初始化刷新率的计算,记录当前时间作为初始时间。

使用OpenCV打开视频流,此处读取摄像头设备,默认使用设备ID为0。

设置视频流的分辨率为指定的resize_w和resize_h大小,并将图像resize为该尺寸。

在使用hands对象之前,使用with语句创建一个上下文环境,设置手部检测和追踪的相关参数,包括最小检测置信度、最小追踪置信度和最大手的数量。

进入循环,判断视频流是否打开。使用cap.read()函数从视频流中读取一帧图像,返回的success表示是否读取成功,image则是读取到的图像。

对读取到的图像进行resize,将其调整为指定的大小。如果读取失败,则打印提示信息并继续下一次循环。

# 主函数    def recognize(self):        # 计算刷新率        fpsTime = time.time()        # OpenCV读取视频流        cap = cv2.VideoCapture(0)        # 视频分辨率        resize_w = 640        resize_h = 480        # 画面显示初始化参数        rect_height = 0        rect_percent_text = 0        with 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("空帧.")                    continue

2.提高性能

将图像的可写标志image.flags.writeable设置为False,以便进行内存优化。

将图像从BGR格式转换为RGB格式,这是因为MediaPipe模型处理的输入要求为RGB格式。

对图像进行水平翻转,即镜像操作,以使图像更符合常见的镜像显示。

使用MediaPipe模型对图像进行处理,得到结果。

将图像的可写标志image.flags.writeable设置为True,以重新启用对图像的写入操作。

将图像从RGB格式转换回BGR格式,以便后续的显示和处理。

这些优化操作旨在提高程序的性能和效率。其中,将图像的可写标志设置为False可以减少不必要的内存拷贝,转换图像的格式和镜像操作则是为了符合MediaPipe模型的输入要求和更好地进行手势识别。最后,将图像转换回BGR格式是为了与OpenCV的显示函数兼容。

                # 提高性能                image.flags.writeable = False                # 转为RGB                image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)                # 镜像                image = cv2.flip(image, 1)                # mediapipe模型处理                results = hands.process(image)                image.flags.writeable = True                image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

3.判断是否有手掌

判断results.multi_hand_landmarks是否存在,即是否检测到手掌。如果存在,则继续执行下面的代码。

遍历results.multi_hand_landmarks中的每个hand_landmarks,即遍历每个检测到的手掌。

使用self.mp_drawing.draw_landmarks函数将检测到的手掌标注在图像上,包括手指的关键点和手指之间的连接线。

# 判断是否有手掌                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())

4.解析手指,存入各个手指坐标

首先解析手指的坐标,并存入landmark_list列表中。然后,根据手指的坐标计算出大拇指和食指的指尖坐标,以及两者的中间点坐标。接下来,绘制了大拇指、食指和两者之间的连线,并使用勾股定理计算了两个指尖之间的长度。

创建一个空的landmark_list列表用于存储手指坐标。

遍历手部关键点的每个元素,将每个关键点的id、x、y和z坐标存储在一个列表中,然后将该列表添加到landmark_list中。

判断landmark_list是否不为空,如果不为空,继续执行下面的代码。

从landmark_list中获取大拇指指尖坐标的列表项,然后计算出在图像上的像素坐标。

从landmark_list中获取食指指尖坐标的列表项,然后计算出在图像上的像素坐标。

计算大拇指指尖和食指指尖的中间点坐标。

绘制大拇指和食指的指尖点,以及中间点。

绘制大拇指和食指之间的连线。

使用勾股定理计算大拇指指尖和食指指尖之间的长度,保存在line_len中。

 # 解析手指,存入各个手指坐标                        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) // 2                            # print(thumb_finger_tip_x)                            thumb_finger_point = (thumb_finger_tip_x, thumb_finger_tip_y)                            index_finger_point = (index_finger_tip_x, index_finger_tip_y)                            # 画指尖2点                            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)                            # 画2点连线                            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))

5.获取电脑最大最小音量

实现获取电脑的最大和最小音量,并将指尖的长度映射到音量范围和矩形显示上,然后将映射后的音量值设置为电脑的音量。具体过程如下:

self.volume_range[0]和self.volume_range[1]分别获取电脑的最小音量和最大音量。

np.interp函数将指尖的长度line_len映射到从50到300的范围,再映射到最小音量和最大音量的范围,得到音量值vol。

np.interp函数将指尖的长度line_len映射到从50到300的范围,再映射到从0到200的范围,得到矩形的高度rect_height。

np.interp函数将指尖的长度line_len映射到从50到300的范围,再映射到从0到100的范围,得到矩形百分比显示的数值rect_percent_text。

self.volume.SetMasterVolumeLevel方法将音量值vol设置为电脑的音量。

# 获取电脑最大最小音量                            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)

6.显示矩形

cv2.putText函数来在图像上显示矩形框的百分比值;
cv2.rectangle函数来绘制矩形框并填充颜色;
cv2.putText函数来在图像上显示当前帧的刷新率FPS;
cv2.imshow函数来显示处理后的图像;
cv2.waitKey函数等待按键输入,当按下ESC键或关闭窗口时退出程序;
HandControlVolume类的recognize方法调用了手势识别的功能。

# 显示矩形                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)                # 显示刷新率FPS                cTime = time.time()                fps_text = 1 / (cTime - fpsTime)                fpsTime = cTime                cv2.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:                    break            cap.release()# 开始程序control = HandControlVolume()control.recognize()

五、实战演示

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
通过演示我们可以发现,食指与大拇指之间在屏幕中的的距离越远,那么我们的音量会越大,反之越小,实现了通过手势对音量的控制。

六、源码分享

import cv2import mediapipe as mpfrom ctypes import cast, POINTERfrom comtypes import CLSCTX_ALLfrom pycaw.pycaw import AudioUtilities, IAudioEndpointVolumeimport timeimport mathimport numpy as npclass HandControlVolume:    def __init__(self):        # 初始化medialpipe        self.mp_drawing = mp.solutions.drawing_utils        self.mp_drawing_styles = mp.solutions.drawing_styles        self.mp_hands = mp.solutions.hands        # 获取电脑音量范围        devices = 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()    # 主函数    def recognize(self):        # 计算刷新率        fpsTime = time.time()        # OpenCV读取视频流        cap = cv2.VideoCapture(0)        # 视频分辨率        resize_w = 640        resize_h = 480        # 画面显示初始化参数        rect_height = 0        rect_percent_text = 0        with 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("空帧.")                    continue                # 提高性能                image.flags.writeable = False                # 转为RGB                image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)                # 镜像                image = cv2.flip(image, 1)                # mediapipe模型处理                results = hands.process(image)                image.flags.writeable = True                image = 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) // 2                            # print(thumb_finger_tip_x)                            thumb_finger_point = (thumb_finger_tip_x, thumb_finger_tip_y)                            index_finger_point = (index_finger_tip_x, index_finger_tip_y)                            # 画指尖2点                            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)                            # 画2点连线                            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)                # 显示刷新率FPS                cTime = time.time()                fps_text = 1 / (cTime - fpsTime)                fpsTime = cTime                cv2.putText(image, "FPS: " + str(int(fps_text)), (10, 70),                            cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)                # 显示画面                cv2.imshow('xyp', image)                if cv2.waitKey(5) & 0xFF == 27 or cv2.getWindowProperty('MediaPipe Hands', cv2.WND_PROP_VISIBLE) < 1:                    break            cap.release()control = HandControlVolume()control.recognize()

文末送书

Dream送书活动——第四十五期
《AI全能助手:ChatGPT工作效率提升技巧与案例》免费包邮送三本! 参与方式:

1.点赞收藏文章
2.在评论区留言:人生苦短,我用Python!(多可评论三条)
3.随机抽取4位免费送出!
4.截止时间: 2024-11-19

本期推荐:
AI全能助手:ChatGPT工作效率提升技巧与案例
在这里插入图片描述
京东:https://item.jd.com/14628194.html

13个知识模块+60个实操场景+AI工具使用方法,多场景落地新手辅助入门,轻松调教出24小时工作助手,让一人胜过一个团队。
关键点:
13个知识模块+60个实操场景:办公技能、求职面试、人际关系、文案写作、演讲提升、活动策划、电商营销、团队管理、实体创业、教育培训、金融行业、亲子教育、文创文旅
等领域皆有涉猎。多场景落地新手辅助入门:以职场演讲为例,本书从开头、结尾、金句、声音优化技巧、文案迭代等多个角度进行深入讲解,力求帮助读者透彻掌握这项技能。AI工具使用方法详解:详细介绍ChatGPT和文心一言等多种AI辅助工具的使用技巧,并做了演示帮助读者理解。形成完整认知体系:对AI给出的回答做了客观中肯的点评,力求给读者提供独特的认知与思考视角,帮助读者形成一套完整的认知与思考逻辑。

内容简介:
本书结合作者的职场工作经验和AI使用经验,讲解了AI工具在不同工作场景中的使用方法和技巧,能帮助读者快速提升工作效率。全书共设置了13个知识模块,每个知识模块都设置了场景模拟与实操案例。真正做到了让读者看得懂,学得会,用得上。全书语言通俗易懂,案例来自实际工作,并有详细的思路点评,能够帮助读者轻松入门并建立系统认知,吃透AI运用的底层逻辑,真正拥有自己的AI全能助手。本书适合想要提升职场办公技能与效率的人士阅读,同样适合作为学校、企业和教育机构的培训教材。

作者简介:
本书结合作者的职场工作经验和 AI 使用经验,讲解了 AI 工具在不同工作场景中的使用方法和技巧,能帮助读者快速提升工作效率。全书共设置了13个知识模块,每个知识模块都设置了场景模拟与实操案例。真正做到了让读者看得懂,学得会,用得上。
全书语言通俗易懂,案例来自实际工作,并有详细的思路点评,能够帮助读者轻松入门并建立系统认知,吃透 AI 运用的底层逻辑,真正拥有自己的 AI 全能助手。本书适合想要提升职场办公技能与效率的人士阅读,同样适合作为学校、企业和教育机构的培训教材。


点击全文阅读


本文链接:http://zhangshiyu.com/post/189911.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1