目錄
1. 計算特徵圖的min_size與max_size
先將SSD300網絡模型附上,如圖:
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。
至於爲什麼將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)
# [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
-*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-
最後感謝xunan003博主的論文,通過他的論文讓我更加理解如何求解默認框尺寸的方法:
https://blog.csdn.net/xunan003/article/details/79186162