nms、softnms、softernms

softnms:https://arxiv.org/abs/1704.04503 代碼:http://bit.ly/2nJLNMu
softernms:https://arxiv.org/abs/1809.08545

1 NMS

目標檢測的pipeline中,通過神經網絡的處理,輸出了一系列的預測框,爲了保證檢測的召回率,這些預測框一般都相互重疊(多個檢測框對應同一個目標)。爲了提升檢測效果,一般會使用置信度過濾+NMS進行檢測結果的後處理。

置信度過濾即人爲設定置信度閾值,只保留超過閾值的檢測框。

NMS用於消除同一目標上的多個重複框,一般是針對各類目標單獨應用NMS,NMS的實現思路爲:

  1. 設定兩個數據集,B表示所有的預測框,D表示保留的檢測框集合;
  2. 將置信度超過閾值的預測框按照置信度從高到底排序;
  3. 選取當前B中置信度最高的檢測框M爲基準,計算B中其餘檢測框和M之間的IOU,將B中和M的IOU大於閾值的檢測框從B中移除,將M放入D中;
  4. 重複步驟3,直到B爲空時停止迭代。最終,D中的檢測框即爲最終的檢測結果。

NMS的計算複雜度爲O(n2)O(n^2),n爲起始階段B中檢測框的數量。

實現代碼部分,摘抄自:https://blog.csdn.net/weixin_41665360/article/details/99818073

import numpy as np

def nms(dets, Nt):

    x1 = dets[:,0]
    y1 = dets[:,1]
    x2 = dets[:,2]
    y2 = dets[:,3]
    scores = dets[:,4]

    order = scores.argsort()[::-1]
    #計算面積
    areas = (x2 - x1 + 1)*(y2 - y1 + 1)

    #保留最後需要保留的邊框的索引
    keep = []
    while order.size > 0:
        # order[0]是目前置信度最大的,肯定保留
        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)
        
        #inds爲保留的檢測框的索引
        inds = np.where(ovr < thresh)[0]

		#+1是爲了跳過當前保留的檢測框(索引0),因爲計算重疊面積時的索引起點爲1,x1[order[1:]]
        order = order[inds + 1]

    return keep

# test
if __name__ == "__main__":
    dets = np.array([[30, 20, 230, 200, 1],
                     [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])

NMS簡單有效,是目標檢測的標準後處理過程。但在更高的目標檢測需求下,也存在四方面的缺陷:

  1. 對於密集目標的場景,本身屬於兩個目標的檢測框可能IOU很高,如果直接將置信度較大的那個檢測框刪除掉,有可能造成漏檢,降低了模型的召回率。softnms就是基於解決這個問題進行設計的;
  2. 閾值難以確定,nms的閾值是一個人爲設置的參數,設置的過高容易造成誤檢,設置的過低容易造成漏檢,這個參數很難確定;
  3. nms將置信度得分作爲預測準確度的衡量指標,但在一些情況下,置信度得分高的預測框不一定更加準確,softernms是爲了解決這個問題進行設計的;
  4. nms的處理包含了較多的循環步驟,GPU並行化的實現難度較大,在預測框數量很多時,nms的處理耗時較多。

2 SoftNMS

softnms適合於解決密集檢測場景下因nms過程中直接刪除高度重疊目標而造成的目標漏檢的問題。如下圖所示場景:
在這裏插入圖片描述兩匹馬的檢測框高度重疊,使用NMS時,在IOU閾值較低時,置信度更低的那匹馬身上的檢測框會被直接刪除,造成漏檢。

下面的式子中,M表示當前置信度最高的檢測框,bib_i表示某個候選檢測框,NtN_t表示nms的閾值。
nms的計算公式可以描述爲:
在這裏插入圖片描述

因此,作者提出的解決方案是,在nms過程中,對於和高置信度目標高IOU重疊的檢測目標,不是直接刪除而是降低其置信度得分,這樣做使得這些目標在後面有機會被當作正確的檢測框得以保留,這樣就避免了目標的誤檢。

在設計目標得分降低策略時,一個指導原則是,一個檢測框和高置信度檢測框的IOU越大,其置信度的下降幅值應該越大。

所以作者首先提出了線性的置信度降低策略,即:
在這裏插入圖片描述
這樣的策略的確達到了預想目的,但缺點是在NtN_t處函數不連續,會造成檢測框的置信度跳變,對檢測結果產生了較大的波動。

因此,作者又提出了連續版本的高斯置信度降低策略,即:
在這裏插入圖片描述
高斯版本的置信度下降策略,即爲作者提出的soft nms。

softnms的計算複雜度和nms相同,都爲O(n2)O(n^2),可以看作是nms的泛化,而nms也可以稱作hard nms,是soft nms的二值化特例。

在這裏插入圖片描述代碼摘抄自:https://blog.csdn.net/weixin_41665360/article/details/99818073

def py_cpu_softnms(dets, Nt=0.3, sigma=0.5, thresh=0.5, method=2):
    """
    py_cpu_softnms
    :param dets:   boexs 座標矩陣 format [x1, y1, x2, y2, score]
    :param Nt:     iou 交疊閾值
    :param sigma:  使用 gaussian 函數的方差
    :param thresh: 最後的分數閾值
    :param method: 使用的方法,1:線性懲罰;2:高斯懲罰;3:原始 NMS
    :return:       留下的 boxes 的 index
    """

    N = dets.shape[0]
    # the order of boxes coordinate is [x1,y1,x2,y2]
    x1 = dets[:, 0]
    y1 = dets[:, 1]
    x2 = dets[:, 2]
    y2 = dets[:, 3]
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)

    for i in range(N):
        # intermediate parameters for later parameters exchange
        tB = dets[i, :4]
        ts = dets[i, 4]
        ta = areas[i]
        pos = i + 1

        if i != N-1:
            maxscore = np.max(dets[:, 4][pos:])
            maxpos = np.argmax(dets[:, 4][pos:])
        else:
            maxscore = dets[:, 4][-1]
            maxpos = -1

        if ts < maxscore:
            dets[i, :] = dets[maxpos + i + 1, :]
            dets[maxpos + i + 1, :4] = tB

            dets[:, 4][i] = dets[:, 4][maxpos + i + 1]
            dets[:, 4][maxpos + i + 1] = ts

            areas[i] = areas[maxpos + i + 1]
            areas[maxpos + i + 1] = ta

        # IoU calculate
        xx1 = np.maximum(dets[i, 0], dets[pos:, 0])
        yy1 = np.maximum(dets[i, 1], dets[pos:, 1])
        xx2 = np.minimum(dets[i, 2], dets[pos:, 2])
        yy2 = np.minimum(dets[i, 3], dets[pos:, 3])

        w = np.maximum(0.0, xx2 - xx1 + 1)
        h = np.maximum(0.0, yy2 - yy1 + 1)
        inter = w * h
        ovr = inter / (areas[i] + areas[pos:] - inter)
        
        # Three methods: 1.linear 2.gaussian 3.original NMS
        if method == 1:  # linear
            weight = np.ones(ovr.shape)
            weight[ovr > Nt] = weight[ovr > Nt] - ovr[ovr > Nt]
        elif method == 2:  # gaussian
            weight = np.exp(-(ovr * ovr) / sigma)
        else:  # original NMS
            weight = np.ones(ovr.shape)
            weight[ovr > Nt] = 0

        dets[:, 4][pos:] = weight * dets[:, 4][pos:]

    # select the boxes and keep the corresponding indexes
    inds = np.argwhere(dets[:, 4] > thresh)
    keep = inds.astype(int).T[0]

    return keep

softnms適合於減少密集檢測場景下因爲nms造成的漏檢,但也會增加在同一個目標上有多個檢測框時的誤檢,如下圖所示:
在這裏插入圖片描述
在這裏插入圖片描述左側是nms的結果,右側是softnms的結果。到底要不要用softnms代替nms,要根據自己的檢測場景和使用nms進行過濾的效果來決定,如果是密集檢測場景,nms過濾的結果存在很多的被遮擋目標的漏檢,那麼使用softnms可以改善檢測效果。如果是非密集的檢測場景,nms的檢測效果是以誤檢爲主,那麼換成softnms之後的效果可能不會有提升。但終究softnms提供了一個新的思路,並且在某些特定情況下可以改善效果,其思想值得學習。

3 SofterNMS

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