看過SSD的tensorflow實現的小夥伴一定對anchor_box的計算很是好奇, 網上也是五花八門的解釋,今天我結合源碼和原理來解釋一下.
default_params = SSDParams(
img_shape=(300, 300),
num_classes=21,
no_annotation_label=21,
feat_layers=['block4', 'block7', 'block8', 'block9', 'block10', 'block11'],
feat_shapes=[(38, 38), (19, 19), (10, 10), (5, 5), (3, 3), (1, 1)],
anchor_size_bounds=[0.15, 0.90],
# anchor_size_bounds=[0.20, 0.90],
anchor_sizes=[(21., 45.),
(45., 99.),
(99., 153.),
(153., 207.),
(207., 261.),
(261., 315.)],
# anchor_sizes=[(30., 60.),
# (60., 111.),
# (111., 162.),
# (162., 213.),
# (213., 264.),
# (264., 315.)],
anchor_ratios=[[2, .5],
[2, .5, 3, 1./3],
[2, .5, 3, 1./3],
[2, .5, 3, 1./3],
[2, .5],
[2, .5]],
anchor_steps=[8, 16, 32, 64, 100, 300],
anchor_offset=0.5,
normalizations=[20, -1, -1, -1, -1, -1],
prior_scaling=[0.1, 0.1, 0.2, 0.2]
)
這個anchor_box的計算與anchor_sizes和anchor_ratios有關.
先解釋一下anchor_box的由來. 我們假設當前層是block4, 那麼我們的feat_shape就是(38, 38),
你可以將其理解爲38*38個cell單元, 每個單元要預測出來幾個anchor_box, 不同的層, 這個數理論上講都是6個, (paper上就是這麼寫的). 每個box的長寬比是有ratios決定的, paper上是兩個 1 , 以及4個其他的值. 但是作者的源碼卻很調皮的改了一下.
看一下我們的長寬比,這裏面沒有那兩個1, 因爲他們是單獨設定的:
anchor_ratios=[[2, .5],
[2, .5, 3, 1./3],
[2, .5, 3, 1./3],
[2, .5, 3, 1./3],
[2, .5],
[2, .5]],
我們看到第一層還有最後兩層, 只有兩種長寬比, 所以這三層就只有4種box.
這個圖可以說介紹的相當準確定了, 網上有錯的, 以這個爲準 . 以block4層爲例, 他每個cell單元只有4個box, 包括一大一小兩個正方形, 以及兩個長方形.
對於小正方形,其邊長爲min_size, 這個min_size是什麼呢?就是前面參數中的anchor_sizes:
anchor_sizes=[(30., 60.),
(60., 111.),
(111., 162.),
(162., 213.),
(213., 264.),
(264., 315.)],
其中每一行爲一組, 每一行對應相應的特徵層, 第一行對應的就是block4, 依次類推, 對於(30, 60), 30就是min_size, 60就是max_size.
大正方形其邊長爲
然後是另外兩個長方形,這個時候anchor_ratios就出馬了,長方形的長寬是有公式可尋的.
這個ratio就是anchor_ratios裏的2, 5, 3, 1/3 之類的.
這樣,原理上box的大小就介紹完了.但是別忘了這個尺寸是在原圖上的尺寸, 我們需要這個尺寸相對於原圖的比例.
看下源碼的這一部分:
# Add first anchor boxes with ratio=1.
# sizes就是(30, 60), img_shape就是原圖尺寸(300, 300)
h[0] = sizes[0] / img_shape[0]
w[0] = sizes[0] / img_shape[1]
di = 1
if len(sizes) > 1:
h[1] = math.sqrt(sizes[0] * sizes[1]) / img_shape[0]
w[1] = math.sqrt(sizes[0] * sizes[1]) / img_shape[1]
di += 1
# r 就是[2, 5, 3, 1/3]
for i, r in enumerate(ratios):
h[i+di] = sizes[0] / img_shape[0] / math.sqrt(r)
w[i+di] = sizes[0] / img_shape[1] * math.sqrt(r)
最後, 既然我們高明白了anchor_size, 這個參數直接決定了當前特徵層的box 的大小, 可以看出越靠近輸入層, box越小, 越靠近輸出層, box越大, 所以 SSD的底層用於檢測小目標, 高層用於檢測大目標.