Tensorflow2.0—SSD网络原理及代码解析(二)-锚点框的生成
分析完SSD网络的原理之后,一起来看看代码吧~
代码转载于:https://github.com/bubbliiiing/ssd-tf2
在train.py中有一行代码:
priors = get_anchors((input_shape[0], input_shape[1]), anchors_size)
这行代码是用于生成锚点框的,原理对应于第一篇博客中2.1知识点。我们进get_anchors函数,发现它是在anchors.py文件中,该文件有一个类和一个函数。
net = {}
priorbox = PriorBox(img_size, anchors_size[0],max_size = anchors_size[1], aspect_ratios=[2],
variances=[0.1, 0.1, 0.2, 0.2],
name='conv4_3_norm_mbox_priorbox')
net['conv4_3_norm_mbox_priorbox'] = priorbox.call([features_map_length[0],features_map_length[0]])
在锚点框生成函数中,都是按照这种格式进行创建的,先实例化一个PriorBox类,然后调用其中的call方法。
def call(self, input_shape, mask=None):
# --------------------------------- #
# 获取输入进来的特征层的宽和高
# 比如38x38
# --------------------------------- #
layer_width = input_shape[self.waxis]
layer_height = input_shape[self.haxis]
# --------------------------------- #
# 获取输入进来的图片的宽和高
# 比如300x300
# --------------------------------- #
img_width = self.img_size[1]
img_height = self.img_size[0]
box_widths = [] #存放anchor box的宽
box_heights = [] #存放anchor box的高
# --------------------------------- #
# self.aspect_ratios一般有两个值
# [1, 1, 2, 1/2]
# [1, 1, 2, 1/2, 3, 1/3]
# --------------------------------- #
for ar in self.aspect_ratios:
# 首先添加一个较小的正方形
if ar == 1 and len(box_widths) == 0:
box_widths.append(self.min_size)
box_heights.append(self.min_size)
# 然后添加一个较大的正方形
elif ar == 1 and len(box_widths) > 0:
box_widths.append(np.sqrt(self.min_size * self.max_size))
box_heights.append(np.sqrt(self.min_size * self.max_size))
# 然后添加长方形
elif ar != 1:
box_widths.append(self.min_size * np.sqrt(ar))
box_heights.append(self.min_size / np.sqrt(ar))
# --------------------------------- #
# 获得所有先验框的宽高1/2
# --------------------------------- #
box_widths = 0.5 * np.array(box_widths)
box_heights = 0.5 * np.array(box_heights)
# --------------------------------- #
# 每一个特征层对应的步长
# --------------------------------- #
step_x = img_width / layer_width
step_y = img_height / layer_height
# --------------------------------- #
# 生成网格中心
# --------------------------------- #
linx = np.linspace(0.5 * step_x, img_width - 0.5 * step_x,
layer_width)
liny = np.linspace(0.5 * step_y, img_height - 0.5 * step_y,
layer_height)
plt.scatter(linx, liny)
# plt.show()
centers_x, centers_y = np.meshgrid(linx, liny)
centers_x = centers_x.reshape(-1, 1)
centers_y = centers_y.reshape(-1, 1)
plt.scatter(centers_x, centers_y)
# plt.show()
# 每一个先验框需要两个(centers_x, centers_y),前一个用来计算左上角,后一个计算右下角
num_priors_ = len(self.aspect_ratios)
prior_boxes = np.concatenate((centers_x, centers_y), axis=1)
print(prior_boxes.shape)
prior_boxes = np.tile(prior_boxes, (1, 2 * num_priors_))
print(prior_boxes.shape)
# 获得先验框的左上角和右下角(由xy坐标转为左右角坐标形式)
prior_boxes[:, ::4] -= box_widths
prior_boxes[:, 1::4] -= box_heights
prior_boxes[:, 2::4] += box_widths
prior_boxes[:, 3::4] += box_heights
# --------------------------------- #
# 将先验框变成小数的形式
# 归一化
# --------------------------------- #
prior_boxes[:, ::2] /= img_width
prior_boxes[:, 1::2] /= img_height
prior_boxes = prior_boxes.reshape(-1, 4)
prior_boxes = np.minimum(np.maximum(prior_boxes, 0.0), 1.0)
num_boxes = len(prior_boxes)
if len(self.variances) == 1:
variances = np.ones((num_boxes, 4)) * self.variances[0]
elif len(self.variances) == 4:
variances = np.tile(self.variances, (num_boxes, 1))
else:
raise Exception('Must provide one or four variances.')
prior_boxes = np.concatenate((prior_boxes, variances), axis=1)
return prior_boxes
步骤总结:
**1.**先创建存放anchor box的长宽的字典box_widths 和 box_heights 。然后根据aspect_ratios来进行生成anchor box的长宽。
**2.**然后生成网格中心,这里的做法是先生成对角线,然后基于np.meshgrid来生成最终的网格中心。
这里,只选取三个有效特征层生成的anchor网格中心点。可以根据上述图片可以看得出来,随着网络层级越深,在原图中生成的锚点数量就越少,说明越深层级的feature map主要是检测大物体的,浅层级的feature map主要是检测小物体的。
**3.**基于网格中心点坐标和每个层级生成的anchor box的长宽一半,可以计算出每个层级生成的锚点框左上角和右下角坐标。
此时,prior_boxes的shape为(1444,16),1444表示的是该层级,生成锚点的数量,同理,在其他层级中,prior_boxes的shape分别为(361, 24),(100, 24),(25, 24),(9, 16),(1, 16),后面数字不相同的原因是不同层级每个锚点生成的box的数目是不一样的。
**4.**将prior_boxes进行归一化,将坐标全部归一化成(0,1),并进行reshape到(-1,4),再进行负值坐标剔除操作。最后与variances 进行横向拼接。