2D Human Pose Estimation (以下简称 2D HPE )旨在从图像或者视频中预测人体关节点(或称关键点,比如头,左手,右脚等)的二维空间位置坐标。2D HPE 的应用场景非常广泛,包括动作识别,动画生成,增强现实等。
传统的 2D HPE 算法,设计手工特征提取图像信息,从而进行关键点的检测。近年来随着深度学习的快速发展,基于深度学习的 2D HPE 算法取得了重大突破,算法精度得到了大幅提升。
当前主流的 2D HPE 方法主要可以分为自底向上(bottom up)和自顶向下(top down)两种方式。自底向上的方法同时预测图片中的所有关键点,然后将不同类型的关键点组合成人体。自顶向下的方法首先检测出输入图片中的一个或者多个人,然后对于每个个体单独预测其关键点。自底向上方法的推断时间不随人数的增加而上升,而自顶向下的方法对于不同尺寸的人体更加鲁棒。
本文将结合 MMPose 对自顶向下的 2D HPE 算法做一些介绍。
本文内容
主流算法
基于回归坐标的方法
基于热图(heatmap)的方法
常用数据集
COCO
MPII
CrowdPose
MMPose 中的 2D HPE 算法实现
模型
数据
训练配置
Optimizer
Learning rate policy
1. 主流算法
基于回归坐标的方法
早期一些自顶向下的深度学习方法用神经网络直接预测人体关键点的 2D 坐标 。
DeepPose 是这类方法的经典代表。DeepPose 采用级联的神经网络来预测人体各个关键点的相对坐标。每一个阶段都拿上一阶段的输出坐标作为输入,并进一步预测更为准确的坐标位置。最终,将预测得到的归一化的相对坐标转换为绝对坐标。
直接回归坐标的方法思路直接,可以直接获得关键点位置,往往有更快的预测速度。然而,由于人体姿态的自由度很大,直接预测坐标的建模方式对神经网络的预测并不友好,预测精度受到了一定制约。
基于热图(heatmap)的方法
近些年,基于热图(heatmap)的人体姿态估计方法成为了主流。基于热图的方法在每个位置预测一个分数,来表征该位置属于关键点的置信度。根据预测的热图,进一步提取关键点的坐标位置。
基于热图的方法更好地保留了空间位置信息,更符合卷积神经网络(Convolutional Neural Network, CNN)的设计特性,从而取得了更好的预测精度。
CPM(Convolutional Pose Machines) 利用序列化的卷积神经网络来学习纹理信息和空间信息,实现2D人体姿态估计。CPM通过设计多阶段的网络结构逐渐扩大网络的感受野,获取远距离的结构关系。每一阶段融合空间信息,纹理信息和中心约束来得到更为准确的热图预测。
CPM :
https://mmpose.readthedocs.io/en/latest/papers/backbones.html#cpm-cvpr-2016
Stacked Hourglass 通过多尺度特征融合,整合人体结构化的空间关系。该方法连续进行上采样和下采样,并在网络中间利用热图进行监督,提升了网络的性能。
Stacked Hourglass :
https://mmpose.readthedocs.io/en/latest/papers/backbones.html#hourglass-eccv-2016
SBL(Simple Baseline) 为人体姿态估计提供了一套基准框架。SBL在主干网络后接反卷积模块来预测热图,通过非常简单的设计获得了出色的性能。
SBL :
https://mmpose.readthedocs.io/en/latest/papers/algorithms.html#simplebaseline2d-eccv-2018
HRNet 针对人体姿态估计设计了一个高效的网络结构。不同于以往方法利用低分辨率的特征预测高分辨率的热图,HRNet 设计了多个不同分辨率的平行分支。
高分辨率特征始终被保持,并且不同尺度的特征能够相互融合。这种设计结合局部纹理特征和全局语义信息,实现了更加准确和鲁棒的姿态预测。
HRNet :
https://mmpose.readthedocs.io/en/latest/papers/backbones.html#hrnet-cvpr-2019
最近,一些研究专注于解决自顶向下的 2D HPE 中存在的问题。
DARK 研究热图和坐标之间的转换关系。其改进关键点坐标的编码过程,生成无偏差的精确热图分布用以模型训练。同时,该方法提出了基于泰勒展开的热图解码过程,降低了基于热图方法带来的量化误差。此外,LiteHRNet 和 VIPNAS 从 2D HPE 在实际应用中模型低复杂度的需求出发,提出了轻量级的高效网络。
2. 常用数据集
MMPose 目前支持 COCO,MPII ,CrowdPose 等 9 种常用的 2D 人体关键点数据集。我们也提供了数据预处理脚本和数据集链接,帮助用户快速得到训练所需的数据。
COCO
COCO数据集是目前最常用的多人人体关键点检测数据集。COCO数据集包含多达 330K 张图片,200K 个标注的人体,是主流的大规模人体关键点数据集。COCO 数据集将人体表示为 17 个关键点,包括鼻子,左右眼,左右耳,左右肩,左右肘,左右腕,左右臀,左右膝,左右脚踝。
图片摘自 MS COCO 官网
COCO 数据集关键点检测的评价指标模拟目标检测的评估指标,采用平均精确度(average precision, AP)和平均召回率(average recall, AR)进行评测。其通过定义对象关键点相似度(object keypoint similarity, OKS)来度量预测姿态和真实姿态的匹配相似性,并计算 AP 在 OKS 取值范围(0.50:0.05:0.95)的平均值 mAP 作为主要的衡量指标。
最近,Jin 等人提出了 COCO-WholeBody 数据集,对COCO 数据集进行扩展,将关键点标注从身体的17个关键点扩展为了全身 133 个关键点。由于该数据集对人脸、人手、人脚等重要部位进行了精细标注,可以用于评估全身人体姿态估计的效果,也可以用于 2D 人脸关键点估计、2D人手关键点估计等任务之中。
COCO数据集:
https://mmpose.readthedocs.io/en/latest/tasks/2d_body_keypoint.html#coco
COCO-WholeBody 数据集:
https://mmpose.readthedocs.io/en/latest/tasks/2d_wholebody_keypoint.html#coco-wholebody
MPII
MPII数据集是重要的单人/多人人体关键点检测数据集。MPII 数据集包含图片数 25K,标注的人体数 40K,每个人体用 16 个关键点表示。数据集图片来源于 YouTube 视频,包含了410种常见活动。准确关键点百分比(Percentage of Correct Keypoints, PCK)被广泛用于评估关键点的准确性,代表了预测关键点与其对应的真实关键点间的归一化距离小于设定阈值的比例。MPII 数据集的评价指标,是以头部长度( head length ) 作为归一化参考的准确关键点百分比,即 PCKh 。
图片摘自CPM论文
MPII 数据集:
https://mmpose.readthedocs.io/en/latest/tasks/2d_body_keypoint.html#mpii
CrowdPose
CrowdPose数据集是近年来提出的密集场景下存在大规模遮挡的2D人体关键点数据集。Li 等人提出了密集指数(CrowdIndx),用来表征图片中人群的密集程度。依此构建的CrowdPose数据集,包含20K的图片数,且具有均匀的密集指数的分布,适合评估算法在密集场景的效果。CrowdPose数据集采用了和COCO数据集一致的评价指标,即mAP。
图片摘自CrowdPose论文
CrowdPose数据集:
https://mmpose.readthedocs.io/en/latest/tasks/2d_body_keypoint.html#crowdpose
3. MMPose 中的 2D HPE 算法实现
MMPose现已支持 DeepPose , CPM , Hourglass 和HRNet 等十余种不同的算法模型。
我们也提供了基于图像和视频的 demo,感兴趣的话不妨先尝试一下~
demo 链接 :
https://mmpose.readthedocs.io/en/latest/demo.html#d-human-pose-demo
3. 1 模型
Backbone
模型的骨干网络(Backbone)为 CPM 模型(具体实现见 cpm.py ),输入为 3 通道的图片,输出为K通道的热度图,其中K为关键点的个数。CPM 的网络结构中,包括num_stages=6 个阶段的CPM网络模块,逐渐优化关键点的预测结果。
代码语言:javascript复制backbone=dict(
type='CPM', # 骨干网络的类型
in_channels=3, # 输入通道数
out_channels=channel_cfg['num_output_channels'], # 输出通道数
feat_channels=128, # 特征维度
num_stages=6) # 模型阶段数
右滑查看完整代码
Head
模型的Head部分,我们采用了TopdownHeatmapMultiStageHead,具体实现见topdown_heatmap_multi_stage_head.py 。
topdown_heatmap_multi_stage_head.py 链接 :
https://github.com/open-mmlab/mmpose/blob/master/mmpose/models/heads/topdown_heatmap_multi_stage_head.py
该网络头用于处理多阶段的骨干网络提取的特征,并输出多阶段的预测结果。模型训练的损失函数为JointsMSELoss ,即热度图上的二范数损失函数。
代码语言:javascript复制keypoint_head=dict(
type='TopdownHeatmapMultiStageHead', # 网络头的类型
in_channels=channel_cfg['num_output_channels'], # 输入通道数
out_channels=channel_cfg['num_output_channels'], # 输出通道数
num_stages=6, # 模型阶段数
num_deconv_layers=0, # 反卷积层数量
extra=dict(final_conv_kernel=0, ), # final_conv 为 identity函数
loss_keypoint=dict(
type='JointsMSELoss', # 损失函数类别
use_target_weight=True)) # 在损失计算中,是否考虑target_weight的影响
3. 2 数据
Data config
以下是与训练数据相关的一些配置,具体实现见 topdown_coco_dataset.py 。
topdown_coco_dataset.py 链接 :
https://github.com/open-mmlab/mmpose/blob/master/mmpose/datasets/datasets/top_down/topdown_coco_dataset.py
代码语言:javascript复制data_cfg = dict(
image_size=[192, 256], # 输入图片尺寸w, h
heatmap_size=[24, 32], # 输出热度图的尺寸 W, H
num_output_channels=channel_cfg['num_output_channels'], # 输出的通道数
num_joints=channel_cfg['dataset_joints'], # 关键点个数
dataset_channel=channel_cfg['dataset_channel'], # 数据集所对应的通道列表
inference_channel=channel_cfg['inference_channel'], # 模型输出的通道列表
soft_nms=False, # 测试阶段,是否使用soft-nms
nms_thr=1.0, # nms的阈值
oks_thr=0.9, # oks (目标关键点相似度)阈值,用于nms操作
vis_thr=0.2, # 关键点可见性阈值
use_gt_bbox=False, # 测试阶段,是否使用人工标注的人体框
det_bbox_thr=0.0, # 检测框的阈值,只用于 use_gt_bbox=False
bbox_file='data/coco/person_detection_results/'
'COCO_val2017_detections_AP_H_56_person.json', # 检测框的文件路径
)
data_root = 'data/coco' # 数据集路径
data = dict(
samples_per_gpu=64, # 训练阶段,每张GPU的批处理大小
workers_per_gpu=2, # 每个GPU的数据读取线程数量
val_dataloader=dict(samples_per_gpu=32), # 验证阶段,每张GPU的批处理大小
test_dataloader=dict(samples_per_gpu=32), # 测试阶段,每张GPU的批处理大小
train=dict(
type='TopDownCocoDataset', # 训练数据集名称
ann_file=f'{data_root}/annotations/person_keypoints_train2017.json', # 标签路径
img_prefix=f'{data_root}/train2017/', # 图片路径前缀
data_cfg=data_cfg,
pipeline=train_pipeline),
val=dict(
type='TopDownCocoDataset', # 验证数据集名称
ann_file=f'{data_root}/annotations/person_keypoints_val2017.json',
img_prefix=f'{data_root}/val2017/',
data_cfg=data_cfg,
pipeline=val_pipeline),
test=dict(
type='TopDownCocoDataset', # 测试数据集名称
ann_file=f'{data_root}/annotations/person_keypoints_val2017.json',
img_prefix=f'{data_root}/val2017/',
data_cfg=data_cfg,
pipeline=val_pipeline),
)
Pipeline
数据预处理的 pipeline 主要由以下几个步骤组成:
1. LoadImageFromFile:读取图片。
2. TopDownRandomFlip:随机水平翻转。
3. TopDownHalfBodyTransform:半身肢体数据增强,生成一些只有上半身或下半身的人体框。
4. TopDownGetRandomScaleRotation:随机旋转和缩放。
5. TopDownAffine:进行仿射变换。
6. ToTensor:图片转换为Tensor。
7. NormalizeTensor:输入Tensor归一化,减均值,除方差。
8. TopDownGenerateTarget:根据标签,生成 GT 热度图。
9. Collect:整合训练中需要用到的数据。
代码语言:javascript复制train_pipeline = [
dict(type='LoadImageFromFile'), # 读取图片
dict(type='TopDownRandomFlip', # 数据增强:随机翻转
flip_prob=0.5), # 翻转概率
dict(
type='TopDownHalfBodyTransform', # 数据增强:生成半身肢体
num_joints_half_body=8, # 半身肢体生成的阈值,至少包含8个关键点
prob_half_body=0.3), # 半身肢体生成的概率
dict(
type='TopDownGetRandomScaleRotation', # 数据增强:随机缩放和旋转
rot_factor=40, # 随机旋转幅度 [-2*rot_factor, 2*rot_factor]
scale_factor=0.5), # 随机缩放幅度 [1-scale_factor, 1 scale_factor]
dict(type='TopDownAffine'), # 仿射变换
dict(type='ToTensor'), # 数据格式转换为 Tensor
dict(
type='NormalizeTensor', # 输入Tensor归一化
mean=[0.485, 0.456, 0.406], # 归一化均值
std=[0.229, 0.224, 0.225]), # 归一化方差
dict(type='TopDownGenerateTarget', # 根据标签,生成GT热图
sigma=2), # 热度图高斯分布的标准差
dict(
type='Collect', # 整合训练中所需要的数据
keys=['img', 'target', 'target_weight'], # 保留的key
meta_keys=[ # 保留的meta-key
'image_file', 'joints_3d', 'joints_3d_visible', 'center', 'scale',
'rotation', 'bbox_score', 'flip_pairs'
]),
]
3.3 训练配置
Optimizer
采用Adam作为优化器,并设置初始学习率为 5e-4。
代码语言:javascript复制optimizer = dict(
type='Adam', # 优化器的类型
lr=5e-4, # 基础学习率
)
optimizer_config = dict(grad_clip=None) # 不进行梯度裁剪
Learning rate policy
学习率采用阶梯状衰减,分别在第170和第200个epoch,降低学习率(默认每次降低到 0.1倍),总计训练210个epoch。
我们采用了预热启动(warmup)策略,初始设置非常小的学习率 ( 0.001 倍的初始学习率 lr ),并在前 500 个迭代轮次中,线性增加为 lr=5e-4 。
代码语言:javascript复制lr_config = dict(
policy='step', # 学习率调整策略
warmup='linear', # 预热启动策略
warmup_iters=500, # 预热启动的训练迭代数
warmup_ratio=0.001, # 预热启动阶段初始学习率为 warmup_ratio * lr
step=[170, 200]) # 学习率下降的轮次,分别在第170和第200个epoch,降低学习率
total_epochs = 210 # 总迭代轮数
本文介绍了 2D HPE 一些自顶向下的算法和常用的数据集,并以CPM为例,介绍了MMPose中的具体算法实现。
希望大家通过本文的阅读能够对2D HPE有一个初步的认识,也欢迎大家使用MMPose来支持相关的研究与应用~
【MMPose Github地址】
https://github.com/open-mmlab/mmpose