赞
踩
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.0 和 1.0 之间)
#min_tracking_confidence此参数设置手部关键点被视为成功追踪所需的最小置信度值(介于 0.0 和 1.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 方法
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。