天天看點

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

繼續閱讀