Tensorflow检测API中的SSD锚点

时间:2018-08-08 09:11:26

标签: python tensorflow object-detection

我想在N×N图像的自定义数据集上训练SSD检测器。因此,我研究了Tensorflow object detection API,发现了基于MobileNet v2的基于COCO的SSD300x300的预训练模型。

在查看用于训练的配置文件时:字段anchor_generator如下所示:(紧随本文之后)

anchor_generator {
  ssd_anchor_generator {
    num_layers: 6
    min_scale: 0.2
    max_scale: 0.9
    aspect_ratios: 1.0
    aspect_ratios: 2.0
    aspect_ratios: 0.5
    aspect_ratios: 3.0
    aspect_ratios: 0.33
    }
}

看着SSD anchor generator proto时,我是否正确假设:base_anchor_height=base_anchor_width=1

如果是,我假设通过阅读Multiple Grid anchors generator得到的锚点是(如果图像是300x300 square): 大小从0.2 * 300 = 60 * 60像素到0.9 * 300 = 270 * 270像素(具有不同的宽高比)?

因此,如果有人想通过固定字段来训练NxN图像:

fixed_shape_resizer {
  height: N
  width: N
}

他会使用相同的配置文件锚点,范围从(0.2 * N,0.2 * N)像素到(0.9 * N,0.9 * N)像素(具有不同的宽高比)?

我做了很多假设,因为代码很难掌握,而且似乎还没有文档。我对么 ?有没有一种简单的方法就可以可视化使用的锚,而无需训练模型?

1 个答案:

答案 0 :(得分:7)

这里有一些函数可用于生成和可视化锚框坐标,而无需训练模型。我们在这里所做的只是调用在训练/推断过程中在图中使用的相关操作。

首先,我们需要知道组成给定尺寸的输入图像的对象检测层的要素图的分辨率(形状)是多少。

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))

Anchor boxes for 3x3 feature map

在上图中,每一行代表3x3特征图中的不同单元格,而每一列代表特定的宽高比。

您最初的假设是正确的,例如在最高层(具有最低分辨率的特征图)中具有宽高比1.0的锚点框的高度/宽度将等于输入图像大小的0.9,而在最低层的锚点框将具有高度/宽度等于输入图像尺寸的0.2。在这些限制之间线性内插中间层的锚点大小。

然而,关于TensorFlow锚点生成的微妙之处值得我们注意:

  1. 请注意,在图像示例中,每个网格单元有6个锚点,但是我们仅指定5个宽高比。这是因为为每层添加了一个附加锚,其大小介于当前层的锚大小和下一层的锚大小之间。可以使用上面的anchor_kwargs中的interpolated_scale_aspect_ratio参数(或同样在您的配置中)来修改(或删除)。
  2. 默认情况下,在对象检测功能图的最低层(具有最高分辨率)中,将忽略预先指定的宽高比列表,而仅用3个宽高比代替。可以使用reduce_boxes_in_lowest_layer布尔参数覆盖。
  3. 正如您正确指出的那样,默认情况下为base_anchor_height = base_anchor_width = 1。但是,如果您输入的图像不是正方形,并且在预处理过程中进行了整形,则实际上不会优化纵横比为1.0的“正方形”锚点来锚定原始图像中正方形的对象(尽管当然可以学习预测这些对象训练中的形状。

完整的要旨可以找到here