超详细!Opencv手势控制音量!附源码!

效果演示:

在这里插入图片描述

废话不多说!直接上源码!下面写有所有代码注释!!

import cv2
import mediapipe as mp   #它包含了各种预训练的机器学习模型,可以用于姿势估计、手势识别等任务
from ctypes import cast, POINTER #ctypes 是 Python 的一个外部函数库,允许调用动态链接库中的函数
from comtypes import CLSCTX_ALL #用于操作 COM 对象
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume#是一个用于操作 Windows 音频控制的库。它被用于获取和控制计算机的音频音量。
import time
import math
import numpy as np


class 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是指向音频端点音量接口的指针
        self.volume.SetMute(0, None)#0关闭静音 1打开静音
        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

        #min_detection_confidence此参数设置手部检测被视为成功所需的最小置信度值(介于 0.01.0 之间)
        #min_tracking_confidence此参数设置手部关键点被视为成功追踪所需的最小置信度值(介于 0.01.0 之间)
        #max_num_hands此参数设置要检测的最大手数。
        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() #success是一个布尔值,表示是否成功读取了一帧图像。image是读取的图像
                image = cv2.resize(image, (resize_w, resize_h))  #设置视频分辨率

                if not success:
                    print("空帧.")
                    continue

                #将图像设置为不可写入,以保护数据
                image.flags.writeable = False
                #将图像从BGR颜色空间转换为RGB颜色空间,模型的输入是RGB图像
                image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
                # 镜像,对图像进行水平翻转,可能是为了更好地适应MediaPipe模型的期望输入
                image = cv2.flip(image, 1)
                # 对输入的图像进行处理。处理后,results 包含了手部追踪的结果,可能包括检测到的手部数量、手部关键点的坐标等多种信息,
                results = hands.process(image)

                # 将图像设置为可写入,以便可以在图像上绘制标注
                image.flags.writeable = True
                # 将图像从RGB颜色空间转换为BGR颜色空间
                image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

                # 判断是否有手掌
                if results.multi_hand_landmarks:
                    # 遍历每个手掌
                    for hand_landmarks in results.multi_hand_landmarks:
                        # 在画面标注手掌所有关键点并连线。draw_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 = []
                        #hand_landmarks.landmark 包含了手部关键点的id以及坐标信息。通过 enumerate 函数遍历每个关键点
                        for landmark_id, finger_axis in enumerate(hand_landmarks.landmark):
                            landmark_list.append([landmark_id, finger_axis.x, finger_axis.y,finger_axis.z])
                       #landmark_list中共存入21个关键点信息。
                      #  0-手腕
                      #  1-大拇指第一关节   2-大拇指第二关节   3-大拇指第三关节   4-大拇指指尖
                      #  5-食指第一关节     6-食指第二关节   7-食指第三关节   8-食指指尖
                      #  9-中指第一关节     10-中指第二关节  11-中指第三关节  12-中指指尖
                      # 13-无名指第一关节    14-无名指第二关节 15-无名指第三关节 16-无名指指尖
                      # 17-小指第一关节      18-小指第二关节  19-小指第三关节  20-小指指尖

                        if landmark_list:

                            # 获取大拇指指尖坐标
                            thumb_finger_tip = landmark_list[4]
                            #thumb_finger_tip也有4个信息。0-关键点ID  1-x坐标(归一化范围0-1)  2-y坐标(归一化范围0-1)  3-z坐标(归一化范围0-1)
                            #math.ceil函数用于向上取整,确保获得的坐标是整数。
                            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
                            #大拇指指尖坐标
                            thumb_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)

                            # 画2点连线
                            image = cv2.line(image,
                                             thumb_finger_point,  #大拇指坐标(起点)
                                             index_finger_point,  #食指坐标(终点)
                                             (255, 0, 255), #连线颜色
                                             5)          #线宽
                            # 勾股定理计算两点间连线的长度。 math.hypot(x,y)计算x,y平方和的平方根
                            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]

                            # # 将俩指间线的长度映射到[min_volume, max_volume]范围内的音量值
                            vol = np.interp(line_len,
                                            [50, 300],  #line_len的范围
                                            [min_volume,#音量范围的最小值
                                             max_volume])   #音量范围的最大值
                            # 将俩指间线的长度映射到[0, 200]范围内的 矩形高度
                            rect_height = np.interp(line_len, [50, 300], [0, 200])
                            # 将俩指间线的长度映射到[0, 100]范围内的 矩形高度百分比
                            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,                  #字体类型,PLAIN 表示简单的字体
                            2,                              #字体大小
                            (255, 0, 0),                       #字体颜色
                            3)                              #字体线宽
                #画音量矩形方框
                image = cv2.rectangle(image,
                                      (30, 100),  #矩形左上角坐标(x,y)
                                      (50, 300),  #矩形右下角坐标(x,y)
                                      (255, 0, 0),#颜色
                                      3)          #矩形线宽
                #画音量实心矩形
                image = cv2.rectangle(image,
                                      (30, math.ceil(300 - rect_height)),#矩形左上角坐标(x,y)
                                      (50, 300),                         #矩形右下角坐标(x,y)
                                      (255, 0, 0),                       #颜色
                                      -1)                                #矩形线宽,-1 表示实心矩形
                # 显示刷新率FPS
                cTime = time.time()   #获取当前时间戳
                fps_text = 1 / (cTime - fpsTime) #计算当前帧的帧率,即每秒处理的帧数。
                fpsTime = cTime #将当前时间戳赋值给fpsTime,以便下一帧计算帧率。

                #在图像上添加文本
                cv2.putText(image,
                            "FPS: " + str(int(fps_text)),#文本内容
                            (10, 70),               #文本的坐标位置
                            cv2.FONT_HERSHEY_PLAIN,      #字体类型,PLAIN 表示简单的字体
                            2,                  #字体大小
                            (255, 0, 0),           #字体颜色
                            3)                  #字体线宽
                # 显示画面
                cv2.imshow('xyp', image)
                #按下q键退出
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break
            #释放摄像头资源
            cap.release()



control = HandControlVolume()  #创建 HandControlVolume 类的实例
control.recognize()           # 调用 recognize 方法


  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值