目標檢測之後處理(檢測框合併)--非極大值抑制

 

一、邊界框的置信度(confidence score)

      置信度是每個bounding box輸出的其中一個重要參數, 所謂置信度其實包含兩個方面:一是這個邊界框含有目標的可能性大小,也就是當前box是否有對象的概率,(注意,是對象,不是某個類別的對象,它用來說明當前box內只是個背景(backgroud)還是有某個物體(對象));二是這個邊界框的準確度。

       前者記爲Pr(object),當該邊界框是背景時(即不包含目標),此時Pr(object)=0;而當該邊界框包含目標時,Pr(object)=1。邊界框的準確度可以用預測框與實際框(ground truth)的IOU(intersection over union,交併比)來表徵,記爲。因此置信度可以定義爲。其中,表示第i個grid cell的第j個bounding box的置信度。對於如何訓練的方法,在損失函數小節中說明。

       很多人可能將Yolo的置信度看成邊界框是否含有目標的概率,但是其實它是兩個因子的乘積,預測框的準確度也反映在裏面。邊界框的大小與位置可以用4個值來表徵:(x,y,w,h),其中(x,y)是邊界框的中心座標,而w和h是邊界框的寬與高。還有一點要注意,中心座標的預測值(x,y)是相對於每個單元格左上角座標點的偏移值,並且單位是相對於單元格大小的,單元格的座標定義如圖6所示。而邊界框的ww和hh預測值是相對於整個圖片的寬與高的比例,這樣理論上4個元素的大小應該在[0,1]範圍。這樣,每個邊界框的預測值實際上包含5個元素:(x,y,w,h,c),其中前4個表徵邊界框的大小與位置,而最後一個值是置信度。

二、NMS

       在物體檢測中,離不了的一個應用就是非極大值抑制(NMS),它主要目的是爲了解決一個目標被多次檢測的問題,也就是爲了消除多餘交叉重複的檢測框,找到最佳的物體檢測的位置。示意圖如下:我們需要將左邊圖中找到右圖中最理想的一個框來標註人臉的位置。本文從原理和代碼兩個方面講解非極大值抑制。

      就像上面的圖片一樣,定位一個人臉,最後算法就找出了一堆的方框,也就是一個人臉被多次檢測,我們需要判別哪些矩形框是沒用的,其實我們希望最後僅僅輸出其中一個最好的預測框,比如對於美女,只想要紅色那個檢測結果。做法就是首先選擇置信度最高的那個0.98的紅框與其他兩個綠框做IOU,若IOU大於設定的閾值,就剔除綠框,最後只剩下紅色的框框。

舉個栗子來理解原理:

非極大值抑制:先假設有6個候選框,根據分類器類別分類概率做排序,從小到大分別屬於車輛的概率分別爲A、B、C、D、E、F。

1、從最大概率矩形框F開始,分別判斷A~E與F的重疊度IOU是否大於某個設定的閾值;

2、假設B、D與F的重疊度超過閾值,那麼就扔掉B、D;並標記第一個矩形框F,是我們保留下來的。

3、從剩下的矩形框A、C、E中,選擇概率最大的E,然後判斷E與A、C的重疊度,重疊度大於一定的閾值,那麼就扔掉;並標記E是我們保留下來的第二個矩形框。

4、一直重複這個過程,找到所有曾經被保留下來的矩形框。

       非極大值抑制(NMS)非極大值抑制顧名思義就是抑制不是極大值的元素,搜索局部的極大值。例如在對象檢測中,滑動窗口經提取特徵,經分類器分類識別後,每個窗口都會得到一個分類及分數。但是滑動窗口會導致很多窗口與其他窗口存在包含或者大部分交叉的情況。這時就需要用到NMS來選取那些鄰域裏分數最高(是某類對象的概率最大),並且抑制那些分數低的窗口。

代碼部分:

#coding=utf-8
# athor: AnnSun
# date: 2020.04.28

import numpy as np

def nms(dets, thresh):
    # 所有box的座標信息。注意這裏是數組而不是列表
    x1 = dets[:, 0]
    y1 = dets[:, 1]
    x2 = dets[:, 2]
    y2 = dets[:, 3]
    scores = dets[:, 4]

    # 計算出所有box的面積;圖片評分(置信度)按降序排序
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)
    order = scores.argsort()[::-1]  # 注意這裏orders是邊界框的索引

    # 保留最後需要保留的邊框的索引
    keep = []
    while order.size > 0:
        # order[0]是目前置信度最大的,肯定保留; i是還未處理的圖片中的最大評分索引
        i = order[0]
        # 保留改圖片的值
        keep.append(i)

        # 計算窗口i與其他窗口的交疊的面積
        xx1 = np.maximum(x1[i], x1[order[1:]])
        yy1 = np.maximum(y1[i], y1[order[1:]])
        xx2 = np.minimum(x2[i], x2[order[1:]])
        yy2 = np.minimum(y2[i], y2[order[1:]])

        # 計算相交框的面積,不相交時用0代替
        w = np.maximum(0.0, xx2 - xx1 + 1)
        h = np.maximum(0.0, yy2 - yy1 + 1)
        inter = w * h

        # 計算IOU:相交的面積/相併的面積
        ovr = inter / (areas[i] + areas[order[1:]] - inter)

        # 只保留比例小於闕值的box,然後繼續處理,因爲這可能是另外一個目標
        inds = np.where(ovr < thresh)[0]
        order = order[inds + 1]

    return keep

# test
def test():
    dets = np.array([[30, 20, 230, 200, 0.6],
                     [50, 50, 260, 220, 0.9],
                     [210, 30, 420, 5, 0.8],
                     [430, 280, 460, 360, 0.7]])
    thresh = 0.35
    keep_dets = nms(dets, thresh)
    print(keep_dets)
    print(dets[keep_dets])


if __name__ == "__main__":
    test()

那麼NMS存在什麼問題呢,其中最主要的問題有這麼幾個:

  • 稠密場景下漏檢多:最大問題就是它將相鄰檢測框的分數均強制歸零(即將重疊部分大於重疊閾值Nt的檢測框移除)。在這種情況下,如果一個真實物體在重疊區域出現,則將導致對該物體的檢測失敗並降低了算法的平均檢測率。(由於和最高置信度的框overlap過大),也就是出現了漏檢。
  • NMS的閾值也不太容易確定,設置過小會出現誤刪,設置過高又容易增大誤檢。
  • 存在一些,所有的bbox都預測不準,對於下面第左圖我們看到,不是所有的框都那麼精準,有時甚至會出現某個物體周圍的所有框都標出來了,但是都不準的情況

  

  • 傳統的NMS方法默認置信度分數較高的框,定位更精確,是基於分類分數的,只有最高分數的預測框能留下來,但是大多數情況下IoU和分類分數不是強相關,很多分類標籤置信度高的框都位置都不是很準,置信度分數高的邊界框並不總是比置信度低的框更可靠。(即分類和迴歸任務沒有直接相關性,因此這個條件並不總是成立)。
  • Ground Truth(標註框)的標註可能並不可靠。很多人爲因素。
  • NMS一般只能使用CPU計算,無法使用GPU計算。

 

假設這是一個函數,那麼這個函數輸入輸出是什麼,中間操作又是怎麼做的
 

未完待續

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章