当前位置:   article > 正文

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

超详细!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 方法


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/80246
推荐阅读
相关标签
  

闽ICP备14008679号