目錄
1. 計算特征圖的min_size與max_size
2. 計算特征圖的預設框尺寸
3. 測試
1. 計算特征圖的min_size與max_size
先将SSD300網絡模型附上,如圖:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL3VFVNJTRU10MNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwYjN2MzMxcTM3IDNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
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)
# 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
-*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-
最後感謝xunan003部落客的論文,通過他的論文讓我更加了解如何求解預設框尺寸的方法:
https://blog.csdn.net/xunan003/article/details/79186162