姿态估计模型评估与关键点精度优化
1. 评估指标
姿态估计评估指标: ├── Object Keypoint Similarity (OKS) │ ├── OKS = Σ[exp(-d²/2s²κ²)δ(v>0)] / Σ[δ(v>0)] │ ├── d:预测关键点与 GT 的欧氏距离 │ ├── s:目标面积的平方根 │ ├── κ:关键点常数(每个关键点不同) │ └── δ:可见性掩码 ├── mAP@OKS │ ├── mAP50:OKS=0.5 的 mAP │ ├── mAP50-95:OKS=0.5:0.05:0.95 的平均 │ └── 关键点 mAP:每个关键点单独计算 └── 推理速度 ├── 延迟(ms) └── FPS2. 模型评估
#!/usr/bin/env python3"""evaluate_pose.py"""fromultralyticsimportYOLO model=YOLO("runs/pose/yolo26n_pose_custom/weights/best.pt")# 评估metrics=model.val(data="data_pose.yaml",split="test",imgsz=640,batch=1,conf=0.25,iou=0.6,)print(f"mAP50 (bbox):{metrics.box.map50:.4f}")print(f"mAP50-95 (bbox):{metrics.box.map:.4f}")print(f"mAP50 (pose):{metrics.pose.map50:.4f}")print(f"mAP50-95 (pose):{metrics.pose.map:.4f}")# 每个关键点的 mAPkpt_names=["nose","left_eye","right_eye","left_ear","right_ear","left_shoulder","right_shoulder","left_elbow","right_elbow","left_wrist","right_wrist","left_hip","right_hip","left_knee","right_knee","left_ankle","right_ankle"]fori,nameinenumerate(kpt_names):print(f"{name}:{metrics.pose.ap50[i]:.4f}")3. 常见精度问题
精度问题排查: ├── 关键点抖动 │ ├── 原因:模型对遮挡敏感 │ ├── 解决:增加遮挡样本、使用时序平滑 │ └── 时序平滑:指数移动平均 ├── 小目标关键点不准 │ ├── 原因:输入分辨率低 │ ├── 解决:增大 imgsz(1280) │ └── 或使用切片推理 ├── 遮挡关键点偏移 │ ├── 原因:模型学习了错误的先验 │ ├── 解决:增加遮挡标注数据 │ └── 或使用上下文信息推断 └── 左右镜像错误 ├── 原因:左右关键点混淆 ├── 解决:增加训练数据多样性 └── 或使用对称约束损失4. 时序平滑
#!/usr/bin/env python3"""temporal_smooth.py - 关键点时序平滑"""importnumpyasnpclassKeypointSmoother:"""指数移动平均平滑器"""def__init__(self,alpha=0.5):self.alpha=alpha self.prev_kpts=Nonedefsmooth(self,keypoints):"""平滑关键点"""ifself.prev_kptsisNone:self.prev_kpts=keypoints.copy()returnkeypoints smoothed=self.alpha*keypoints+(1-self.alpha)*self.prev_kpts self.prev_kpts=smoothed.copy()returnsmoothed# 使用smoother=KeypointSmoother(alpha=0.6)smoothed_kpts=smoother.smooth(raw_keypoints)5. 切片推理(小目标)
#!/usr/bin/env python3"""sliced_inference.py - 切片推理"""importcv2importnumpyasnpdefsliced_pose_inference(model,image,slice_size=640,overlap=0.2):"""切片推理,提升小目标关键点精度"""h,w=image.shape[:2]step=int(slice_size*(1-overlap))all_detections=[]foryinrange(0,h,step):forxinrange(0,w,step):# 切片x2=min(x+slice_size,w)y2=min(y+slice_size,h)x1=max(0,x2-slice_size)y1=max(0,y2-slice_size)crop=image[y1:y2,x1:x2]# 推理results=model.predict(crop,conf=0.3,verbose=False)# 坐标映射回原图forrinresults:ifr.keypointsisnotNone:forkp,boxinzip(r.keypoints.data,r.boxes):kpts=kp.numpy()kpts[:,0]+=x1 kpts[:,1]+=y1 bbox=box.xyxy[0].numpy()bbox[0]+=x1 bbox[1]+=y1 bbox[2]+=x1 bbox[3]+=y1 all_detections.append({'keypoints':kpts,'bbox':bbox,'conf':float(box.conf),})# NMS 去重returnnms_pose(all_detections)6. 精度优化清单
优化清单: ├── 数据 │ ├── 增加训练数据量(5000+ 张) │ ├── 增加遮挡/截断样本 │ ├── 增加不同角度/距离样本 │ └── 标注质量检查 ├── 模型 │ ├── 增大输入分辨率(1280) │ ├── 使用更大模型(yolo26s-pose) │ └── 冻结 backbone 微调 ├── 训练 │ ├── 增大 pose 损失权重 │ ├── 增加 epochs │ ├── 使用余弦退火学习率 │ └── 数据增强调优 ├── 推理 │ ├── 时序平滑(视频场景) │ ├── 切片推理(小目标) │ └── 多尺度测试 └── 后处理 ├── NMS 阈值调优 ├── 关键点置信度过滤 └── 骨架约束检查总结
| 问题 | 解决方案 | 效果 |
|---|---|---|
| 关键点抖动 | 时序平滑 | 减少 50% 抖动 |
| 小目标不准 | 切片推理 | +5 mAP |
| 遮挡偏移 | 增加遮挡数据 | +3 mAP |
| 左右混淆 | 对称约束 | +1 mAP |