人体姿态估计-评价指标(一)
摘要
人体姿态估计可以细分成四个任务:单人姿态估计 (Single-Person Skeleton Estimation)、多人姿态估计 (Multi-person Pose Estimation)、人体姿态跟踪 (Video Pose Tracking)、3D人体姿态估计 (3D Skeleton Estimation)。
单人姿态估计: 输入是切割出来的单个行人,然后再找出行人的关键点,使用的方法也就是自顶向下(Topdown),先检测人的bounding box,再用single-stage的方法检测每个人的骨骼关键点,常使用的benchmark数据集MPII,使用的是PCKh的指标(可以认为预测的关键点与GT标注的关键点经过head size normalize后的距离)。但是经过这几年的算法提升,整体结果目前已经非常高了(最高的已经有93.9%了)。
多人姿态估计: 输入是一张整图,可能包含多个行人,目的是需要把图片中所有行人的关键点都能正确的做出估计,同样有两种方法:自顶向下(top-down)、自底向上(bottom-up)。对于top-down的方法,往往先找到图片中所有行人,然后对每个行人做姿态估计,寻找每个人的关键点。单人姿态估计往往可以被直接用于这个场景。对于bottom-up,思路正好相反,先是找图片中所有parts (关键点),比如所有头部,左手,膝盖等。然后把这些parts(关键点)组装成一个个行人。使用的是OKS(object keypoint similarity) 指标。
人体姿态跟踪: 输入的是视频,对视频中的每一个行人进行人体以及每个关键点跟踪,相比行人跟踪来讲,人体关键点在视频中的temporal motion可能比较大,比如一个行走的行人,手跟脚会不停的摆动,所以跟踪难度会比跟踪人体框大。目前主要有的数据集是PoseTrack。
3D人体姿态估计: 输入的是RGB图像,输出3D的人体关键点的话,就是3D 人体姿态估计。这个有一个经典的数据集Human3.6M。
目前主要的难点: 图像的复杂性-遮挡(不可见)、拥挤,算法的复杂度。
评价指标
oks(object keypoint similarity)
oks是目前常用的人体骨骼关键点检测算法的评价指标,这个指标启发于目标检测中的IoU指标,目的就是为了计算真值和预测人体关键点的相似度。
OKS:
参数详细解释,其中:
表示当前图片所有groundtruth行人中id为p的人,
,
表示当前图中共有行人的数量
表示id为
的keypoint
表示当前检测的一组关键点中id为
的关键点与groundtruth行人中id为
的人的关键点中id为
的关键点的欧式距离,
,
为当前的关键点检测结果,
为groundtruth
表示groundtruth行人中id为p的人的尺度因子,其值为行人检测框面积的平方根:
,
、
为检测框的宽和高
表示id为
类型的关键点归一化因子,这个因子是通过对所有的样本集中的groundtruth关键点由人工标注与真实值存在的标准差,
越大表示此类型的关键点越难标注。根据[1]中所述,对coco数据集中的5000个样本统计出17类关键点的归一化因子,
的取值可以为:{鼻子:0.026,眼睛:0.025,耳朵:0.035,肩膀:0.079,手肘:0.072,手腕:0.062,臀部:0.107,膝盖:0.087,脚踝:0.089},因此此值可以当作常数看待,但是使用的类型仅限这个里面。如果使用的关键点类型不在此当中,可以使用另外一种统计方法计算此值,详细见下文
表示groundtruth中id为
的行人第
个关键点的可见性,其中
表示关键点未标记,可能的原因是图片中不存在,或者不确定在哪,
表示关键点无遮挡并且已经标注,
表示关键点有遮挡但已标注。同样,预测的关键点有两个属性:
表示未预测出,
表示预测出
表示如果条件
成立,那么
,否则
,在此处的含义是:仅计算groundtruth中已标注的关键点
此指标计算的相关python代码如下:
oks = [0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95]
sigmas = np.array([.26, .25, .25, .35, .35, .79, .79, .72, .72, .62,.62, 1.07, 1.07, .87, .87, .89, .89])/10.0
variances = (sigmas * 2)**2
def compute_kpts_oks(dt_kpts, gt_kpts, area):
"""
this function only works for computing oks with keypoints,
:param dt_kpts: 模型输出的一组关键点检测结果 dt_kpts.shape=[3,14],dt_kpts[0]表示14个横坐标值,dt_kpts[1]表示14个纵坐标值,dt_kpts[3]表示14个可见性,
:param gt_kpts: groundtruth的一组关键点标记结果 gt_kpts.shape=[3,14],gt_kpts[0]表示14个横坐标值,gt_kpts[1]表示14个纵坐标值,gt_kpts[3]表示14个可见性,
:param area: groundtruth中当前一组关键点所在人检测框的面积
:return: 两组关键点的相似度oks
"""
g = np.array(gt_kpts)
xg = g[0::3]
yg = g[1::3]
vg = g[2::3]
assert(np.count_nonzero(vg > 0) > 0)
d = np.array(dt_kpts)
xd = d[0::3]
yd = d[1::3]
dx = xd - xg
dy = yd - yg
e = (dx**2 + dy**2) /variances/ (area+np.spacing(1)) / 2 #加入np.spacing()防止面积为零
e=e[vg > 0]
return np.sum(np.exp(-e)) / e.shape[0]
详细情况参考相关代码: coco benchmark
注意事项:
对于参数
的取值问题,如果增加了新的关键点类型或者用于其它物体的关键点检测,也可以根据自己的样本集进行设定,具体为:
AP(Average Precision)平均准确率
此指标用于计算测试集的精度百分比,单人姿态估计和多人姿态估计的计算方式不同。
单人姿态估计AP:
单人姿态估计,一次仅对一个行人进行估计,即在oks指标中
,因此一张图片中groundtruth为一个行人(GT),对此行人进行关键点检测后会获得一组关键点(DT),最后会计算出GT与DT的相似度oks为一个标量,然后人为的给定一个阈值T,然后可以通过所有图片的oks计算AP:
多人姿态估计AP:
多人姿态估计,如果采用的检测方法是自顶向下,先把所有的人找出来再检测关键点,那么其AP计算方法如同单人姿态估计AP; 如果采用的检测方法是自底向上,先把所有的关键点找出来然后再组成人,那么假设一张图片中共有M个人,预测出N个人,由于不知道预测出的N个人与groundtruth中的M个人的一一对应关系,因此需要计算groundtruth中每一个人与预测的N个人的oks,那么可以获得一个大小为
的矩阵,矩阵的每一行为groundtruth中的一个人与预测结果的N个人的oks,然后找出每一行中oks最大的值作为当前GT的oks。最后每一个GT行人都有一个标量oks,然后人为的给定一个阈值T,然后可以通过所有图片中的所有行人计算AP:
多人姿态估计 矩阵计算代码:
def compute_oks(dts, gts):
if len(dts) * len(gts) == 0:
return np.array([])
oks_mat = np.zeros((len(dts), len(gts)))
# compute oks between each detection and ground truth object
for j, gt in enumerate(gts):
# create bounds for ignore regions(double the gt bbox)
g = np.array(gt['keypoints'])
xg = g[0::3]; yg = g[1::3]; vg = g[2::3]
k1 = np.count_nonzero(vg > 0)
bb = gt['bbox']
x0 = bb[0] - bb[2]; x1 = bb[0] + bb[2] * 2
y0 = bb[1] - bb[3]; y1 = bb[1] + bb[3] * 2
for i, dt in enumerate(dts):
d = np.array(dt['keypoints'])
xd = d[0::3]; yd = d[1::3]
if k1>0:
# measure the per-keypoint distance if keypoints visible
dx = xd - xg
dy = yd - yg
else:
# measure minimum distance to keypoints in (x0,y0) & (x1,y1)
z = np.zeros((len(sigmas)))
dx = np.max((z, x0-xd),axis=0)+np.max((z, xd-x1),axis=0)
dy = np.max((z, y0-yd),axis=0)+np.max((z, yd-y1),axis=0)
e = (dx**2 + dy**2) / variances / (gt['area']+np.spacing(1)) / 2
if k1 > 0:
e=e[vg > 0]
oks_mat[i, j] = np.sum(np.exp(-e)) / e.shape[0]
return oks_mat
mAP(mean Average Precision)
mAP是常用检测指标,具体就是给AP指标中的人工阈值T设定不同的值,然后会获得多个AP指标,最后再对多个AP指标求平均,最终获得mAP