我是做人体姿态估计的方向,在姿态估计中有必不可少的 一步,就是坐标变换。最直观的从原始不规则图像变换到45*45(或者其他)尺寸的heatmap,原始图像的人体关键点的坐标就需要做相应的变换才可以生成正确的新的坐标,从而生成正确的heatmap。
而这一问题看似很容易,却是在一直困扰着我,我在做的项目里这一问题一直没彻底解决,而今天经过反复测试找到一个比较合适的实现方案,实现由原图变换到heatmap,heatmap解出原坐标。下面进入正文。
简单的坐标变换
首先,图像直接resize变换,是可以直接用坐标除以比例得到新的坐标。注意直接用cv2.resize()图像可能会发生形变,如下图:

这是我1280*720图像直接resize到 368*368的结果,红点是在原始图像画上去的,resize后到了上图中,上图中绿色的圈圈(下面的)则是经过简单的坐标计算:
仿射变换
仿射变换(Affine Transformation或 Affine Map)是二维情况下坐标之间的变换。它保持了二维图形的“平直性”(即:直线经过变换之后依然是直线)和“平行性”(即:二维图形之间的相对位置关系保持不变,平行线依然是平行线,且直线上点的位置顺序不变)。
数学上,仿射变换就是用2*3的变换矩阵来计算。
仿射变换可以写为如下的形式:(x,y)为原始坐标 (x',y')为变换后的坐标
矩阵计算比较常用,可以写为:
也就是简单的新的坐标 = 变换矩阵 * 原始坐标。而在姿态估计中,GT坐标都是三维,第三维为0胡总和1.1表示坐标正确,0表示不可信。因此很容易用仿射变换来求解变换后的坐标。
仿射变换是一系列原子操作组合而成,平移、缩放、旋转、反转、错切 。这些操作组合可以完成各种变换。
姿态估计中的坐标变换
姿态估计中自上而下的方法,需要先从原图把每个人切出来,然后切出来的图片一般要变换成正方形,然后再缩放到对应的heatmap尺寸,一般是(64*64)。所以上面提到的直接求比例是行不通的。
如果用常规方法去做,会非常麻烦,因为不只是需要heatmap的时候进行变换,再最后预测出heatmap后,依然需要求出坐标而且变换到原图。我之前都是直接做,所以一直出错。而如果借助仿射变换,这件事就很容易了。我借助的别人一个函数,来求仿射矩阵,变换图像;同时由heatmap反求坐标变换到原始图像。代码在我的工具类可以看到。下面看一下我的测试结果:
<1> 原始图像(忽略上面的蓝色点,是我刚开始求出的错误的):

<2> 裁剪后的图像以及坐标变换:

可以看出计算出的坐标是正确的。
<3> 裁剪后的图像关键点生成heatmap再反变换到原始图像:

可以看出是正确的坐标了。
踩坑
在上面<2>中,手动求坐标就是一开始提到公式,这里有两个坑:
新的坐标 = 变换矩阵 * 原始坐标, 我之前这一步错在用原始坐标 去乘 变换矩阵,不懂原理嘛。- 这里的乘法是正式的矩阵乘法,用numpy实现就是:
np.dot(M,a), 这样算出来的就是二维坐标,即 (x,y),我之前这里也错了,如果直接用 M * a就是对应位置相乘,得到的是 2*3形状的矩阵。 - 还有就是以前求不对变换矩阵,后面需要就直接调用即可
new_coordinates = np.dot(trans_final, coordinates)有个心得,就是在做姿态估计准备训练模型前,先试几组坐标变换看对不对在做后面的事。
至此,困扰很久的坐标问题终于得到解决!