1:SSD更具體的框架如下:

2: Prior Box
縮進在SSD中引入了Prior Box,實際上與anchor非常類似,就是一些目標的預選框,后續通過softmax分類+bounding box regression獲得真實目標的位置。SSD按照如下規則生成prior box:
- 以feature map上每個點的中點為中心(offset=0.5),生成一些列同心的prior box(然后中心點的坐標會乘以step,相當於從feature map位置映射回原圖位置)
- 正方形prior box最小邊長為
,最大邊長為:
- 每在prototxt設置一個aspect ratio,會生成2個長方形,長寬為:
和
圖4 prior box
- 而每個feature map對應prior box的min_size和max_size由以下公式決定,公式中m是使用feature map的數量(SSD 300中m=6):
第一層feature map對應的min_size=S1,max_size=S2;第二層min_size=S2,max_size=S3;其他類推。在原文中,Smin=0.2,Smax=0.9,但是在SSD 300中prior box設置並不能和paper中上述公式對應:
min_size | max_size | |
---|---|---|
conv4_3 |
30
|
60
|
fc7 |
60
|
111
|
conv6_2 |
111
|
162
|
conv7_2 |
162
|
213
|
conv8_2 |
213
|
264
|
conv9_2 |
264
|
315
|
不過依然可以看出,SSD使用低層feature map檢測小目標,使用高層feature map檢測大目標,這也應該是SSD的突出貢獻了。其中SSD 300在conv4_3生成prior box的conv4_3_norm_priorbox層prototxt定義如下:
- layer {
- name: "conv4_3_norm_mbox_priorbox"
- type: "PriorBox"
- bottom: "conv4_3_norm"
- bottom: "data"
- top: "conv4_3_norm_mbox_priorbox"
- prior_box_param {
- min_size: 30.0
- max_size: 60.0
- aspect_ratio: 2
- flip: true
- clip: false
- variance: 0.1
- variance: 0.1
- variance: 0.2
- variance: 0.2
- step: 8
- offset: 0.5
- }
- }
知道了priorbox如何產生,接下來分析prior box如何使用。這里以conv4_3為例進行分析。
圖5
從圖5可以看到,在conv4_3 feature map網絡pipeline分為了3條線路:
- 經過一次batch norm+一次卷積后,生成了[1, num_class*num_priorbox, layer_height, layer_width]大小的feature用於softmax分類目標和非目標(其中num_class是目標類別,SSD 300中num_class = 21)
- 經過一次batch norm+一次卷積后,生成了[1, 4*num_priorbox, layer_height, layer_width]大小的feature用於bounding box regression(即每個點一組[dxmin,dymin,dxmax,dymax],參考Faster RCNN 2.5節)
- 生成了[1, 2, 4*num_priorbox]大小的prior box blob,其中2個channel分別存儲prior box的4個點坐標和對應的4個variance
縮進后續通過softmax分類+bounding box regression即可從priox box中預測到目標,熟悉Faster RCNN的讀者應該對上述過程應該並不陌生。其實pribox box的與Faster RCNN中的anchor非常類似,都是目標的預設框,沒有本質的差異。區別是每個位置的prior box一般是4~6個,少於Faster RCNN默認的9個anchor;同時prior box是設置在不同尺度的feature maps上的,而且大小不同。
縮進還有一個細節就是上面prototxt中的4個variance,這實際上是一種bounding regression中的權重。在圖4線路(2)中,網絡輸出[dxmin,dymin,dxmax,dymax],即對應下面代碼中bbox;然后利用如下方法進行針對prior box的位置回歸:
- decode_bbox->set_xmin(
- prior_bbox.xmin() + prior_variance[0] * bbox.xmin() * prior_width);
- decode_bbox->set_ymin(
- prior_bbox.ymin() + prior_variance[1] * bbox.ymin() * prior_height);
- decode_bbox->set_xmax(
- prior_bbox.xmax() + prior_variance[2] * bbox.xmax() * prior_width);
- decode_bbox->set_ymax(
- prior_bbox.ymax() + prior_variance[3] * bbox.ymax() * prior_height);
上述代碼可以在SSD box_utils.cpp的void DecodeBBox()函數見到