# ROI Pooling 与 ROI Align
# 1 ROI
首先,何为ROI
? ROI
是Region of Interest
的缩写,即感兴趣区域。在不同的情况下用户关心的图像区域是不同的,因此ROI
所指含义也不同。对于物体检测的目标ROI
,ROI
就是要找到对象在图像中Bounding Box
区域。如
对于物体检测的目标,我们关心的是对象是人,则上图中红色的框中人的框选区域就是ROI
。
而在Fast RCNN
中,ROI Pooling
作用在backbone
和检测头之间,其关心的是物体检测框的候选框proposal boxes
,而非真正的target boxes
,对proposal boxes
经过置信度评分过滤和极大值抑制后才能生成target boxes
。因此,此时的ROI
指的是候选框proposal boxes
所指的区域。如下图,目标框是准确框出图中的两只狗狗,但**通过selective search
或RPN
网络生成的proposal boxes
**如图中花花绿绿的框所时,ROI Pooling
和ROI Align
正是作用在这些proposal boxes
所指的ROI
上。
图片来源于https://d2l.ai/chapter_computer-vision/anchor.html (opens new window)
# 2.ROI Pooling
将深度学习用于目标检测,网络架构一般是分成Backbone
基干网络用于提取图像特征,和检测头用来实现分类和检测框位置回归。2015年4月份微软的Ross Girshick
发表的Fast RCNN
论文中提出了ROI Pooling
, 解决了模型固定大小输入的问题,提升了检测的性能,一起来看ROI Pooling
到底是怎么回事。
ROI Pooling
顾名思意,就是池化操作的一种,只不过这个池化操作是作用在ROI
上面,而非整个图像区域。先看fast RCNN
的整体架构,
图片来自
fast RCNN
论文
图中Deep ConvNet
是深度卷积网络,也就是backbone
是用来提取特征得到feature map
的,feature map
就是对输入的图像经过层层卷积后得到的shape为NCHW
的张量,其中feature map
的宽高大小通常等于输入图像宽高除以stride
,stride
是输入图像经过卷积池化得到的feature map
的缩放倍数,ROI Pooling
正是紧跟在feature map
后面,作用在feature map
的池化操作。输入图像中红色的框是一个proposal box
,这个proposal box
是selective search
或region proposal network
算法自动生成的ROI
区域,还不是物体检测框bounding box
,proposal boxes
的尺度是相对于输入图像的,因此可根据stride
参数,将其映射到feature map
尺度上,也就是图中的ROI Projection
。得到feature map
和经过stride
缩放投射到feature map
上的proposal box
后,就可以开始进行池化操作了。
从上图中可以看到ROI Pooling
的全过程,绿色虚线框以外的部分是得到proposals
并变换到feature map
上的过程,绿色虚线框内是在1个通道上进行ROI Pooling
的过程。ROI Pooling
与空间金字塔池化 (opens new window)Spatial Pyramid Pooling
一样,都是无论输出的WH
大小,指定池化后输入结果的WH
,据此自适应计算池化核的大小,然后在划分后的池化核范围内进行最大值池化或均值池化,如图中绿色框中所示。空间金字塔池化Spatial Pyramid Pooling是何凯明于2014年06月份在微软亚洲研究院时发表的论文Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition
中提出的。ROI Pooling
是作用在每一个proposal
上的,假如是batch size=B
, proposals
的数量为N
, 则ROI Pooling
后的张量大小为, proposals
投射到feature map
上时,计算结果很有可能是不能整除的,如图中21/32=0.625
,这时选择近似取整的方法选定proposal
对应的feature map
对应的区域,如上图中将红色的框近似到蓝色框上,这里做了第一次量化取整,引入了误差。在计算自适应池化核大小时,遇到非整除的情况,这里对计算结果进行了向上取整和向下取整的近似,如8/3
通过近似得到池化核区域的大小为3,3,2
,这里做了第二次量化,引入了误差。
通过以上的说明,有可能依然没有说清除,说明白,最能描述一个算法本身的应该是代码实现,ROI Pooling
的具体代码实现可以参考 github ROI Pooling (opens new window)。
# 3.ROI Align
从上面介绍中可知ROI Pooling
中有两处对计算结果取了近似,一次是在将proposal
投射到feature map
上时,对浮点数进行了近似取整,另外一次是在进行池化操作计算池化核的大小时也进行了近似取整。这对于分类问题影响不大,但对于检测问题因对检测框进行了近似,会影响检测的定位精度。ROI Align
也是一种池化操作,只不过其不对proposals
映射和池化核大小做近似,而是使用浮点数计算,然后使用双线性插值再近似浮点数位置的值来做池化,比直接近似取整更加准确。ROI Align
是He KaiMing
和Fast RCNN
的原作者Ross Girshick
一起在2017
年03
月份发表的论文Mask RCNN
中的提出的,用于目标检测和实例分割。有个小插曲,2015年Ross Girshick
发表Fast RCNN
时,其署名单位是微软,2017
年署名单位已经是Facebook
的FAIR
了。
还以上图中狗的检测为例,这次直接使用feature map
红色的框进行计算,而不再将其取整近似到蓝色框上。且在绿色框中的池化操作,池化核的大小也采用均分,而非近似的方法。其实现过程如下图:
ROI Align
主要涉及到的是如何求浮点位置的feature map
上的值,其确定是通过双线性插值来实现的,比ROI Pooling
中的取整近似要更准确。关于双线性插值的介绍可参考(五)线性插值 (opens new window),ROI Align
的代码实现可参考:
其中计算线性插值的函数使用的方式是按邻近4个像素点对当前点贡献多少来决定的,也就是面积加权平均,可参考上面的线性插值介绍的文章。在计算坐标时
// 公式是(src+0.5)/(dst+0.5) = srcWidth/dstWidth,即中心点对齐
const T yy = start_y + ph * b_size_h + static_cast<T>(iy + 0.5f) * b_size_h / static_cast<T>(b_grid_h);
for (int ix = 0; ix < b_grid_w; ++ix) {
const T xx = start_x + pw * b_size_w + static_cast<T>(ix + 0.5f) * b_size_w / static_cast<T>(b_grid_w);
T x = xx, y = yy;
// situation 1: out of range
if (y < -1.0 || y > h || x < -1.0 || x > w) {
PreCalc<T> pc{0, 0, 0, 0, 0, 0, 0, 0};
pre_calc[idx] = pc;
idx += 1;
continue;
}
}
通过ROI Pooling
或ROI Align
后得到的张量的宽高同SPPNet
,是固定的,因此其后可以跟全连接层用来实现,检测框类别的判断和更好的位置回归。