有些同学已经学习目标检测有了一段时间,对于Fast系列和yolo系列的目标检测算法都如数家珍,包括每次改进升级用到了哪些tricks或者新的改进。现在也出现了很多anchor-free的算法,摒弃了anchor的使用。
但是,作者想说的是,我们是否真正的把anchor与预测框之间的关系搞得很明白,是否阅读过每一行anchor相关的代码?笔者近阶段写了一系列的博文均以代码为核心,分析算法思想,希望可以帮助大家将理论知识应用于实际而不只是读了多少篇论文,看了多少个实验结果。
feat = np.random.normal(0,0.5,[4,13,13,75])
anchors = [[142, 110],[192, 243],[459, 401]]
本次示例专注于anchor解析,故随机生成feat和某一层的anchors参数。feat的维度为(4, 13, 13, 75)。anchors维度为(3,2)
def yolo_head_simluate(feats, anchors, num_classes)
进入仿真函数
grid_shape = np.shape(feats)[1:3] # height, width
print(grid_shape)
grid_y = np.tile(np.reshape(np.arange(0, stop=grid_shape[0]), [-1, 1, 1, 1]),
[1, grid_shape[1], 1, 1])
grid_x = np.tile(np.reshape(np.arange(0, stop=grid_shape[1]), [1, -1, 1, 1]),
[grid_shape[0], 1, 1, 1])
grid = np.concatenate([grid_x, grid_y],-1)
print(np.shape(grid))
第一步,将grid进行维度变化,得到(13,13,1,2)的维度。上面这段复杂的代码目的只是改变grid的维度,并将第一维和第二维从0-12进行排序。
feats = np.reshape(feats, [-1, grid_shape[0], grid_shape[1], num_anchors, num_classes + 5])
第二步:修改输出层特征维度,变为(batch_size,13,13,3,85)。
box_xy = (sigmoid(feats[..., :2]) + grid)
box_wh = np.exp(feats[..., 2:4]) * anchors_tensor
box_confidence = sigmoid(feats[..., 4:5])
box_class_probs = sigmoid(feats[..., 5:])
将预测框调成真实值,其中box_xy对应框的中心的,box_wh对应长和宽。
这段代码应该重点进行理解。引用论文中的示意图:
bx和by通过sigmod函数将输出值限定在0-1之间,再加上grid,获得距离中心坐标的偏移量。这里grid表示图上平均划分13*13后的位置。
bw和bh通过exp(feats[:,2:4]*anchor长宽获得。
grid通过concatenate函数生成,详细解释一下grid里有什么。grid的维度是(13,13,1,2),个人理解是:13*13个点,每个点中的维度是(1,2),也就是一行两列。两列中的值均是0-12排列。也就是每个点中表示这相应位置的数,既偏移量。如,第(0,3)点中存储的是[3,0]。
另外需要注意的是,这里有个python广播的用法,示例如下:
feats=np.arange(0,30).reshape((3,2,5))
print("feats.shape={}".format(feats.shape))
grid=np.ones((1,1,2))
print("grid.shape={}".format(grid.shape))
box_xy=feats[...,:2]+grid
print("box_xy.shape={}".format(box_xy.shape))
#结果:
feats.shape=(3, 2, 5)
grid.shape=(1, 1, 2)
box_xy.shape=(3, 2, 2)
fig = plt.figure()
ax = fig.add_subplot(121)
plt.ylim(-2,15)
plt.xlim(-2,15)
plt.scatter(grid_x,grid_y)
plt.scatter(5,5,c='black')
plt.gca().invert_yaxis()
anchor_left = grid_x - anchors_tensor/2
anchor_top = grid_y - anchors_tensor/2
print(np.shape(anchors_tensor))
rect1 = plt.Rectangle([anchor_left[0,5,5,0,0],anchor_top[0,5,5,0,1]],anchors_tensor[0,0,0,0,0],anchors_tensor[0,0,0,0,1],color="r",fill=False)
rect2 = plt.Rectangle([anchor_left[0,5,5,1,0],anchor_top[0,5,5,1,1]],anchors_tensor[0,0,0,1,0],anchors_tensor[0,0,0,1,1],color="r",fill=False)
rect3 = plt.Rectangle([anchor_left[0,5,5,2,0],anchor_top[0,5,5,2,1]],anchors_tensor[0,0,0,2,0],anchors_tensor[0,0,0,2,1],color="r",fill=False)
ax.add_patch(rect1)
ax.add_patch(rect2)
ax.add_patch(rect3)
ax = fig.add_subplot(122)
plt.ylim(-2,15)
plt.xlim(-2,15)
plt.scatter(grid_x,grid_y)
plt.scatter(5,5,c='black')
plt.scatter(box_xy[0,5,5,:,0],box_xy[0,5,5,:,1],c='r')
plt.gca().invert_yaxis()
pre_left = box_xy[...,0] - box_wh[...,0]/2
pre_top = box_xy[...,1] - box_wh[...,1]/2
rect1 = plt.Rectangle([pre_left[0,5,5,0],pre_top[0,5,5,0]],box_wh[0,5,5,0,0],box_wh[0,5,5,0,1],color="r",fill=False)
rect2 = plt.Rectangle([pre_left[0,5,5,1],pre_top[0,5,5,1]],box_wh[0,5,5,1,0],box_wh[0,5,5,1,1],color="r",fill=False)
rect3 = plt.Rectangle([pre_left[0,5,5,2],pre_top[0,5,5,2]],box_wh[0,5,5,2,0],box_wh[0,5,5,2,1],color="r",fill=False)
ax.add_patch(rect1)
ax.add_patch(rect2)
ax.add_patch(rect3)
plt.show()
上面这段代码用于画图显示,与anchor和box无关,不做详解。