
MediaPipe BlazePose 33关键点实战PythonOpenCV 实现深蹲计数与角度可视化在健身领域准确的动作计数和姿势分析是提升训练效果的关键。传统方法依赖人工观察或穿戴式设备而基于计算机视觉的解决方案正逐渐成为更高效的选择。本文将带你从零实现一个基于BlazePose 33个关键点的深蹲分析系统通过Python和OpenCV实时计算关节角度并可视化训练数据。1. 环境配置与基础框架搭建首先需要准备开发环境。推荐使用Python 3.8版本并创建虚拟环境隔离依赖python -m venv pose-env source pose-env/bin/activate # Linux/Mac pose-env\Scripts\activate # Windows安装核心依赖库pip install mediapipe opencv-python numpy matplotlib基础代码框架包含三个主要组件视频输入处理、姿态检测引擎和可视化输出。创建一个squat_analyzer.py文件初始化基础结构import cv2 import mediapipe as mp import numpy as np from matplotlib import pyplot as plt class SquatAnalyzer: def __init__(self): self.mp_pose mp.solutions.pose self.pose self.mp_pose.Pose( static_image_modeFalse, model_complexity1, smooth_landmarksTrue, min_detection_confidence0.5, min_tracking_confidence0.5) self.mp_drawing mp.solutions.drawing_utils self.cap None self.angle_history [] def process_frame(self, frame): # 待实现的核心处理逻辑 pass def run(self, video_source0): self.cap cv2.VideoCapture(video_source) while self.cap.isOpened(): ret, frame self.cap.read() if not ret: break processed_frame self.process_frame(frame) cv2.imshow(Squat Analysis, processed_frame) if cv2.waitKey(1) 0xFF ord(q): break self.cap.release() cv2.destroyAllWindows() if __name__ __main__: analyzer SquatAnalyzer() analyzer.run() # 默认使用摄像头提示测试时可通过传入视频路径替代摄像头输入如analyzer.run(squat_demo.mp4)2. 关键点解析与角度计算BlazePose的33个关键点中对深蹲分析最重要的是下肢关节。我们需要特别关注以下点位关键点编号身体部位用途23左髋关节计算髋部角度24右髋关节计算髋部角度25左膝关节计算膝盖角度26右膝关节计算膝盖角度27左踝关节动作幅度检测28右踝关节动作幅度检测角度计算采用向量夹角公式。以下函数计算三个关键点形成的夹角def calculate_angle(a, b, c): 计算三个点之间的夹角(度) :param a: 点A坐标 (x,y) :param b: 点B坐标 (中心点) :param c: 点C坐标 :return: 角度值(0-180) a np.array(a) b np.array(b) c np.array(c) ba a - b bc c - b cosine_angle np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc)) angle np.arccos(cosine_angle) return np.degrees(angle)更新process_frame方法实现实时角度计算def process_frame(self, frame): # 转换颜色空间并处理 image cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results self.pose.process(image) # 绘制关键点连接 annotated_image frame.copy() self.mp_drawing.draw_landmarks( annotated_image, results.pose_landmarks, self.mp_pose.POSE_CONNECTIONS) if results.pose_landmarks: landmarks results.pose_landmarks.landmark # 获取关键点坐标 left_hip [landmarks[23].x, landmarks[23].y] right_hip [landmarks[24].x, landmarks[24].y] left_knee [landmarks[25].x, landmarks[25].y] right_knee [landmarks[26].x, landmarks[26].y] left_ankle [landmarks[27].x, landmarks[27].y] right_ankle [landmarks[28].x, landmarks[28].y] # 计算膝盖角度 left_knee_angle calculate_angle(left_hip, left_knee, left_ankle) right_knee_angle calculate_angle(right_hip, right_knee, right_ankle) # 显示角度信息 cv2.putText(annotated_image, fL Knee: {int(left_knee_angle)}, tuple(np.multiply(left_knee, [640, 480]).astype(int)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 2) # 记录角度历史用于分析 self.angle_history.append((left_knee_angle right_knee_angle)/2) return annotated_image3. 深蹲计数逻辑实现准确的深蹲计数需要定义完整的动作周期。我们通过膝关节角度变化来识别起始位置膝盖角度 150度下蹲阶段角度持续减小至 90度上升阶段角度恢复至 150度完整计数完成1→2→3→1的循环实现计数状态机class SquatAnalyzer: def __init__(self): # ...原有初始化... self.squat_count 0 self.squat_state up # 初始状态 def update_counter(self, knee_angle): if self.squat_state up and knee_angle 90: self.squat_state down elif self.squat_state down and knee_angle 150: self.squat_count 1 self.squat_state up def process_frame(self, frame): # ...原有处理逻辑... if results.pose_landmarks: # ...角度计算... avg_angle (left_knee_angle right_knee_angle)/2 self.update_counter(avg_angle) # 显示计数 cv2.putText(annotated_image, fSquats: {self.squat_count}, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2) return annotated_image优化计数逻辑增加动作质量检测def update_counter(self, knee_angle): if self.squat_state up: if knee_angle 90: # 进入下蹲 self.squat_state down self.min_angle knee_angle else: self.min_angle None elif self.squat_state down: if knee_angle (self.min_angle or 180): self.min_angle knee_angle if knee_angle 150: # 完成站立 if self.min_angle and self.min_angle 80: # 深度达标 self.squat_count 1 self.squat_state up else: cv2.putText(annotated_image, Squat too shallow!, (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,0,255), 2)4. 实时数据可视化为了更直观地展示训练数据我们添加实时角度曲线和统计面板def __init__(self): # ...原有初始化... self.fig, (self.ax1, self.ax2) plt.subplots(2, 1, figsize(8,6)) plt.ion() # 开启交互模式 def update_plots(self): # 清空原有图形 self.ax1.clear() self.ax2.clear() # 角度变化曲线 if len(self.angle_history) 0: self.ax1.plot(self.angle_history, b-) self.ax1.set_title(Knee Angle Variation) self.ax1.set_ylim(0, 180) self.ax1.axhline(y90, colorr, linestyle--) # 计数统计 self.ax2.text(0.1, 0.5, fTotal Squats: {self.squat_count}, fontsize14) self.ax2.axis(off) plt.tight_layout() plt.pause(0.01) def process_frame(self, frame): # ...原有处理... self.update_plots() return annotated_image优化可视化效果添加运动指标def update_plots(self): # ...角度曲线... # 运动指标面板 stats_text [ fTotal Squats: {self.squat_count}, fCurrent Angle: {self.angle_history[-1]:.1f}° if self.angle_history else , fDepth {✓ if (self.min_angle and self.min_angle 80) else ✗} ] self.ax2.text(0.1, 0.8, \n.join(stats_text), fontsize12, verticalalignmenttop) self.ax2.axis(off)5. 系统优化与高级功能5.1 多角度同步分析同时监测髋关节和膝关节角度提供更全面的动作分析def process_frame(self, frame): # ...原有处理... if results.pose_landmarks: # 髋关节角度计算 left_hip_angle calculate_angle( [landmarks[11].x, landmarks[11].y], # 左肩 left_hip, left_knee) # 添加到可视化 cv2.putText(annotated_image, fL Hip: {int(left_hip_angle)}, tuple(np.multiply(left_hip, [640, 480]).astype(int)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,0), 2) # 更新曲线图 self.ax1.plot(self.angle_history, b-, labelKnee) self.ax1.plot(self.hip_angle_history, g-, labelHip) self.ax1.legend()5.2 姿势矫正提示检测常见错误姿势并提供实时反馈def check_posture(self, landmarks): warnings [] # 膝盖内扣检测 left_knee landmarks[25].x right_knee landmarks[26].x if left_knee right_knee 0.05: # 左膝过度内扣 warnings.append(Left knee collapsing inward) # 背部弯曲检测 shoulder (landmarks[11].y landmarks[12].y)/2 hip (landmarks[23].y landmarks[24].y)/2 if shoulder - hip 0.1: # 上身前倾过多 warnings.append(Keep your chest up) return warnings # 在process_frame中调用 warnings self.check_posture(landmarks) for i, warning in enumerate(warnings): cv2.putText(annotated_image, warning, (50, 150i*30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,255), 2)5.3 数据持久化与报告生成添加训练数据记录功能def save_session_data(self): timestamp datetime.now().strftime(%Y%m%d_%H%M%S) filename fsquat_session_{timestamp}.csv with open(filename, w) as f: f.write(time,angle\n) for i, angle in enumerate(self.angle_history): f.write(f{i/30:.2f},{angle}\n) # 假设30FPS print(fSession data saved to {filename}) # 在程序退出时调用 def run(self, video_source0): try: # ...原有运行逻辑... finally: if len(self.angle_history) 0: self.save_session_data()6. 部署与性能优化6.1 多线程处理使用生产者-消费者模式提升实时性from threading import Thread from queue import Queue class VideoStream: def __init__(self, src0): self.stream cv2.VideoCapture(src) self.stopped False self.Q Queue(maxsize128) def start(self): Thread(targetself.update, args()).start() return self def update(self): while True: if self.stopped: return ret, frame self.stream.read() if not ret: self.stop() return if not self.Q.full(): self.Q.put(frame) def read(self): return self.Q.get() def stop(self): self.stopped True # 修改SquatAnalyzer的run方法 def run(self, video_source0): vs VideoStream(video_source).start() while True: frame vs.read() processed_frame self.process_frame(frame) cv2.imshow(Squat Analysis, processed_frame) if cv2.waitKey(1) 0xFF ord(q): vs.stop() break cv2.destroyAllWindows()6.2 模型优化技巧通过调整MediaPipe参数提升性能self.pose self.mp_pose.Pose( static_image_modeFalse, model_complexity0, # 使用轻量级模型 smooth_landmarksTrue, enable_segmentationFalse, min_detection_confidence0.5, min_tracking_confidence0.5)对于低性能设备可降低输入分辨率def process_frame(self, frame): # 缩小处理分辨率 small_frame cv2.resize(frame, (0,0), fx0.5, fy0.5) results self.pose.process(small_frame) # 结果坐标需要转换回原始尺寸 if results.pose_landmarks: for landmark in results.pose_landmarks.landmark: landmark.x * 2 landmark.y * 27. 应用扩展思路7.1 多动作识别系统扩展框架支持多种健身动作class FitnessAnalyzer: def __init__(self): self.analyzers { squat: SquatAnalyzer(), pushup: PushupAnalyzer(), lunge: LungeAnalyzer() } self.current_mode squat def switch_mode(self, mode): if mode in self.analyzers: self.current_mode mode def process_frame(self, frame): return self.analyzers[self.current_mode].process_frame(frame)7.2 3D姿态可视化结合MediaPipe的Z坐标数据实现三维展示def plot_3d_skeleton(landmarks): fig plt.figure() ax fig.add_subplot(111, projection3d) xs [lm.x for lm in landmarks] ys [lm.y for lm in landmarks] zs [-lm.z for lm in landmarks] # 转换Z轴方向 # 绘制关键点 ax.scatter(xs, ys, zs) # 绘制骨骼连接 for connection in mp.solutions.pose.POSE_CONNECTIONS: start connection[0] end connection[1] ax.plot([xs[start], xs[end]], [ys[start], ys[end]], [zs[start], zs[end]], r-) plt.show()7.3 云端数据同步实现训练数据的上传与分析import requests def upload_session_data(user_id, session_data): api_url https://your-api-endpoint.com/sessions payload { user_id: user_id, exercise: squat, data: session_data } try: response requests.post(api_url, jsonpayload) if response.status_code 200: print(Data uploaded successfully) return response.json() except Exception as e: print(fUpload failed: {str(e)})8. 实际应用案例8.1 家庭健身助手将系统集成到智能镜中打造交互式健身体验class SmartMirror: def __init__(self): self.analyzer SquatAnalyzer() self.mirror_display MirrorDisplay() def run(self): while True: frame self.mirror_display.get_frame() processed self.analyzer.process_frame(frame) # 在镜面叠加分析结果 self.mirror_display.show( processed, self.analyzer.angle_history, self.analyzer.squat_count)8.2 健身房动作分析终端部署到固定设备供会员使用class GymTerminal: def __init__(self): self.analyzer FitnessAnalyzer() self.user_db UserDatabase() def start_session(self, user_id): user self.user_db.get_user(user_id) print(fWelcome {user.name}! Starting your workout...) cap cv2.VideoCapture(0) while True: ret, frame cap.read() processed self.analyzer.process_frame(frame) cv2.imshow(Workout Analysis, processed) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() # 保存训练数据 self.user_db.save_session( user_id, self.analyzer.get_session_data())8.3 康复训练监控为物理治疗患者提供动作质量反馈class RehabMonitor: def __init__(self, patient_id): self.patient_id patient_id self.analyzer SquatAnalyzer() self.therapist TherapistNotifier() def check_range_of_motion(self): if len(self.analyzer.angle_history) 10: return current_range max(self.analyzer.angle_history[-10:]) - min(self.analyzer.angle_history[-10:]) if current_range 60: # 活动范围不足 self.therapist.notify( self.patient_id, Limited range of motion detected)