产生anchor的机制

产生anchor的机制

在当前的目标检测算法中,几乎都用到了anchor的思想,所以对于具体如何产生anchor的也要有个清晰的认识,因为这关系到产生多少anchor,产生多大的anchor,这在不同的问题上是不同的,比如如果一个图上面要检测的目标只有一个的话,而且这个目标都在图的中心,那么这时候就没有必要产生那么多的anchor。

产生anchor有两种思路,一种是一下子产生完,另外一种是先产生一个位置的anchor,然后再产生其它位置的anchor,而产生其它位置的anchor的时候其实就是做一个平移就可以了。下面以第二种办法,来详细地看一下代码。

第一步,产生一个以(0,0)为中心的anchor


import numpy as np

def generate_anchors(base_size=16, ratios=None, scales=None):
    """ 
    Generate anchor (reference) windows by enumerating aspect ratios X
    scales w.r.t. a reference window.
    """

    if ratios is None:
        ratios = np.array([0.5, 1, 2]) 

    if scales is None:
        scales = np.array([2 ** 0, 2 ** (1.0 / 3.0), 2 ** (2.0 / 3.0)])

    num_anchors = len(ratios) * len(scales)   # 这是每个地方产生的anchors

    # initialize output anchors
    anchors = np.zeros((num_anchors, 4)) 
    # scale base_size
    anchors[:, 2:] = base_size * np.tile(scales, (2, len(ratios))).T   # 右边是(9,2)的shape, 和左边是一样的,相当于是anchors的大小。

    # compute areas of anchors
    areas = anchors[:, 2] * anchors[:, 3]   # 每个anchor的面积

    # correct for ratios    # 要*上对应的ratio  w/ratio   h*ratio.
    anchors[:, 2] = np.sqrt(areas / np.repeat(ratios, len(scales)))   # 左边是(9,) 右边也是(9,)的。  
    anchors[:, 3] = anchors[:, 2] * np.repeat(ratios, len(scales))

    # 上面得到的是[0,0,w,h]  
    # transform from (x_ctr, y_ctr, w, h) -> (x1, y1, x2, y2)
    # 下面把中心变化到小格子的中心,要减去这个anchor的w/2和h/2.
    anchors[:, 0::2] -= np.tile(anchors[:, 2] * 0.5, (2, 1)).T
    anchors[:, 1::2] -= np.tile(anchors[:, 3] * 0.5, (2, 1)).T
    # 最后得到的是以(0,0)为中心的bbox.其信息是左上角和右下角。
    return anchors


里面的每一步已经解释清晰, 最终产生的anchor就是以(0,0)为中心的,函数入口处的参数base_size是要产生anchor的feature map的大小,由此可见在这时候产生的anchor还只是相对于feature map的大小,而不是原图的大小。

以上面产生的anchor为基础进行平移得到整个feature map上的anchors

def shift(shape, stride, anchors):   # shape是(10,8)  # 并不是图的shape, 而是要产生多少行和多少列的anchors
    shift_x = (np.arange(0, shape[1]) + 0.5) * stride
    shift_y = (np.arange(0, shape[0]) + 0.5) * stride

    shift_x, shift_y = np.meshgrid(shift_x, shift_y)

    shifts = np.vstack((
        shift_x.ravel(), shift_y.ravel(),
        shift_x.ravel(), shift_y.ravel()
    )).transpose()

    # add A anchors (1, A, 4) to
    # cell K shifts (K, 1, 4) to get
    # shift anchors (K, A, 4)
    # reshape to (K*A, 4) shifted anchors
    A = anchors.shape[0]
    K = shifts.shape[0]
    all_anchors = (anchors.reshape((1, A, 4)) + shifts.reshape((1, K, 4)).transpose((1, 0, 2)))
    all_anchors = all_anchors.reshape((K * A, 4))

    return all_anchors

其中传入的shape是下面的办法产生的,相当于种几行菜的感觉。

 image_shapes = [(image_shape + 2 ** x - 1) // (2 ** x) for x in self.pyramid_levels]

«««< HEAD 而且上面的乘以stride之后就转成了相对于原图的位置和中心。所以经过这一步之后最终产生的anchor依然是相对于原图的位置。估计这样做的好处是在测试的时候方便。这一点要注意,虽然中间经过了一些其它的操作,但是最终拿去用的anchor是相对于原图的。 ======= 而且上面的乘以stride之后就转成了相对于原图的位置和中心。所以经过这一步之后最终产生的anchor依然是相对于原输入的位置。估计这样做的好处是在测试的时候方便。这一点要注意,虽然中间经过了一些其它的操作,但是最终拿去用的anchor是相对于原输入的。

注意,上面的原输入指的是输入进网络的,并不是原始的图,

注意,产生的anchor最终都会转化为相对于网络的输入的大小,原始标注的gt信息也会在进入网络的时候转成相对于网络输入的大小的,这样这两个信息就一致了,然后就可以去做回归的gt了。

6d923d60bf3b199fe698abbbc29f0262d41d8345

打赏,谢谢~~

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,多谢支持~

打开微信扫一扫,即可进行扫码打赏哦