Faster RCNN原理及Pytorch代码解读——RPN(一):Anchor的生成

这里省略了特征提取模块部分,个人感觉没什么好讲的,就是选用一个网络充当特征提取器,这个不是我们这个系列的重点,后面讲的部分都是以VGG16作为特征提取网络,需要注意一点就是由于VGG16的网络设计,经过conv层不改变特征图的尺寸,经过pool层特征图尺寸会缩小到原来的一半。VGG16一共有5个pool层,我们选用第4个pool层的输出作为提取出来的特征图,这样相比于原图就缩小了16倍,即下采样倍数是16。

为了方便理解,假设输入图像的维度为3×600×800,那么经改特征提取网络得到的特征图大小就512×37×50

Anchor的生成

Anchor(锚框)

理解什么是Anchor对理解RPN和整个Faster RCNN都十分重要。
Anchor本质上是在原图上预先定义好(这个预先定义十分关键)的一系列大小不一的矩形框,为什么要引入Anchor呢?这是因为之前的目标检测都是模型直接回归边框的位置,而通过引入Anchor相当于加入了强先验信息,然后通过锚框再去筛选与修正,最后再得到预测框。这样做的好处在与是在Anchor的基础上做物体检测,这样要比从无到有的直接拟合物体的边框容易一些。具体的做法就是让模型去预测Anchor与真实边框的偏移值,而不是直接预测边框的坐标。
Anchor有了,那怎么将Anchor跟特征图联系在一起呢?具体做法是, 首先对feature map进行3×3的卷积操作, 得到的每一个点的维度是512维, 这512维的数据对应着原始图片上的很多不同的大小与宽高区域的特征,这些区域的中心点都相同。 如果下采样率为默认的16, 则每一个点的坐标乘以16即可得到对应的原图坐标。如下图所示
在这里插入图片描述
这样的话,特征图的每一个点都对应着9个锚框,因此一共有37×50×9=16650个Anchors。且由于这9个Anchors大小宽高不同, 对应到原图基本可以覆盖所有可能出现的物体。

下面来看代码,看完代码就会知道生成Anchor了。
代码在lib/modle/rpn/generate_anchors.py中,直接运行作者demo中的generate_anchors.py可以得到以下输出,其中每一行表示锚框的左上角和右下角坐标。
在这里插入图片描述

主要的函数为:generate_anchors函数

def generate_anchors(base_size=16, ratios=[0.5, 1, 2],
                     scales=2**np.arange(3, 6)):
    """
    Generate anchor (reference) windows by enumerating aspect ratios X
    scales wrt a reference (0, 0, 15, 15) window.
    """

    # 首先创建一个基本anchor为[0, 0, 15, 15],分别是左上角坐标和右下角坐标
    base_anchor = np.array([1, 1, base_size, base_size]) - 1
    # 将基本anchor进行宽高变化,生成三种宽高比的anchor
    ratio_anchors = _ratio_enum(base_anchor, ratios)
    # 将上述anchor再进行尺度变化,得到最终的9种anchors
    anchors = np.vstack([_scale_enum(ratio_anchors[i, :], scales)
                         for i in xrange(ratio_anchors.shape[0])])
    # 返回对应于feature map大小的anchors
    return anchors

参数有三个:

1.base_size=16
这个参数指定了最初的类似感受野的区域大小,因为经过多层卷积池化之后,feature map上一点的感受野对应到原始图像就会是一个区域,这里默认设置为16是因为使用了VGG16作为特征提取器,前面说了其下采样率为16,所以feature map上一点对应到原图的大小为16x16的区域。当然也可以根据不同的特征提取网络来设置。

2.ratios=[0.5,1,2]
这个参数指的是要将16x16的区域,按照1:2,1:1,2:1三种比例进行变换。

3.scales=2**np.arange(3, 6)
这个参数是要将输入的区域,的宽和高进行三种倍数,分别是 2 3 = 8 2^3=8 23=8 2 4 = 16 2^4=16 24=16 2 5 = 32 2^5=32 25=32倍的放大,如16x16的区域变成 ( 16 ∗ 8 ) ∗ ( 16 ∗ 8 ) = 128 ∗ 128 (16*8)*(16*8)=128*128 (168)(168)=128128的区域 ( 16 ∗ 16 ) ∗ ( 16 ∗ 16 ) = 256 ∗ 256 (16*16)*(16*16)=256*256 (1616)(1616)=256256的区域, ( 16 ∗ 32 ) ∗ ( 16 ∗ 32 ) = 512 ∗ 512 (16*32)*(16*32)=512*512 (1632)(1632)=512512的区域。

宽高变化

def _ratio_enum(anchor, ratios):
    """
    对设定好的Anchor进行宽高变化,得到不同宽高比的Anchor
    """
	# 获取锚框的宽、高和中心点坐标
    w, h, x_ctr, y_ctr = _whctrs(anchor)
    # 得到锚框尺寸,size为256
    size = w * h
    # 对尺寸进行变换,size_ratios为[128,256,512]
    size_ratios = size / ratios
    # 将尺寸开根号作为锚框的宽
    ws = np.round(np.sqrt(size_ratios))
    # 将宽乘ratios得到锚框的高
    hs = np.round(ws * ratios)
    # 还原为锚框的左上角和右下角坐标的表现形式
    anchors = _mkanchors(ws, hs, x_ctr, y_ctr)
    return anchors
def _whctrs(anchor):
    """
    Return width, height, x center, and y center for an anchor (window).
    """

    w = anchor[2] - anchor[0] + 1
    h = anchor[3] - anchor[1] + 1
    x_ctr = anchor[0] + 0.5 * (w - 1)
    y_ctr = anchor[1] + 0.5 * (h - 1)
    return w, h, x_ctr, y_ctr
def _mkanchors(ws, hs, x_ctr, y_ctr):
    """
    Given a vector of widths (ws) and heights (hs) around a center
    (x_ctr, y_ctr), output a set of anchors (windows).
    """

    ws = ws[:, np.newaxis]
    hs = hs[:, np.newaxis]
    anchors = np.hstack((x_ctr - 0.5 * (ws - 1),
                         y_ctr - 0.5 * (hs - 1),
                         x_ctr + 0.5 * (ws - 1),
                         y_ctr + 0.5 * (hs - 1)))
    return anchors

尺寸变化

def _scale_enum(anchor, scales):
    """
    Enumerate a set of anchors for each scale wrt an anchor.
    """

    w, h, x_ctr, y_ctr = _whctrs(anchor)
    ws = w * scales
    hs = h * scales
    anchors = _mkanchors(ws, hs, x_ctr, y_ctr)
    return anchors

Guess you like

Origin blog.csdn.net/weixin_41693877/article/details/107059287