本篇博客主要讲解faster rcnn的基本结构及相关代码讲解。

总体结构介绍

fast rcnn框架 faster rcnn介绍_tensorflow


  从上图中我们可以看出,faster rcnn一共有三个部分,我们大致先说下:

  1.第一个部分为特征提取部分,经过卷积层得到特征图,也就是feature map
  2.第二个部分为RPN(region proposal network)区域候选网络,这是对fast rcnn重点改进的一部分,它的主要作用是得到感兴趣区域(ROI)。该网络会输出两个值,侯选框和分类(一张图像有许多候选框,分类这里分背景和物品也就是前景)
  3.分类回归层,这是最终的输出环节,最终输出目标图片预测框和分类

  这里可以看到1,3部分为主体结构;第2部分的主要作用是选择候选区域是一个辅助结构。

  RPN以及fast-RCNN主体结构均有预测输出,所以整个loss由4个部分组成(RPN的侯选框和分类,fast-RCNN主体结构的侯选框和分类)。需要关注的两点是:
1.两者候选框的回归任务,预测都是误差值,然后用在初始框上的
2.由前面的讲述不难理解,最终的框得到了两次修正,先产生最初的框,RPN修正一次,后面的回归分类任务修正一次。

具体代码及意义讲解


def _build_network(self, is_training=True):
    # select initializers
    if cfg.TRAIN.TRUNCATED:
      initializer = tf.truncated_normal_initializer(mean=0.0, stddev=0.01)
      initializer_bbox = tf.truncated_normal_initializer(mean=0.0, stddev=0.001)
    else:
      initializer = tf.random_normal_initializer(mean=0.0, stddev=0.01)
      initializer_bbox = tf.random_normal_initializer(mean=0.0, stddev=0.001)
	#get feature map
    net_conv = self._image_to_head(is_training)
    with tf.variable_scope(self._scope, self._scope):
      # build the anchors for the image
      self._anchor_component()
      # region proposal network
      rois = self._region_proposal(net_conv, is_training, initializer)
      # region of interest pooling
      if cfg.POOLING_MODE == 'crop':
        pool5 = self._crop_pool_layer(net_conv, rois, "pool5")
      else:
        raise NotImplementedError

    fc7 = self._head_to_tail(pool5, is_training)
    with tf.variable_scope(self._scope, self._scope):
      # region classification
      cls_prob, bbox_pred = self._region_classification(fc7, is_training, 
                                                        initializer, initializer_bbox)

    self._score_summaries.update(self._predictions)

    return rois, cls_prob, bbox_pred

上述代码是总体框架:
  前面是初始化内容不用看,我们从net_conv = self._image_to_head(is_training)看起。
1.net_conv = self._image_to_head(is_training)。
  这其实就是前面的卷积层内容,输出feature map(特征图),一般输出的大小是1*60*40*512([图片个数,高度,宽度,通道数])。
  需要提一句的是Faster RCNN首先是支持输入任意大小的图片的,比如输入的P*Q,进入网络之前对图片进行了规整化尺度的设定,如可设定图像短边不超过600,图像长边不超过1000,我们可以假定M*N=1000*600(如果图片少于该尺寸,可以边缘补0,即图像会有黑色边缘)
  经过Conv layers,图片大小变成(M/16)*(N/16),即:60*40(1000/16≈60,600/16≈40);则,Feature Map就是60*40*512-d(注:VGG16是512-d,ZF是256-d),表示特征图的大小为60*40,数量为512

2.往下看self._anchor_component()。

  这个函数主要产生anchor。这个东西是什么呢?

  前面提到经过Conv layers后,图片大小变成了原来的1/16,令feat_stride=16。

  然后我们来生成Anchors,首先要认识到特征图(60*40)上的一个点,可以对应到原图(1000*600)上一个16*16大小的区域(点位)(不难想象)。然后我们要对特征图上每一个点生成不同的矩形框。一般来说每个特征图上的点会生成9个矩形框,那么一共既是60*40*9(21600)个anchor box。这些anchor box会被映射到原图中,如下图:

fast rcnn框架 faster rcnn介绍_卷积_02


  当然特征图上每个点不可能随便生成9个anchor box,它是有一定规则的,根据长宽比和面积大小产生,就如上图一样。我们首先给定一个base anchor大小为[16,16](特征图与原图比例为1:16),我们先看下面积保持不变,长、宽比分别为ratios=[0.5, 1, 2]。产生的Anchors box,如下图:

fast rcnn框架 faster rcnn介绍_tensorflow_03


  接下来我们再看下面积大小变化scales=[8, 16, 32]。

fast rcnn框架 faster rcnn介绍_fast rcnn框架_04


  综合以上3*3种情况,如图:

fast rcnn框架 faster rcnn介绍_深度学习_05


  特征图每一个点位都照此处理。

详细的代码过程讲解(建议看一下,有助于理解):3.rois = self._region_proposal(net_conv, is_training, initializer)

  这里就是实现区域候选网络(RPN)。

fast rcnn框架 faster rcnn介绍_fast rcnn框架_06


  rpn如上图feature map经过一个3*3卷积之后,特征图保存不变,然后出现了两个1*1的卷积。

  上面1*1卷积是预测类别的(背景还是物体,是一个二分类[0,1],[1,0]表示),特征图大小变成了[1,h(60),w(40),2*9],其实就是对以上60*40*9个anchor进行预测。所以图上的18就是每个点位中9个锚的二分类情况。

  下面的1*1卷积是用来预测anchor box与真实框之间的偏移量,特征图大小变成了[1,h(60),w(40),4*9]。这个偏移量样本是如何计算的呢?看以下公式:

anchor box: 中心点位置坐标x_a,y_a和宽高w_a,h_a
ground truth:标定的框也对应一个中心点位置坐标x*,y*和宽高w*,h*
所以,偏移量:
△x=(x*-x_a)/w_a   △y=(y*-y_a)/h_a 
△w=log(w*/w_a)   △h=log(h*/h_a)

  通过ground truth box与预测的anchor box之间的差异来进行学习,从而是RPN网络中的权重能够学习到预测box的能力
  生成的60*40*9个anchor box,然后累加上训练好的△x, △y, △w, △h,从而得到了相较于之前更加准确的预测框region proposal,进一步对预测框进行越界剔除和使用nms非最大值抑制,剔除掉重叠的框;比如,设定IoU为0.7的阈值,即仅保留覆盖率不超过0.7的局部最大分数的box(粗筛)。最后留下大约2000个anchor,然后再取前N个box(比如300个);这样,进入到下一层ROI Pooling时region proposal大约只有300个

综上所述,这段函数其实就是做分类,回归和筛选,得到proposal。
详情看代码详解
RPN网络

4.pool5 = self._crop_pool_layer(net_conv, rois, “pool5”)
  通过以上代码,我们得到了ROI,然后我们就要将ROI映射到feature map上(因为ROI的大小是在原图的基础上)。这个做法就和fast rcnn的做法差不多一致。通过这个方法得到了一个一个特征图。我们详细看下:

我们了解一下它的输入吧:
bottom:特征图,shape=(1,60,40,512)
rois:筛选出来的roi,shape=(N,5)
作用:根据roi得到对应的特征图部分

def _crop_pool_layer(self, bottom, rois, name):
    with tf.variable_scope(name) as scope:
    #得到索引
      batch_ids = tf.squeeze(tf.slice(rois, [0, 0], [-1, 1], name="batch_id"), [1])
      # Get the normalized coordinates of bounding boxes
      bottom_shape = tf.shape(bottom)
      height = (tf.to_float(bottom_shape[1]) - 1.) * np.float32(self._feat_stride[0])
      width = (tf.to_float(bottom_shape[2]) - 1.) * np.float32(self._feat_stride[0])
      x1 = tf.slice(rois, [0, 1], [-1, 1], name="x1") / width
      y1 = tf.slice(rois, [0, 2], [-1, 1], name="y1") / height
      x2 = tf.slice(rois, [0, 3], [-1, 1], name="x2") / width
      y2 = tf.slice(rois, [0, 4], [-1, 1], name="y2") / height
#以上其实是在归一化,下面函数的要求,会讲到。

      # Won't be back-propagated to rois anyway, but to save time
      bboxes = tf.stop_gradient(tf.concat([y1, x1, y2, x2], axis=1))
      pre_pool_size = cfg.POOLING_SIZE * 2
      crops = tf.image.crop_and_resize(bottom, bboxes, tf.to_int32(batch_ids), [pre_pool_size, pre_pool_size], name="crops")
#可以看下这篇博客
    return slim.max_pool2d(crops, [2, 2], padding='SAME')

5.fc7 = self._head_to_tail(pool5, is_training)

cls_prob, bbox_pred =self._region_classification(fc7,is_training, initializer, initializer_bbox)

这里两行代码一起讲,第一行代码将pool5变成fc的结构,之后送入**_region_classification**,得到最终的分类和偏移量。

这个过程就是以下第三部份。

fast rcnn框架 faster rcnn介绍_tensorflow_07