2020/7/20
Hey,这篇论文主要从以下三个方面对多人姿态估计进行了创新:
1、数据预处理(坐标点变换和ground truth heatmap生成)过程中的中心点对齐问题。
2、使用多尺度监督引导网络生成热度图(论文中将此视为空间注意力机制)
3、用于keypoint和body part heatmap监督的损失函数:focal L2 loss
今天先分析一下论文中的数据预处理问题。剩下两个问题接下来两天再详细分析。论文Section Definition of Heatmaps 中强调,将像素当作是位于一个1*1单元格(cell)的中心,这一观点遵从经典的图像缩放原则(比如opencv和PIL的resize函数),同时也是OpenPose生成关节点高斯热图时采用的。
中心点对齐的计算方法
我们常常需要得出缩放前后某个像素位置和原始位置的精确坐标值,这时候就需要考虑几何中心点对齐,以下分析都是基于中心点对齐的缩放。缩放过程中,我们需要为目标图像dstImg上的每个像素位置找到与之对应的原图像srcImg的位置,并把srcImg上对应的像素值填入dstImg的该坐标位置处。若映射回原图srcImg的坐标位置不是整数,那么就需要做插值,算出映射回去的非整数坐标位置上的原图上的像素值是多少,因此就会有缩放过程中选择使用几阶插值算法了。
注意:图像坐标系从0开始,0,1,2,3… 像素分别位于每一个像素单元cell的中心。
上面的公式可以这样理解,对于x坐标的映射关系为:srcX = dstX*(srcX/dstX) + [0+(stride-1)] / 2,其中[]内左侧0是srcImg上第一个像素方格的坐标,右侧(stride-1)是srcImg上的stride长度的右侧图像坐标,两者的均值就是中间点坐标(因为把像素pixel看成了有面积的cell,那么精确的位置应该在cell中心)。其中stridex=srcW/dstW。
为了加强理解,再来一个例子,这个例子是为了说明图像的放大或者缩小将会影响 [0+(stride-1)] / 2 的符号。
下图的例子是把原图放大了,即放大了3/2倍(即stride=2/3),我们来计算一下在dst中的第一个像素位置应该对应于原图src中的位置是(stride-1)/2=-1/6,映射到了原始图像src的第一个像素的左侧了。
为了加强理解,我们用一个真正的2-D图像缩放并采用双线性灰度插值的例子详细说明这个过程。
np_image = np.zeros((5, 5))
np_image[2, 2] = 1.0
image = PIL.Image.fromarray(np_image)
PILImg = torchvision.transforms.functional.resize(image, (10, 10)) #(等价于PIL中的resize)
opencvImg = cv2.resize(np_image, (10, 10)) # 和PIL中的resize相同
我们查看一下np_image矩阵如下:
然后查看一下PILImg或opencvImg如下:
以上面(4,4)位置灰度值为例,我们来看这个灰度值是如何计算得到的,见下图:
我们先把变换后的图像中待求位置映射回原图中的位置(1.75, 1.75),然后沿着平行于zoy平面上插值一次,接着再在另一个平行于xoz平面上插值一次,就得到了双线性插值的结果为1*0.75*0.75=0.5625。
注意:图像坐标系从0开始,0,1,2,3… 像素分别位于每一个像素单元cell的中心。
到了这里,就不难理解项目中生成keypoint Gaussian heatmap和body part Gaussian heatmap的这段代码:
# x, y coordinates of centers of bigger grid, stride / 2 -0.5是为了在计算响应图时,使用grid的中心
self.grid_x = np.arange(width) * stride + stride / 2 - 0.5 # x -> width
self.grid_y = np.arange(height) * stride + stride / 2 - 0.5 # y -> height
# x ,y indexes (type: int) of heatmap feature maps
self.Y, self.X = np.mgrid[0:self.config.height:stride, 0:self.config.width:stride]
# 对<numpy.lib.index_tricks.MGridClass object> slice操作,比如L[:10:2]前10个数,每隔两个取一个
# # basically we should use center of grid, but in this place classic implementation uses left-top point.
self.X = self.X + stride / 2 - 0.5
self.Y = self.Y + stride / 2 - 0.5
上面这段代码位于:https://github.com/jialee93/Improved-Body-Parts/blob/316e71fa93e1dc444b1cfd4fc312c21c13bfe93f/py_cocodata_server/py_data_heatmapper.py#L40
如此一来,我们其实是让网络直接预测输入图像空间下的gaussian peak的采样,并且在预测阶段插值回原始输入分辨率。生成的ground truth heatmap例子:
keypoint heatmap(左)body part heatmap(右)
今天时间有限,就先把原作者在知乎的文章分享给大家,接下来两天再详细给大家介绍一下,老规矩还是给大家介绍一下租用GPU做实验的方法,我们是在智星云租用的GPU,使用体验很好。具体大家可以参考:智星云官网: http://www.ai-galaxy.cn/,淘宝店:https://shop36573300.taobao.com/公众号: 智星AI,
PEACE
参考文献:
https://zhuanlan.zhihu.com/p/109118177
https://github.com/hellojialee/Improved-Body-Parts
https://arxiv.org/abs/1911.10529