Python | 基于Mediapipe框架的手势识别系统

news2024/11/15 15:26:56

一、项目要求

        1、题目

         本题着力于解决会商演示系统中的非接触式人机交互问题,具体而言,其核心问题就是通过计算机视觉技术实现对基于视频流的手势动作进行实时检测和识别。通过摄像头采集并识别控制者连续的手势动作,完成包括点击、平移、缩放、抓取、旋转等5种基本交互功能,除此之外还可针对不同客户的具体业务需求,可在这五种基本手势动作的基础上进行扩展。

        选手可利用传统计算机视觉方法或基于机器学习/深度学习的方法,通过对基于摄像头采集的连续视频输入中用户的手势动作进行检测和识别,输出相应的控制信号,从而完成会商演示系统的交互。结合业务需求,进行算法模型的开发,实现真实环境下对用户控制手势的识别,达到实时交互的目的。

        2、技术要求与指标

        能够实现对摄像头拍摄的视频流中控制手势进行检测和识别,并以此实时控制演示系统。指标要求:

        (1)每一种手势动作的检测识别准确率达到80%以上

        (2)每一个手势动作的检测和识别时间(即从执行完手势动作到输出结果之间的时间)不超过500ms

        一般开发环境以及开发语言不限(可使用Python+OpencCV,深度 学习框架可使用PyTorch、TensorFlow 等)。开发过程允许使用开源代码,但需要在文档中详细注明,且其许可证需保证商业可用,不能采用商用模块。

二、运行环境

        本系统能够运行在基于PC操作系统Windows环境下,要求Windows操作系统安装Python 3.9 及以上环境, 要求安装相关库OpenCV、Mediapipe、PyQt5、Qtawesome。

安装OpenCV —— python3.9安装OpenCV

安装Mediapipe —— Python安装Mediapipe

安装PyQt5 —— PyCharm安装PyQt5及其工具(Qt Designer、PyUIC、PyRcc)

安装Qtawesome —— Python安装Qtawesome

三、效果

        1、基本动作

                包括点击、抓取、平移、缩放、旋转5个基本动作

        2、扩展动作

                包括数字一、二、三、四、五、六、我爱你

四、代码

        1、项目结构

        2、项目代码

                (1)布局代码

# -*- coding: utf-8 -*-
import sys

import qtawesome
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon


class WindowLayout(object):
    def __init__(self):
        # 容器
        self.central_widget = None

        # 组件
        self.close_button = None
        self.other_button = None
        self.minimize_button = None

        self.start_button = None
        self.camera_button = None

        self.camera_label = None
        self.result_label = None
        self.running_label = None

    def setupUi(self, MainWindow):
        # 平台
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1250, 730)

        # ------------- 代码布局 ------------- #
        self.central_widget = QtWidgets.QWidget(MainWindow)
        self.central_widget.setObjectName("central_widget")
        MainWindow.setCentralWidget(self.central_widget) # 把容器放到平台上面

        # 关闭按钮
        self.close_button = QtWidgets.QPushButton(self.central_widget)
        self.close_button.setGeometry(QtCore.QRect(60, 60, 30, 30))
        self.close_button.setObjectName("close_button")
        # 空白按钮
        self.other_button = QtWidgets.QPushButton(self.central_widget)
        self.other_button.setGeometry(QtCore.QRect(120, 60, 30, 30))
        self.other_button.setObjectName("other_button")
        # 最小化按钮
        self.minimize_button = QtWidgets.QPushButton(self.central_widget)
        self.minimize_button.setGeometry(QtCore.QRect(180, 60, 30, 30))
        self.minimize_button.setObjectName("minimize_button")

        # 打开摄像头 按钮
        self.camera_button = QtWidgets.QPushButton(self.central_widget)
        self.camera_button.setIcon(qtawesome.icon('fa5s.video', color='white'))
        self.camera_button.setText(" 打开相机")
        self.camera_button.setGeometry(QtCore.QRect(60, 130, 150, 40))
        self.camera_button.setObjectName("camera_button")
        # 手势检测 按钮
        self.start_button = QtWidgets.QPushButton(self.central_widget)
        self.start_button.setIcon(qtawesome.icon('fa5s.eye', color='white'))
        self.start_button.setText(" 手势检测")
        self.start_button.setGeometry(QtCore.QRect(60, 190, 150, 40))
        self.start_button.setObjectName("start_button")

        # 检测结果展示部分
        self.result_label = QtWidgets.QLabel(self.central_widget)
        self.result_label.setText("结果")
        self.result_label.setGeometry(QtCore.QRect(50, 490, 170, 170))
        self.result_label.setObjectName("result_label")

        # 摄像头展示部分
        self.camera_label = QtWidgets.QLabel(self.central_widget)
        self.camera_label.setText("手势识别")
        self.camera_label.setGeometry(QtCore.QRect(300, 60, 900, 600))
        self.camera_label.setObjectName("camera_label")

        # 程序运行状态
        self.running_label = QtWidgets.QLabel(self.central_widget)
        self.running_label.setText("程序运行状态")
        self.running_label.setGeometry(QtCore.QRect(310, 665, 900, 40))
        self.running_label.setObjectName("running_label")

        # ------------- 界面美化 ------------- #
        self.central_widget.setStyleSheet('''
            QWidget#central_widget{
                border-radius:7px;
                border-image:url(background.png)};}
            ''')

        self.close_button.setStyleSheet('''
            QPushButton{
                background-color: rgba(247, 102, 119, 0.8);
                border-radius:8px;}
            QPushButton:hover{
                background-color: rgba(255, 0, 0, 0.7);}''')
        self.other_button.setStyleSheet('''
            QPushButton{
                background-color: rgba(250, 210, 116, 0.8);
                border-radius:8px;}
            QPushButton:hover{
                background-color: rgba(255, 255, 0, 0.8);}''')
        self.minimize_button.setStyleSheet('''
            QPushButton{
                background-color: rgba(50, 200, 50, 0.8);
                border-radius:8px;}
            QPushButton:hover{
                background-color: rgba(0, 250, 0, 0.8);}''')

        self.camera_button.setStyleSheet('''        
            QPushButton{
                border:none;
                color:white;
                font-size:15px;
                font-weight:bold;
                border-radius:8px;
                font-family:Roman times;
                background-color: rgba(200, 200, 200, 0.5);}
            QPushButton:hover{
                background-color: rgba(200, 200, 200, 0.6);}
        ''')

        self.start_button.setStyleSheet('''
            QPushButton{
                border:none;
                color:white;
                font-size:15px;
                font-weight:bold;
                border-radius:8px;
                font-family:Roman times;
                background-color: rgba(200, 200, 200, 0.4);}
            QPushButton:hover{
                background-color: rgba(200, 200, 200, 0.6);}
        ''')

        self.camera_label.setAlignment(Qt.AlignCenter)
        self.camera_label.setStyleSheet('''
                color:white;
                font-size:45px;
                font-weight:bold;
                font-family:Roman times;
                background-color: rgba(255, 255, 255, 0.3)
            ''')

        self.result_label.setAlignment(Qt.AlignCenter)
        self.result_label.setStyleSheet('''
                border-radius:5px;
                color:white;
                font-size:35px;
                font-weight:bold;
                font-family:Roman times;
                background-color: rgba(255, 255, 255, 0.3)
            ''')

        self.running_label.setStyleSheet('''
                color:white;
                font-size:16px;
                font-weight:bold;
                font-family:Roman times;
            ''')

        # 设置整体样式
        MainWindow.setWindowOpacity(1)  # 设置窗口透明度
        MainWindow.setAttribute(QtCore.Qt.WA_TranslucentBackground)  # 隐藏外围边框
        MainWindow.setWindowFlag(QtCore.Qt.FramelessWindowHint)  # 隐藏系统状态栏,并且生成的窗口用户不能移动和改变大小
        MainWindow.setWindowIcon(QIcon('Logo.ico'))  # 设置logo

        QtCore.QMetaObject.connectSlotsByName(MainWindow)



if __name__ == '__main__':
    # 创建一个Qt应用程序对象,用于管理应用程序的事件循环和窗口系统的交互。
    app = QtWidgets.QApplication(sys.argv)
    # 创建一个WindowLayout(自己写的类)对象,创建的时候自动进行初始化__init__
    windowLayout = WindowLayout()
    # 生成一个QtWidgets.QMainWindow对象,用于设置到 WindowLayout.setupUi() 方法中
    mainWindow = QtWidgets.QMainWindow()
    # 调用 WindowLayout.setupUi() 方法,将QtWidgets.QMainWindow对象作为参数传入
    windowLayout.setupUi(mainWindow)
    # 调用 QWidget.setupUi() 方法,展示界面
    mainWindow.show()
    # 调用系统方法进行界面关闭
    sys.exit(app.exec_())

                (2)逻辑代码

# -*- coding: utf-8 -*-
import math
import sys
from time import time

import cv2
import mediapipe as mp
from PyQt5.QtGui import *
from PyQt5.QtGui import QImage
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
from PyQt5.QtWidgets import QDesktopWidget

from window_layout import WindowLayout


class WindowLogic(QMainWindow, WindowLayout):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)

        # ====== UI逻辑属性 ====== #
        self.close_button.clicked.connect(self.close_window)            # 关闭窗口按钮
        self.minimize_button.clicked.connect(self.showMinimized)        # 最小化窗口按钮
        self.camera_button.clicked.connect(self.camera_judgement)       # 打开相机按钮
        self.start_button.clicked.connect(self.recognition_judgement)   # 手势识别按钮

        # ====== 相机属性 ====== #
        self.cap = cv2.VideoCapture()   # 相机
        self.source = 0                 # 相机标号
        self.WIN_WIDTH = 900    # 相机展示画面的宽度
        self.WIN_HEIGHT = 600   # 相机展示画面的高度

        # ====== 手势识别属性 ====== #
        self.sole_hand_name = ''            # 单一手势名称
        self.sole_hand_landmarks = []       # 单一手势坐标
        self.current_frame_name = []        # 当前帧中手势名称
        self.current_frame_landmarks = []   # 当前帧中手势坐标
        self.current_frame_msg = []         # 当前帧信息
        self.stream_frame_msg = []          # 视频流前30帧信息
        self.INF = 65535.                   # 角度错误值
        self.normal_unbend_angle = 49.      # 正常手指伸直阈值
        self.normal_threshold_angle = 65.   # 正常手指弯曲阈值
        self.thumb_threshold_angle = 53.    # 大拇指弯曲阈值
        self.isGestureRecognition_flag = False  # 是否打开手势识别标志
        self.resultRetainFrame = 0

        # ====== 手势控制属性 ====== #
        self.desktop = QDesktopWidget()
        self.screen_rect = self.desktop.screenGeometry()
        self.screen_width = self.screen_rect.width()
        self.screen_height = self.screen_rect.height()
        self.x = None
        self.y = None

        # ====== 时间属性 ====== #
        self.camera_start_time = None
        self.recognize_start_time = None

    # ================================== 打开相机 ================================== #
    def camera_judgement(self):
        """ 打开相机按钮 逻辑判断器 """
        # 打开摄像头
        if not self.cap.isOpened():
            self.running_label.setText(u"正在打开相机,请稍等...")
            QApplication.processEvents()
            self.camera_start_time = time()
            self.cap.open(self.source)
            self.running_label.setText("相机模块初始化时间 : {:.3f}".format(time() - self.camera_start_time) + 's')
            try:
                self.camera_button.setText(u' 关闭相机')
                self.show_camera()
            except:
                QMessageBox.about(self, '警告', '相机不能正常被打开')
        # 关闭摄像头,释放cap
        else:
            if self.isGestureRecognition_flag:
                QMessageBox.about(self, '提示', '请先关闭手势识别模块')
            else:
                self.cap.release()
                self.camera_button.setText(u' 打开相机')
                self.start_button.setText(u' 手势检测')
                self.running_label.setText(u'已关闭相机模块')

    def show_camera(self):
        """ 展示摄像头画面 """
        while self.cap.isOpened():
            ret, frame = self.cap.read()
            QApplication.processEvents()
            show = cv2.resize(frame, (self.WIN_WIDTH, self.WIN_HEIGHT))
            show = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
            show_image = QImage(show.data, show.shape[1], show.shape[0], QImage.Format_RGB888)
            self.camera_label.setPixmap(QPixmap.fromImage(show_image))
        self.camera_label.setPixmap(QPixmap(""))

    # ================================== 手势识别 ================================== #
    def recognition_judgement(self):
        """ 手势识别按钮 逻辑判断器 """
        if not self.cap.isOpened():
            QMessageBox.about(self, '提示', '请先打开摄像头')
        else:
            if not self.isGestureRecognition_flag:
                self.isGestureRecognition_flag = True
                self.start_button.setText(u' 关闭检测')
                self.gesture_recognize()
                self.isGestureRecognition_flag = False
                self.start_button.setText(u' 手势检测')
            else:
                self.isGestureRecognition_flag = False
                self.start_button.setText(u' 手势检测')
                self.running_label.setText(u'已关闭检测模块')
                self.result_label.setText("结果")

    def gesture_recognize(self):
        """ 手势识别 """
        recognize_start_time = time()
        self.running_label.setText(u"正在打开手势识别模块,请稍等...")
        QApplication.processEvents()
        mp_drawing = mp.solutions.drawing_utils
        mp_hands = mp.solutions.hands
        hands = mp_hands.Hands(static_image_mode=False, max_num_hands=2,
                               min_detection_confidence=0.75,
                               min_tracking_confidence=0.75)
        self.stream_frame_msg = []
        self.running_label.setText("检测模块初始化时间 : {:.3f}".format(time() - recognize_start_time) + 's')

        while self.isGestureRecognition_flag:
            self.recognize_start_time = time()
            QApplication.processEvents()
            ret, frame = self.cap.read()
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            result = hands.process(frame)
            frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)  # 颜色空间的转换

            self.current_frame_name = []
            self.current_frame_landmarks = []


            if result.multi_handedness:
                hand_num = len(result.multi_handedness)

                # 一只手
                if hand_num == 1:
                    if result.multi_hand_landmarks:

                        # 跑1次 for 循环
                        for hand_landmarks in result.multi_hand_landmarks:
                            mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)

                            self.sole_hand_name = ''
                            self.sole_hand_landmarks = []
                            for i in range(21):
                                x = hand_landmarks.landmark[i].x * frame.shape[1]
                                y = hand_landmarks.landmark[i].y * frame.shape[0]
                                self.sole_hand_landmarks.append((x, y))

                            if self.sole_hand_landmarks:
                                angle_list = self.get_hand_angle(self.sole_hand_landmarks)
                                self.sole_hand_name = self.recognize_static_gesture(angle_list)

                        self.current_frame_landmarks.append(self.sole_hand_landmarks)
                        self.current_frame_landmarks.append([])

                        self.current_frame_name.append(self.sole_hand_name)
                        self.current_frame_name.append('null')
                # 两只手
                elif hand_num == 2:
                    if result.multi_hand_landmarks:
                        # 跑2次 for 循环
                        for hand_landmarks in result.multi_hand_landmarks:
                            mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)

                            self.sole_hand_name = ''
                            self.sole_hand_landmarks = []

                            for i in range(21):
                                x = hand_landmarks.landmark[i].x * frame.shape[1]
                                y = hand_landmarks.landmark[i].y * frame.shape[0]
                                self.sole_hand_landmarks.append((x, y))

                            if self.sole_hand_landmarks:
                                angle_list = self.get_hand_angle(self.sole_hand_landmarks)
                                self.sole_hand_name = self.recognize_static_gesture(angle_list)

                            self.current_frame_landmarks.append(self.sole_hand_landmarks)
                            self.current_frame_name.append(self.sole_hand_name)

                    # 将当前手势的坐标、名称保存到 current_frame_msg 中
                self.current_frame_msg = [self.current_frame_landmarks, self.current_frame_name]
                # 将当前帧信息保存到 stream_frame_msg 中
                self.stream_frame_msg.append(self.current_frame_msg)

                self.recognize_dynamic_gesture()

                if len(self.stream_frame_msg) > 80:
                    j = self.stream_frame_msg[-40:-1]
                    j.append(self.stream_frame_msg[-1])
                    self.stream_frame_msg = j

            show_video = cv2.cvtColor(cv2.resize(frame, (self.WIN_WIDTH, self.WIN_HEIGHT)), cv2.COLOR_BGR2RGB)
            show_image = QImage(show_video.data, show_video.shape[1], show_video.shape[0], QImage.Format_RGB888)
            self.camera_label.setPixmap(QPixmap.fromImage(show_image))

        self.show_camera()

    def recognize_dynamic_gesture(self):
        """ 识别动态手势 """
        if len(self.stream_frame_msg) > 15:
            # 抓取
            if self.one_hand_judge_fist(-1) and not self.one_hand_judge_fist(-5):
                self.show_label('抓取/零')
            # 点击
            elif self.one_hand_judge_point(-1) and not self.one_hand_judge_point(-5):
                self.show_label('点击/一')
            # 平移
            elif self.one_hand_judge_spread(-1) and self.one_hand_judge_spread(-5) and self.one_hand_judge_spread(-10):
                if math.fabs(self.count_one_hand_delta_x(-1, -10)) >= 180 or \
                        math.fabs(self.count_one_hand_delta_x(-1, -5)) >= 90:
                    self.show_label('平移')
            elif self.two_hand_judge_spread(-1) and self.two_hand_judge_spread(-5) and self.two_hand_judge_spread(-10):
                # 缩放
                if (self.count_two_hand_delta_x(-1,-10,0) <= -50 and self.count_two_hand_delta_x(-1,-10,1) >= 50
                    and math.fabs(self.count_two_hand_delta_y(-1, -10, 0)) <= 20 and math.fabs(self.count_two_hand_delta_y(-1, -10, 1)) <= 20) or \
                        (self.count_two_hand_delta_x(-1,-10,0) >= 50 and self.count_two_hand_delta_x(-1,-10,1) <= -50
                         and math.fabs(self.count_two_hand_delta_y(-1, -10, 0)) <= 20 and math.fabs(self.count_two_hand_delta_y(-1, -10, 1)) <= 20):
                    self.show_label('缩放')
                # 旋转
                elif math.fabs(self.count_two_hand_delta_y(-1, -10, 0)) >= 50 \
                        and math.fabs(self.count_two_hand_delta_x(-1, -10, 0)) >= 50 \
                        and math.fabs(self.count_two_hand_delta_y(-1, -10, 1)) >= 50 \
                        and math.fabs(self.count_two_hand_delta_x(-1, -10, 1)) >= 50:
                    self.show_label('旋转')
            # I love Y
            elif self.two_hand_judge_thumbUp(-1) and self.two_hand_judge_thumbUp(-5) and self.two_hand_judge_thumbUp(-10):
                self.show_label('我爱你')
            # 手势 二
            elif self.one_hand_judge_two(-1) and not self.one_hand_judge_two(-5):
                self.show_label("二")
            # 手势 三
            elif self.one_hand_judge_three(-1) and not self.one_hand_judge_three(-5):
                self.show_label("三")
            # 手势 四
            elif self.one_hand_judge_four(-1) and not self.one_hand_judge_four(-5):
                self.show_label("四")
            # 手势 五
            elif self.one_hand_judge_spread(-1) and not self.one_hand_judge_spread(-5):
                self.show_label("五")
            # 手势 六
            elif self.one_hand_judge_six(-1) and not self.one_hand_judge_six(-5):
                self.show_label("六")

    def recognize_static_gesture(self, angle_list):
        """
        识别静态手势
        :param angle_list: 手势弯曲角度列表
        :return: 静态手势类型
        """
        gesture_type = None
        if self.INF not in angle_list:
            if (angle_list[0] > self.thumb_threshold_angle) and (angle_list[1] > self.normal_threshold_angle) \
                    and (angle_list[2] > self.normal_threshold_angle) and (angle_list[3] > self.normal_threshold_angle) \
                    and (angle_list[4] > self.normal_threshold_angle):
                gesture_type = "fist"

            elif (angle_list[0] < self.normal_unbend_angle) and (angle_list[1] < self.normal_unbend_angle) \
                    and (angle_list[2] < self.normal_unbend_angle) and (angle_list[3] < self.normal_unbend_angle) and \
                    (angle_list[4] < self.normal_unbend_angle):
                gesture_type = "spread"

            elif (angle_list[0] > 5) and (angle_list[1] < self.normal_unbend_angle) \
                    and (angle_list[2] > self.normal_threshold_angle) and (angle_list[3] > self.normal_threshold_angle) \
                    and (angle_list[4] > self.normal_threshold_angle):
                gesture_type = "point"

            elif (angle_list[0] > self.thumb_threshold_angle) and (angle_list[1] < self.normal_unbend_angle) and (
                    angle_list[2] < self.normal_unbend_angle) and (angle_list[3] > self.normal_threshold_angle) and (
                    angle_list[4] > self.normal_threshold_angle):
                gesture_type = "two"

            elif (angle_list[0] > self.thumb_threshold_angle) and (angle_list[1] < self.normal_unbend_angle) and (
                    angle_list[2] < self.normal_unbend_angle) and (angle_list[3] < self.normal_unbend_angle) and (
                    angle_list[4] > self.normal_threshold_angle):
                gesture_type = "three"

            elif (angle_list[0] > self.thumb_threshold_angle) and (angle_list[1] < self.normal_unbend_angle) and (
                    angle_list[2] < self.normal_unbend_angle) and (angle_list[3] < self.normal_unbend_angle) and (
                    angle_list[4] < self.normal_threshold_angle):
                gesture_type = "four"

            elif (angle_list[0] < self.normal_unbend_angle) and (angle_list[1] > self.normal_threshold_angle) and (
                    angle_list[2] > self.normal_threshold_angle) and (angle_list[3] > self.normal_threshold_angle) and (
                    angle_list[4] < self.normal_unbend_angle):
                gesture_type = "six"
            elif (angle_list[0] < self.normal_unbend_angle) and (angle_list[1] > self.normal_threshold_angle) and (
                    angle_list[2] > self.normal_threshold_angle) and (angle_list[3] > self.normal_threshold_angle) and (
                    angle_list[4] > self.normal_threshold_angle):
                gesture_type = "thumbUp"

        return gesture_type

    def get_hand_angle(self, coordinate):
        """
        获取当前手势弯曲角度
        :param coordinate: 手势弯曲角度坐标
        :return: 手势弯曲角度列表
        """
        angle_list = []
        # 大拇指角度
        angle = self.compute_hand_angle(
            ((int(coordinate[0][0]) - int(coordinate[2][0])),(int(coordinate[0][1]) - int(coordinate[2][1]))),
            ((int(coordinate[3][0]) - int(coordinate[4][0])),(int(coordinate[3][1]) - int(coordinate[4][1]))))
        angle_list.append(angle)

        # 食指角度
        angle = self.compute_hand_angle(
            ((int(coordinate[0][0]) - int(coordinate[6][0])),(int(coordinate[0][1]) - int(coordinate[6][1]))),
            ((int(coordinate[7][0]) - int(coordinate[8][0])),(int(coordinate[7][1]) - int(coordinate[8][1]))))
        angle_list.append(angle)

        # 中指角度
        angle = self.compute_hand_angle(
            ((int(coordinate[0][0]) - int(coordinate[10][0])),(int(coordinate[0][1]) - int(coordinate[10][1]))),
            ((int(coordinate[11][0]) - int(coordinate[12][0])),(int(coordinate[11][1]) - int(coordinate[12][1]))))
        angle_list.append(angle)

        # 无名指角度
        angle = self.compute_hand_angle(
            ((int(coordinate[0][0]) - int(coordinate[14][0])),(int(coordinate[0][1]) - int(coordinate[14][1]))),
            ((int(coordinate[15][0]) - int(coordinate[16][0])),(int(coordinate[15][1]) - int(coordinate[16][1]))))
        angle_list.append(angle)

        # 小拇指角度
        angle = self.compute_hand_angle(
            ((int(coordinate[0][0]) - int(coordinate[18][0])),(int(coordinate[0][1]) - int(coordinate[18][1]))),
            ((int(coordinate[19][0]) - int(coordinate[20][0])),(int(coordinate[19][1]) - int(coordinate[20][1]))))
        angle_list.append(angle)
        return angle_list

    def compute_hand_angle(self, A, B):
        """
        计算指定手指弯曲角度
        :param A: 向量端点A
        :param B: 向量端点B
        :return: 向量AB
        """
        ax, ay = A[0], A[1]
        bx, by = B[0], B[1]
        try:
            angle = math.degrees(math.acos((ax * bx + ay * by) / (((ax ** 2 + ay ** 2) ** 0.5) * ((bx ** 2 + by ** 2) ** 0.5))))
        except:
            angle = self.INF

        if angle > 180.:
            angle = self.INF
        return angle

    def one_hand_judge_fist(self, i):
        return self.stream_frame_msg[i][1][0] == 'fist' and self.stream_frame_msg[i][1][1] == 'null'

    def one_hand_judge_spread(self, i):
        return self.stream_frame_msg[i][1][0] == 'spread' and self.stream_frame_msg[i][1][1] == 'null'

    def one_hand_judge_point(self, i):
        return self.stream_frame_msg[i][1][0] == 'point' and self.stream_frame_msg[i][1][1] == 'null'

    def two_hand_judge_fist(self, i):
        return self.stream_frame_msg[i][1][0] == 'fist' and self.stream_frame_msg[i][1][1] == 'fist'

    def two_hand_judge_spread(self, i):
        return self.stream_frame_msg[i][1][0] == 'spread' and self.stream_frame_msg[i][1][1] == 'spread'

    def two_hand_judge_thumbUp(self, i):
        return self.stream_frame_msg[i][1][0] == 'thumbUp' and self.stream_frame_msg[i][1][1] == 'thumbUp'

    def one_hand_judge_two(self, i):
        return self.stream_frame_msg[i][1][0] == 'two' and self.stream_frame_msg[i][1][1] == 'null'

    def one_hand_judge_three(self, i):
        return self.stream_frame_msg[i][1][0] == 'three' and self.stream_frame_msg[i][1][1] == 'null'

    def one_hand_judge_four(self, i):
        return self.stream_frame_msg[i][1][0] == 'four' and self.stream_frame_msg[i][1][1] == 'null'

    def one_hand_judge_six(self, i):
        return self.stream_frame_msg[i][1][0] == 'six' and self.stream_frame_msg[i][1][1] == 'null'

    def count_one_hand_delta_x(self, i, j):
        """
        组合计算 - 计算一只手横坐标偏移量
        :param i: 起始手势帧数
        :param j: 终止手势帧数
        :return: 手势关键点水平偏移量 delta_x
        """
        return self.stream_frame_msg[i][0][0][0][0] - self.stream_frame_msg[j][0][0][0][0]

    def count_two_hand_delta_x(self, i, j, hand):
        """
        组合计算 - 计算 两只手中指定手 横坐标偏移量
        :param i: 起始手势帧数
        :param j: 终止手势帧数
        :param hand: 指定手编号
        :return: 指定手势关键点水平偏移量 delta_x
        """
        return self.stream_frame_msg[i][0][hand][0][0] - self.stream_frame_msg[j][0][hand][0][0]

    def count_two_hand_delta_y(self, i, j, hand):
        """
        组合计算 - 计算 两只手中指定手 纵坐标偏移量
        :param i: 起始手势帧数
        :param j: 终止手势帧数
        :param hand: 指定手编号
        :return: 指定手势关键点垂直偏移量 delta_y
        """
        return self.stream_frame_msg[i][0][hand][0][1] - self.stream_frame_msg[j][0][hand][0][1]

    def show_label(self,text):
        """
        打印结果
        :param text: 展示到结果区域的文字
        """
        self.result_label.setText(text)
        self.running_label.setText(u"手势检测中...")

    # ================================== 页面控制 ================================== #
    def close_window(self):
        """ 关闭窗口 """
        if self.cap.isOpened():
            self.cap.release()
        self.close()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = WindowLogic()
    win.show()
    sys.exit(app.exec_())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1365132.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Opencv实验合集——实验七:二维码和条形码匹配

1.概念 二维码&#xff08;QR码&#xff09; 概念&#xff1a; 二维码是一种矩阵式的二维条码&#xff0c;由黑白方块组成&#xff0c;可以存储大量的信息&#xff0c;包括文本、链接、数字等。QR码的编码方式是在矩阵中通过不同的黑白方块组合表示不同的信息。 特点&#xf…

【读书笔记】《白帽子讲web安全》跨站脚本攻击

目录 前言&#xff1a; 第二篇 客户端脚本安全 第3章 跨站脚本攻击&#xff08;XSS&#xff09; 3.1XSS简介 3.2XSS攻击进阶 3.2.1初探XSS Payload 3.2.2强大的XSS Payload 3.2.2.1 构造GET与POST请求 3.2.2.2XSS钓鱼 3.2.2.3识别用户浏览器 3.2.2.4识别用户安装的软…

【linux学习】重定向

目录 重定向标准输出、标准输入和标准错误标准输出重定向标准错误重定向将标准输出和标准错误重定向到同一个文件处理不想要的输出标准输入重定向 管道过滤器uniq-报告或者忽略文件中重复的行wc-打印行数、字数和字节数grep-打印匹配行head/tail 打印文件的开头部分/结尾部分te…

嵌入式系统习题(考试相关)

文章目录 上一篇嵌入式系统概述ARM技术概述ARM指令Thumb指令集ARM程序设计 上一篇 嵌入式系统复习–基于ARM的嵌入式程序设计 嵌入式系统概述 嵌入式系统中常用的通信接口包括哪些&#xff1f; RS-232C串行通信接口&#xff0c;RS-422串行通信接口&#xff0c;RS-485串行通信…

【APACHE】的认识和基础配置参数

#主页传送:江南的江 #每日鸡汤&#xff1a;人生没有如果和假设&#xff0c;只有后果和结果。生活有进有退&#xff0c;输什么也不能输心情。生活简单就是迷人的&#xff0c;学会简单其实就是不简单。要学会平静地接受现实&#xff0c;学会对自己说声顺其自然&#xff0c;学会坦…

基于实时Linux+FPGA实现NI CompactRIO系统详解

利用集成的软件工具链&#xff0c;结合信号调理I/O模块&#xff0c;轻松构建和部署实时应用程序。 什么是CompactRIO&#xff1f; CompactRIO系统提供了高处理性能、传感器专用I/O和紧密集成的软件工具&#xff0c;使其成为工业物联网、监测和控制应用的理想之选。实时处理器提…

leetcode——杨辉三角

https://leetcode.cn/problems/pascals-triangle/ 杨辉三角&#xff1a; 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 核心思想&#xff1a;找出杨辉三角的规律&#xff0c;发…

如何用UE5 的小白人替换成自己的 metahumen 数字人

1、用QuixelBridge 插件导入制作好的metahumen数字人 2、创建项目时如有选择第三人称游戏&#xff0c;在内容目录中找到第三人称游戏小白人的蓝图类&#xff0c;对其进行复制一个&#xff0c;重命名&#xff0c;我这里命名为BP_METAHUMEN&#xff0c; 并移到Metahumen目录下方便…

python统计分析——箱线图(plt.boxplot)

参考资料&#xff1a;用python动手学统计学 使用matplotlib.pyplot.boxplot()函数绘制箱线图 import numpy as np import pandas as pd from matplotlib import pyplot as pltdata_set1np.array([2,3,3,4,4,4,4,5,5,6]) data_set2np.array([[2,3,3,4,4,4,4,5,5,6],[5,6,6,7,7…

【AI视野·今日Sound 声学论文速览 第四十一期】Thu, 4 Jan 2024

AI视野今日CS.Sound 声学论文速览 Thu, 4 Jan 2024 Totally 8 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers Multichannel blind speech source separation with a disjoint constraint source model Authors Jianyu Wang, Shanzheng Guan多通道卷积…

ArcGIS制图技巧总结

Part 1 制图综述 1.1 制图的目的 随着GIS在各行各业的深入应用&#xff0c;各信息化部门和生产单位都逐渐建立起自己的GIS的应用&#xff0c;同时积累了大量的地理数据。随着应用深度和广度的推进&#xff0c;针对数据建立专题应用越来越迫切&#xff0c;对行业专题制图的需…

XCode Build报错

XCode Build时报以下错误 B/BL out of range 143266484 (max /-128MB) 错误提示表明生成的机器代码太大&#xff0c;超出了限制 需要在XCode工程中添加宏定义&#xff0c;使得生成的可执行文件超过限制 步骤&#xff1a; 在项目设置页面中&#xff0c;选择 “Build Settings…

爆肝整理,接口测试+为什么要做接口测试总结,策底贯通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、什么是接口测试…

vue3 指令详解

系列文章目录 TypeScript 从入门到进阶专栏 文章目录 系列文章目录前言一、v-model &#xff08;双向绑定功能&#xff09;二、v-bind(用于将一个或多个属性绑定到元素的属性或组件的 prop)三、v-if、v-else、v-else-if(用于根据条件选择性地渲染元素)四、v-show&#xff08;根…

JavaSE 万字总结知识点(期末复习指南)

目录 一.Java的特性 二.注释 三.数据类型 基本数据类型 包装类 引用数据类型 四.运算符 五.逻辑控制 选择语句 循环语句 六.数组 七.方法 八.类与对象 构造方法 内部类 九.继承和多态 十.抽象类与接口 抽象类 接口 十一.异常 一.Java的特性 Java最初由Sun…

C++中vector的insert参数问题

今天模拟实现vector的过程中&#xff0c;发现pos传参给insert后是不能直接使用的&#xff0c;如果扩容的话&#xff0c;就要更新pos&#xff0c;于是就像用引用来解决这个问题 一般的pos还是能正常传参&#xff0c;但是如果传某容器的迭代器的话&#xff0c;就会报错 原因&…

LeetCode-加一(66)

题目描述&#xff1a; 给定一个由整数组成的非空数组所表示的非负整数&#xff0c;在该数的基础上加一。 最高位数字存放在数组的首位&#xff0c; 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外&#xff0c;这个整数不会以零开头。 思路&#xff1a; 这里主要分…

Go-gin-example 添加注释 第一部分 新建项目及api编写

文章目录 go-gin-example环境准备初始化 Go Modules基础使用 gin 安装测试gin是否引入 gin搭建Blog APIsgo-ini简述配置文件 阶段目标 编写简单API错误码包 完成一个demo初始化项目初始化项目数据库编写项目配置包拉取go-ini配置包在conf目录下新建app.ini文件&#xff0c;写入…

React.Children.map 和 js 的 map 有什么区别?

JavaScript 中的 map 不会对为 null 或者 undefined 的数据进行处理&#xff0c;而 React.Children.map 中的 map 可以处理 React.Children 为 null 或者 undefined 的情况。 React 空节点&#xff1a;可以由null、undefined、false、true创建 import React from reactexport …

详解ajax、fetch、axios的区别

众所周知它们都用来发送请求&#xff0c;其实它们区别还蛮大的。这也是面试中的高频题&#xff0c;本文将详细进行讲解。 1. ajax 英译过来是Aysnchronous JavaScript And XML&#xff0c;直译是异步JS和XML&#xff08;XML类似HTML&#xff0c;但是设计宗旨就为了传输数据&a…