天天看点

SSD300默认框尺寸计算1. 计算特征图的min_size与max_size2. 计算特征图的默认框尺寸3. 测试

目录

1. 计算特征图的min_size与max_size

2. 计算特征图的默认框尺寸

3. 测试

1. 计算特征图的min_size与max_size

先将SSD300网络模型附上,如图:

SSD300默认框尺寸计算1. 计算特征图的min_size与max_size2. 计算特征图的默认框尺寸3. 测试

SSD300是因为该网络的输入尺寸是300x300,所以别名是SSD300,从SSD网络模型与原文中知道,有6个feature_map层,分别是[conv4_3, fc7, conv8_2, conv9_2, conv10_2, conv11_2],但是我们在代码中一般不会这样起名,而是[conv4_3, fc7, conv6_2, conv7_2, conv8_2, conv9_2] ,这些都是小问题,不用太在乎。

从原文中,我们知道有6个feature map 层,每层的默认框数量如下:

每层默认框数量

conv4_3 4
fc7 6
conv6_2 6
conv7_2 6
conv8_2 4
conv9_2 4

要求默认框的尺寸,要先求每层feature map的min_size 与 max_size,所以要求解6组min_size与max_size,代码如下:

这个代码好像是SSD作者写的,我从别的地方借鉴的,然后修改了一点。

def compute_min_max_size(image_size=300):
    """
    :function: 计算SSD默认框的最小尺寸与最大尺寸
    :param image_size: 网络输入图片最小尺寸,图片尽量是正方形
    :return: 计算的最小尺寸与最大尺寸
    """
    min_dim = image_size  #######输入图片尺寸
    # conv4_3 ==> 38 x 38
    # fc7 ==> 19 x 19
    # conv6_2 ==> 10 x 10
    # conv7_2 ==> 5 x 5
    # conv8_2 ==> 3 x 3
    # conv9_2 ==> 1 x 1
    #####prior_box来源层,可以更改。很多改进都是基于此处的调整。
    mbox_source_layers = ['conv4_3', 'fc7', 'conv6_2', 'conv7_2', 'conv8_2', 'conv9_2']
    # in percent %
    ####这里即是论文中所说的Smin=0.2,Smax=0.9的初始值,经过下面的运算即可得到min_sizes,max_sizes。具体如何计算以及两者代表什么,请关注我的博客SSD详解。这里产生很多改进。
    min_ratio = 20
    max_ratio = 90
    ####math.floor()函数表示:求一个最接近它的整数,它的值小于或等于这个浮点数。
    ####取一个间距步长,即在下面for循环给ratio取值时起一个间距作用。可以用一个具体的数值代替,这里等于17。
    step = int(math.floor((max_ratio - min_ratio) / (
                len(mbox_source_layers) - 2)))
    min_sizes = []  ###经过以下运算得到min_sizes和max_sizes。
    max_sizes = []
    min_sizes = min_sizes + [min_dim * 10 / 100.]
    max_sizes = max_sizes + [min_dim * 20 / 100.]
    ####从min_ratio至max_ratio+1每隔step=17取一个值赋值给ratio。注意range函数的作用。
    for ratio in range(min_ratio, max_ratio + 1, step):
        ########min_sizes.append()函数即把括号内部每次得到的值依次给了min_sizes。
        min_sizes.append(min_dim * ratio / 100.)
        # print("min_size:", min_sizes)
        max_sizes.append(min_dim * (ratio + step) / 100.)
        # print("max_size:", max_sizes)

    return [min_sizes, max_sizes]
           

结合代码讲解,首先要晓得输入图片尺寸,SSD300的输入尺寸是300x300,所以这里的min_dim就是300啦。

mbox_source_layers这个变量作用不大,主要是为了计算有几个feature map层,我们已经知道有6个啦,这里可能是为了使代码更高大上。

min_ratio 与 max_ratio就是论文中的Smin=0.2, Smax=0.9。

SSD300默认框尺寸计算1. 计算特征图的min_size与max_size2. 计算特征图的默认框尺寸3. 测试

  至于为什么将min_ratio与max_ration置为90,一是为了计算step, 二是为了后面间隔取值用的。 

step = int(math.floor((max_ratio - min_ratio) / (len(mbox_source_layers) - 2)))
           

step就是一个步长,使用range()函数,用于在min_ratio~max_ration之间取n个值,这里n = 5,也正好符合我们的要求,为什么这样说呢?feature map层不是有6层吗?这是因为第一层(conv4_3层)单独计算,为什么这样做,我也不晓得,可能有特别的用意,有晓得的同学,请在评论区评论,谢谢。

conv4_3层的min_size,max_size计算公式如下:

min_size = min_dim * 10 / 100.0

max_size = min_dim * 20 / 100.0 

这里除以100.0的意思应该就是将增加100倍的ratio降低100倍,使其重归于0.2~0.9 

剩下5层的计算公式如下:

min_size = min_dim * ratio / 100.0

max_size = min_dim * (ratio + step) / 100.0 

最终计算结果如下:

min_size:  [30.0, 60.0, 111.0, 162.0, 213.0, 264.0]
max_size:  [60.0, 111.0, 162.0, 213.0, 264.0, 315.0]
           

计算结果

min_size max_size
conv4_3 30.0 60.0
fc7 60.0 111.0
conv6_2 111.0 162.0
conv7_2 162.0 213.0
conv8_2 213.0 264.0
conv9_2 264.0 315.0

2. 计算特征图的默认框尺寸

每个特征层的min_size与max_size计算出来后,就可以计算每个特征层的默认框尺寸啦。

aspect_ratios = [[2], [2, 3], [2, 3], [2, 3], [2], [2]]
           

这个是每个特征层的长宽比,这里并不是paper中所给出的ar={1,2,3,1/2,1/3},这个比例是计算出来的。

首先我们要知道,我们在前面也讲了,每层的特征图的每个中心点分别会产生4、6、6、6、4、4个默认框,但我们要知道为什么是这几个默认框,这里就和aspect_ratios有关系了。

在SSD中6层卷积层的每个特征图的每个中心点会产生2个不同大小的正方形默认框,另外每设置一个aspect_ratio则会增加两个长方形默认框,而文中代码对于6层的aspect_ratio个数分别为1、2、2、2、1、1,所以这也就是为什么会产生4、6、6、6、4、4个默认框了。例如conv4_3默认生成两个不同大小的正方形默认框,另外又有一个aspect_ratio=2产生了两个长方形默认框,所以总共有4个。再如fc7,默认生成两个正方形默认框,另外又有aspect_ratio=[2,3],所以又生成了4个不同的长方形默认框,共有6个不同大小的默认框。

接着我们再讲这些产生的默认框的大小计算。这里参考paper中的计算公式,我们可以知道,对于产生的正方形的默认框,一大一小共两个,其边长计算公式为:

小边长=min_size

大边长=sqrt(min_size*max_size)。

对于产生的长方形默认框,我们需要计算它的高(height)和宽(width),其中

height=1/sqrt(aspect_ratio)*min_size

width=sqrt(aspect_ratio)*min_size

对其高和宽翻转后得到另一个面积相同但宽高相互置换的长方形。

具体代码如下:

aspect_ratios = [[2], [2, 3], [2, 3], [2, 3], [2], [2]]
# mbox_source_layers = ['conv4_3', 'fc7', 'conv6_2', 'conv7_2', 'conv8_2', 'conv9_2']
def compute_default_box():
    min_sizes, max_sizes = compute_min_max_size()
    conv4_3 = np.zeros(shape=(4, 2))
    fc7 = np.zeros(shape=(6, 2))
    conv6_2 = np.zeros(shape=(6, 2))
    conv7_2 = np.zeros(shape=(6, 2))
    conv8_2 = np.zeros(shape=(4, 2))
    conv9_2 = np.zeros(shape=(4, 2))

    mbox_source_layers = [conv4_3, fc7, conv6_2, conv7_2, conv8_2, conv9_2]
    for i in range(6):
        # 每层feature中默认框的数量
        num = len(aspect_ratios[i]) * 2 + 2
        # for j in range(num):
        # 计算小正方形边长  小边长=min_size
        mbox_source_layers[i][0, :] = min_sizes[i]
        # 计算大正方形边长  大边长=sqrt(min_size*max_size)
        mbox_source_layers[i][1, :] = np.sqrt(min_sizes[i] * max_sizes[i])
        # 计算第一个长方形宽 width=sqrt(aspect_ratio)*min_size
        mbox_source_layers[i][2, 0] = np.sqrt(aspect_ratios[i][0]) * min_sizes[i]
        # 计算第一个长方形高 height=1/sqrt(aspect_ratio)*min_size
        mbox_source_layers[i][2, 1] = 1.0 / np.sqrt(aspect_ratios[i][0]) * min_sizes[i]
        # 计算第二个长方形宽 等于第一个长方形的高
        mbox_source_layers[i][3, 0] = mbox_source_layers[i][2, 1]
        # 计算第二个长方形高 等于第一个长方形的宽
        mbox_source_layers[i][3, 1] = mbox_source_layers[i][2, 0]
        # 判断一哈是否要计算第三和第四个长方形
        if num == 6:
            # 计算第三个长方形的宽
            mbox_source_layers[i][4, 0] = np.sqrt(aspect_ratios[i][1]) * min_sizes[i]
            # 计算第三个长方形高
            mbox_source_layers[i][4, 1] = 1.0 / np.sqrt(aspect_ratios[i][1]) * min_sizes[i]
            # 计算第四个长方形宽 等于第三个长方形的高
            mbox_source_layers[i][5, 0] = mbox_source_layers[i][4, 1]
            # 计算第四个长方形高 等于第三个长方形的宽
            mbox_source_layers[i][5, 1] = mbox_source_layers[i][4, 0]

    return mbox_source_layers
           

3. 测试

完整代码如下,运行即可,可根据需求,打印出想要的结果:

# -*- coding: utf-8 -*-
import math
import numpy as np

def compute_min_max_size(image_size=300):
    """
    :function: 计算SSD默认框的最小尺寸与最大尺寸
    :param image_size: 网络输入图片最小尺寸,图片尽量是正方形
    :return: 计算的最小尺寸与最大尺寸
    """
    min_dim = image_size  #######输入图片尺寸
    # conv4_3 ==> 38 x 38
    # fc7 ==> 19 x 19
    # conv6_2 ==> 10 x 10
    # conv7_2 ==> 5 x 5
    # conv8_2 ==> 3 x 3
    # conv9_2 ==> 1 x 1
    #####prior_box来源层,可以更改。很多改进都是基于此处的调整。
    mbox_source_layers = ['conv4_3', 'fc7', 'conv6_2', 'conv7_2', 'conv8_2', 'conv9_2']
    # in percent %
    ####这里即是论文中所说的Smin=0.2,Smax=0.9的初始值,经过下面的运算即可得到min_sizes,max_sizes。具体如何计算以及两者代表什么,请关注我的博客SSD详解。这里产生很多改进。
    min_ratio = 20
    max_ratio = 90
    ####math.floor()函数表示:求一个最接近它的整数,它的值小于或等于这个浮点数。
    ####取一个间距步长,即在下面for循环给ratio取值时起一个间距作用。可以用一个具体的数值代替,这里等于17。
    step = int(math.floor((max_ratio - min_ratio) / (
                len(mbox_source_layers) - 2)))
    min_sizes = []  ###经过以下运算得到min_sizes和max_sizes。
    max_sizes = []
    min_sizes = min_sizes + [min_dim * 10 / 100.]
    max_sizes = max_sizes + [min_dim * 20 / 100.]
    ####从min_ratio至max_ratio+1每隔step=17取一个值赋值给ratio。注意range函数的作用。
    for ratio in range(min_ratio, max_ratio + 1, step):
        ########min_sizes.append()函数即把括号内部每次得到的值依次给了min_sizes。
        min_sizes.append(min_dim * ratio / 100.)
        # print("min_size:", min_sizes)
        max_sizes.append(min_dim * (ratio + step) / 100.)
        # print("max_size:", max_sizes)

    return [min_sizes, max_sizes]
###这一步要仔细理解,即计算卷积层产生的prior_box距离原图的比例,
# 先验框中心点的坐标会乘以scale,相当于从feature map位置映射回原图位置,
# 比如conv4_3输出特征图大小为38*38,而输入的图片为300*300,所以38*8约等于300,
# 所以映射步长为8。这是针对300*300的训练图片。
scale = [8, 16, 32, 64, 100,300]
aspect_ratios = [[2], [2, 3], [2, 3], [2, 3], [2], [2]]
# mbox_source_layers = ['conv4_3', 'fc7', 'conv6_2', 'conv7_2', 'conv8_2', 'conv9_2']
def compute_default_box():
    min_sizes, max_sizes = compute_min_max_size()
    conv4_3 = np.zeros(shape=(4, 2))
    fc7 = np.zeros(shape=(6, 2))
    conv6_2 = np.zeros(shape=(6, 2))
    conv7_2 = np.zeros(shape=(6, 2))
    conv8_2 = np.zeros(shape=(4, 2))
    conv9_2 = np.zeros(shape=(4, 2))

    mbox_source_layers = [conv4_3, fc7, conv6_2, conv7_2, conv8_2, conv9_2]
    for i in range(6):
        # 每层feature中默认框的数量
        num = len(aspect_ratios[i]) * 2 + 2
        # for j in range(num):
        # 计算小正方形边长  小边长=min_size
        mbox_source_layers[i][0, :] = min_sizes[i]
        # 计算大正方形边长  大边长=sqrt(min_size*max_size)
        mbox_source_layers[i][1, :] = np.sqrt(min_sizes[i] * max_sizes[i])
        # 计算第一个长方形宽 width=sqrt(aspect_ratio)*min_size
        mbox_source_layers[i][2, 0] = np.sqrt(aspect_ratios[i][0]) * min_sizes[i]
        # 计算第一个长方形高 height=1/sqrt(aspect_ratio)*min_size
        mbox_source_layers[i][2, 1] = 1.0 / np.sqrt(aspect_ratios[i][0]) * min_sizes[i]
        # 计算第二个长方形宽 等于第一个长方形的高
        mbox_source_layers[i][3, 0] = mbox_source_layers[i][2, 1]
        # 计算第二个长方形高 等于第一个长方形的宽
        mbox_source_layers[i][3, 1] = mbox_source_layers[i][2, 0]
        # 判断一哈是否要计算第三和第四个长方形
        if num == 6:
            # 计算第三个长方形的宽
            mbox_source_layers[i][4, 0] = np.sqrt(aspect_ratios[i][1]) * min_sizes[i]
            # 计算第三个长方形高
            mbox_source_layers[i][4, 1] = 1.0 / np.sqrt(aspect_ratios[i][1]) * min_sizes[i]
            # 计算第四个长方形宽 等于第三个长方形的高
            mbox_source_layers[i][5, 0] = mbox_source_layers[i][4, 1]
            # 计算第四个长方形高 等于第三个长方形的宽
            mbox_source_layers[i][5, 1] = mbox_source_layers[i][4, 0]

    return mbox_source_layers


if __name__ == "__main__":
   # 测试min_size, max_size
    # min_sizes, max_sizes = compute_min_max_size()
    # print("min_size: ",min_sizes)
    # print("max_size: ", max_sizes)

    # 测试默认框尺寸
    feature_map = compute_default_box()
    # conv6_2 = feature_map[2]
    for i in range(6):
        num = feature_map[i].shape[0]
        print("第%d个 feature map 小正方形边长:"%(i+1), feature_map[i][0, 0], feature_map[i][0, 1])
        print("第%d个 feature map 大正方形边长:"%(i+1), feature_map[i][1, 0], feature_map[i][1, 1])
        print("第%d个 feature map 第一个长方形宽:"%(i+1), feature_map[i][2, 0], " 高:", feature_map[i][2, 1])
        print("第%d个 feature map 第二个长方形宽:"%(i+1), feature_map[i][3, 0], " 高:", feature_map[i][3, 1])
        if num == 6:
            print("第%d个 feature map 第三个长方形宽:"%(i+1), feature_map[i][4, 0], " 高:", feature_map[i][4, 1])
            print("第%d个 feature map 第四个长方形宽:"%(i+1), feature_map[i][5, 0], " 高:", feature_map[i][5, 1])
        print("-*-"*20)

    # conv7_2 = feature_map[-3]
    # 选择层数索引
    count = 2
    num = feature_map[count].shape[0]
    x_center = 200
    y_center = 200
    plt.figure("default box")
    ax = plt.gca()
    plt.xlim(0, 400)
    plt.ylim(0, 400)
    plt.scatter(x=x_center, y=y_center)
    color = ['red', 'm', 'orange', 'yellowgreen']
    for i in range(num):
        x_min = x_center - feature_map[count][i, 0] / 2.0
        y_min = y_center - feature_map[count][i, 1] / 2.0
        if i == 0 or i == 1:
            rect = patches.Rectangle(xy=(x_min, y_min), width=feature_map[count][i,0], height=feature_map[count][i, 1],
                                 linewidth=3, linestyle='-', edgecolor=color[i], facecolor='none')
            ax.add_patch(rect)
        elif i == 2 or i == 3:
            rect = patches.Rectangle(xy=(x_min, y_min), width=feature_map[count][i, 0], height=feature_map[count][i, 1],
                                     linewidth=3, linestyle='-', edgecolor=color[2], facecolor='none')
            ax.add_patch(rect)
        elif i == 4 or i == 5:
            rect = patches.Rectangle(xy=(x_min, y_min), width=feature_map[count][i, 0], height=feature_map[count][i, 1],
                                     linewidth=3, linestyle='-', edgecolor=color[3], facecolor='none')
            ax.add_patch(rect)
    plt.show()

# [30.0, 60.0, 111.0, 162.0, 213.0, 264.0]
# [60.0, 111.0, 162.0, 213.0, 264.0, 315.0]
           

 6个特征层的默认框尺寸如下:

第1个 feature map 小正方形边长: 30.0 30.0
第1个 feature map 大正方形边长: 42.42640687119285 42.42640687119285
第1个 feature map 第一个长方形宽: 42.42640687119285  高: 21.213203435596423
第1个 feature map 第二个长方形宽: 21.213203435596423  高: 42.42640687119285
-*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-
第2个 feature map 小正方形边长: 60.0 60.0
第2个 feature map 大正方形边长: 81.60882305241266 81.60882305241266
第2个 feature map 第一个长方形宽: 84.8528137423857  高: 42.426406871192846
第2个 feature map 第二个长方形宽: 42.426406871192846  高: 84.8528137423857
第2个 feature map 第三个长方形宽: 103.92304845413263  高: 34.64101615137755
第2个 feature map 第四个长方形宽: 34.64101615137755  高: 103.92304845413263
-*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-
第3个 feature map 小正方形边长: 111.0 111.0
第3个 feature map 大正方形边长: 134.09697983176207 134.09697983176207
第3个 feature map 第一个长方形宽: 156.97770542341357  高: 78.48885271170677
第3个 feature map 第二个长方形宽: 78.48885271170677  高: 156.97770542341357
第3个 feature map 第三个长方形宽: 192.25763964014536  高: 64.08587988004847
第3个 feature map 第四个长方形宽: 64.08587988004847  高: 192.25763964014536
-*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-
第4个 feature map 小正方形边长: 162.0 162.0
第4个 feature map 大正方形边长: 185.75790696495264 185.75790696495264
第4个 feature map 第一个长方形宽: 229.1025971044414  高: 114.55129855222069
第4个 feature map 第二个长方形宽: 114.55129855222069  高: 229.1025971044414
第4个 feature map 第三个长方形宽: 280.59223082615813  高: 93.53074360871939
第4个 feature map 第四个长方形宽: 93.53074360871939  高: 280.59223082615813
-*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-
第5个 feature map 小正方形边长: 213.0 213.0
第5个 feature map 大正方形边长: 237.13287414443406 237.13287414443406
第5个 feature map 第一个长方形宽: 301.2274887854693  高: 150.61374439273462
第5个 feature map 第二个长方形宽: 150.61374439273462  高: 301.2274887854693
-*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-
第6个 feature map 小正方形边长: 264.0 264.0
第6个 feature map 大正方形边长: 288.37475617675 288.37475617675
第6个 feature map 第一个长方形宽: 373.3523804664971  高: 186.67619023324852
第6个 feature map 第二个长方形宽: 186.67619023324852  高: 373.3523804664971
-*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-
           
SSD300默认框尺寸计算1. 计算特征图的min_size与max_size2. 计算特征图的默认框尺寸3. 测试

 最后感谢xunan003博主的论文,通过他的论文让我更加理解如何求解默认框尺寸的方法:

https://blog.csdn.net/xunan003/article/details/79186162

继续阅读