手势音量识别

项目大概:

OpenCV实现手势音量控制:使用OpenCV和mediapipe库进行手势识别,并利用手势距离控制电脑音量。

今天在看一个大佬的文章的时候发现了好玩的东西,可以用手势控制电脑音量,

原文如下:

OpenCV实现手势音量控制-CSDN博客

他的文章非常详细非常好。

1.问题出现

但是我在将源码赋值下来跑的时候就发现了一个问题,每次启动之后直接就退出了,摄像头启动后也是马上关闭。然后就查了很多资料也没有发现什么结果。加上我对openCV也不是很了解,于是我直接询问了ChatGPT哈哈哈

不得不说openAI真的做的太好了,我先让他分析了一下大佬的这段代码

2.chatGPT解决问题

然后是这样回复我的,图片太大不好截图,我就复制内容了

(1)先让它理解这段代码

这段代码是一个用于手势控制电脑音量的程序,主要通过使用MediaPipe库检测摄像头捕捉的图像中的手势,计算手指间的距离,并将其映射到电脑音量和矩形显示上。以下是代码的主要功能和结构:

  1. 导入必要的库:
  • `cv2`: OpenCV库,用于图像处理和显示。

  • `mediapipe`: 提供了用于手部检测的API。

  • `pycaw`: 用于控制系统音量的库。

  • `time`, `math`, `numpy`: 基本的时间、数学和数组处理库。

  1. 创建 `HandControlVolume` 类:
  • 在初始化方法中,设置了Mediapipe手部检测和绘制的相关对象,以及获取系统音量范围的对象。
  1. `recognize` 方法:
  • 打开摄像头并设置一些视频参数,包括分辨率和画面显示的一些参数。

  • 使用 `mp.solutions.hands` 创建手部检测对象。

  • 在循环中,处理每一帧图像:

  • 通过 `hands.process` 处理图像,得到手部关键点的坐标。

  • 遍历检测到的每只手,获取手指的关键点坐标。

  • 根据手指关键点的坐标计算手指长度,并将手指长度映射到电脑音量和矩形显示上。

  • 更新电脑音量。

  • 在图像上标注手指和连接线,并显示矩形和帧率。

  • 使用 OpenCV 的窗口显示图像,按 ESC 键退出循环。

  1. 关于音量控制:
  • 使用 `pycaw` 库获取系统音量范围。

  • 使用 `self.volume.SetMasterVolumeLevel` 方法设置电脑音量。

  1. 关于图像显示:
  • 使用 OpenCV 在图像上绘制手指关键点、连接线、矩形和帧率。

  • 使用 `cv2.imshow` 显示图像,按 ESC 键退出循环。

  1. 其他:
  • 代码中对帧率、图像显示和音量设置进行了一些调整和控制。

总体来说,这段代码结合了手势检测和音量控制,通过摄像头捕捉手势并映射到系统音量上,以及通过图形界面展示手势和音量信息。

(2)再让它分析问题

然后我就告诉他我的问题,会闪退,他是这样分析的

如果程序在打开摄像头后立即退出,有几个可能的原因需要检查:

  1. **摄像头连接问题:** 确保摄像头正常工作,可以尝试使用其他摄像头或检查摄像头的连接状态。

  2. **摄像头读取问题:** 在代码中,有一行 `cap = cv2.VideoCapture(0)` 用于打开摄像头。确保摄像头设备的索引正确(通常是 0),或者尝试使用其他索引。

  3. **错误处理:** 在循环内有一个 `cap.isOpened()` 的条件判断,如果摄像头未成功打开,会打印 "空帧" 并继续下一次循环。检查是否有打印出 "空帧",这可能是导致程序退出的原因之一。

  4. **窗口名称检查:** 在退出条件中,有一行 `cv2.getWindowProperty('MediaPipe Hands', cv2.WND_PROP_VISIBLE) < 1 `,确保这个窗口名称与实际显示的窗口名称匹配。或者,可以直接使用 `cv2.waitKey(5) & 0xFF == 27` 作为退出条件。

  5. **MediaPipe Hands 库版本:** 检查使用的 `mediapipe` 库的版本是否与代码兼容。某些版本的库可能会导致问题,可以尝试更新或降级库版本。

尝试解决以上问题中的一个或多个,看看是否能够解决程序瞬间退出的问题。如果问题仍然存在,可以尝试在程序中加入更多的输出语句,以便更好地调试并定位问题所在。

注意我标红的部分

前几条我都检查了没问题,到这里我就发现了这个退出条件

然后修改为它给的条件直接完美运行

(3)发现问题所在

于是问题就来了,为什么这个判断条件会导致程序直接退出呢

原因如下

这个条件 cv2.getWindowProperty('MediaPipe Hands', cv2.WND_PROP_VISIBLE) < 1 的目的是检查名为 'MediaPipe Hands' 的窗口是否可见。如果这个窗口不可见(比如被关闭了),条件的值就会小于 1,触发退出循环。

然而,如果 'MediaPipe Hands' 窗口不存在,cv2.getWindowProperty 可能会抛出异常,导致程序崩溃。因此,建议在使用这个条件之前,确保你已经创建了一个具有相同名称的窗口。

在大佬的代码中,使用 cv2.imshow('xyp', image) 创建的窗口名称是 'xyp ',而不是 'MediaPipe Hands' 。因此,条件应该修改为 cv2.getWindowProperty('xyp', cv2.WND_PROP_VISIBLE) < 1

修改后的退出条件如下:

其实就是对代码的不熟悉导致的问题罢了哈哈哈哈哈哈解决啦~~~

3.修改后的完美运行代码

python 复制代码
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 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.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
                if cv2.waitKey(5) & 0xFF == 27 or cv2.getWindowProperty('xyp', cv2.WND_PROP_VISIBLE) < 1:
                    break
            cap.release()
control = HandControlVolume()
control.recognize()

结语

发现问题就解决问题,最后也谢谢大佬提供的代码供大家学习

拜拜ヾ( ̄▽ ̄)Bye~Bye~

相关推荐
程序员小远4 分钟前
软件测试常见Bug清单
自动化测试·软件测试·python·功能测试·测试工具·测试用例·bug
guoji778818 分钟前
安全与对齐的深层博弈:Gemini 3.1 Pro 安全护栏与对抗测试深度拆解
人工智能·安全
实在智能RPA26 分钟前
实在 Agent 和通用大模型有什么不一样?深度拆解 AI Agent 的感知、决策与执行逻辑
人工智能·ai
独隅30 分钟前
PyTorch 模型部署的 Docker 配置与性能调优深入指南
人工智能·pytorch·docker
lihuayong37 分钟前
OpenClaw 系统提示词
人工智能·prompt·提示词·openclaw
黑客说1 小时前
AI驱动剧情,解锁无限可能——AI游戏发展解析
人工智能·游戏
踩着两条虫1 小时前
AI驱动的Vue3应用开发平台深入探究(十):物料系统之内置组件库
android·前端·vue.js·人工智能·低代码·系统架构·rxjava
小仙女的小稀罕1 小时前
听不清重要会议录音急疯?这款常见AI工具听脑AI精准转译
开发语言·人工智能·python
书到用时方恨少!1 小时前
Python random 模块使用指南:从入门到精通
开发语言·python