人体姿态检测分为两种方式,一种是自顶向下,一种是自底向上。
自顶向下:
先找人,将人体进行目标框检测,再在目标框内去找人体的关键点,再进行关键点的连接。
自底向上:
先找点,后归纳。这里我们不需要先找人的目标框。我们要找到图像中的所有人体关键点,再把属于同一个人的关键点归为一类。
关键点检测评价体系
在目标框检测和实例分割或者语义分割中,使用的是IoU,上图中蓝色的部分代表ground truth。绿色部分是预测出来的部分。但是人体关键点检测就不是IoU了,而是OKS。
- OKS(Object Keypoint Similarity相似度)
对于我们的人体关键点检测的时候,我们对关键点标注的格式是这样的
这里x、y代表坐标,v代表是否可见。具体的意义如下:
- v=0,未标注点
- v=1,已标注,但不可见
- v=2,已标注并图像可见
OKS的公式如下
- k表示第i 个骨骼点的归一化因子,这个因子是通过对所有的数据集中所有ground truth计算标准差而得到的,反映出当前骨骼点对整体的影响程度。值越大,说明在整个数据集中对这个点的标注效果越差;值越小,说明整个数据集中对这个点的标注效果越好。
是标注和预测关键点之间的欧式距离。
为标准差,s^2是用来做尺度归一化,k^2是数据集标注的标准差。
- 当完美预测的时候OKS=1,当预测与ground truth差距比较大的时候,OKS=0
以上图中人眼为例,蓝色的代表ground truth,红色部分表示预测的位置,我们可以看到预测值围绕ground truth成一个正态分布。对比正态分布的概率密度函数也可以看的出来
那么OKS的整个式子代表着所有预测值的概率分布与ground truth的比值。
对关键点因为是人工标注,所以每个人的标注可能各不相同,经过对大量图像的统计,这些标注会呈现一个正态分布。如果是对眼睛的标注和对臀部的标注,那么对眼睛的标注的尺度更小,而对臀部的标注的尺度更大。换句话说我们会对眼睛的要求比臀部更高,这个就是s的意义。
k是一个常数。这样就会对不同的关键点的范围的要求不同。
这一个一个圆代表的意义如下
绿色的部分是ground truth,如果我们预测的点刚好与之重合,那么OKS=1,蓝色圈代表着2倍标准差,此时的OKS=0.88,红色的圈4倍标准差,此时OKS=0.61。
自顶向下
之前我们说自顶向下是先找人,再找点,那么找人的目标框的方法如下:
- One Stage: YOLO、SSD等;
- Two Stage: Faster RCNN,Mask RCNN等;
- Anchor Free: CornerNet,CenterNet;
对于自顶向下的关键点检测的方法有: Mask RCNN(增加了关键点检测分支后的)、AlphaPose等。
对于Mask RCNN,我们来看一下是如何进行人体关键点检测的
对于Mask分支,首先我们不再进行一个80分类的预测,而是改成28*28*17的预测,这个17就是人体的关键点的数量,mask分支也就变成了keypoints分支。具体如下
- 增加keypoint head(28*28*17)
- 对每一个关键点预测一个"Heat map"
- 通过one-hot encoding来进行一个softmax的计算一个最有可能的空间的位置
对于feature map的热力分布图(Heatmap)
这里显示的是该关键点出现在某一个区域的最大概率。越红的地方代表概率最大,而蓝色或者浅色的地方代表概率比较小。这里有17个heatmap,就意味着有17个点,17个类。
当我们找到这些点之后,再将其连接起来就好了。
堆叠沙漏网络(Stacked Hourglass Networks)
首先我们知道越浅层的网络只能提取出一些图片的浅层信息,比如颜色、纹理等等。越深层的网络越能提取出深层信息,比如语义信息(人类能够理解的信息,比如人脸,身体形状等等)。
这是一个最简单的CNN的示例图。堆叠沙漏网络使用了多尺度的卷积特征,在之前的网络中,大多使用最后一层的卷积特征,因为我们认为最后一层卷积特征会含有丰富的语义信息。但是这样会造成信息的丢失,对于姿态估计这种任务,全身不同的关键点,比如手腕、鼻子等并不是在相同的feature map上有最好的识别精度。
比如说胳膊会在第二个卷积层上比较容易识别,头部会在第四个卷积层上更容易识别。所以如果仅仅在最后一层来进行识别的话会造成信息丢失。所以这个时候需要使用可以识别多个feature map的网络结构。
堆叠沙漏网络特别像一个横着的沙漏,而且它是高度对称的。这里的bottom-up指的是将图片从高分辨率到低分辨率。然后相反,top-down的过程是将图片从低分辨率上升到高分辨率。
这里是一个堆叠沙漏网络的一个小模块,堆叠沙漏网路设计的初衷就是为了捕捉每一个尺度下的信息。通常的做法就是使用多个通道(pipeline)单独处理不同尺度下的信息,然后在网络的最后组合这些特征。上图中max pooling将图像从高分辨率降低到一个很低的分辨率,在每一个max pooling的过程当中,网络产生的分支在提前池化的分辨率下使用更多的卷积。在达到最低分辨率的时候,采用Up Sampleing(上采样),再同时结合不同尺度下的特征,再进行不同特征下的元素相加。当达到一个不错分辨率的时候,采用两个1*1的卷积层进行最后的预测。
上图中的每一个卷积都使用的是残差网络模块,就是右上角的模块。
这是一个热力图(Heat Map),第一张图像是输入图像最终预测的关键点图。第二张图是对颈部节点的概率预测,颜色越深就代表着这个地方存在概率是最大。蓝色区域就代表不可能存在颈部。
在上图中,N1代表第一个沙漏网络,它提取出来的混合特征会经过一个1*1的全卷积之后分成上下两个分支,上面的分支继续经过一个1*1的卷积,继续进入下一个沙漏网络。下面的分支会生成一个热力图(Heat Map),也就是蓝色的矩形框。一般的卷积层的通道数都会比较高,比如256或者512等等,但是这个Heat Map的通道数需要跟我们预测的关键点的数量一致,比如说是17个,这也是这个蓝色矩形框比较窄的原因。在生成了Heat Map之后依然要通过一个1*1的卷积来调整通道数,使得通道数跟上面的分支一致,方便进行后面的合并。合并之后再作为下一个沙漏网络的输入。
这里需要注意的是,一般的卷积神经网络的损失函数只考虑最后一个卷积层的输出与ground truth进行一个损失,但是在沙漏网络中,由于每一个沙漏网络都会产生一个Heat Map,故每一个沙漏网络的loss都会放进最终的loss中进行计算,这样比只考虑最后一个沙漏网络的输出精度要大。考虑这种每一步的损失叫做中间监督。
上图的这个沙漏模块只是一个简单的沙漏模块,可以称为一阶沙漏模块。我们可以把中间虚线框不断的替换成沙漏模块,如下图所示
这样就是一个完整的沙漏模块,称为四阶沙漏模块。作者其实是串联了四个四阶沙漏模块,最后做一个输出。
该图是作者给出的实验结果,我们可以看到效果还是很不错的。他使用的是MPII的数据集。
该方法对于单个的人体姿态估计是比较好的,但是对于多人来说,不同的人的关键点当离的比较近的时候可能会产生干扰。
AlphaPose
AlphaPose是一个自顶向下的多人姿态估计框架,是由上海交通大学的卢策吾团队和腾讯优图提出的,发表于ICCV,在MPII数据集上达到76.7mAP。AlphaPose采用了当时先进的Faster RCNN作为人体框检测器 SPPE(single-person pose estimator)人体姿态估计模型。SPPE的原理其实就是上面说的堆叠沙漏网络。
AlphaPose抛出了两个问题
- 边界框定位错误
- 姿态冗余
由于SPPE对人体目标框的要求非常苛刻,所以如果一旦预测的人体边界框不准的时候就无法预测出人体的关键点。
上图中,红色的框是ground truth,黄色的框是预测的边界框,此时黄色的框和红色的框的IoU是大于0.5的,一般我们认为黄色的框在对于目标检测来说已经达到了我们的要求了。但是当我们把红色的框和黄色的框分别裁剪送入到SPPE之后,我们会发现红色的框的热力图在feature map中清晰可见,而黄色的框的热力图中却没有我们需要的关键点的高概率区域。
上图中同一个人体产生了三个不同的边界框,而每一个边界框都经过SPPE后产生了一系列的姿态关键点,这就是姿态冗余。
针对这两个问题,AlphaPose提出了RMPE(Regional Multi-Person Pose Estimation)框架,它是一种区域多人姿态估计框架,即使在人体边界框定位出错的情况下也能进行正确估计,该框架包含三个部分
- SSTN(Symmetric Spatial Transformer Network)对称空间变换网络
- P_Pose NMS(Parametric Pose Non-Maximum-Suppression)参数化姿态非极大值抑制
- PGPG(Poss-Guided Proposals Generator)姿态引导区域框生成器
SSTN对称空间变换网络
SSTN的结构图如上图所示,首先输入一张图片,该图片中有两个人体,一个穿绿色衣服,一个穿黑色衣服。我们的目的是检测穿绿色衣服的人体姿态。由图片可知,穿绿色衣服的人体并未位于图片中间,由于SPPE对位置错误非常敏感,我们需要使得穿绿色衣服的人体位于图片的中间。我们将这幅图片送入STN网络(空间变换网络),该网络使用网格生成器和采样器去提取一个人所在的区域,STN能很好地自动选取ROI,使用STN去提取一个高质量的人体区域框。
STN网络包含三部分
- 本地网络(Loalisation Network)
- 网格生成器(Grid Genator)
- 采样器(Sampler)
本地网络是一个用来回归变换参数θ的网络,它的输入是特征图像,然后经过一系列的隐藏网络层(全连接或者卷积网,再加一个回归层)输出空间变换参数。θ的形式可以多样,如需实现2D仿射变换,θ 就是一个6维(2x3)向量的输出。θ的尺寸大小依赖于变换的类型。
网格生成器(Grid Generator)是依据预测的变换参数来构建一个采样网格,它是一组输入图像中的点经过采样变换后得到的输出。网格生成器其实得到的是一种映射关系
。假设特征图像U每个像素的坐标为
,V的每个像素坐标为
,空间变换函数
为二维仿射变换函数,那么
和
的对应关系可以写为:
采样器利用采样网格和输入的特征图同时作为输入产生输出,得到了特征图经过变换之后的结果。
以上是前向计算,反向传播中,输出对采样器的求导公式为:
grid generator的求导公式
代码语言:javascript复制import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d()
self.fc1 = nn.Linear(320, 50)
self.fc2 = nn.Linear(50, 10)
self.localization = nn.Sequential(
nn.Conv2d(1, 8, kernel_size=7),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.ReLU(True),
nn.Conv2d(8, 10, kernel_size=5),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.ReLU(True)
)
self.fc_loc = nn.Sequential(
nn.Linear(10 * 3 * 3, 32),
nn.ReLU(True),
nn.Linear(32, 3 * 2)
)
self.fc_loc[2].weight.data.zero_()
self.fc_loc[2].bias.data.copy_(torch.tensor([1, 0, 0, 0, 1, 0], dtype=torch.float))
def stn(self, x):
xs = self.localization(x)
xs = xs.view(-1, 10 * 3 * 3)
# 经过本地网络的卷积和全连接层得到一个θ
theta = self.fc_loc(xs)
theta = theta.view(-1, 2, 3)
# 生成一个仿射变换的网格
grid = F.affine_grid(theta, x.size())
# 对特征图进行坐标仿射变换
x = F.grid_sample(x, grid)
return x
def forword(self, x):
x = self.stn(x)
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320)
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
return F.log_softmax(x, dim=1)
我们继续回到SSTN,通过STN网络的图像变换,获得穿绿色衣服的人体居中图像(剪裁后的图像),再输入SPPE预测人体姿态。再通过SDTN(空间反变换网络,STN结构的反向结构)反向输入回原始图像,将剪裁后的图像还原至原始图像。
在SPPE的下方还有一个Parallel SPPE,它的作用是用来优化STN。Parallel SPPE在检测出人体姿态后,会判断剪裁后的人体是否位于图像中心的位置,如果不在中心就返回较大的误差,从而自动优化STN网络。在测试阶段,Parallel SPPE不会使用,因此只有在训练阶段Parallel SPPE才会产生作用。Parallel SPPE可以看作是训练阶段的正则化过程,有助于避免局部最优的情况(STN不能把姿态转换到提取到人体区域框的居中位置)。但是SDTN的反向修正可以减少网络的错误进而降低陷入局部最优的可能性。这些错误对于训练STN是很有影响的。通过Parallel SPPE,可以提高STN将人体姿态移动到检测框中间的能力。
尽管STN可以部分修改输入,但是不可能完美的将人定位在标签的位置。在坐标空间上的输入和SPPE标注的差异会很大程度的损害训练姿态估计的能力。这会导致我们主分支SPPE的性能下降。因此,为了确保STN和SPPE同时发挥自己的作用,一个固定权重的Parallel SPPE是不可缺少的。Parallel SPPE总是会产生较大的误差,会将没有中心的姿态来推动STN产生一个有中心的姿态,但是不会影响到主分支SPPE的效果。
P_Pose NMS参数化姿态非极大值抑制
P_Pose NMS有两个标准来进行冗余姿态消除。一个是置信度消除,一个是空间距离消除,只要满足其中的一个标准,多余的姿态就会被消除。我们先来看一下置信度消除。
置信度消除的原理是统计两个姿态关节点置信度相似的总个数。Pi和Pj是估计出来的两个人体姿态。假设Pi=0.98,Pj=0.91。首先我们需要根据置信度将这两个姿态进行一个排序,很明显Pi>Pj。然后我们选取Pi作为参考姿态,以此来判断Pj是否需要被消除。Ki、Kj是Pi、Pj的关节点坐标,并且Bi是Pi的关节点Ki的热点区域,是Pi检测框的1/10。
此时我们需要判断Kj是否在Bi的范围内。如果Kj在Bi范围内,且此时两个关节点的置信度相似(比如说Ki的置信度Ci为0.98,Kj的置信度Cj为0.97),则以下函数值为1。如果两个关节点的置信度不相似,则该函数的值为0.
如果关节点Kj不在Bi的范围内,则该函数值直接为0。因为总共有17个关节点,则我们需要再重复16次以上的步骤,得到所有关节点的相似度的值。在对比了17个关节点之后统计相似关节点的总数,如果这个这个值大于5,则Pj就会被消除。
空间距离消除指的是计算两个姿态关节点的空间距离总和,依然是选取置信度最高的姿态Pi作为参考姿态,然后计算两个姿态关节点的位置距离,如果Pj姿态离Pi姿态比较近,说明这两个姿态的重叠度比较高,Pj就会被消除。计算公式如下
PGPG姿态引导区域框生成器
对于Alphapose,适当的数据增强有助于训练SSTN SPPE模块,一种增强方法是在训练阶段使用检测出来的人体检测框,但是由于在进行目标检测只能生成一个人体检测框,所以STN SPPE模块得不到充分训练,因此需要PGPG进行数据增强。
不同姿态的偏移量分布是不同的,P(δB|atom(P))表示原子姿态P的偏移量分布,atom(P)是原子姿态(代表一个种类的姿态,通过聚类获得,比如站、躺等姿态)。
P(δB|atom(P))
计算每个原子姿态边界框的偏移量,将真实边框的边长进行归一化处理后,偏移量会形成一个频率分布,将频率分布拟合成高斯混合分布。可以得到不同原子姿态的高斯混合分布,如上图所示。根据这些高斯分布,可以生成大量的预测框,就可以训练sstn模块。这就是数据增强PGPG的原理和过程。
自底向上
OpenPose
OpenPose是一个自底向上的多人体姿态估计框架,是美国卡耐基梅隆大学(CMU)基于卷积神经网络和监督学习并以caffe为框架开发的开源库。自底向上的人体姿态估计并不会先去识别一个人体,而是先找人体的关键点。
输入作为一张图像,然后它会有两个分支。一个是部分置信度图(Part Confidence Maps),一个是部分向量场图(Part Affinity Fields)。部分置信度图是为了找关键点热力图,部分向量场图是为了找这些关键点之间的联系,或者是人体的连接关系,四肢走向。通过这两个分支既找到了关键点,又找到了关键点的联系,然后再进行细微的优化调整,最后把属于同一个人的关键点归纳到一起之后再进行连线。
无论是自顶向下还是自底向上,第一步都一定是找特征,我们可以通过主干网络来获取,主干网络可以是VGG、ResNet、DenseNet、DarkNet、CSP、FPN、PAN或者是之前说的沙漏网络。获取了feature map之后,第二步开始分枝,进行多任务网络,上面的分支就是找关键点的热力图,下面的分支就是找关键点之间的联系。如果一张图片中只有单独的一个人,此时就非常简单。但是如果存在多人,或者是多人重叠遮挡,有些不同的人的关键点离的非常近,或者是彼此有遮挡。如果在自顶向下的时候会有一个误判,但是在自底向上的方向,它有第二条支路,找关键点之间的联系,如果不同的关键点属于同一个人的话,它们的连线应该落在同一个人身上。所以第二条支路的意义就是区分不同的关键点是否属于同一个人。
上图中,图片输入通过主干网络(这里是VGG19),得到预测人体关键点位置的Condifence Maps(置信度图,其实就是热力图)。
同时,feature map通过第二个分支,得到了Part Aaffinities(向量场图)。我们来看一下在训练过程中,是如何来找一个关键点的,我们以找一个运动员右手腕关键点为例。
上图中,将图像送入卷积网络。实际上,我们要找的是右手腕,但是在经过第一个Stage我们看到在第二幅热力图中,运动员的左手腕有明显的热力图点,而右手腕附近却只有零散的淡绿色图。这是找错了,需要修正,经过第二个Stage后,热力图点集中在右手腕附近,而左手腕附近还有零散的淡绿色。此时需要进一步修正,在经过第三个Stage后,我们看到所有的热力图点只有右手腕有了,其他地方都消失了。但是我们需要注意的是,热力图的feature map的通道数与关键点的数量是一致的,所以我们要寻找的17个关键点其实是同时寻找的,只不过分布在不同通道的feature map上。如下图所示
它是在feature map的不同通道上分别寻找鼻子、脖子、右肩、右肘、右手腕等。这是一个最终的效果图。
上图是一个一般性的步骤,第一步得到所有人的所有关键点,第二步是将属于同一个人的关键点给连接起来。但是该如何连接,其实并没有那么简单。
这里我们以左肘部和左手腕为例来说明,其实一开始我们会将所有的关键点都连接起来,不管它们到底是不是同一个人的,之后经过训练和ground truth的比对,会获得连接正确和错误的概率,通过上图,我们知道连接正确的概率为1,而连接错误的概率会很小,以此来去掉连接错误的连线,留下正确的连线。