原理
https://blog.csdn.net/qq_35608277/article/details/80213663
目标检测architecture通常可以分为两个阶段:
(1)region proposal:给定一张输入image找出objects可能存在的所有位置。这一阶段的输出应该是一系列object可能位置的bounding box。这些通常称之为region proposals或者 regions of interest(ROI),在这一过程中用到的方法是基于滑窗的方式和selective search。
(2)final classification:确定上一阶段的每个region proposal是否属于目标一类或者背景。
这个architecture存在的一些问题是:
产生大量的region proposals 会导致performance problems,很难达到实时目标检测。
在处理速度方面是suboptimal。
无法做到end-to-end training。
ROI pooling提出的根本原因,ROI pooling层能实现training和testing的显著加速,并提高检测accuracy。该层有两个输入:
从具有多个卷积核池化的深度网络中获得的固定大小的feature maps;
一个表示所有ROI的N*5的矩阵,其中N表示ROI的数目。第一列表示图像index,其余四列表示其余的左上角和右下角坐标;
换个说法,实际上这部分输入就是rois:指的是RPN层的输出,一堆矩形框,形状为1x5x1x1(4个坐标+索引index),其中值得注意的是:坐标的参考系不是针对feature map这张图的,而是针对原图的(神经网络最开始的输入)。
ROI pooling具体操作如下:
根据输入image,将ROI映射到feature map对应位置;
将映射后的区域划分为相同大小的sections(sections数量与输出的维度相同);
对每个sections进行max pooling操作;
ROI Pooling的输出
输出是batch个vector,其中batch的值等于roi的个数,vector的大小为channelwh;ROI Pooling的过程就是将一个个大小不同的box矩形框,都映射成大小为w*h的矩形框;
这样我们就可以从不同大小的方框得到固定大小的相应 的feature maps。值得一提的是,输出的feature maps的大小不取决于ROI和卷积feature maps大小。ROI pooling 最大的好处就在于极大地提高了处理速度。
举例
举例:(数字是随意举例的,仅仅为了说明道理)
假设输入的ROI大小为8060,期望输出的ROI固定大小为106;
那么将输入的ROI(8060)划分为106块,即每块的大小为(80/10,60/6)。
对每一块分别进行最大或者最小Pooling操作,即得到了10*6的期望大小的输出ROI。
我们有一个88大小的feature map,一个ROI(57),以及输出大小为2*2. 则分成5/2,7/2
region proposal 投影之后位置(左上角,右下角坐标):(0,3),(7,8)。
将其划分为(22)个sections(因为输出大小为22),我们可以得到:
说明:在此案例中region proposals 是57大小的,在pooling之后需要得到22的,所以在57的特征图划分成22的时候不是等分的,行是5/2,第一行得到2,剩下的那一行是3,列是7/2,第一列得到3,剩下那一列是4。
对每个section做max pooling,可以得到:
RPN输出
分类
# vgg16提取后的特征图,先进行3*3卷积
# 3*3的conv,作为rpn网络 cfg.RPN_CHANNELS=512是卷积后的通道数
rpn = slim.conv2d(net_conv, cfg.RPN_CHANNELS, [3, 3], trainable=is_training, weights_initializer=initializer,
scope="rpn_conv/3x3")
# 每个框进行2分类,判断前景还是背景
# 1*1的conv,得到每个位置的9个anchors分类特征[1,w,h,9*2],
rpn_cls_score = slim.conv2d(rpn, self._num_anchors * 2, [1, 1], trainable=is_training,
weights_initializer=initializer,
padding='VALID', activation_fn=None, scope='rpn_cls_score')
然后进行reshape,拿一张图片举个例子,图片的shape是(W,H,D=18),然后我们会把他reshape以进行softmax(进行softmax的matrix的一边需要等于num of class,在这里是一个二分类,即是否含有物体,所以是2)。
所以我们会把(W,H,D)reshape成(2,9WH)。这里很重要!!!!对应的代码:
# change it so that the score has 2 as its channel size
# reshape成标准形式
# [1,W,H,9*2]-->[1,9*W*H,2] 分类得分,每个点有9个anchors,每个anchors有2个得分
rpn_cls_score_reshape = self._reshape_layer(rpn_cls_score, 2, 'rpn_cls_score_reshape')
然后我们进行softmax,得出对这9WH每一个的两个score,一个是有物体,一个是没有物体。对应的代码:
# 每个anchors是正样本还是负样本。 以最后一维为特征长度,得到所有特征的概率[1,9*W*H,2]
rpn_cls_prob_reshape = self._softmax_layer(rpn_cls_score_reshape, "rpn_cls_prob_reshape")
# 每个位置的9个anchors预测的类别。得到每个位置的9个anchors预测的类别,[1,?,9,?]的列向量
rpn_cls_pred = tf.argmax(tf.reshape(rpn_cls_score_reshape, [-1, 2]), axis=1, name="rpn_cls_pred")
# 变换回原始纬度,[1,?*9.?,2]-->[1,?,?,9*2]
rpn_cls_prob = self._reshape_layer(rpn_cls_prob_reshape, self._num_anchors * 2, "rpn_cls_prob")
回归
# 1*1的conv,每个位置的9个anchors回归位置偏移[1,?,?,9*4]
rpn_bbox_pred = slim.conv2d(rpn, self._num_anchors * 4, [1, 1], trainable=is_training,
weights_initializer=initializer,
padding='VALID', activation_fn=None, scope='rpn_bbox_pred')
https://blog.csdn.net/sxlsxl119/article/details/101288364
坐标计算‘
https://zhuanlan.zhihu.com/p/31426458
https://blog.csdn.net/zijin0802034/article/details/77685438
pytorch code
https://blog.csdn.net/Skies_/article/details/107339095
ref
https://blog.deepsense.ai/region-of-interest-pooling-explained/
https://blog.csdn.net/sxlsxl119/article/details/101288364