手撕NMS,numpy實現目標檢測的常用模組NMS

2021-09-25 15:15:18 字數 4592 閱讀 6826

然後關鍵的部分來了,我們對iou進行篩選,若iou大於我們所設定的閾值,則我們認為,此框與基準框檢測的為同一目標,為重複檢測,我們就將其抑制,若iou低於我們設定的閾值,我們就進行下一步的迴圈,取出經過第一輪所篩選後的框,仍然取出剩下的框的分數最高的box,作為新的基準框,對剩下的框再次計算iou,仍然遵循上述的iou大於則抑制,小於等於則進入下一輪迴圈,直至框的數量為0.

這樣說可能仍然有些難以理解。

我們舉個例子會更好理解。假設我們**人臉出現了七個框:

分數分別為[0.9,0.8,0.7,0.6,0.5,0.4,0.3]

首先我們取出分數為0.9的框,作為基準框,剩餘的六個框依次與其進行iou計算

計算的iou假設為[0.7,0.8,0.6,0.4,0.6,0.2]

假設我們設定的iou閾值thresh為0.5,

則上述與基準框iou大於0.5的框均會抑制,即只剩下iou為0.4和0.2的框,也就是對應的分數為0.5和0.3的框。

此時我們進行新一輪的迴圈,我們將分數最高的0.5取出,將此框為基準框,計算剩下的box與其iou,若iou大於0.5則抑制,iou小於等於0.5則保留,假設剩下的分數為0.3的框與基準框iou為0.3,則保留,進入下一輪的迴圈。

如此多次迴圈後,每次的基準框都會被保留,直到所有的box數量為0。

好了,原理已經清楚,剩下的就是準備使用numpy手撕nms函式了。

我們首先從**的結果中獲得box的資訊:

以dets來接收box的資訊,

從dets中讀取box的資訊:

#dets為送入nms的框的資訊的集合,它是[x1,x2,y1,y2,score]的集合

#依次讀取x1,x2,y1,y2注意此時的他們都不是單獨的數,而是乙個用於存放所有的box的資訊集合

x1 = dets[:, 0]

y1 = dets[:, 1]

x2 = dets[:, 2]

y2 = dets[:, 3]

scores = dets[:, 4]

讀取了座標資訊和分數資訊後,我們求取其面積,為後面的iou計算做準備:

areas = (x2 - x1)*(y2 - y1)

但是實際中,我們為了保證區域面積不為零所以常用下面的方式:

areas = (x2 - x1 + 1) * (y2 - y1 + 1)

接下來我們要對分數進行排序,因為我們始終需要進行一項取最高的分數的操作,所以我們一開始就對分數進行由高到低的排序,這樣後面就無需重複操作。

order = scores.argsort()[::-1]

x = np.array([3, 1, 2])

np.argsort(x)

array([1, 2, 0])

可以看出numpy中的argsort函式可以對乙個array進行由低到高的操作,並返回其索引值。上面的返回值1,2,0代表著由低到高分別是第0號元素,第2號元素,第3號元素。

我們需要的是由高到低,所以進行[::-1]倒順序操作。

得到的order就是分數由高到低的box的索引值。

然後我們建立乙個空的list來存放我們的nms結果(即每輪迴圈的基準box)

keep =

最關鍵的迴圈步驟:

我們首先判斷box數量是否為零,由於我們擁有了order,order的size就是box的多少,所以我們先判斷:

while order.size > 0:

判斷大於零,我們進行操作,依照上面的,由於order已經由高到低,所以我們讀取order[0],作為基準框,並將基準框,裝入我們的結果中:

xx1 = np.maximum(x1[i], x1[order[1:]]) #與基準框相比大的xmin

yy1 = np.maximum(y1[i], y1[order[1:]]) #與基準框相比大的ymin

xx2 = np.minimum(x2[i], x2[order[1:]]) #與基準框相比小的xmax

yy2 = np.minimum(y2[i], y2[order[1:]]) #與基準框相比小的ymax

這裡我們使用了numpy的maximum和minimum操作,我們來看看,這兩個函式是如何工作的,

np.maximum([-2, -1, 0, 1, 2], 0)

array([0, 0, 0, 1, 2])

先找到滿足條件的值,然後返回大的那個,所以此時就是先比較基準框與其他框的四個點的大小,得到的即是重合的四個點四個座標,每個框都會與基準框有乙個重疊的區域我們把它定義為inter,inter的寬和高為:

w = np.maximum(0.0, xx2 - xx1 + 1)  

h = np.maximum(0.0, yy2 - yy1 + 1)

同樣的為了保證inter 面積不會小於零,所以我們將其寬高與0進行比較。

inter = w * h
計算各個框與基準框重疊的面積,然後計算iou:

ovr = inter / (areas[i] + areas[order[1:]] - inter)  #計算iou
我們定義ovr為iou,iou的定義為兩個框的重疊面積除以總的union,

下一步,在與基準框iou計算的所有框中我們進行篩選,大於閾值的抑制,保留小於閾值的,注意此時的box的數量時比所有的box少一的,因為我們取出了基準框:

inds = np.where(ovr <= thresh)[0]
a = np.array([2,4,6,8,10])

np.where(a > 5) # 返回索引

(array([2, 3, 4]),)

a[np.where(a > 5)] # 等價於 a[a>5]

array([ 6, 8, 10]).

inds為我們儲存的與基準框iou小於閾值的框的索引值,此索引值是0到n-1中取的,n為總共所有的box,由於已經取出了order[0],所以減去1。

然後我們更新box,即是更新order,從inds來更新,由於一開始我們取出了order[0],所以我們在使用inds來更新order時需要加1操作。

order = order[inds + 1]
import numpy as np

def py_cpu_nms(dets, thresh):

"""pure python nms baseline."""

#dets為送入nms的框的資訊的集合,它是[x1,y1,x2,y2,score]的集合

x1 = dets[:, 0] #依次讀取x1,x2,y1,y2注意此時的他們都不是單獨的數,而是乙個用於存放所有的box的資訊集合

y1 = dets[:, 1]

x2 = dets[:, 2]

y2 = dets[:, 3]

scores = dets[:, 4]

areas = (x2 - x1 + 1) * (y2 - y1 + 1) #計算各個box的面積,為了避免出現為0,所以使用了加1

order = scores.argsort()[::-1] #將scores按照由高到低的方式進行排序,並返回由高到低的索引值給order,order也是乙個list

keep = #用於存放最後的結果

while order.size > 0: #迴圈,直至order中沒有元素

i = order[0] #讀取分數最大的box的索引並保留

xx1 = np.maximum(x1[i], x1[order[1:]]) #將剩餘的box與分數最大的box進行求重疊面積的座標計算,此時xx1,yy1,xx2,yy2也是乙個個集合

yy1 = np.maximum(y1[i], y1[order[1:]])

xx2 = np.minimum(x2[i], x2[order[1:]])

yy2 = np.minimum(y2[i], y2[order[1:]])

w = np.maximum(0.0, xx2 - xx1 + 1) #計算重疊區域的wh計算,得出的結果仍然不是單獨的數,是乙個集合

h = np.maximum(0.0, yy2 - yy1 + 1)

inter = w * h #inter也是集合 是重疊區域的面積

ovr = inter / (areas[i] + areas[order[1:]] - inter) #計算iou

inds = np.where(ovr <= thresh)[0] #保留那些iou小於閾值的box的索引值,即剩下的box(仍是按照分數高低進行,因為一開始就是按照分數由高到低遍歷)

order = order[inds + 1] #由於order[0]一開始就被取出,所以索引值需要加一,即新的order也是乙個列表

return keep

以上就是nms的全部內容了,若對您有幫助,不勝榮幸。

後續有空的話可能會更新soft-nms的手撕**。

python實現目標跟蹤(opencv)

1.單目標跟蹤 import cv2 import sys major ver,minor ver,subminor ver cv2.version split print major ver,minor ver,subminor ver if name main 建立 tracker type m...

使用SSD實現目標檢測

本人主要參考這位大神的研究 利用這位大神的 執行了一遍ssd,之後會進一步研究ssd框架的原理和利用ssd布置自己的機械人。安裝和實現的過程中遇到了下面的一些編譯和執行的問題,我想先記錄下來,以備不時之需。第一 在對進行檢測的時候遇到 import matplotlib.pyplot as plt ...

python opencv實現目標區域裁剪功能

這個任務是自己在專案中資料處理的一部分內容,待處理的如下所示 我需要將目標區域給裁剪出來,要不然在後期訓練網路的時候整幅影象過大,且目標區域過小,得到結果不好,還會加劇計算量。在網上找了各個大佬的部落格看,沒找到合適的,便自己動手寫了,順便自己的小破站剛搭建起來,記錄一下自己的思路。思路去尋找目標區...