Google出了一個開源的、跨平台的、可定制化的機器學習解決方案工具包,給在線流媒體(當然也可以用於普通的視頻、圖像等)提供了機器學習解決方案。感興趣的同學可以打開這個網址了解詳情:https://mediapipe.dev/。它提供了手勢、人體姿勢、人臉、物品等識別和追蹤功能,並提供了C++、Python、JavaScript等編程語言的工具包以及iOS、Android平台的解決方案,今天我們就來看一下如何使用MediaPipe提供的人體姿勢識別功能,使用Python編程完成一個“仰卧起坐檢測”的程序。


准備工作
-
安裝Python3.8.x
-
創建一個Python項目,建議使用virtualenv創建一個項目專有的Python環境
-
安裝包:Opencv-Python、mediapipe、numpy
開始編程
-
創建一個人體姿勢識別模塊,在這個模塊中,我們使用mediapipe模塊來實現人體姿勢識別,並獲取姿勢數據。
1 import cv2 2 import mediapipe as mp 3 import math 4 5 6 class PoseDetector(): 7 ''' 8 人體姿勢檢測類 9 ''' 10 11 def __init__(self, 12 static_image_mode=False, 13 upper_body_only=False, 14 smooth_landmarks=True, 15 min_detection_confidence=0.5, 16 min_tracking_confidence=0.5): 17 ''' 18 初始化 19 :param static_image_mode: 是否是靜態圖片,默認為否 20 :param upper_body_only: 是否是上半身,默認為否 21 :param smooth_landmarks: 設置為True減少抖動 22 :param min_detection_confidence:人員檢測模型的最小置信度值,默認為0.5 23 :param min_tracking_confidence:姿勢可信標記的最小置信度值,默認為0.5 24 ''' 25 self.static_image_mode = static_image_mode 26 self.upper_body_only = upper_body_only 27 self.smooth_landmarks = smooth_landmarks 28 self.min_detection_confidence = min_detection_confidence 29 self.min_tracking_confidence = min_tracking_confidence 30 # 創建一個Pose對象用於檢測人體姿勢 31 self.pose = mp.solutions.pose.Pose(self.static_image_mode, self.upper_body_only, self.smooth_landmarks, 32 self.min_detection_confidence, self.min_tracking_confidence) 33 34 def find_pose(self, img, draw=True): 35 ''' 36 檢測姿勢方法 37 :param img: 一幀圖像 38 :param draw: 是否畫出人體姿勢節點和連接圖 39 :return: 處理過的圖像 40 ''' 41 imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 42 # pose.process(imgRGB) 會識別這幀圖片中的人體姿勢數據,保存到self.results中 43 self.results = self.pose.process(imgRGB) 44 if self.results.pose_landmarks: 45 if draw: 46 mp.solutions.drawing_utils.draw_landmarks(img, self.results.pose_landmarks, 47 mp.solutions.pose.POSE_CONNECTIONS) 48 return img 49 50 def find_positions(self, img): 51 ''' 52 獲取人體姿勢數據 53 :param img: 一幀圖像 54 :param draw: 是否畫出人體姿勢節點和連接圖 55 :return: 人體姿勢數據列表 56 ''' 57 # 人體姿勢數據列表,每個成員由3個數字組成:id, x, y 58 # id代表人體的某個關節點,x和y代表坐標位置數據 59 self.lmslist = [] 60 if self.results.pose_landmarks: 61 for id, lm in enumerate(self.results.pose_landmarks.landmark): 62 h, w, c = img.shape 63 cx, cy = int(lm.x * w), int(lm.y * h) 64 self.lmslist.append([id, cx, cy]) 65 66 return self.lmslist 67 68 def find_angle(self, img, p1, p2, p3, draw=True): 69 ''' 70 獲取人體姿勢中3個點p1-p2-p3的角度 71 :param img: 一幀圖像 72 :param p1: 第1個點 73 :param p2: 第2個點 74 :param p3: 第3個點 75 :param draw: 是否畫出3個點的連接圖 76 :return: 角度 77 ''' 78 x1, y1 = self.lmslist[p1][1], self.lmslist[p1][2] 79 x2, y2 = self.lmslist[p2][1], self.lmslist[p2][2] 80 x3, y3 = self.lmslist[p3][1], self.lmslist[p3][2] 81 82 # 使用三角函數公式獲取3個點p1-p2-p3,以p2為角的角度值,0-180度之間 83 angle = int(math.degrees(math.atan2(y1 - y2, x1 - x2) - math.atan2(y3 - y2, x3 - x2))) 84 if angle < 0: 85 angle = angle + 360 86 if angle > 180: 87 angle = 360 - angle 88 89 if draw: 90 cv2.circle(img, (x1, y1), 20, (0, 255, 255), cv2.FILLED) 91 cv2.circle(img, (x2, y2), 30, (255, 0, 255), cv2.FILLED) 92 cv2.circle(img, (x3, y3), 20, (0, 255, 255), cv2.FILLED) 93 cv2.line(img, (x1, y1), (x2, y2), (255, 255, 255, 3)) 94 cv2.line(img, (x2, y2), (x3, y3), (255, 255, 255, 3)) 95 cv2.putText(img, str(angle), (x2 - 50, y2 + 50), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 255), 2) 96 97 return angle
-
編寫situps.py,在這個程序中我們將使用opencv讀取視頻文件,當然你也可以使用攝像頭直接拍攝。調用人體姿勢識別模塊進行視頻中人體姿勢識別並獲取姿勢數據,通過人體姿勢中3個位置點的數據:肩膀(11)、臀部(23)、膝蓋(25),計算這3個點的角度,判斷仰卧起坐是否達標。


# 導入opencv工具包 import cv2 # 導入numpy import numpy as np # 導入姿勢識別器 from poseutil import PoseDetector # 打開視頻文件 cap = cv2.VideoCapture('videos/situp.mp4') # 姿勢識別器 detector = PoseDetector() # 方向與個數 dir = 0 # 0為躺下,1為坐起 count = 0 while True: # 讀取攝像頭,img為每幀圖片 success, img = cap.read() if success: h, w, c = img.shape # 識別姿勢 img = detector.find_pose(img, draw=True) # 獲取姿勢數據 positions = detector.find_positions(img) if positions: # 獲取仰卧起坐的角度 angle = detector.find_angle(img, 11, 23, 25) # 進度條長度 bar = np.interp(angle, (50, 130), (w // 2 - 100, w // 2 + 100)) cv2.rectangle(img, (w // 2 - 100, h - 150), (int(bar), h - 100), (0, 255, 0), cv2.FILLED) # 角度小於55度認為坐起 if angle <= 55: if dir == 0: count = count + 0.5 dir = 1 # 角度大於120度認為躺下 if angle >= 120: if dir == 1: count = count + 0.5 dir = 0 cv2.putText(img, str(int(count)), (w // 2, h // 2), cv2.FONT_HERSHEY_SIMPLEX, 10, (255, 255, 255), 20, cv2.LINE_AA) # 打開一個Image窗口顯示視頻圖片 cv2.imshow('Image', img) else: # 視頻結束退出 break # 如果按下q鍵,程序退出 key = cv2.waitKey(1) if key == ord('q'): break # 關閉攝像頭 cap.release() # 關閉程序窗口 cv2.destroyAllWindows()
運行測試
