下拉多选择框 实现方式_非极大值抑制Non-Maximum Suppression(NMS)一文搞定理论+多平台实现...
薰風說
Non-Maximum Suppression的翻譯是非“極大值”抑制,而不是非“最大值”抑制。這就說明了這個算法的用處:找到局部極大值,并篩除(抑制)鄰域內其余的值。
這是一個很基礎的,簡單高效且適用于一維到多維的常見算法。因為特別適合目標檢測問題,所以一直沿用至今,隨著目標檢測研究的深入和要求的提高(eg:原來只想框方框,現在想框多邊形框),NMS也延伸出了不少變體。
與此同時,因為其比較基礎,簡單高效,因此我們更應該掌握它的實現。
一、為何/何時/如何NMS? Why&When&How NMS?
非極大值抑制[1](Non-Maximum Suppression,NMS),顧名思義就是抑制不是極大值的元素,可以理解為局部最大搜索。
這個局部代表的是一個鄰域,鄰域的“維度”和“大小”都是可變的參數。
NMS在計算機視覺領域有著非常重要的應用,如視頻目標跟蹤、3D重建、目標識別以及紋理分析等。
1. 為何要用NMS Why NMS?
首先,目標檢測與圖像分類不同,圖像分類往往只有一個輸出,但目標檢測的輸出個數卻是未知的。除了Ground-Truth(標注數據)訓練,模型永遠無法百分百確信自己要在一張圖上預測多少物體。
所以目標檢測問題的老大難問題之一就是如何提高召回率。召回率(Recall)是模型找到所有某類目標的能力(所有標注的真實邊界框有多少被預測出來了)。檢測時按照是否檢出邊界框與邊界框是否存在,可以分為下表四種情況:
是所有某類物體中被檢測出的概率,并由下式給出:
為了提高這個值,很直觀的想法是“寧肯錯殺一千,絕不放過一個”。因此在目標檢測中,模型往往會提出遠高于實際數量的區域提議(Region Proposal,SSD等one-stage的Anchor也可以看作一種區域提議)。
這就導致最后輸出的邊界框數量往往遠大于實際數量,而這些模型的輸出邊界框往往是堆疊在一起的。因此,我們需要NMS從堆疊的邊框中挑出最好的那個。
目標檢測中的NMS2. 何時使用NMS? When NMS?
回顧我在R-CNN中提到的流程:
NMS使用在4. 回歸邊框之后,即所有的框已經被分類且精修了位置。且所有區域提議的預測結果已經由置信度與閾值初步篩選之后。
3. 如何非極大值抑制 How NMS?
一維簡單例子
由于重點是二維(目標檢測)的實現,因此一維只放出偽代碼便于理解。
判斷一維數組I[W]的元素I[i](2<=i<=W-1)是否為局部極大值,即大于其左鄰元素I[i-1]和右鄰元素I[i+1]
算法流程如下圖所示:
算法流程3-5行判斷當前元素是否大于其左鄰與右鄰元素,如符合條件,該元素即為極大值點。對于極大值點I[i],已知I[i]>I[i+1],故無需對i+1位置元素做進一步處理,直接跳至i+2位置,對應算法流程第12行。
若元素I[i]不滿足算法流程第3行判斷條件,將其右鄰I[i+1]作為極大值候選,對應算法流程第7行。采用單調遞增的方式向右查找,直至找到滿足I[i]>I[i+1]的元素,若i<=W-1,該點即為極大值點,對應算法流程第10-11行。
推廣至目標檢測
首先,根據之前分析確認NMS的前提,輸入與輸出。
使用前提
目標檢測模型已經完成了整個前向計算,并給出所有可能的邊界框(位置已精修)。
算法輸入
算法對一幅圖產生的所有的候選框,每個框有坐標與對應的打分(置信度)。
如一組5維數組:
- 每個組表明一個邊框,組數是待處理邊框數
- 4個數表示框的坐標:X_max,X_min,Y_max,Y_min
- 1個數表示對應分類下的置信度
注意:每次輸入的不是一張圖所有的邊框,而是一張圖中屬于某個類的所有邊框(因此極端情況下,若所有框的都被判斷為背景類,則NMS不執行;反之若存在物體類邊框,那么有多少類物體則分別執行多少次NMS)。
除此之外還有一個自行設置的參數:閾值 TH。
算法輸出
輸入的一個子集,同樣是一組5維數組,表示篩選后的邊界框。
算法流程
二、算法實現
1. 交并比
交并比(Intersection over Union)是目標檢測NMS的依據,因此首先要搞懂交并比及其實現。
衡量邊界框位置,常用交并比指標,交并比(Injection Over Union,IOU)發展于集合論的雅卡爾指數(Jaccard Index)[3],被用于計算真實邊界框Bgt(數據集的標注)以及預測邊界框Bp(模型預測結果)的重疊程度。
具體來說,它是兩邊界框相交部分面積與相并部分面積之比,如下所示:
Python(numpy)代碼實現
import2. NMS的Python實現
從R-CNN開始,到fast R-CNN,faster R-CNN……都不難看到NMS的身影,且因為實現功能類似,基本的程序都是定型的,這里就分析Faster RCNN的NMS實現:
Python(numpy)代碼實現
注意,這里的NMS是單類別的!多類別則只需要在外加一個for循環遍歷每個種類即可
def py_cpu_nms(dets, thresh): """Pure Python NMS baseline.""" #dets某個類的框,x1、y1、x2、y2、以及置信度score#eg:dets為[[x1,y1,x2,y2,score],[x1,y1,y2,score]……]]# thresh是IoU的閾值 x1 = dets[:, 0] y1 = dets[:, 1]x2 = dets[:, 2] y2 = dets[:, 3] scores = dets[:, 4] #每一個檢測框的面積 areas = (x2 - x1 + 1) * (y2 - y1 + 1) #按照score置信度降序排序 order = scores.argsort()[::-1] keep = [] #保留的結果框集合 while order.size > 0: i = order[0] keep.append(i) #保留該類剩余box中得分最高的一個 #得到相交區域,左上及右下 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:重疊面積 /(面積1+面積2-重疊面積) ovr = inter / (areas[i] + areas[order[1:]] - inter) #保留IoU小于閾值的box inds = np.where(ovr <= thresh)[0] order = order[inds + 1] #因為ovr數組的長度比order數組少一個,所以這里要將所有下標后移一位 return keepFaster R-CNN的MATLAB實現與python版實現一致,代碼在這里:nms.m.另外,nms_multiclass.m是多類別nms,加了一層for循環對每類進行nms而已.
3. NMS的Pytorch實現
在Pytorch中,數據類型從numpy的數組變成了pytorch的tensor,因此具體的實現需要改變寫法,但核心思路是不變的。
這里的實現參照了知乎大佬TeddyZhang的專欄
IoU計算的Pytorch源碼為:(注意矩陣維度的變化)
# IOU計算# 假設box1維度為[N,4] box2維度為[M,4]def iou(self, box1, box2):N = box1.size(0)M = box2.size(0)lt = torch.max( # 左上角的點box1[:, :2].unsqueeze(1).expand(N, M, 2), # [N,2]->[N,1,2]->[N,M,2]box2[:, :2].unsqueeze(0).expand(N, M, 2), # [M,2]->[1,M,2]->[N,M,2])rb = torch.min(box1[:, 2:].unsqueeze(1).expand(N, M, 2),box2[:, 2:].unsqueeze(0).expand(N, M, 2),)wh = rb - lt # [N,M,2]wh[wh < 0] = 0 # 兩個box沒有重疊區域inter = wh[:,:,0] * wh[:,:,1] # [N,M]area1 = (box1[:,2]-box1[:,0]) * (box1[:,3]-box1[:,1]) # (N,)area2 = (box2[:,2]-box2[:,0]) * (box2[:,3]-box2[:,1]) # (M,)area1 = area1.unsqueeze(1).expand(N,M) # (N,M)area2 = area2.unsqueeze(0).expand(N,M) # (N,M)iou = inter / (area1+area2-inter)return iou其中:
- torch.unsqueeze(1) 表示增加一個維度,增加位置為維度1
- torch.squeeze(1) 表示減少一個維度
其中:
- torch.numel() 表示一個張量總元素的個數
- torch.clamp(min, max) 設置上下限
- tensor.item() 把tensor元素取出作為numpy數字
4. C++實現NMS
C++代碼來自這個博客,真希望我也能有大佬們的碼力233……畢竟搞工程早晚會掣肘于Python的
NMS和soft-nms算法 - outthinker - 博客園?www.cnblogs.com程序整體思路:
先將box中的數據分別存入x1,y1,x2,y2,s中,分別為坐標和置信度,算出每個框的面積,存入area,基于置信度s,從小到達進行排序,做一個while循環,取出置信度最高的,即排序后的最后一個,然后將該框進行保留,存入pick中,然后和其他所有的框進行比對,大于規定閾值就將別的框去掉,并將該置信度最高的框和所有比對過程,大于閾值的框存入suppress,for循環后,將I中滿足suppress條件的置為空。直到I為空退出while。
static碎碎念&絮叨一下
作為一個半路出家的初學者(本科電子信息工程,跨保CS),對coding一直處于某種“焦慮”的狀態。
比如我可以花時間看懂別人的實現,也能在這個基礎上小修小補,但從頭搭建一個程序總會讓我有一種莫名的抵觸情緒。
而我也認識到,如果我想在個行業做出點成果,那不僅僅是需要git clone,調包調參那么簡單,我必須從頭開始一點點實現。甚至深入到一些框架的底層另起爐灶才能實現自己大膽的想法。
我離能夠隨心所欲地實現自己想法還有多遠呢……希望越早越好吧,如果有幸你能看到這里,又有些經驗可以分享的話。能說給我聽聽嗎?
參考文獻
[1]Neubeck A , Gool L J V . Efficient Non-Maximum Suppression[C]// 18th International Conference on Pattern Recognition (ICPR 2006), 20-24 August 2006, Hong Kong, China. IEEE Computer Society, 2006.
另外,在最后,還是非常感謝以下博客細致的解讀與實現,雖然并沒有全都貼上,但每個都認真學習了一遍,受益匪淺:
非極大值抑制(Non-Maximum Suppression,NMS)?www.cnblogs.comTyan:非極大值抑制(Non-Maximum Suppression)?zhuanlan.zhihu.comNMS和soft-nms算法 - outthinker - 博客園?www.cnblogs.comhttps://blog.csdn.net/sinat_34474705/article/details/80045294?blog.csdn.net燕小花:目標檢測之非極大值抑制(NMS)各種變體?zhuanlan.zhihu.comTeddyZhang:NMS算法詳解(附Pytorch實現代碼)?zhuanlan.zhihu.com不知道讀者是否喜歡這種內容,如果喜歡我會在近期推出NMS變體的算法原理與實現,以及難例挖掘的算法原理與實現。總結
以上是生活随笔為你收集整理的下拉多选择框 实现方式_非极大值抑制Non-Maximum Suppression(NMS)一文搞定理论+多平台实现...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python根据文件名获取文件路径_py
- 下一篇: 藤三七花的功效与作用、禁忌和食用方法