多尺度物体目标检测

锚框生成过多的问题

  上一节学习到,我们是基于生成的锚框来预测物体类别和偏移量,而且我们对于一张原始图片,对于每个像素都会生成多个锚框。
  问题:我们生成了大量的锚框,而在其中有大量的重复区域,造成了运算量过于复杂。那么如何减少我们的计算量呢?
  其实很简单,我们只需要对原始图像均匀采样一小部分像素,并以采样的像素为中心生成锚框,这样我们可以通过控制采样的像素个数,而控制整体的运算复杂度。

尺度

  尺度这个概念其实在生活中我们常常接触,举一些比较形象的例子,比如说我们用手机拍照时,可以通过缩小放大画面,从而改变尺度。我们在用地图的时候,也可以缩小和放大改变尺度。
  计算机在不知道我们感兴趣的物体尺寸情况下,我们就需要考虑多尺度以获得感兴趣物体的最佳尺度。
  具体一点到我们的CNN中,我们CNN中一般越靠近输出层的特征图高和宽会越来越小,我们可以看做是,在越靠近输出层的特征图每个像素感受野越大,也就是说每个像素能反映原图片中越大的区域,也就是尺度越大。(比如在靠近输入层地方的特征图,一个像素只能对应原图像中3*3的像素区域,但是在靠近输出层地方的特征图,一个像素有可能就对应30*30的像素区域)
  目标检测过程中,较小目标相比于较大目标在图像上出现的位置可能性更多,比如形状为$11,21,22$的目标在形状为$22$的图像区域上出现的位置可能性为$4,2,1$种。所以我们可以在靠近输出层的大尺度特征图中生成大小比例大、数量较少的锚框,来检测尺寸比较大的物体。在前面的一些小尺寸特征图中生成大小比例小、数量较多的锚框,来检测尺寸较小的物体。

如何在MXNet中生成多尺度的锚框

  问题来了,我们如何用MXNet中函数生成多尺度的锚框呢?其实很简单,我们直接使用不同大小的特征图作为输入,然后生成锚框,最后还原的时候乘以原始图像的宽和高就OK了。也就是我们可以通过定义一个特征图来对整个原始图像均匀采样。
  我们知道生成锚框的函数为contrib.nd.MultiBoxPrior,这个函数的输出为(1,锚框总数,4)其中各个不同的批量共享这些锚框。这个函数输出的四个坐标自动的除以了输入的宽和高,归一化到了0和1之间。这就代表了输出的锚框是相对于整个图像的相对位置,由于我们生成锚框是对特征图每个像素都生成,所以我们基于这个特征图生成的锚框一定是对原始图像均匀采样得到的。

1
2
3
4
5
6
7
8
9
10
11
12
def show_diff_scale_anchors(axes,fmap_w,fmap_h,sizes,ratios,img_scale):
#显示不同尺度的图像
fmap = nd.zeros(shape=(1,3,fmap_h,fmap_w))
#生成锚框
anchors = contrib.nd.MultiBoxPrior(fmap,sizes=sizes,ratios=ratios)
#显示多尺度
show_all_bboxes(axes,anchors[0]*img_scale)

plt.figure(figsize=(7,7))
img = image.imread('../img/catdog.jpg').asnumpy()
fig = plt.imshow(img)
show_diff_scale_anchors(fig.axes,4,4,[0.15],[1,2,0.5],img_scale)

  上面我们在$4*4$的特征图上生成锚框,一共采样了16个像素,每个像素有3个锚框,结果如下:

图片失效啦

  接下来我们尝试加大尺度,在$2*2$的特征图上生成锚框,此时我们的锚框大小比例应该加大,而个数应该减小,结果如下:

1
2
3
4
plt.figure(figsize=(7,7))
img = image.imread('../img/catdog.jpg').asnumpy()
fig = plt.imshow(img)
show_diff_scale_anchors(fig.axes,2,2,[0.3],[0.7,0.5],img_scale)

图片失效啦

  最后我们尝试最大的尺度,使用$1*1$的特征图,此时我们应该是在中心像素生成锚框,结果如下:

1
2
3
4
plt.figure(figsize=(7,7))
img = image.imread('../img/catdog.jpg').asnumpy()
fig = plt.imshow(img)
show_diff_scale_anchors(fig.axes,1,1,[0.75],[0.7,0.5],img_scale)

图片失效啦

总结

  

  1. 不同尺度的特征图在原始输入图像中含有不同的感受野,越靠近输出层的特征图每个像素的感受野越大,越适合用来检测大物体
  2. 我们可以使用contrib.nd.MultiBoxPrior函数很方便生成不同尺度的锚框,最后我们还原时乘以输入图像的高和宽就行
  3. SSD就是一种多尺度的目标检测框架