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

一、项目要求

        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 sysimport qtawesome
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIconclass WindowLayout(object):def __init__(self):# 容器self.central_widget = None# 组件self.close_button = Noneself.other_button = Noneself.minimize_button = Noneself.start_button = Noneself.camera_button = Noneself.camera_label = Noneself.result_label = Noneself.running_label = Nonedef 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'))  # 设置logoQtCore.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 timeimport 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 QDesktopWidgetfrom window_layout import WindowLayoutclass 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 = Noneself.y = None# ====== 时间属性 ====== #self.camera_start_time = Noneself.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, '警告', '相机不能正常被打开')# 关闭摄像头,释放capelse: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 = Trueself.start_button.setText(u' 关闭检测')self.gesture_recognize()self.isGestureRecognition_flag = Falseself.start_button.setText(u' 手势检测')else:self.isGestureRecognition_flag = Falseself.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_utilsmp_hands = mp.solutions.handshands = 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 = jshow_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) >= 50and 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) <= -50and 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 Yelif 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 = Noneif 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_typedef 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_listdef 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.INFif angle > 180.:angle = self.INFreturn angledef 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.mzph.cn/news/604277.shtml

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

相关文章

离散数学1

注&#xff1a;线性代数已经更新了最大部分的内容&#xff0c;因此过段时间再补充剩余内容。 小王能歌善舞。因此&#xff0c;小王必须得会唱歌也必须得会跳舞&#xff0c;才满足题意 小王能唱歌或者小王能跳舞。因此&#xff0c;小王会唱歌也会跳舞满足。小王不会唱歌但会跳舞…

JavaScript常用事件演示

文章目录 一、在JavaScript中什么是事件&#xff1f;二、什么是JavaScript 常用事件&#xff1f;三、常用JS事件代码示例:四、事件总结 一、在JavaScript中什么是事件&#xff1f; JavaScript 使我们有能力创建动态页面。事件是可以被 JavaScript 侦测到的行为。 网页中的每个…

C++|【34】C++中的const

文章目录 constconst最基本的用法const和指针const和类 const const最基本的用法 用于限定a的大小&#xff0c;使a变成一个恒定不变的值。 应用场景&#xff1a;比如设置一些和标准相关的值&#xff0c;如上下限等等。 const和指针 指针总是包含两部分信息&#xff0c;一个是…

如何信任机器学习模型的预测结果?

在本篇中&#xff0c;我将通过一个例子演示在 MATLAB 如何使用 LIME 进行复杂机器学习模型预测结果的解释。 我使用数据集 carbig&#xff08;MATLAB 自带的数据集&#xff09;训练一个回归模型&#xff0c;用于预测汽车的燃油效率。数据集 carbig 是 70 年代到 80 年代生产的汽…

docker的基础知识

介绍docker 什么是docker Docker是一种开源的容器化平台&#xff0c;可以让开发人员将应用程序与其依赖的运行时环境一起打包到一个称为容器的独立单元中。这个容器可以在任何支持Docker的机器上运行&#xff0c;提供了一种快速和可移植的方式来部署应用程序。Docker的核心组件…

Python库学习(十四):ORM框架-SQLAlchemy

1.介绍 SQLAlchemy 是一个用于 Python 的 SQL 工具和对象关系映射&#xff08;ORM&#xff09;库。它允许开发者通过 Python 代码而不是 SQL查询语言来操作数据库。SQLAlchemy 提供了一种灵活且强大的方式来与关系型数据库交互&#xff0c;支持多种数据库后端&#xff0c;如 P…

【十七】【动态规划】DP41 【模板】01背包、416. 分割等和子集、494. 目标和,三道题目深度解析

动态规划 动态规划就像是解决问题的一种策略&#xff0c;它可以帮助我们更高效地找到问题的解决方案。这个策略的核心思想就是将问题分解为一系列的小问题&#xff0c;并将每个小问题的解保存起来。这样&#xff0c;当我们需要解决原始问题的时候&#xff0c;我们就可以直接利…

GeoServer发布地图服务(WMS、WFS)

文章目录 1. 概述2. 矢量数据源3. 栅格数据源 1. 概述 我们知道将GIS数据大致分成矢量数据和栅格数据&#xff08;地形和三维模型都是兼具矢量和栅格数据的特性&#xff09;。但是如果用来Web环境中&#xff0c;那么使用图片这个栅格形式的数据载体无疑是最为方便的&#xff0…

WebofScience快速检索文献的办法

Web of Science为什么老是搜不到文章&#xff0c;原来是要在这个地方全部勾选 如果是搜标题的话&#xff0c;选Title&#xff0c;输入你要搜的文章标题 另外&#xff0c;也可以在浏览器直接搜文章标题&#xff0c;得到文章的DOI&#xff0c;然后选DOI&#xff0c;直接搜DOI也行…

中通快递查询,中通快递单号查询,批量删除不需要的快递单号

快递单号的管理现在是许多企业和个人日常工作中不可或缺的一部分&#xff0c;面对堆积如山的快递单号&#xff0c;如何快速、准确地处理成了许多人的难题。今天&#xff0c;我们将为大家带来一款强大的快递单号处理软件——快递批量查询高手&#xff0c;让你从此告别繁琐的手动…

SpringCloud-高级篇(十一)

&#xff08;1&#xff09;搭建Redis-主从架构 前面我们实现了Redis的持久化&#xff0c;解决了数据安全问题&#xff0c;但是还有需要解决的问题&#xff0c;下面学习Redis的主从集群&#xff0c;解决Redis的并发能力的问题 Redis的集群往往是主从集群&#xff0c;Redsi为什么…

【leetcode】字符串中的第一个唯一字符

题目描述 给定一个字符串 s &#xff0c;找到 它的第一个不重复的字符&#xff0c;并返回它的索引 。如果不存在&#xff0c;则返回 -1 。 用例 示例 1&#xff1a; 输入: s “leetcode” 输出: 0 示例 2: 输入: s “loveleetcode” 输出: 2 示例 3: 输入: s “aabb”…

游戏缺少x3daudio1_7.dll文件怎么办?x3daudio1_7.dll丢失总共有六个解决方法

导语&#xff1a;在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“x3daudio1_7.dll丢失”。那么&#xff0c;x3daudio1_7.dll到底是什么文件呢&#xff1f;它的作用和影响又是什么呢&#xff1f;本文将为您详细介绍x3daudio1_7.dll的相关知…

LabVIEW开发自动光学焊点检测系统

LabVIEW开发自动光学焊点检测系统 LabVIEW于开发了一个自动光学焊点检测系统&#xff0c;旨在提高电子元件焊接的质量和效率。通过利用LabVIEW的高级视觉开发模块&#xff0c;该系统能够准确地识别和分类电路板上的不同焊点类型&#xff0c;如桥接、虚焊、漏焊和多锡。这一进步…

Java最大优先队列设计与实现

Java 学习面试指南&#xff1a;https://javaxiaobear.cn 1、API设计 类名MaxPriorityQueue构造方法MaxPriorityQueue(int capacity)&#xff1a;创建容量为capacity的MaxPriorityQueue对象成员方法private boolean less(int i,int j)&#xff1a;判断堆中索引i处的元素是否小…

Docker中swarm管理工具

Docker中swarm管理工具 1 安装swarm swarm是Docker自带的容器集群管理工具。 1.1 集群IP 主机名ip地址网卡名软件master192.168.108.201ens33Dockernode1192.168.108.202ens33Docker 修改主机名 # 管理节点&#xff0c;修改主机名 hostnamectl set-hostname master# 子节…

PHP代码审计之实战审代码篇2

4. 仔细观察如下代码&#xff0c;思考代码有什么缺陷&#xff0c;可能由此引发什么样的问题&#xff1f; <?php require_once("/home/rconfig/classes/usersession.class.php"); require_once("/home/rconfig/classes/ADLog.class.php"); require_onc…

【二】使用create-vue创建vue3的helloworld项目(推荐)

create-vue 官网&#xff1a;快速上手 | Vue.js create-vue 是 Vue3 的专用脚手架&#xff0c;使用 vite 创建 Vue3 的项目&#xff0c;也可以选择安装需要的各种插件&#xff0c;使用更简单。 1、使用方式 npm create vuelatest这个命令会安装和执行 create-vue&#xff0…

【MATLAB源码-第105期】基于matlab的4PAM调制解调仿真,输出误码率和误符号曲线并且和理论值对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 4PAM&#xff08;4-Pulse Amplitude Modulation&#xff0c;4脉冲幅度调制&#xff09;是一种数字调制技术&#xff0c;它通过改变载波信号的幅度来表示数据。在4PAM中&#xff0c;载波的幅度可以采用四种不同的水平&#xf…

天翼云搭建AIGC开发环境综合教程

一、英伟达环境安装主要流程 1、下载安装对应系统版本nVidia驱动程序安装验证 2、CUDA开发套件安装验证 3、深度学习框架安装验证MiniConda3PyTorch 4、容器化CUDA环境安装验证 5、cuDNN深度学习优化驱动安装CNN训练验证 6、制作天翼云主机私有镜像 7、分享镜像给其他用…