赞
踩
通过Pyqt及mediapipe库实现动态手势识别效果演示,即“左右摇动手指”,使得QLabel的选择状态移动,手指向左摇动,选定的QLabel向左移动,手指向右移动,选定的QLabel向右移动。
Mediapipe是google的一个开源项目,支持跨平台的常用ML方案。可以提供人脸识别、人体关节点识别、人体手部关节点识别等功能,使用接口简单,直接import mediapipe as np
并选择相应的solution
,按照相应的步骤操作即能实现相应的识别操作。
通过Pyqt创建结果显示界面,opencv-python实现摄像头图像捕获功能。
手势识别效果如下面几张动态图所示。画面左侧显示摄像头实时捕获图像,显示静态手势识别结果。右侧显示5行15个模拟按键,选中状态为红低黄色边框,未选中为灰色底绿色边框。
向左移动效果。
向右移动效果。
左右移动效果。
整个工程文件夹结构如下图所示。
通过
mediapipe.soulutions.hands
模块设置手势识别相关参数。
其原理在于将一张RGB图像传入手势识别网络后,结果输出手部关节点的坐标,有了各个关节点的坐标后,根据关节点坐标之间的相对数值、各个关节线段之间的角度等特征进行手势的分类。
mediapipe
具体是通过什么样的算法将每个关节点的坐标给预测出来的,这里就不详细展开讲了,现在已经有很多开源的手部关节点识别模型。
总之通过关节点的相对位置,能够实现诸如“1”、“2”……等常规简单意义手势分类如下所示。
动态手势即在某一时间段内静态手势按照一定的顺序的排列。如果人为定义一段“动态手势”时间为2秒,使用帧率为30hz的相机进行拍摄,那么在该手势执行段共获得2×30=60张图片,这60张图片一定是按“某种规则”进行的排序。
人类做出手势,从每个时间节点,人手的关节点在这60张图像中的坐标位置来说,严格意义上来讲,一定不存在两次“完全一样”的手势,只能说当两次手势均满足一定的规则意义,才将它们判定为同一种手势。
这种判定规则,需要算法设计人员自行定义。
在本次实验中,本人的思路是,先让手做出静态手势“1”的效果,因为“1”的食指现对于图片的方向应该是在垂直状态的,这个垂直
状态可以通过理论计算判定,达到这种“垂直”状态后,然后通过判断食指上的三个点的坐标与整幅图像的相对位置,从而判断手指是“向左”还是“向右”移动。
即食指所在的直线与图像的水平线的角度,如果在某段时间内是
<75°
,那么将其判断为“向左移动”
,如果在某段时间内是>115°
,那么将其判断为“向右移动”
。这里为什么不用90°
,是由于你即使将手指指向垂直方向,理论计算值也不会那么准,一定是在90°
附近的某个区间内,这里选定了,当计算值在[75°, 115°]
时,均将手势判断为垂直状态。
HandpostureDetect模块集成了手势识别相关的操作任务,包括关节点角度等的计算。
这里参考了此篇博客的内容
https://blog.csdn.net/weixin_45930948/article/details/115444916,本人在其基础上,将其原来的detect()
函数封装成HandPostureDetect
类,方便在模块化编程中调用。添加了if __name__ == '__main__':
代码入口,可以直接运行本脚本,调用opencv输出窗口,并在窗口上打印识别结果。
在HandpostureDetect内定义两个pyqtsignal分别用于输出静态手势与动态手势的状态。
import cv2 import mediapipe as mp import math import imutils import numpy as np from PyQt5.QtCore import pyqtSignal, QObject def vector_2d_angle(v1,v2): ''' 求解二维向量的角度 ''' v1_x = v1[0] v1_y = v1[1] v2_x = v2[0] v2_y = v2[1] try: angle_ = math.degrees(math.acos((v1_x*v2_x+v1_y*v2_y)/(((v1_x**2+v1_y**2)**0.5)*((v2_x**2+v2_y**2)**0.5)))) except: angle_ = 65535. if angle_ > 180.: angle_ = 65535. return angle_ def hand_angle(hand_): ''' 获取对应手相关向量的二维角度,根据角度确定手势 ''' angle_list = [] # ---------------------------- thumb 大拇指角度 angle_ = vector_2d_angle( ((int(hand_[0][0]) - int(hand_[2][0])), (int(hand_[0][1]) -int(hand_[2][1]))), ((int(hand_[3][0]) - int(hand_[4][0])), (int(hand_[3][1]) - int(hand_[4][1]))) ) angle_list.append(angle_) # ---------------------------- index 食指角度 angle_ = vector_2d_angle( ((int(hand_[0][0]) - int(hand_[6][0])), (int(hand_[0][1]) - int(hand_[6][1]))), ((int(hand_[7][0]) - int(hand_[8][0])), (int(hand_[7][1]) - int(hand_[8][1]))) ) angle_list.append(angle_) # ---------------------------- middle 中指角度 angle_ = vector_2d_angle( ((int(hand_[0][0])- int(hand_[10][0])),(int(hand_[0][1])- int(hand_[10][1]))), ((int(hand_[11][0])- int(hand_[12][0])),(int(hand_[11][1])- int(hand_[12][1]))) ) angle_list.append(angle_) # ---------------------------- ring 无名指角度 angle_ = vector_2d_angle( ((int(hand_[0][0])- int(hand_[14][0])),(int(hand_[0][1])- int(hand_[14][1]))), ((int(hand_[15][0])- int(hand_[16][0])),(int(hand_[15][1])- int(hand_[16][1]))) ) angle_list.append(angle_) # ---------------------------- pink 小拇指角度 angle_ = vector_2d_angle( ((int(hand_[0][0])- int(hand_[18][0])),(int(hand_[0][1])- int(hand_[18][1]))), ((int(hand_[19][0])- int(hand_[20][0])),(int(hand_[19][1])- int(hand_[20][1]))) ) angle_list.append(angle_) return angle_list def h_gesture(angle_list): ''' # 二维约束的方法定义手势 # fist five gun love one six three thumbup yeah ''' thr_angle = 65. thr_angle_thumb = 53. thr_angle_s = 49. gesture_str = None if 65535. not in angle_list: if (angle_list[0]>thr_angle_thumb) and (angle_list[1]>thr_angle) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle): gesture_str = "握拳" elif (angle_list[0]<thr_angle_s) and (angle_list[1]<thr_angle_s) and (angle_list[2]<thr_angle_s) and (angle_list[3]<thr_angle_s) and (angle_list[4]<thr_angle_s): gesture_str = "five" elif (angle_list[0]<thr_angle_s) and (angle_list[1]<thr_angle_s) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle): gesture_str = "eight" elif (angle_list[0]<thr_angle_s) and (angle_list[1]<thr_angle_s) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]<thr_angle_s): gesture_str = "seven" elif (angle_list[0]>5) and (angle_list[1]<thr_angle_s) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle): gesture_str = "one" elif (angle_list[0]<thr_angle_s) and (angle_list[1]>thr_angle) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]<thr_angle_s): gesture_str = "six" elif (angle_list[0]>thr_angle_thumb) and (angle_list[1]<thr_angle_s) and (angle_list[2]<thr_angle_s) and (angle_list[3]<thr_angle_s) and (angle_list[4]>thr_angle): gesture_str = "three" elif (angle_list[0]<thr_angle_s) and (angle_list[1]>thr_angle) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle): gesture_str = "thumbUp" elif (angle_list[0]>thr_angle_thumb) and (angle_list[1]<thr_angle_s) and (angle_list[2]<thr_angle_s) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle): gesture_str = "two" return gesture_str def get_line_decline(two_points_line): k = - (two_points_line[1][1]-two_points_line[0][1]) / (two_points_line[1][0]-two_points_line[0][0]) result = np.arctan(k) * 57.30 return result class HandPostureDetect(QObject): handSignal = pyqtSignal(int) gestureSignal = pyqtSignal(str) def __init__(self, parent=None): super(HandPostureDetect, self).__init__(parent) print("[INFO] initalizing Handposture Detector...") self.mp_drawing = mp.solutions.drawing_utils self.mp_hands = mp.solutions.hands self.hands = self.mp_hands.Hands(static_image_mode=False, max_num_hands=1, min_detection_confidence=0.75, min_tracking_confidence=0.75) self.degree_list = [] def detect(self, frame): try: # frame = imutils.resize(frame, width=720) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) frame = cv2.flip(frame, 1) results = self.hands.process(frame) frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) if results.multi_hand_landmarks: for hand_landmarks in results.multi_hand_landmarks: self.mp_drawing.draw_landmarks(frame, hand_landmarks, self.mp_hands.HAND_CONNECTIONS) hand_local = [] for i in range(21): x = hand_landmarks.landmark[i].x * frame.shape[1] y = hand_landmarks.landmark[i].y * frame.shape[0] hand_local.append((x, y)) # 判断动态手势 two_points_line = [hand_local[8], hand_local[7]] degree = get_line_decline(two_points_line) degree = round(degree) self.degree_list.append(degree) if len(self.degree_list) > 20: del(self.degree_list[0]) for i in range(len(self.degree_list)): if self.degree_list[i] < -75 or 75 < self.degree_list[i]: self.degree_list[i] = 0 # print(self.degree_list) count = 0 for i in self.degree_list: if i < 0: count += 1 count_1 = 0 for i in self.degree_list: if i > 0: count_1 += 1 if self.degree_list[0] == 0 and self.degree_list[1] == 0 and self.degree_list[2]!= 0 and count > 13: self.handSignal.emit(1) if self.degree_list[0] == 0 and self.degree_list[1] == 0 and self.degree_list[2]!= 0 and count_1 > 13: self.handSignal.emit(2) if hand_local: angle_list = hand_angle(hand_local) gesture_str = h_gesture(angle_list) print(gesture_str) self.gestureSignal.emit(gesture_str) # 在图像上写出识别结果 cv2.putText(frame, gesture_str, (0, 100), 0, 1.3, (0, 0, 255), 3) else: gesture_str = '' self.gestureSignal.emit(gesture_str) except: frame = frame return frame if __name__ == '__main__': cap = cv2.VideoCapture(0) A = HandPostureDetect() while True: _ret, frame = cap.read() # frame = cv2.flip(frame, 1) frame = A.detect(frame) cv2.imshow('1', frame) cv2.waitKey(1)
Widget模块用于定义最顶层的GUI界面相关内容,GUI显示也是模块化,将左侧的图像显示区域与右侧的算法结果验证区域分开。
from UI.ui_Widget import * from Camera import * from menu import * from PyQt5.QtGui import QImage from HandpostureDetect import HandPostureDetect # 手势监测算子 from audio import Voice class Widget(QWidget, Ui_Widget): def __init__(self, parent=None): super(Widget, self).__init__(parent) # 实例化标题栏 self.m1 = Menu() self.setupUi(self) self.stackedWidget.addWidget(self.m1) self.stackedWidget.setCurrentIndex(0) # 初始化多个相机 # 设置打开摄像头的序号数值(opencv打开多摄像头必须按降序开) self.camera_thread = Camera() self.camera_thread.set_cam_number(0) # 设置手势监测算子 self.camera_thread.needflip = 0 self.camera_thread.set_detector(HandPostureDetect) self.camera_thread.detector.handSignal[int].connect(self.m1.set_index) self.camera_thread.detector.gestureSignal[str].connect(self.show_hand_posture) self.camera_thread.sendPicture[QImage].connect(self.receive) self.camera_thread.detect_flag = 1 self.camera_thread.open_camera() # 创建音频 self.voice = Voice('xxxxxx有限公司') self.voice.start() # 更新QLabel图像数据 def receive(self, img): try: img_height = self.label_3.height() img_width = self.label_3.width() # QImage.scaled若图像尺寸较大,会卡顿,暂时没有什么好方法 new_img = img.scaled(QSize(img_width, img_height)) self.label_3.setPixmap(QPixmap.fromImage(new_img)) except: pass def show_hand_posture(self, hand_posture): self.label_5.setText(hand_posture) self.label_5.setStyleSheet('QLabel{' 'font: 16pt bold "楷体";' 'font-weight:900;' 'text-align:center center;' 'color:white;' 'border:5px solid;' 'border-radius:10px;' 'border-color: rgb(0, 255, 127);' 'background-color: grey;/*定义最小高度和最小宽度*/' 'min-height: 60px;' 'min-width: 60px;}')
具体实现手势识别验证QLabel相关操作。实现代码请联系本人获取。
请与本人私信联系。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。