最近在看 逐字理解目标检测simple-faster-rcnn-pytorch-master代码(二)作者讲的蛮仔细的,但是在看到 generate_anchor_base() 方法时,我就有点懵了。
def generate_anchor_base(base_size=16, ratios=[0.5, 1, 2], #
anchor_scales=[8, 16, 32]): #对特征图features以基准长度为16、选择合适的ratios和scales取基准锚点anchor_base。(选择长度为16的原因是图片大小为600*800左右,基准长度16对应的原图区域是256*256,考虑放缩后的大小有128*128,512*512比较合适)
#根据基准点生成9个基本的anchor的功能,ratios=[0.5,1,2],anchor_scales=[8,16,32]是长宽比和缩放比例,anchor_scales也就是在base_size的基础上再增加的量,本代码中对应着三种面积的大小(16*8)^2 ,(16*16)^2 (16*32)^2 也就是128,256,512的平方大小
py = base_size / 2.
px = base_size / 2.
anchor_base = np.zeros((len(ratios) * len(anchor_scales), 4),
dtype=np.float32) #(9,4),注意:这里只是以特征图的左上角点为基准产生的9个anchor,
for i in six.moves.range(len(ratios)): #six.moves 是用来处理那些在python2 和 3里面函数的位置有变化的,直接用six.moves就可以屏蔽掉这些变化
for j in six.moves.range(len(anchor_scales)):
h = base_size * anchor_scales[j] * np.sqrt(ratios[i])
w = base_size * anchor_scales[j] * np.sqrt(1. / ratios[i]) #生成9种不同比例的h和w
'''
这9个anchor形状应为:
90.50967 *181.01933 = 128^2
181.01933 * 362.03867 = 256^2
362.03867 * 724.07733 = 512^2
128.0 * 128.0 = 128^2
256.0 * 256.0 = 256^2
512.0 * 512.0 = 512^2
181.01933 * 90.50967 = 128^2
362.03867 * 181.01933 = 256^2
724.07733 * 362.03867 = 512^2
该函数返回值为anchor_base,形状9*4,是9个anchor的左上右下坐标:
-37.2548 -82.5097 53.2548 98.5097
-82.5097 -173.019 98.5097 189.019
-173.019 -354.039 189.019 370.039
-56 -56 72 72
-120 -120 136 136
-248 -248 264 264
-82.5097 -37.2548 98.5097 53.2548
-173.019 -82.5097 189.019 98.5097
-354.039 -173.019 370.039 189.019
'''
index = i * len(anchor_scales) + j
anchor_base[index, 0] = py - h / 2.
anchor_base[index, 1] = px - w / 2.
anchor_base[index, 2] = py + h / 2.
anchor_base[index, 3] = px + w / 2. #计算出anchor_base画的9个框的左下角和右上角的4个anchor坐标值
按照我的理解,base_size=16,那么 ratio = [0.5,1,2]时,应该很直接,宽高分别为:(16,8),(16,16),(32,16),然后每一种宽高再分别乘以 scale = [8,16,32],不就如下结果吗?
这样不就9种出来了吗,但是为什么就是不跟上面的结果一样呢?别人代码经历了这么多年,不可能出问题,那就是我没想通,最想不通的就是对 ratio 开根号。在网上看了很多资料,都没有提及,其中在bilibili看到一个人讲了,就提了一句,有个平方关系,但是还是没想明白。
后来想,这里面应该是有个数学公式在里面,后来突然醒悟,想了很久终于想通了。
那就是有很多人没提及,面积不变的基础上进行宽高比转换,当 base_size = 16 的时候,其先进行尺度变换,生成三种尺度,如下:
(base_size * 8, base_size * 8),(base_size * 16, base_size * 16),(base_size * 32, base_size * 32)= (128,128),(256, 256),(512, 512)
这个时候,三种尺度已经出来了,也就是说三种面积已经出来了,分别是 128^2,256^2, 512^2,也就是说,9种 anchor box 其实只有三种面积。
我们知道,矩形的面积计算公式为:
长 * 宽 = 面积 (1-1)
现在面积知道了,我们要求宽和高了,很明显,三种面积里面都有一个我们已经知道,分别是:128,256,512,就是正方形。这里我们就拿一个面积来讲解,剩下的是一样的道理。我们选取 面积为 128的讲。
现在面积我们知道,是 128,那对应的宽高有一个很容易想到,那就是 w=16, h=16,那剩下的两种宽高呢?(不是( w=16, h=8),( w=32, h=16)),因为要面积固定,就是三种宽高比,对应的面积都是同一个,(这里举列是128),好了,因为面积知道,根据公式(1-1),以及宽高比 ratio = [0.5,1,2] 我们知道
w * h = area,
h / w = ratio,
w = h / ratio,
h^2 / ratio = area,
h = sqrt(ratio * area), 其中 area = (base_size * scale)^2
h = base_size * scale * sqrt(ratio) 对应代码中 h = base_size * anchor_scales[j] * np.sqrt(ratios[i])
w = h / ratio 但是代码中是 w = base_size * scale * sqrt(1 / ratios) 作者是不像我们那么想,直接用公式 w * w *ratio = (base_size * scale)^2, 即 w = base_size * scale / sqrt(ratio)替换,都是一样的。
以上我们用的 面积为128,其有三种宽高比,即 [0.5,1,5],那么对应三种不同形状的矩形,但是面积相同。
同理,剩下的两个面积也各自对应三种宽高比
所以,以上有 9种不同形状的矩形,这9种不同形状的矩形中心是同一个。
下图是面积 128 计算三种不同宽高的具体计算,都是约等于,其实具体写出来跟代码中一样,256,512类似: