日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人工智能 > pytorch >内容正文

pytorch

【深度学习】非极大值抑制Non-Maximum Suppression(NMS)一文搞定理论+多平台实现...

發布時間:2025/3/8 pytorch 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【深度学习】非极大值抑制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從堆疊的邊框中挑出最好的那個。

目標檢測中的NMS

2. 何時使用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維數組,表示篩選后的邊界框。

    算法流程

  • 將所有的框按類別劃分,并剔除背景類,因為無需NMS。

  • 對每個物體類中的邊界框(B_BOX),按照分類置信度降序排列。

  • 在某一類中,選擇置信度最高的邊界框B_BOX1,將B_BOX1從輸入列表中去除,并加入輸出列表。

  • 逐個計算B_BOX1與其余B_BOX2的交并比IoU,若IoU(B_BOX1,B_BOX2) > 閾值TH,則在輸入去除B_BOX2。

  • 重復步驟3~4,直到輸入列表為空,完成一個物體類的遍歷。

  • 重復2~5,直到所有物體類的NMS處理完成。

  • 輸出列表,算法結束

  • 二、算法實現

    1. 交并比

    交并比(Interp over Union)是目標檢測NMS的依據,因此首先要搞懂交并比及其實現。

    衡量邊界框位置,常用交并比指標,交并比(Injection Over Union,IOU)發展于集合論的雅卡爾指數(Jaccard Index)[3],被用于計算真實邊界框Bgt(數據集的標注)以及預測邊界框Bp(模型預測結果)的重疊程度。

    具體來說,它是兩邊界框相交部分面積與相并部分面積之比,如下所示:

    Python(numpy)代碼實現

    import numpy as np def compute_iou(box1, box2, wh=False):"""compute the iou of two boxes.Args:box1, box2: [xmin, ymin, xmax, ymax] (wh=False) or [xcenter, ycenter, w, h] (wh=True)wh: the format of coordinate.Return:iou: iou of box1 and box2."""if wh == False:xmin1, ymin1, xmax1, ymax1 = box1xmin2, ymin2, xmax2, ymax2 = box2else:xmin1, ymin1 = int(box1[0]-box1[2]/2.0), int(box1[1]-box1[3]/2.0)xmax1, ymax1 = int(box1[0]+box1[2]/2.0), int(box1[1]+box1[3]/2.0)xmin2, ymin2 = int(box2[0]-box2[2]/2.0), int(box2[1]-box2[3]/2.0)xmax2, ymax2 = int(box2[0]+box2[2]/2.0), int(box2[1]+box2[3]/2.0)## 獲取矩形框交集對應的左上角和右下角的坐標(interp)xx1 = np.max([xmin1, xmin2])yy1 = np.max([ymin1, ymin2])xx2 = np.min([xmax1, xmax2])yy2 = np.min([ymax1, ymax2])## 計算兩個矩形框面積area1 = (xmax1-xmin1) * (ymax1-ymin1) area2 = (xmax2-xmin2) * (ymax2-ymin2)inter_area = (np.max([0, xx2-xx1])) * (np.max([0, yy2-yy1]))#計算交集面積iou = inter_area / (area1+area2-inter_area+1e-6)#計算交并比 return iou

    2. 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 keep

    Faster 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) 表示減少一個維度

    # NMS算法# bboxes維度為[N,4],scores維度為[N,], 均為tensordef nms(self, bboxes, scores, threshold=0.5):x1 = bboxes[:,0]y1 = bboxes[:,1]x2 = bboxes[:,2]y2 = bboxes[:,3]areas = (x2-x1)*(y2-y1) # [N,] 每個bbox的面積_, order = scores.sort(0, descending=True) # 降序排列keep = []while order.numel() > 0: # torch.numel()返回張量元素個數if order.numel() == 1: # 保留框只剩一個i = order.item()keep.append(i)breakelse:i = order[0].item() # 保留scores最大的那個框box[i]keep.append(i)# 計算box[i]與其余各框的IOU(思路很好)xx1 = x1[order[1:]].clamp(min=x1[i]) # [N-1,]yy1 = y1[order[1:]].clamp(min=y1[i])xx2 = x2[order[1:]].clamp(max=x2[i])yy2 = y2[order[1:]].clamp(max=y2[i])inter = (xx2-xx1).clamp(min=0) * (yy2-yy1).clamp(min=0) # [N-1,]iou = inter / (areas[i]+areas[order[1:]]-inter) # [N-1,]idx = (iou <= threshold).nonzero().squeeze() # 注意此時idx為[N-1,] 而order為[N,]if idx.numel() == 0:breakorder = order[idx+1] # 修補索引之間的差值return torch.LongTensor(keep) # Pytorch的索引值為LongTensor

    其中:

    • 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 void sort(int n, const float* x, int* indices) { // 排序函數(降序排序),排序后進行交換的是indices中的數據 // n:排序總數// x:帶排序數// indices:初始為0~n-1數目 int i, j; for (i = 0; i < n; i++) for (j = i + 1; j < n; j++) { if (x[indices[j]] > x[indices[i]]) { //float x_tmp = x[i]; int index_tmp = indices[i]; //x[i] = x[j]; indices[i] = indices[j]; //x[j] = x_tmp; indices[j] = index_tmp; } } }int nonMaximumSuppression(int numBoxes, const CvPoint *points, const CvPoint *oppositePoints, const float *score, float overlapThreshold, int *numBoxesOut, CvPoint **pointsOut, CvPoint **oppositePointsOut, float **scoreOut) { // numBoxes:窗口數目// points:窗口左上角坐標點// oppositePoints:窗口右下角坐標點 // score:窗口得分// overlapThreshold:重疊閾值控制// numBoxesOut:輸出窗口數目 // pointsOut:輸出窗口左上角坐標點// oppositePoints:輸出窗口右下角坐標點 // scoreOut:輸出窗口得分 int i, j, index; float* box_area = (float*)malloc(numBoxes * sizeof(float)); // 定義窗口面積變量并分配空間 int* indices = (int*)malloc(numBoxes * sizeof(int)); // 定義窗口索引并分配空間 int* is_suppressed = (int*)malloc(numBoxes * sizeof(int)); // 定義是否抑制表標志并分配空間 // 初始化indices、is_supperssed、box_area信息 for (i = 0; i < numBoxes; i++) { indices[i] = i; is_suppressed[i] = 0; box_area[i] = (float)( (oppositePoints[i].x - points[i].x + 1) * (oppositePoints[i].y - points[i].y + 1)); } // 對輸入窗口按照分數比值進行排序,排序后的編號放在indices中 sort(numBoxes, score, indices); for (i = 0; i < numBoxes; i++) // 循環所有窗口 { if (!is_suppressed[indices[i]]) // 判斷窗口是否被抑制 { for (j = i + 1; j < numBoxes; j++) // 循環當前窗口之后的窗口 { if (!is_suppressed[indices[j]]) // 判斷窗口是否被抑制 { int x1max = max(points[indices[i]].x, points[indices[j]].x); // 求兩個窗口左上角x坐標最大值 int x2min = min(oppositePoints[indices[i]].x, oppositePoints[indices[j]].x); // 求兩個窗口右下角x坐標最小值 int y1max = max(points[indices[i]].y, points[indices[j]].y); // 求兩個窗口左上角y坐標最大值 int y2min = min(oppositePoints[indices[i]].y, oppositePoints[indices[j]].y); // 求兩個窗口右下角y坐標最小值 int overlapWidth = x2min - x1max + 1; // 計算兩矩形重疊的寬度 int overlapHeight = y2min - y1max + 1; // 計算兩矩形重疊的高度 if (overlapWidth > 0 && overlapHeight > 0) { float overlapPart = (overlapWidth * overlapHeight) / box_area[indices[j]]; // 計算重疊的比率 if (overlapPart > overlapThreshold) // 判斷重疊比率是否超過重疊閾值 { is_suppressed[indices[j]] = 1; // 將窗口j標記為抑制 } } } } } } *numBoxesOut = 0; // 初始化輸出窗口數目0 for (i = 0; i < numBoxes; i++) { if (!is_suppressed[i]) (*numBoxesOut)++; // 統計輸出窗口數目 } *pointsOut = (CvPoint *)malloc((*numBoxesOut) * sizeof(CvPoint)); // 分配輸出窗口左上角坐標空間 *oppositePointsOut = (CvPoint *)malloc((*numBoxesOut) * sizeof(CvPoint)); // 分配輸出窗口右下角坐標空間 *scoreOut = (float *)malloc((*numBoxesOut) * sizeof(float)); // 分配輸出窗口得分空間 index = 0; for (i = 0; i < numBoxes; i++) // 遍歷所有輸入窗口 { if (!is_suppressed[indices[i]]) // 將未發生抑制的窗口信息保存到輸出信息中 { (*pointsOut)[index].x = points[indices[i]].x; (*pointsOut)[index].y = points[indices[i]].y; (*oppositePointsOut)[index].x = oppositePoints[indices[i]].x; (*oppositePointsOut)[index].y = oppositePoints[indices[i]].y; (*scoreOut)[index] = score[indices[i]]; index++; } } free(indices); // 釋放indices空間 free(box_area); // 釋放box_area空間 free(is_suppressed); // 釋放is_suppressed空間 return LATENT_SVM_OK; }

    碎碎念&絮叨一下

    作為一個半路出家的初學者(本科電子信息工程,跨保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.

    往期精彩回顧適合初學者入門人工智能的路線及資料下載機器學習及深度學習筆記等資料打印機器學習在線手冊深度學習筆記專輯《統計學習方法》的代碼復現專輯 AI基礎下載機器學習的數學基礎專輯 獲取本站知識星球優惠券,復制鏈接直接打開: https://t.zsxq.com/qFiUFMV 本站qq群704220115。加入微信群請掃碼:

    總結

    以上是生活随笔為你收集整理的【深度学习】非极大值抑制Non-Maximum Suppression(NMS)一文搞定理论+多平台实现...的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 成年网站 | 欧美日韩中字 | 亚洲一区国产精品 | 成人午夜视频免费观看 | 日韩精品专区 | 爱av导航 | 亚洲国产视频在线 | 特a级黄色片 | 性生交大片免费看狂欲 | 国产孕妇一区二区三区 | 国产美女免费视频 | 艳妇臀荡乳欲伦交换gif | 人人澡人人澡人人澡 | 日本在线观看网站 | 黄片毛片在线看 | 国产精品热 | 日韩精品五区 | 西西人体44www大胆无码 | 欧美激情一区二区三区p站 欧美mv日韩mv国产网站app | 免费成人深夜夜视频 | 国产美女啪啪 | 午夜精品久久久久久久四虎美女版 | 爱情岛论坛亚洲品质自拍视频 | 国产黄页| 久久青青国产 | xxx69美国| 91n在线观看 | www性| 9·1·黄·色·视·频| 免费黄网站在线看 | 老湿影院av| av色资源 | 沈樵精品国产成av片 | 国产香蕉视频在线播放 | 国产福利视频导航 | av观看免费 | 天天色综 | 国产成人av一区二区三区不卡 | 精品黑人一区二区三区在线观看 | 成人av一区二区三区在线观看 | 熟女俱乐部一区二区视频在线 | 婷婷久久亚洲 | 欧美第十页 | www黄色在线观看 | 少妇逼逼| 美女裸体网站久久久 | 丰满双乳秘书被老板狂揉捏 | 视频一区二区在线 | 国产三级国产精品国产专区50 | 香蕉久久国产av一区二区 | 色婷婷狠 | 国产精品性色 | 亚洲社区在线 | 久久久精品免费视频 | 在线视频在线观看 | 黄色大片一级 | 狠狠干狠狠爱 | 黄色片视频免费在线观看 | 三大队在线观看 | 国产精品无码毛片 | 中文字字幕第183页 欧美特级一级片 | 久久久三级视频 | 激情综合网婷婷 | 9久9久9久女女女九九九一九 | 91久久久久久久久久 | av不卡在线| 国产成人午夜精品无码区久久 | 国产一级黄色电影 | 日韩在线资源 | 成人毛片视频在线观看 | 性做久久久久久久免费看 | 亚洲二区在线视频 | 日本一区二区在线免费观看 | 国产精品27p | 免费看黄av| 超碰人人超 | 欧美精品四区 | 国产精品久久久久久无人区 | 免费一级全黄少妇性色生活片 | 91大神在线免费观看 | 男女互插视频 | 中文字幕日韩欧美在线 | 日韩福利视频一区 | 欧美成人精精品一区二区频 | 狠狠操人人干 | 久草三级| 欧洲金发美女大战黑人 | 亚洲成人资源 | 奇米影视四色777 | 亲子伦视频一区二区三区 | 麻豆激情网 | 久草视频资源 | 日本在线一区 | av加勒比在线 | 99视屏 | 国产麻豆免费视频 | 色老汉av一区二区三区 | 91亚洲精华国产精华精华液 | 亚洲综合二区 |