Pose Estimation 指标

关键点检测的指标主要是 OKS 和 PCK。其中 OKS 是现在的常用指标,PCK 主要用在 MPII 等较老的数据集上,从 OKS 还衍生出 AP 和 AR 两个指标。

OKS (Object Keypoint Similarity)

OKSp=iexp(dpi22Sp2σi2)δ(vpi=1)iδ(vpi=1)

p 表示 person id

i 表示 keypoint id

dpi 表示预测的关节点和标注的关节点的欧氏距离

Sp 表示尺度缩放因子,Sp=(x2x1)(y2y1)

σi 表示第 i 个骨骼点的归一化因子,对数据集中所有 groundtruth 计算的标准差而得到的,反映出当前骨骼点标注时候的标准差, σi 越大则越难标注

vpi 表示关节点是否可见

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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

OKS 矩阵

对于多人姿态估计,若 gt 中 M 个人,预测了 N 个人,计算两两之间的 OKS 构成 M×N 矩阵,最后选择每个 gt 的人中最大的 OKS 值作为结果。

PCK (Percentage of Correct Keypoints)

PCKpi=pδ(dpidpdefTi)p1

p 表示 person id

i 表示 keypoint id

dpi 表示预测的关节点和标注的关节点的欧氏距离

dpdef 表示尺度缩放因子,对于 FLIC 使用的是躯干直径(左肩到左臀或右肩到左臀),对于 MPII 用的是头部对角线的长度(PCKh

Ti 表示第 i 个骨骼点的阈值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def compute_pck_pckh(dt_kpts,gt_kpts,refer_kpts):
"""
pck指标计算
:param dt_kpts:算法检测输出的估计结果,shape=[n,h,w]=[行人数,2,关键点个数]
:param gt_kpts: groundtruth人工标记结果,shape=[n,h,w]
:param refer_kpts: 尺度因子,用于预测点与groundtruth的欧式距离的scale。
           pck指标:躯干直径,左肩点-右臀点的欧式距离;
           pckh指标:头部长度,头部rect的对角线欧式距离;
:return: 相关指标
"""
dt=np.array(dt_kpts)
gt=np.array(gt_kpts)
assert(len(refer_kpts)==2)
assert(dt.shape[0]==gt.shape[0])
ranges=np.arange(0.0,0.1,0.01)
kpts_num=gt.shape[2]
ped_num=gt.shape[0]
#compute dist
scale=np.sqrt(np.sum(np.square(gt[:,:,refer_kpts[0]]-gt[:,:,refer_kpts[1]]),1))
dist=np.sqrt(np.sum(np.square(dt-gt),1))/np.tile(scale,(gt.shape[2],1)).T
#compute pck
pck = np.zeros([ranges.shape[0], gt.shape[2]+1])
for idh,trh in enumerate(list(ranges)):
for kpt_idx in range(kpts_num):
pck[idh,kpt_idx] = 100*np.mean(dist[:,kpt_idx] <= trh)

# compute average pck
pck[idh,-1] = 100*np.mean(dist <= trh)
return pck

PCK 现在用的不多,主要用的是 OKS

AP(Average Precision)& AR(Average Recall)

AP 和 AR 都是针对整个数据集而言的。在算 Precision 或者 Recall 之前,必然先要对关键点检测结果进行排序,很多文章都没有明确这里排序的依据是什么。从实现上来看,是根据人检测框的置信度高低进行排序的。

在计算 AP 和 AR 之前,先要画出 OKS=k 下的 PR 曲线。先对每个检测结果排序,然后计算从头到第 k 个结果时的 Precision 和 Recall,就能画出 PR 曲线了,算出曲线下面积,在 COCO 中用的是 11 点采样法。Precision 和 Recall 的计算方式如下所示:
P=TPTP+FP
R=TPTP+FN

对于多人关键点检测的任务,首先要做的是将检测到的结果 dtgt 做匹配(OKS>k 则为一对匹配,每个 dt 会和 OKS 最大的相匹配),那么就可能会出现有的 dt 没有与之相匹配的 gt。从代码中可知,COCO 中将 TP 定义为有匹配并且匹配到的不是 ignoregt,FP 的定义为没有匹配的 gtTP+FN 其实就是所有的 gt 个数(比如标注了 100 个 instance,那么 TP+FN=100)。

OKS=k 下,APOKS=k 就是此时采样后的 Precision 均值。AP=110k0.5:0.05:0.95APOKS=kAR 同理。

关于 COCO 上各个指标的具体定义可以参考下图:

人体姿态估计-评价指标(一)_ZXF_1991 的博客 - CSDN 博客

目标检测中的 mAP 是什么含义? - 知乎

mAP for Object Detection