【目标检测】(13) 先验框解码,调整预测框,附TensorFlow完整代码
各位同學好,今天和大家分享一下如何使用 TensorFlow 對 YOLOV3 和 YOLOV4 網絡的輸出特征進行解碼,微調每個先驗框的坐標和寬高,使其逼近真實標簽框。
YOLOV3 和 YOLOV4 調整先驗框的方法類似,代碼通用。閱讀本篇文章之前,建議先看以下文章
YOLOV2中的先驗框:https://blog.csdn.net/dgvv4/article/details/123772756
YOLOV3特征提取網絡:https://blog.csdn.net/dgvv4/article/details/121997986
YOLOV4特征提取網絡:https://blog.csdn.net/dgvv4/article/details/123818580
1. 生成先驗框
網絡的三個有效輸出特征層分別輸出 52*52,26*26,13*13 的特征圖。13*13的輸出特征負責預測大尺度物體,26*26負責預測中等大小的物體,52*52負責預測小尺度物體。YOLOV3 有9個先驗框,最大的三個先驗框分配給了13*13的特征圖,中等大小的三個先驗框分配給了26*26的特征圖,最小的三個先驗框分配給了52*52的特征圖。
如下圖,以13*13的特征圖為例。物體的中心點落在了紅色網格中,那么就需要由這個紅色網格生成的三個先驗框中的一個去預測這個物體,由三個先驗框中與物體真實框的 iou 最大的先驗框去預測這個物體。
然而網絡輸出三種尺度 13*13、26*26、52*52 的特征圖,物體的中心點肯定會落在三種尺度的網格中,那么該物體是由哪一種尺度的網格生成的先驗框去預測的呢。是由9個先驗框中,與真實標簽框 iou 最大的那個先驗框所在尺度的網格來預測。
2. 模型輸出結果
網絡的輸入特征圖的shape是 [416, 416, 3],經過一系列卷積層,輸出三個尺度的特征結果,它們的shape分別是 [52, 52, (3*(5+num_classes))],[26, 26, (3*(5+num_classes))],[13, 13, (3*(5+num_classes))],。
其中通道數?3*(5+num_classes) 可以理解為:每個網格生成 3 個預測框,每個預測框包含了預測框相較于先驗框的偏移量,坐標偏移量(tx, ty),寬高偏移量(tw, th),預測框中是否包含目標物體 c,預測框內的物體屬于每個類別的條件概率num_classes,在VOC數據集中num_classes=20。其中 (tx, ty) 是負無窮到正無窮的任何數,(tw, th) 是歸一化后的寬高。
3. 微調先驗框
以某個網格的先驗框的調整為例,如下圖所示,虛線框代表:和物體的真實標簽框 iou 值最大的那個先驗框,該先驗框的寬高為(pw, ph);藍色框代表微調先驗框后生成的預測框。
(cx,cy)是先驗框中心點所在的網格的左上坐標(歸一化后的坐標),由于坐標偏移量?(tx,ty) 可以是從負無窮到正無窮的任何數,為了防止坐標調整偏移過大,給偏移量添加sigmoid函數。將坐標偏移量限制在0-1之間,將預測框的中心點限制在它所在的網格內。高寬偏移量 (tw, th) 是歸一化后的寬高調整值。最終預測框的寬高 (bw, bh)?
4. 代碼展示
從網絡的輸出特征中提取出預測框的中心點坐標,預測框的寬高,預測框的置信度,框內物體屬于每個類別的條件概率。
import tensorflow as tf#(一)解碼網絡的輸出層的信息 def anchors_decode(feats, anchors, num_classes):'''feats是某一個特征層的輸出結果, 如shape=[b, 13, 13, 3*(5+num_classes)]anchors代表每個特征層, 每個網格的三個先驗框[3,2]num_classes代表分類類別的數量'''# 計算每個網格幾個先驗框=3num_anchors = len(anchors)# 獲得圖像網格的寬和高的shape=[h,w]=[13,13]grid_shape = feats.shape[1:3]#(1)獲得網格中每個網格點的坐標信息# 獲得網格點的x坐標信息[1]==>[1,13,1,1]grid_x = tf.reshape(range(0, grid_shape[1]), shape=[1,-1,1,1])# 在y維度上擴張,將前面的數據進行復制然后直接接在原數據后面# [1,13,1,1]==>[13,13,3,1]grid_x = tf.tile(grid_x, [grid_shape[0], 1, num_anchors, 1])# 獲得網格點的y坐標信息,方法同上[13]==>[13,1,1,1]grid_y = tf.reshape(range(0, grid_shape[0]), shape=[-1,1,1,1])# 維度擴張[13,1,1,1]==>[13,13,3,1]grid_y = tf.tile(grid_y, [1, grid_shape[1], num_anchors, 1])# 在通道維度上合并[13,13,3,2],每個網格的坐標信息, 橫縱坐標都是0-12,grid = tf.concat([grid_x, grid_y], axis=-1)# 轉換成tf.float32類型grid = tf.cast(grid, tf.float32)#(2)調整先驗框的信息,13*13個網格,每個網格有3個先驗框,每個先驗框有(x,y)坐標# [3,2]==>[1,1,3,2]anchors_tensor = tf.reshape(anchors, shape=[1,1,num_anchors,2])# [1,1,3,2]==>[13,13,3,2]anchors_tensor = tf.tile(anchors_tensor, [grid_shape[0], grid_shape[1], 1, 1])# 轉換成float32類型anchors_tensor = tf.cast(anchors_tensor, tf.float32)#(3)調整網絡輸出特征圖的結果# [b, 13, 13, 3*(5+num_classes)] = [b, 13, 13, 3, (5+num_classes)]'''代表13*13個網格, 每個網格有3個先驗框, 每個先驗框有(5+num_classes)項信息其中, 5代表: 中心點坐標(x,y), 寬高(w,h), 置信度cnum_classes: 檢測框屬于某個類別的條件概率, VOC數據集中等于20'''feats = tf.reshape(feats, shape=[-1, grid_shape[0], grid_shape[1], num_anchors, 5+num_classes])#(4)調整先驗框中心坐標及寬高# 對預測框中心點坐標歸一化處理,只能在所處的網格中調整anchor_xy = tf.nn.sigmoid(feats[..., :2])box_xy = anchor_xy + grid # 每個網格的預測框坐標# 網格的預測框寬高默認是歸一化之后的,對寬高取指數anchors_wh = tf.exp(feats[..., 2:4])box_wh = anchors_wh * anchors_tensor # 預測框的寬高# 獲得預測框的置信度和每個類別的條件概率box_conf = tf.nn.sigmoid(feats[..., 4:5])box_prob = tf.nn.sigmoid(feats[..., 5:])# 返回預測框信息return box_xy, box_wh, box_conf, box_prob隨機生成一個13*13特征層的輸出特征圖,并給每個網格設置三種長寬比的先驗框,來驗證三個先驗框微調的效果。
#(二)驗證 if __name__ == '__main__':feat = tf.random.normal([4,13,13,75], mean=0, stddev=0.5) # 構建輸出特征圖anchors = tf.constant([[142, 110],[192, 243],[459, 401]]) # 每個網格的先驗框坐標# 返回調整后的預測框信息box_xy, box_wh, box_conf, box_prob = anchors_decode(feat, anchors, 20)'''某個網格調整后的預測框寬高[[ 80.93765 , 106.666855],[ 119.06944 , 248.29587 ],[ 413.01917 , 339.91293 ]]'''以某一個網格為例,將結果可視化出來。左圖是最初的先驗框,三個先驗框的中心點在一起,右圖是調整后的預測框,紅色點是每個調整后的預測框的中心點。
總結
以上是生活随笔為你收集整理的【目标检测】(13) 先验框解码,调整预测框,附TensorFlow完整代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于Pytorch全连接神经网络实现多分
- 下一篇: PyTorch实现 | 车牌OCR识别,