人体姿态估计是计算机视觉领域一个经典且实用的研究方向。简单来说,就是从图像或视频中识别出人体的关键部位(如头部、肩膀、手肘等关节点的位置),进而构建出人体的骨架结构。这项技术在动作识别、人机交互、运动分析等领域有着广泛的应用前景。
我最近用PyTorch实现了一个基于Keypoint RCNN的解决方案。Keypoint RCNN是Mask RCNN的扩展版本,专门用于关键点检测。相比传统方法,它能够同时完成目标检测和关键点定位,且具有端到端的训练优势。实测下来,在COCO数据集上能达到不错的效果,单人场景下关键点识别准确率超过85%。
在众多姿态估计算法中,Keypoint RCNN有几个显著优势:
对比其他方案:
Keypoint RCNN的核心组件包括:
python复制Backbone(ResNet50+FPN) → RPN → ROI Heads → Keypoint Head
**特征金字塔网络(FPN)**特别重要,它通过自顶向下和横向连接,融合了不同尺度的特征。对于姿态估计这种需要精细定位的任务,多尺度特征能同时捕捉整体结构和局部细节。
关键点头部的设计也很有讲究:
python复制class KeypointRCNNHead(nn.Module):
def __init__(self, in_channels, num_keypoints):
super().__init__()
self.deconv = nn.ConvTranspose2d(in_channels, 256, kernel_size=4, stride=2, padding=1)
self.conv = nn.Conv2d(256, num_keypoints, kernel_size=1)
def forward(self, x):
x = F.relu(self.deconv(x))
x = self.conv(x)
return x
这种设计通过转置卷积实现上采样,比简单的插值方法能学到更有效的空间信息。
使用COCO2017数据集时,需要注意:
python复制dataset = CocoKeypoints(
root='data/coco',
annFile='annotations/person_keypoints_train2017.json',
transforms=make_transform(train=True)
)
关键预处理步骤:
重要提示:COCO的17个关键点定义顺序是固定的,预处理时需严格保持一致,否则会导致学习混乱。
当需要处理自定义数据时,建议采用以下格式:
json复制{
"images": [{"id": 1, "file_name": "img1.jpg", ...}],
"annotations": [{
"image_id": 1,
"keypoints": [x1,y1,v1, x2,y2,v2, ...], // v=0:未标注,1:标注但不可见,2:可见
"num_keypoints": 17
}]
}
其中关键点可见性标记(v)对训练质量影响很大,标注时需特别注意。
关键点预测使用改进的MSE损失:
python复制class KeypointLoss(nn.Module):
def __init__(self, sigma=2):
self.sigma = sigma
def forward(self, pred, target, weights):
# pred: [N,K,H,W], target: [N,K,H,W], weights: [N,K]
diff = (pred - target) ** 2
loss = diff.sum(dim=[2,3]) * weights
return loss.mean()
这里sigma控制高斯核的宽度,影响热图的敏感度。实测发现sigma=2在COCO数据上效果最佳。
推荐使用渐进式学习率策略:
python复制optimizer = torch.optim.SGD(
params=model.parameters(),
lr=0.02,
momentum=0.9,
weight_decay=1e-4
)
scheduler = torch.optim.lr_scheduler.MultiStepLR(
optimizer,
milestones=[8, 11],
gamma=0.1
)
关键训练技巧:
COCO官方使用OKS(Object Keypoint Similarity)作为主要指标:
code复制OKS = Σ[exp(-d_i²/2s²κ_i²)δ(v_i>0)] / Σ[δ(v_i>0)]
其中:
在验证集上,AP@0.50:0.95是最核心的评判标准,好的模型应该能达到60以上。
使用Torch自带的量化工具:
python复制model_fp32 = load_trained_model()
model_fp32.eval()
# 动态量化
model_int8 = torch.quantization.quantize_dynamic(
model_fp32,
{torch.nn.Linear, torch.nn.Conv2d},
dtype=torch.qint8
)
实测在CPU上推理速度可提升3倍,精度损失小于2%。
导出为ONNX格式:
python复制dummy_input = torch.randn(1, 3, 480, 640)
torch.onnx.export(
model,
dummy_input,
"keypoint_rcnn.onnx",
opset_version=11,
input_names=["input"],
output_names=["keypoints"]
)
然后使用TensorRT优化:
bash复制trtexec --onnx=keypoint_rcnn.onnx \
--saveEngine=keypoint_rcnn.engine \
--fp16
在T4 GPU上,FP16模式可实现100+ FPS的实时推理。
获得热图后需要解码为坐标:
python复制def decode_heatmap(heatmap):
# heatmap: [K,H,W]
hmax = F.max_pool2d(heatmap, 3, stride=1, padding=1)
keep = (hmax == heatmap).float()
coords = (heatmap * keep).view(K, -1).argmax(1)
y = coords // W
x = coords % W
return torch.stack([x, y], dim=1)
这个简单的峰值提取方法比复杂的概率采样更高效。
在视频应用中,常见的关键点抖动可以通过以下方法缓解:
python复制class EMAFilter:
def __init__(self, alpha=0.4):
self.alpha = alpha
self.prev = None
def __call__(self, points):
if self.prev is None:
self.prev = points
else:
self.prev = self.alpha * points + (1-self.alpha) * self.prev
return self.prev
对于被遮挡的关键点,建议:
对小尺寸人体,可以:
构建一个深蹲检测器:
python复制def analyze_squat(keypoints):
# 获取关键点索引
l_hip, r_hip = keypoints[11], keypoints[12]
l_knee, r_knee = keypoints[13], keypoints[14]
# 计算膝盖弯曲角度
def angle(a, b, c):
ba = a - b
bc = c - b
cosine = torch.dot(ba, bc) / (torch.norm(ba)*torch.norm(bc))
return torch.acos(cosine) * 180 / math.pi
left_angle = angle(l_hip, l_knee, (l_knee[0], l_knee[1]-10))
return left_angle < 90 # 深蹲阈值
识别举手动作:
python复制def is_hand_raised(shoulder, elbow, wrist):
# 肩、肘、腕的y坐标应该依次升高
return (wrist[1] < elbow[1] < shoulder[1]) and \
(abs(wrist[0] - shoulder[0]) < 0.2 * image_width)
计算两个人之间的姿态相似度:
python复制def pose_similarity(kps1, kps2):
# 归一化处理
kps1 = (kps1 - kps1.mean(0)) / kps1.std(0)
kps2 = (kps2 - kps2.mean(0)) / kps2.std(0)
# 计算关节角度差异
angles1 = compute_joint_angles(kps1)
angles2 = compute_joint_angles(kps2)
return F.cosine_similarity(angles1, angles2)
将2D关键点提升到3D的常用方法:
适合移动端的架构改进:
利用无标注数据的方法:
在部署到边缘设备时,我发现模型的前处理(图像归一化)和后处理(关键点解码)往往会成为性能瓶颈。一个实用的优化是将这些操作移到GPU上执行,使用CUDA核函数实现,这样能减少CPU-GPU之间的数据传输。例如,可以将图像归一化和热图解码写成自定义的PyTorch算子,通过TorchScript编译后能获得显著的加速效果。