【detectron】对输入样本如何产生anchor

detectron中图片产生anchor的函数是get_field_of_anchors,它位于data_utils.py,要生成anchor需要下列参数,因此该函数主要输入就是以下几个参数,不做过多的解释了。

  • stride:
  • anchor_sizes
  • anchor_aspect_ratios

该函数生成生成anchor的过程如下:

1.首先对一个cell生程anchor,此时这个anchor没有位置点信息,只有长宽而已,这个长宽满足我们设计的anchor_sizes和anchor_aspect_ratios,generate_anchors代码放在最后面。

    # Anchors at a single feature cell,在一个cell产生的anchor
    cell_anchors = generate_anchors(
        stride=stride, sizes=anchor_sizes, aspect_ratios=anchor_aspect_ratios
    )
    num_cell_anchors = cell_anchors.shape[0] #anchor的数量

2.之后根据我们输入图片的长宽,以及stride,计算在这样一张图片上以stride为步长要在哪些位置生成anchor,此时才有了放置这些anchor的点.

detectron是在fpn_max_size为基准构成的正方形上生成放置anchor的点的,怎么理解呢,这里举出两个例子,假设我输如的图片大小为1024×1024。

(1)这里设置训练需要的样本最大边长为1024,即cfg.TRAIN.MAX_SIZE = 1024。由于我们原本的图片就是1024,所以就不用缩放了。retinanet默认cfg.FPN.COARSEST_STRIDE = 128。所以fpn_max_size = 128×ceil(1024/128 ) = 1024。之后以stride为步长,在fpn_max_size×fpn_max_size大小的图上生成放置anchor的点,那么单个边放置的个数就是field_size = ceil(1024/8) = 128。所以生成的anchor的位置点个数为128×128个。

(2)这里设置训练需要的样本最大边长为800,即cfg.TRAIN.MAX_SIZE = 800。此时要对我们的样本最长边缩放至800,retinanet默认cfg.FPN.COARSEST_STRIDE = 128。所以fpn_max_size = 128×ceil(800/128 ) = 896。之后以stride为步长,在fpn_max_size×fpn_max_size大小的图上生成放置anchor的点,那么单个边放置的个数就是field_size = ceil(896/8) = 112。所以生成的anchor的位置点个数为112×112个。

最后放置anchor的点坐标为shifts.它的第一列和第三列相同,第二列和第四列相同。

ps:其实对cfg.FPN.COARSEST_STRIDE这个参数理解还不是非常透彻,希望能与大家交流

    fpn_max_size = cfg.FPN.COARSEST_STRIDE * np.ceil(
        cfg.TRAIN.MAX_SIZE / float(cfg.FPN.COARSEST_STRIDE)
    )
    field_size = int(np.ceil(fpn_max_size / float(stride)))#以strid为步长,每隔一个stride,生成一个anchor,field_size表示最大的那个边长能生成多少个anchor
    shifts = np.arange(0, field_size) * stride
    shift_x, shift_y = np.meshgrid(shifts, shifts)
    shift_x = shift_x.ravel()
    shift_y = shift_y.ravel()
    shifts = np.vstack((shift_x, shift_y, shift_x, shift_y)).transpose()

3.有了这些放置点的位置,将a步骤的anchor挪过去,就相当于在该位置生成了一个anchor。

# Broacast anchors over shifts to enumerate all anchors at all positions
    # in the (H, W) grid:
    #   - add A cell anchors of shape (1, A, 4) to
    #   - K shifts of shape (K, 1, 4) to get
    #   - all shifted anchors of shape (K, A, 4)
    #   - reshape to (K*A, 4) shifted anchors
    A = num_cell_anchors
    K = shifts.shape[0]
    field_of_anchors = (
        cell_anchors.reshape((1, A, 4)) +
        shifts.reshape((1, K, 4)).transpose((1, 0, 2))
    )  
    field_of_anchors = field_of_anchors.reshape((K * A, 4))  #field_of_anchors的行数相当于anchor的数量

附录:generate_anchors代码:

def generate_anchors(
    stride=16, sizes=(32, 64, 128, 256, 512), aspect_ratios=(0.5, 1, 2)
):
    """Generates a matrix of anchor boxes in (x1, y1, x2, y2) format. Anchors
    are centered on stride / 2, have (approximate) sqrt areas of the specified
    sizes, and aspect ratios as given.
    #这里的stride可以理解为元anchor的边长(base_size)
    产生anchor的原理是:
    (1)先对元anchor生成不同aspect_ratios下的base_anchors
    (2)再根据该层实际设计的anchor的sizes,将base_anchors进行扩大,扩大的倍数就是下面的:np.array(sizes, dtype=np.float) / stride
    """
    return _generate_anchors(
        stride,
        np.array(sizes, dtype=np.float) / stride,
        np.array(aspect_ratios, dtype=np.float)
    )


def _generate_anchors(base_size, scales, aspect_ratios):
    """Generate anchor (reference) windows by enumerating aspect ratios X
    scales wrt a reference (0, 0, base_size - 1, base_size - 1) window.
    
    """
    anchor = np.array([1, 1, base_size, base_size], dtype=np.float) - 1 #这个就是元anchor
    anchors = _ratio_enum(anchor, aspect_ratios)    #根据aspect_ratios,对元anchor生成不同ratio下的base_anchors
    #根据该层实际设计的anchor的边长size,以及size与stride的关系(size/stride =scales),将base_anchors的边长乘以scales
    anchors = np.vstack(
        [_scale_enum(anchors[i, :], scales) for i in range(anchors.shape[0])]   
    )
    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] #由(3,)变为(3,1)
    hs = hs[:, np.newaxis]
    anchors = np.hstack(  #hstack横向拼接,保证行不变,列合并,所以是anchors的大小是(3,4)
        (
            x_ctr - 0.5 * (ws - 1), #anchor左上角的x,大小为(3,1)
            y_ctr - 0.5 * (hs - 1), #anchor左上角的y,大小为(3,1)
            x_ctr + 0.5 * (ws - 1), #anchor右上角的x,大小为(3,1)
            y_ctr + 0.5 * (hs - 1)  #anchor右上角的y,大小为(3,1)
        )
    )
    return anchors


def _ratio_enum(anchor, ratios):
    """Enumerate a set of anchors for each aspect ratio wrt an anchor."""
    w, h, x_ctr, y_ctr = _whctrs(anchor)#返回高宽以及中心
    size = w * h  #面积
    #下面这三个是计算在面积相同情况固定长宽比下的长hs与宽ws
    size_ratios = size / ratios
    ws = np.round(np.sqrt(size_ratios))
    hs = np.round(ws * ratios)
    anchors = _mkanchors(ws, hs, x_ctr, y_ctr) #根据求取的长宽和中心seanchor
    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

 

每次再看生成anchor的过程总是健忘的一点是:到底anchor生成的(绝对)大小是基于对应的FPN的大小,还是基于原图的大小
显然在计算IOU的时候anchor是与对应的GT进行计算,因此一定是基于原图的大小,要与与GT处于同一个尺度(比例)下。
搞混的原因是在不同的FPN层产生不同的大小的anchor,导致总以为anchor是在对应的FPN特征图上产生的。

其实在生产anchor中最重要的参数就是stride,不同的层在产生anchor的过程中stride是不一样的。这个stride实际上就是特征图相对于原图的下采样的倍数。
假设输入是512*512,经过经过层层卷积后得到C2层(经过横向链接对应于P2层),大小为64*64,也就是下采样了8倍。按照FPN的定义,这一层用来产生小anchor,对于64*64大小的特征图上每一点产生3种比例的面积为32*32的anchor(这个32是anchor大小,可以自定义,与GT同处于一个比例下)。截止到目前我们产生的anchor的位置都是基于当前特征图的,因此还需要转变为基于原图的。这时就用到了stride,通过stride可以将当前特征图上每一个点都映射回原图。可想而知,映射回原图后就是在原图每8个点处产生anchor。
同理对于C3层(经过横向链接对应于P3层),特征图大小为32*32,下采样了16倍,即stride = 16。在这一层产生大小为64*64的anchor,对应与原图就是每16个点处产生anchor。
依次类推。

基于上面的讲解,如果想要提升小目标的检测效果,通过简单调整FPN的方案有:
1. 设置更小的anchor_size
2. 利用分辨率更高的特征图。在上面的讲解中我们利用下采样8倍的特征图作为FPN中分辨率最高的特征图,在这种情况下对应于原图是每8个点产生anchor,anchor产生的还是不够密集,可能错过小目标。那么我们可以将下采样4倍的特征图作为FPN中分辨率最高的特征图,即大小为128*128,对应与原图是每4个点处产生anchor,小anchor的数量就明显多了,但是计算量也会随之上升。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值