以下是一些可用于生成和可视化锚框坐标而无需训练模型的函数。我们在这里所做的就是调用训练/推理期间图中使用的相关操作。
首先,我们需要知道构成给定大小的输入图像的对象检测层的特征图的分辨率(形状)是多少。
import tensorflow as tf
from object_detection.anchor_generators.multiple_grid_anchor_generator import create_ssd_anchors
from object_detection.models.ssd_mobilenet_v2_feature_extractor_test import SsdMobilenetV2FeatureExtractorTest
def get_feature_map_shapes(image_height, image_width):
"""
:param image_height: height in pixels
:param image_width: width in pixels
:returns: list of tuples containing feature map resolutions
"""
feature_extractor = SsdMobilenetV2FeatureExtractorTest()._create_feature_extractor(
depth_multiplier=1,
pad_to_multiple=1,
)
image_batch_tensor = tf.zeros([1, image_height, image_width, 1])
return [tuple(feature_map.get_shape().as_list()[1:3])
for feature_map in feature_extractor.extract_features(image_batch_tensor)]
例如,这将返回特征图形状的列表[(19,19), (10,10), (5,5), (3,3), (2,2), (1,1)]
您可以将其传递给第二个函数,该函数返回锚框的坐标。
def get_feature_map_anchor_boxes(feature_map_shape_list, **anchor_kwargs):
"""
:param feature_map_shape_list: list of tuples containing feature map resolutions
:returns: dict with feature map shape tuple as key and list of [ymin, xmin, ymax, xmax] box co-ordinates
"""
anchor_generator = create_ssd_anchors(**anchor_kwargs)
anchor_box_lists = anchor_generator.generate(feature_map_shape_list)
feature_map_boxes = {}
with tf.Session() as sess:
for shape, box_list in zip(feature_map_shape_list, anchor_box_lists):
feature_map_boxes[shape] = sess.run(box_list.data['boxes'])
return feature_map_boxes
在您的示例中,您可以这样称呼它:
boxes = get_feature_map_boxes(
min_scale=0.2,
max_scale=0.9,
feature_map_shape_list=get_feature_map_shapes(300, 300)
)
您不需要指定纵横比,因为配置中的纵横比与默认值相同create_ssd_anchors
.
最后,我们将锚框绘制在反映给定层分辨率的网格上。请注意,模型中的锚框和预测框的坐标在 0 和 1 之间标准化。
def draw_boxes(boxes, figsize, nrows, ncols, grid=(0,0)):
fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=figsize)
for ax, box in zip(axes.flat, boxes):
ymin, xmin, ymax, xmax = box
ax.add_patch(patches.Rectangle((xmin, ymin), xmax-xmin, ymax-ymin,
fill=False, edgecolor='red', lw=2))
# add gridlines to represent feature map cells
ax.set_xticks(np.linspace(0, 1, grid[0] + 1), minor=True)
ax.set_yticks(np.linspace(0, 1, grid[1] + 1), minor=True)
ax.grid(True, which='minor', axis='both')
fig.tight_layout()
return fig
如果我们以具有 3x3 特征图的第四层为例
draw_boxes(feature_map_boxes[(3,3)], figsize=(12,16), nrows=9, ncols=6, grid=(3,3))
在上图中,每一行代表 3x3 特征图中的不同单元格,而每一列代表特定的长宽比。
您最初的假设是正确的,例如,最高层中宽高比为 1.0 的锚框(具有最低分辨率的特征图)的高度/宽度将等于输入图像大小的 0.9,而最低层中的锚框将具有高度/宽度等于输入图像尺寸的 0.2。中间层的锚尺寸在这些限制之间线性插值。
然而,关于 TensorFlow 锚点生成,有一些微妙之处值得注意:
- 请注意,在图像示例中,每个网格单元有 6 个锚点,但我们仅指定 5 个长宽比。这是因为为每个层添加了一个额外的锚点,其大小介于当前层的锚点大小和下一层的锚点大小之间。可以使用以下命令修改(或删除)
interpolated_scale_aspect_ratio
上面的anchor_kwargs参数,或者同样在你的配置中。
- 默认情况下,在对象检测特征图的最低层(具有最高分辨率)中,预先指定的长宽比列表将被忽略,并仅替换为 3 个长宽比。这可以被覆盖
reduce_boxes_in_lowest_layer
布尔参数。
- 正如您正确指出的那样,默认情况下
base_anchor_height = base_anchor_width = 1
。但是,如果您的输入图像不是正方形并且在预处理过程中被重新整形,那么纵横比 1.0 的“正方形”锚点实际上不会针对锚定原始图像中正方形的对象进行优化(当然,它可以学习在训练期间预测这些形状)。
完整的要点可以找到here https://gist.github.com/david-macleod/60cb2b00616562b9b613bea618f9956b.