AI 视频姿态估计 x 生物力学 x 运动医学
点击或拖拽视频文件到这里
支持 .mp4 .mov .avi .webm
用户上传扣球视频后,系统通过 MediaPipe BlazePose(33 关键点 3D 人体姿态估计模型)逐帧提取人体姿态序列。对检测失败的帧进行线性插值填补,使用 Savitzky-Golay 滤波器(窗口 11 帧,2 阶多项式)平滑轨迹,以髋部中点为原点进行坐标归一化。随后计算关节角度、角速度、落地冲击指标、膝外翻角等生物力学参数,通过动力链时序分析和国际运动医学文献阈值比对,对 ACL 损伤、膝外翻、肩袖损伤、网球肘、跟腱损伤 5 类常见损伤进行独立分级评估,最终生成骨骼标注分析视频、三栏综合数据图表和文字评估报告。
| 功能名称 | 羽毛球扣球损伤生物力学分析与风险预警系统 |
|---|---|
| 适用场景 | 羽毛球扣球技术动作的运动生物力学分析、损伤风险评估、教练辅助指导、运动康复监测 |
| 开发语言 | Python 3.10+ |
| 核心依赖 | Flask、MediaPipe 0.10.9、OpenCV、NumPy、SciPy、Matplotlib、PyTorch |
| AI 模型 | MediaPipe BlazePose GHUM 3D — Google 33 关键点人体姿态估计模型(CVPR 2020) |
| 部署方式 | Flask + nginx 反向代理 + systemd 服务化,HTTPS 加密传输 |
| 运行环境 | Ubuntu 22.04 LTS,支持跨平台部署(Windows/macOS/Linux) |
Flask 文件接收
最大 500MB
MP4/MOV/AVI/WebM
MediaPipe Pose
33 关键点 3D
SG 滤波平滑
关节角度/角速度
落地冲击检测
膝外翻计算
关节激活时序
DTW 平滑度
链断裂检测
5 类损伤独立评分
文献阈值比对
综合风险等级
骨骼标注视频
三栏数据图表
JSON 结构化报告
1. 视频上传与文件管理 — Flask 框架处理 multipart/form-data 上传,自动创建 data/uploads/(视频存储)和 data/output/(结果输出)目录,每次分析以视频文件名为标识隔离结果文件。
2. 姿态提取引擎 — 基于 mediapipe==0.10.9 的 BlazePose 模型,逐帧提取 33 个关键点的 3D 世界坐标(x, y, z, visibility)。对检测置信度 <0.5 的帧进行线性插值填补,使用 Savitzky-Golay 滤波器(窗口 11 帧,2 阶多项式)平滑关节轨迹,消除逐帧抖动噪声。以髋部中点(左右髋关节连线中点)为原点进行坐标归一化,消除人物在画面中的绝对位置和体型差异影响。
3. 生物力学计算 — 通过三点法计算关节角度:θ = arccos((BA·BC) / (|BA| × |BC|)) × 180/π;中心差分法计算角速度:ω = dθ/dt × fps。膝外翻角通过将髋-膝-踝向量投影至 XZ 额状面计算偏角。落地检测通过寻找膝关节屈曲角全局最小值帧实现,同时计算落地瞬间的屈膝角、膝外翻角、髋屈曲角等冲击指标。
4. 动力链分析 — 检测各关节(膝→髋→肩→肘→腕)角速度峰值时刻,与标准羽毛球扣球动力链顺序逐一比对评分。计算相邻关节角速度曲线的重叠积分评估力量传递平滑度。标准动力链为 膝→髋→肩→肘→腕,断裂(如肩在髋之前发力)会导致远端关节代偿,是肩袖损伤和网球肘的重要诱因。
5. 损伤风险评估 — 分层评估架构,5 类损伤独立评估:
| 损伤类型 | 检测指标 | 安全阈值 | 风险阈值 | 文献依据 |
|---|---|---|---|---|
| ACL 前交叉韧带 | 落地屈膝角 | < 150° | > 150° | Hewett et al. (2005) |
| 膝外翻 (MCL) | 膝外翻角 | < 10° | > 10° | Hewett et al. (2005) |
| 肩袖损伤 | 肩关节峰值角速度 | < 85 rad/s | > 85 rad/s | Ramasamy et al. (2021) |
| 网球肘 | 击球时肘伸展角 | < 170° | > 170°(锁死) | Sorensen (2019) |
| 跟腱损伤 | 踝关节背屈角 + 落地冲击 | 正常范围 | 过度背屈 + 高冲击 | 综合评估 |
每类损伤评估后取最高等级作为综合评级(low / medium / high / critical),同时给出针对性的改进建议。
6. 可视化输出 — 生成三类成果: © 骨骼标注分析视频(MP4)— 原视频叠加 33 关键点骨架连线,风险关节以颜色高亮(绿/黄/橙/红对应低/中/高/极高风险),叠加风险标签和改进建议文字; © 三栏综合数据图表(PNG,150 DPI)— Panel 1 关节角度时间曲线 + ACL 阈值线,Panel 2 角速度时间曲线 + 精英运动员参考线,Panel 3 损伤风险水平条形图 + 综合评级; © JSON 结构化报告 — 包含所有量化指标和风险评估结果的完整数据。
| 字段 | 说明 |
|---|---|
| 姿态模型 | MediaPipe Pose (BlazePose GHUM 3D),33 关键点,检测置信度阈值 0.5 |
| 滤波参数 | Savitzky-Golay 滤波器,窗口 window_length=11,多项式阶数 polyorder=2 |
| 落地检测 | 膝关节屈曲角全局最小值帧,搜索窗口为整个动作周期 |
| 动力链标准 | 标准顺序 = [膝, 髋, 肩, 肘, 腕],允许 ±3 帧偏差 |
| 风险等级 | low(低)/ medium(中)/ high(高)/ critical(极高),4 级制 |
| 视频格式 | 支持 MP4 / MOV / AVI / WebM,分辨率建议 720p+,时长 2-30 秒 |
| 上传限制 | MAX_CONTENT_LENGTH = 500MB |
| 识别范围 | 单人全身扣球动作,需头部到脚部均可见,侧面或斜侧面 45° 拍摄最佳 |
| 骨骼连线 | 12 组连接(肩-肩、肩-髋×2、肩-肘×2、肘-腕×2、髋-膝×2、膝-踝×2) |
# MediaPipe BlazePose 33 关键点拓扑 — 持拍侧关节索引 RACKET_SIDE = { 'shoulder': 11, # 右肩(假设右手持拍) 'elbow': 13, # 右肘 'wrist': 15, # 右腕 'hip': 23, # 右髋 'knee': 25, # 右膝 'ankle': 27, # 右踝 } # 风险类型 → 关节映射 RISK_JOINT_MAP = { '膝关节 (Knee)': 25, # ACL / 膝外翻 '肩关节 (Shoulder)': 11, # 肩袖损伤 '肘关节 (Elbow)': 13, # 网球肘 '跟腱 (Achilles)': 27, # 跟腱损伤 '全身动力链': -1, # 动力链断裂 } # 风险等级 → 可视化颜色 (BGR) RISK_COLORS = { 'critical': (0, 0, 255), # 红色 — 极高风险 'high': (0, 165, 255), # 橙色 — 高风险 'medium': (0, 255, 255), # 黄色 — 中等风险 'low': (0, 255, 0), # 绿色 — 低风险 }
import mediapipe as mp import numpy as np from scipy.signal import savgol_filter def extract_poses(video_path, output_npy=None): cap = cv2.VideoCapture(video_path) fps = cap.get(cv2.CAP_PROP_FPS) or 30.0 poses_raw = [] with mp.solutions.pose.Pose( static_image_mode=False, model_complexity=2, # 最高精度模型 min_detection_confidence=0.5, min_tracking_confidence=0.5 ) as pose: while True: ret, frame = cap.read() if not ret: break frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) result = pose.process(frame_rgb) if result.pose_world_landmarks: lm = result.pose_world_landmarks kps = np.array([[l.x, l.y, l.z] for l in lm.landmark]) else: kps = np.full((33, 3), np.nan) poses_raw.append(kps) cap.release() # 线性插值填补缺失帧 + SG 滤波平滑 poses = np.array(poses_raw) for j in range(33): for c in range(3): nan_mask = np.isnan(poses[:, j, c]) if nan_mask.any(): xp = np.where(~nan_mask)[0] fp = poses[~nan_mask, j, c] poses[:, j, c] = np.interp(np.arange(len(poses)), xp, fp) poses[:, j, c] = savgol_filter(poses[:, j, c], 11, 2) return poses, fps
def compute_all_angles(poses_norm): def angle_3pts(a, b, c): ba = a - b; bc = c - b cos = np.clip(np.sum(ba * bc, axis=-1) / (np.linalg.norm(ba, axis=-1) * np.linalg.norm(bc, axis=-1) + 1e-8), -1.0, 1.0) return np.degrees(np.arccos(cos)) hip, knee, ankle = 23, 25, 27 # 右侧关节索引 shoulder, elbow, wrist = 11, 13, 15 angles = {} angles['knee_flexion'] = angle_3pts(poses_norm[:, hip], poses_norm[:, knee], poses_norm[:, ankle]) angles['shoulder_angle'] = angle_3pts(poses_norm[:, 23], poses_norm[:, shoulder], poses_norm[:, elbow]) angles['elbow_angle'] = angle_3pts(poses_norm[:, shoulder], poses_norm[:, elbow], poses_norm[:, wrist]) return angles def compute_landing_impact_indicators(poses_norm, fps): angles = compute_all_angles(poses_norm) knee_angles = angles['knee_flexion'] landing_frame = int(np.argmin(knee_angles)) return { 'landing_frame': landing_frame, 'knee_flexion_at_landing': float(knee_angles[landing_frame]), }
| # | 文献 |
|---|---|
| 1 | Hewett, T.E., et al. (2005). Biomechanical Measures of Neuromuscular Control and Valgus Loading of the Knee Predict Anterior Cruciate Ligament Injury Risk in Female Athletes. American Journal of Sports Medicine, 33(4): 492-501. |
| 2 | Ramasamy, Y., et al. (2021). Three-Dimensional Biomechanical Analysis of Upper Limb Movements in Badminton Smash. Sports Biomechanics. |
| 3 | Sorensen, K. Badminton Clear Stroke Biomechanics and Kinetic Chain Analysis. International Journal of Performance Analysis in Sport. |
| 4 | Bazarevsky, V., et al. (2020). BlazePose: On-device Real-time Body Pose Tracking. arXiv:2006.10204. |
| 5 | Cao, Z., et al. (2017). Realtime Multi-Person 2D Pose Estimation Using Part Affinity Fields. CVPR 2017. |