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.rhkb.cn/news/233219.html

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

相关文章

小白入门基础 - Restful

一&#xff1a;REST与RESTful&#xff1a; REST&#xff1a;表现层状态转移&#xff0c;资源在网络中以某种形式进行状态转移。 RESTful是基于REST理念的一套开发风格&#xff0c;是具体的开发规则。 服务器端只返回数据&#xff0c;以json或者xml的格式。 RESTful开发规范&a…

【大数据】Spark学习笔记

初识Spark Spark和Hadoop HadoopSpark起源时间20052009起源地MapReduceUniversity of California Berkeley数据处理引擎BatchBatch编程模型MapReduceResilient distributed Datesets内存管理Disk BasedJVM Managed延迟高中吞吐量中高优化机制手动手动APILow levelhigh level流…

MySQL之视图外连接、内连接和子查询的使用

目录 一、视图 1.1 含义 1.2 操作 1.3 SQL数据 二、连接查询案例 &#xff08;1&#xff09;查询" 01 "课程比" 02 "课程成绩高的学生的信息及课程分数 &#xff08;2&#xff09;查询同时存在" 01 "课程和" 02 "课程的情况 &a…

docker安裝gocd-server,并配置gitlab授权登录

gocd的地址&#xff1a;Installing GoCD server on Windows | GoCD User Documentation gocd文档&#xff1a;GitHub - gocd/docker-gocd-server: Docker server image for GoCD 一、docker拉取gocd镜像 #拉取server镜像 docker pull gocd/gocd-server:v21.1.0docker pull g…

java SSM水质历史数据可视化设计myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM水质历史数据可视化设计是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主…

QT常用控件使用及布局

QT常用控件使用及布局 文章目录 QT常用控件使用及布局1、创建带Ui的工程2、ui界面介绍1、界面设计区2、对象监视区3、对象监属性编辑区4、信号与槽5、布局器6、控件1、Layouts1、布局管理器2、布局的dome 2、Spacers3、Buttons4、项目视图组(Item Views)5、项目控件组(Item Wid…

“数据要素×”正式来袭|美创“全栈能力、深入场景”保障数据价值安全释放

千呼万唤&#xff0c;1月4日&#xff0c;国家数据局等17部门联合印发的《“数据要素”三年行动计划&#xff08;2024—2026年&#xff09;》&#xff08;下称《三年行动计划》&#xff09;正式发布&#xff01; 作为国家数据局成立以来公开发布的首个重磅文件&#xff0c;《三年…

大数据StarRocks(四) :常用命令

这次主要介绍生产工作中使用Starrocks时的常用命令 4.1 连接StarRocks 4.1.1 Linux命令行连接 [roothadoop1011 fe]# yum install mysql -y [roothadoop1011 fe]# mysql -h hadoop101 -uroot -P9030 -p4.1.2 Windows客户端 DBeaver 连接 4.2 常用命令 4.2.1 查看状态 1. 查…

uniapp获取手机当前信息及应用版本

appVersion 是app端查询的数据信息 appWgtVersion 是浏览器端查询的数据信息 onLoad() {const systemInfo uni.getSystemInfoSync();console.log(systemInfo);// #ifdef H5const uniAppVersion systemInfo.appVersion;// #endif// #ifndef H5const uniAppVersion systemIn…

案例分享:Qt多国语言输入法软键盘

若该文为原创文章&#xff0c;转载请注明出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/135346374 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结…

vue-springboot基于java的实验室安全考试系统

本系统为用户而设计制作实验室安全考试系统&#xff0c;旨在实现实验室安全考试智能化、现代化管理。本实验室安全考试管理自动化系统的开发和研制的最终目的是将实验室安全考试的运作模式从手工记录数据转变为网络信息查询管理&#xff0c;从而为现代管理人员的使用提供更多的…

模板管理支持批量操作,DataEase开源数据可视化分析平台v2.2.0发布

2024年1月8日&#xff0c;DataEase开源数据可视化分析平台正式发布v2.2.0版本。 这一版本的功能升级包括&#xff1a;在“模板管理”页面中&#xff0c;用户可以通过模板管理的批量操作功能&#xff0c;对已有模板进行快速重新分类、删除等维护操作&#xff1b;数据大屏中&…

大数据 - Doris系列《二》- Doris安装(亲测成功版)

目录 &#x1f436;2.1 安装前准备 &#x1f959;1.设置系统最大文件打开句柄数 >启动一个程序的时候&#xff0c;打开文件的数量就是句柄数 &#x1f959;2.设置文件包含限制一个进程可以拥有的VMA(虚拟内存区域)的数量 &#x1f959;3.时钟同步 &#x1f959;4.关闭交…

labelImg的安装与使用

目录 1、查看本机是否安装labelImg 2、安装labelImg 3、创建自己的数据集 3.1 建立新文件夹 3.2 打开labelImg 注意&#xff1a;出现闪退的情况处理。 4、文件格式转换 4.1 修改文件夹路径 4.2 新建datasets文件夹 4.3 修改图片路径 4.4 执行 1、查看本机是否安装la…

uniCloud 云函数

相对于云函数&#xff0c;官方更推荐使用 云对象 新建云函数 编辑云函数 uniCloud-aliyun/cloudfunctions/hello_func/index.js use strict; exports.main async (event, context) > {let {name} eventreturn 你好&#xff0c;${name}! };云函数接收的参数从event中解构获…

部署可道云网盘的一个漏洞解决

目录 1漏洞展示 2.防范措施 1漏洞展示 因为可道云网盘的上传文档有保存在 /data/Group/public/home/文档/ 中,当别有用心之人知道个人部署的域名与上次的文件后&#xff0c;可以进行访问拿到uid。例我在我部署的网盘上上次一个aa.php 文件&#xff0c;然后拿来演示 然后通过…

密码学中的Hash函数

目录 一. 介绍 二. hash函数的五个基本性质 &#xff08;&#xff11;&#xff09;压缩性 &#xff08;&#xff12;&#xff09;正向计算简单性 &#xff08;&#xff13;&#xff09;逆向计算困难性 &#xff08;&#xff14;&#xff09;弱无碰撞性 &#xff08;&…

RabbitMQ(八)消息的序列化

目录 一、为什么需要消息序列化&#xff1f;二、常用的消息序列化方式1&#xff09;Java原生序列化&#xff08;默认&#xff09;2&#xff09;JSON格式3&#xff09;Protobuf 格式4&#xff09;Avro 格式5&#xff09;MessagePack 格式 三、总结 RabbitMQ 是一个强大的消息中间…

安全基础~信息搜集3

文章目录 知识补充APP信息搜集php开发学习理解漏洞 知识补充 端口渗透总结 python Crypto报错&#xff1a;https://blog.csdn.net/five3/article/details/86160683 APP信息搜集 1. AppInfoScanner 移动端(Android、iOS、WEB、H5、静态网站)信息收集扫描工具 使用教程 演示&…

【Harmony OS - 网络请求】

在一个应用开发中&#xff0c;网络请求是必不可少的&#xff0c;我们一般用的fetch、axios来进行http请求&#xff0c;在鸿蒙中也可以通过createHppt来发生一个http请求&#xff0c;它们都是异步请求返回的Promise&#xff0c;下面我们将介绍’ohos.net.http’和axios这两种方式…