✨博客主页:王乐予🎈
✨年轻人要:Living for the moment(活在当下)!💪
🏆推荐专栏:【图像处理】【千锤百炼Python】【深度学习】【排序算法】
目录
- 😺一、MediaPipe概述
- 😺二、MediaPipe姿态特征点检测
- 🐶2.1 概述
- 🐶2.2 度量函数
- 😺三、代码实现
- 🐶3.1 utils.py
- 🐶3.2 main.py
- 😺四、参考
😺一、MediaPipe概述
MediaPipe 是一款由 Google Research 开发并开源的多媒体机器学习模型应用框架。
MediaPipe目前支持的解决方案(Solution)及支持的平台如下图所示:
😺二、MediaPipe姿态特征点检测
🐶2.1 概述
通过 MediaPipe Pose Marker,可以检测图片或视频中人体的特征点。使用此任务识别关键的身体位置,分析姿势并对动作进行分类。该任务会在图片坐标和三维世界坐标中输出身体姿势地标。
姿势特征点使用一系列模型来预测姿势特征点。第一个模型检测图片帧中是否存在人体,第二个模型则在身体上定位地标。
姿势特征点模型会跟踪 33 个身体特征点位置,表示以下身体部位的大致位置:
点位信息如下:
0 - nose
1 - left eye (inner)
2 - left eye
3 - left eye (outer)
4 - right eye (inner)
5 - right eye
6 - right eye (outer)
7 - left ear
8 - right ear
9 - mouth (left)
10 - mouth (right)
11 - left shoulder
12 - right shoulder
13 - left elbow
14 - right elbow
15 - left wrist
16 - right wrist
17 - left pinky
18 - right pinky
19 - left index
20 - right index
21 - left thumb
22 - right thumb
23 - left hip
24 - right hip
25 - left knee
26 - right knee
27 - left ankle
28 - right ankle
29 - left heel
30 - right heel
31 - left foot index
32 - right foot index
🐶2.2 度量函数
坐姿检测将使用不同关键点的向量夹角做判定,向量内角图如下:
内角计算与向量的起止顺序有关,在上图中,假定选择kpt1和kpt2为人体的两个关键点,kpt3为向量起始点即kpt1的垂直方向任意位置的点,则夹角为:
θ = arccos ( P 12 → × P 13 → ∣ P 12 → ∣ ∣ P 13 → ∣ ) \theta =\arccos (\frac{\overrightarrow{P_{12} } \times \overrightarrow{P_{13} } }{\left | \overrightarrow{P_{12} } \right | \left | \overrightarrow{P_{13} } \right | } ) θ=arccos( P12 P13 P12×P13)
不妨设kpt3的y3坐标为0,则带入坐标值有:
θ = arccos ( y 1 2 − y 1 × y 2 y 1 ( x 2 − x 1 ) 2 + ( y 2 − y 1 ) 2 ) \theta =\arccos (\frac{y_{1}^{2} - y_{1}\times y_{2} }{y_{1}\sqrt{(x_{2}-x_{1})^{2}+(y_{2}-y_{1})^{2} } } ) θ=arccos(y1(x2−x1)2+(y2−y1)2y12−y1×y2)
根据上图可知 θ \theta θ为锐角,如果向量方向为由kpt2指向kpt1,则需要在kpt2的垂直方向标记点kpt3,此时 θ \theta θ为钝角。
😺三、代码实现
utils.py
:包含度量函数的定义与姿态检测函数main.py
:主函数,获取需要的关键点数据,绘图
🐶3.1 utils.py
import math as m# 度量函数
def findAngle(x1, y1, x2, y2):theta = m.acos((y2 - y1) * (-y1) / (m.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) * y1))degree = int(180/m.pi)*thetareturn degree"""
歪头监控:计算 左耳(7点)和 右耳(8点)的夹角
低头监控:计算 左嘴角(9点)和 左肩膀(11点)的夹角
侧脸监控:计算 右眼内(4点)和 左耳(7点)的距离,计算 左眼内(1点)和 右耳(8点)的距离
高低肩监控:计算 左肩膀(11点)和 右肩膀(12点)的夹角 *****有的人左肩和右肩一个高一个低*****
撑桌监控:如果 左嘴角(9点)或者 右嘴角(10点)的 y 坐标 大于 左肩膀(11点)或 右肩膀(12点)的 y 坐标,视为撑桌
仰头监控:计算 鼻子(0点)和 左耳(7点)的夹角
趴桌监控:如果 左肩膀(11点)和 右肩膀(12点)的 归一化y坐标 之和大于0.75,判定为趴桌
"""
def all_detection(nose_x, nose_y, # 鼻子(0点)的 x 坐标 和 y 坐标left_eye_inner_x, left_eye_inner_y, # 左眼内(1点)的 x 坐标 和 y 坐标right_eye_inner_x, right_eye_inner_y, # 右眼内(4点)的 x 坐标 和 y 坐标left_ear_x, left_ear_y, # 左耳(7点)的 x 坐标 和 y 坐标right_ear_x, right_ear_y, # 右耳(8点)的 x 坐标 和 y 坐标left_mouth_x, left_mouth_y, # 左嘴角(9点)的 x 坐标 和 y 坐标right_mouth_x, right_mouth_y, # 右嘴角(10点)的 x 坐标 和 y 坐标left_shoulder_x, left_shoulder_y, # 左肩膀(11点)的 x 坐标 和 y 坐标right_shoulder_x, right_shoulder_y, # 右肩膀(12点)的 x 坐标 和 y 坐标left_shoulder_x_norm, left_shoulder_y_norm, # 归一化后的左肩膀(11点)的 x 坐标 和 y 坐标right_shoulder_x_norm, right_shoulder_y_norm # 归一化后的右肩膀(12点)的 x 坐标 和 y 坐标):waitou_inclination = findAngle(left_ear_x, left_ear_y, right_ear_x, right_ear_y)ditou_inclination = findAngle(left_mouth_x, left_mouth_y, left_shoulder_x, left_shoulder_y)gaodijian_inclination = findAngle(left_shoulder_x, left_shoulder_y, right_shoulder_x, right_shoulder_y)yangtou_inclination = findAngle(nose_x, nose_y, left_ear_x, left_ear_y)if waitou_inclination < 80:tmp = '左歪头'elif waitou_inclination > 100:tmp = '右歪头'elif (left_shoulder_y_norm + right_shoulder_y_norm) > 1.5:tmp = '趴桌'elif ditou_inclination < 115:tmp = '低头'elif left_ear_x < right_eye_inner_x:tmp = '左侧脸'elif right_ear_x > left_eye_inner_x:tmp = '右侧脸'elif gaodijian_inclination > 100:tmp = '高低肩'elif gaodijian_inclination < 80:tmp = '高低肩'elif (left_mouth_y or right_mouth_y) > (left_shoulder_y or right_shoulder_y):tmp = '撑桌'elif yangtou_inclination > 90:tmp = '仰头'else:tmp = '正脸'return tmp
🐶3.2 main.py
import cv2
import mediapipe as mp
from utils import *mp_pose = mp.solutions.pose
pose = mp_pose.Pose(model_complexity=1, min_detection_confidence=0.5, min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utilscap = cv2.VideoCapture(0)while True:ret, frame = cap.read()h, w = frame.shape[:2]image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)keypoints = pose.process(image)image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)lm = keypoints.pose_landmarkslmPose = mp_pose.PoseLandmark# 歪头监控left_ear_x = int(lm.landmark[lmPose.LEFT_EAR].x * w) # 左耳(7点)x 坐标left_ear_y = int(lm.landmark[lmPose.LEFT_EAR].y * h) # 左耳(7点)y 坐标right_ear_x = int(lm.landmark[lmPose.RIGHT_EAR].x * w) # 右耳(8点)x 坐标right_ear_y = int(lm.landmark[lmPose.RIGHT_EAR].y * h) # 右耳(8点)y 坐标# 低头监控left_mouth_x = int(lm.landmark[lmPose.MOUTH_LEFT].x * w) # 左嘴角(9点)x 坐标left_mouth_y = int(lm.landmark[lmPose.MOUTH_LEFT].y * h) # 左嘴角(9点)y 坐标left_shoulder_x = int(lm.landmark[lmPose.LEFT_SHOULDER].x * w) # 左肩膀(11点)x 坐标left_shoulder_y = int(lm.landmark[lmPose.LEFT_SHOULDER].y * h) # 左肩膀(11点)y 坐标# 侧脸监控left_eye_inner_x = int(lm.landmark[lmPose.LEFT_EYE_INNER].x * w) # 左眼内(1点)x 坐标left_eye_inner_y = int(lm.landmark[lmPose.LEFT_EYE_INNER].y * h) # 左眼内(1点)y 坐标right_eye_inner_x = int(lm.landmark[lmPose.RIGHT_EYE_INNER].x * w) # 右眼内(4点)x 坐标right_eye_inner_y = int(lm.landmark[lmPose.RIGHT_EYE_INNER].y * h) # 右眼内(4点)y 坐标# 高低肩监控right_shoulder_x = int(lm.landmark[lmPose.RIGHT_SHOULDER].x * w) # 右肩膀(12点)x 坐标right_shoulder_y = int(lm.landmark[lmPose.RIGHT_SHOULDER].y * h) # 右肩膀(12点)y 坐标# 撑桌监控right_mouth_x = int(lm.landmark[lmPose.MOUTH_RIGHT].x * w) # 左嘴角(10点)x 坐标right_mouth_y = int(lm.landmark[lmPose.MOUTH_RIGHT].y * h) # 左嘴角(10点)y 坐标# 仰头监控nose_x = int(lm.landmark[lmPose.NOSE].x * w) # 鼻子(0点)x 坐标nose_y = int(lm.landmark[lmPose.NOSE].y * h) # 鼻子(0点)y 坐标# 趴桌监控left_shoulder_x_norm = lm.landmark[lmPose.LEFT_SHOULDER].x # 左肩膀(11点)x 坐标-归一化left_shoulder_y_norm = lm.landmark[lmPose.LEFT_SHOULDER].y # 左肩膀(11点)y 坐标-归一化right_shoulder_x_norm = lm.landmark[lmPose.RIGHT_SHOULDER].x # 右肩膀(12点)x 坐标-归一化right_shoulder_y_norm = lm.landmark[lmPose.RIGHT_SHOULDER].y # 右肩膀(12点)y 坐标-归一化results = all_detection(nose_x, nose_y,left_eye_inner_x, left_eye_inner_y,right_eye_inner_x, right_eye_inner_y,left_ear_x, left_ear_y,right_ear_x, right_ear_y,left_mouth_x, left_mouth_y,right_mouth_x, right_mouth_y,left_shoulder_x, left_shoulder_y,right_shoulder_x, right_shoulder_y,left_shoulder_x_norm, left_shoulder_y_norm,right_shoulder_x_norm, right_shoulder_y_norm)print(results)mp_drawing.draw_landmarks(image, keypoints.pose_landmarks, mp_pose.POSE_CONNECTIONS)cv2.imshow("Image", image)if cv2.waitKey(1) & 0xFF == ord('q'):breakcap.release()
cv2.destroyAllWindows()
需要注意的是:utils.py
中的判定指标不是固定的,根据摄像头的位置动态调整才能达到满意的效果。
😺四、参考
Google:pose_landmarker
LearnOpencv:building-a-body-posture-analysis-system-using-mediapipe