目標檢測中的非極大值抑制(NMS)原理與實現解析

一、技術背景

在目標檢測任務中,模型通常會對同一目標生成多個重疊的候選框(如錨框或預測框)。非極大值抑制(Non-Maximum Suppression, NMS) 是一種關鍵的后處理技術,用于去除冗余的檢測結果,保留置信度最高且位置最優的邊界框。本文將通過一段Python代碼解析NMS的核心實現邏輯,并演示其在OpenCV環境中的實際效果。


二、算法核心思想

NMS的核心是通過以下步驟篩選邊界框:

  1. 按置信度排序:優先處理置信度最高的預測框。
  2. 計算交并比(IoU):與當前框重疊度高的候選框將被抑制。
  3. 迭代篩選:重復上述過程直至處理完所有候選框。
    在這里插入圖片描述

三、代碼實現解析

1. 輸入數據結構

輸入為字典類型 predicts_dict,鍵為類別名稱,值為該類別對應的邊界框列表。每個邊界框格式為 [x1, y1, x2, y2, score],表示左上角和右下角坐標及置信度。

predicts_dict = {'black1': [[83,54,165,163,0.8], [67,48,118,132,0.5], ...]}

2. 核心函數 non_max_suppress

def non_max_suppress(predicts_dict, threshold):for object_name, bbox in predicts_dict.items():bbox_array = np.array(bbox, dtype=float)# 提取坐標和置信度x1, y1, x2, y2, score = bbox_array[:,0], bbox_array[:,1], bbox_array[:,2], bbox_array[:,3], bbox_array[:,4]# 按置信度降序排序order = score.argsort()[::-1]area = (x2 - x1 + 1) * (y2 - y1 + 1)keep = []  # 保留的索引列表while order.size > 0:i = order[0]  # 當前最高分框keep.append(i)# 計算IoUxx1 = 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:]])inter = np.maximum(0.0, xx2 - xx1 + 1) * np.maximum(0.0, yy2 - yy1 + 1)iou = inter / (area[i] + area[order[1:]] - inter)# 保留IoU低于閾值的框inds = np.where(iou <= threshold)[0]order = order[inds + 1]# 更新篩選后的結果predicts_dict[object_name] = bbox_array[keep].tolist()return predicts_dict

關鍵步驟說明:

  • 坐標提取與排序:將邊界框轉換為NumPy數組后,按置信度降序排列。
  • IoU計算:通過最大-最小值法計算交集區域,公式為:
    IoU = Intersection Union ? Intersection \text{IoU} = \frac{\text{Intersection}}{\text{Union} - \text{Intersection}} IoU=Union?IntersectionIntersection?
  • 動態索引更新:通過 order = order[inds + 1] 跳過被抑制的框,逐步縮小處理范圍。
3. 可視化測試代碼
  • 繪制原始預測框:在全黑圖像上繪制未經過NMS處理的邊界框及置信度。
  • NMS處理與對比:調用 non_max_suppress 后,在另一窗口展示抑制后的結果。
# 繪制原始框
for box in bbox:cv2.rectangle(img, (x1, y1), (x2, y2), (255,255,255), 2)
# 處理并繪制NMS后的框
predicts_dict_nms = non_max_suppress(predicts_dict, 0.1)
for box in bbox_nms:cv2.rectangle(img_cp, (x1, y1), (x2, y2), (255,255,255), 2)

四、優化與注意事項

  1. 閾值選擇:閾值過小可能導致漏檢,過大則冗余框增多(通常目標檢測任務中閾值設為0.5)。
  2. 多類別處理:代碼支持同時對多個類別獨立進行NMS,如輸入 black1black2 兩個類別的預測結果。
  3. 坐標修正:代碼中 +1 的操作是為了避免零寬度/高度,確保面積計算正確。
import cv2
import random
import numpy as npdef non_max_suppress(predicts_dict, threshold):for object_name, bbox in predicts_dict.items():  # 對每一個類別分別進行NMS;一次讀取一對鍵值(即某個類別的所有框)bbox_array = np.array(bbox, dtype=np.float)print(bbox_array)# 下面分別獲取框的左上角坐標(x1,y1),右下角坐標(x2,y2)及此框的置信度;這里需要注意的是圖像左上角可以看做坐標點(0,0),右下角可以看做坐標點(1,1),也就是說從左往右x值增大,從上往下y值增大x1 = bbox_array[:, 0]y1 = bbox_array[:, 1]x2 = bbox_array[:, 2]y2 = bbox_array[:, 3]scores = bbox_array[:, 4]  # class confidence, ndarrayprint(scores, type(scores))        order = scores.argsort()[::-1]  # argsort函數返回的是數組值從小到大的索引值,[::-1]表示取反。即這里返回的是數組值從大到小的索引值areas = (x2 - x1 + 1) * (y2 - y1 + 1)  # 當前類所有框的面積(python會自動使用廣播機制,相當于MATLAB中的.*即兩矩陣對應元素相乘);x1=3,x2=5,習慣上計算x方向長度就是x=3、4、5這三個像素,即5-3+1=3,而不是5-3=2,所以需要加1print(areas, type(areas))    keep = []# 按confidence從高到低遍歷bbx,移除所有與該矩形框的IoU值大于threshold的矩形框while order.size > 0:i = order[0]keep.append(i)  # 保留當前最大confidence對應的bbx索引# 獲取所有與當前bbx的交集對應的左上角和右下角坐標,并計算IoU(注意這里是同時計算一個bbx與其他所有bbx的IoU)xx1 = np.maximum(x1[i], x1[order[1:]])  # 最大置信度的左上角坐標分別與剩余所有的框的左上角坐標進行比較,分別保存較大值;因此這里的xx1的維數應該是當前類的框的個數減1print("xx1:", xx1)yy1 = np.maximum(y1[i], y1[order[1:]])xx2 = np.minimum(x2[i], x2[order[1:]])yy2 = np.minimum(y2[i], y2[order[1:]])inter = np.maximum(0.0, xx2-xx1+1) * np.maximum(0.0, yy2-yy1+1)iou = inter / (areas[i] + areas[order[1:]] - inter)  # 注意這里都是采用廣播機制,同時計算了置信度最高的框與其余框的IoUprint(iou, type(iou))print(np.where(iou <= threshold))inds = np.where(iou <= threshold)[0]  # 保留iou小于等于闕值的框的索引值print('inds:', inds)order = order[inds + 1]  # 將order中的第inds+1處的值重新賦值給order;即更新保留下來的索引,加1是因為因為沒有計算與自身的IOU,所以索引相差1,需要加上bbox = bbox_array[keep]predicts_dict[object_name] = bbox.tolist()return predicts_dict# 下面在一張全黑圖片上測試非極大值抑制的效果
img = np.zeros((600,600), np.uint8)
predicts_dict = {'black1': [[83, 54, 165, 163, 0.8], [67, 48, 118, 132, 0.5], [91, 38, 192, 171, 0.6]]}
# predicts_dict = {'black1': [[83, 54, 165, 163, 0.8], [67, 48, 118, 132, 0.5], [91, 38, 192, 171, 0.6]], 'black2': [[59, 120, 137, 368, 0.12], [54, 154, 148, 382, 0.13]] }
"""
# 在全黑的圖像上畫出設定的幾個框
for object_name, bbox in predicts_dict.items():for box in bbox:x1, y1, x2, y2, score = box[0], box[1], box[2], box[3], box[-1]y_text = int(random.uniform(y1, y2))  # uniform()是不能直接訪問的,需要導入 random 模塊,然后通過 random 靜態對象調用該方法。uniform() 方法將隨機生成下一個實數,它在 [x, y) 范圍內cv2.rectangle(img, (x1, y1), (x2, y2), (255, 255, 255), 2)cv2.putText(img, str(score), (x2 - 30, y_text), 2, 1, (255, 255, 0))cv2.namedWindow("black1_roi")  # 創建一個顯示圖像的窗口cv2.imshow("black1_roi", img)  # 在窗口中顯示圖像;注意這里的窗口名字如果不是剛剛創建的窗口的名字則會自動創建一個新的窗口并將圖像顯示在這個窗口cv2.waitKey(0)  # 如果不添這一句,在IDLE中執行窗口直接無響應。在命令行中執行的話,則是一閃而過。
cv2.destroyAllWindows()  # 最后釋放窗口是個好習慣!
"""
# 在全黑圖片上畫出經過非極大值抑制后的框
img_cp = np.zeros((600,600), np.uint8)
predicts_dict_nms = non_max_suppress(predicts_dict, 0.1)
for object_name, bbox in predicts_dict_nms.items():for box in bbox:x1, y1, x2, y2, score = int(box[0]), int(box[1]), int(box[2]), int(box[3]), box[-1]y_text = int(random.uniform(y1, y2))  # uniform()是不能直接訪問的,需要導入 random 模塊,然后通過 random 靜態對象調用該方法。uniform() 方法將隨機生成下一個實數,它在 [x, y) 范圍內cv2.rectangle(img_cp, (x1, y1), (x2, y2), (255, 255, 255), 2)cv2.putText(img_cp, str(score), (x2 - 30, y_text), 2, 1, (255, 255, 0))cv2.namedWindow("black1_nms")  # 創建一個顯示圖像的窗口cv2.imshow("black1_nms", img_cp)  # 在窗口中顯示圖像;注意這里的窗口名字如果不是剛剛創建的窗口的名字則會自動創建一個新的窗口并將圖像顯示在這個窗口cv2.waitKey(0)  # 如果不添這一句,在IDLE中執行窗口直接無響應。在命令行中執行的話,則是一閃而過。
cv2.destroyAllWindows()  # 最后釋放窗口是個好習慣!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/898852.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/898852.shtml
英文地址,請注明出處:http://en.pswp.cn/news/898852.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

探秘鴻蒙 HarmonyOS NEXT:鴻蒙存儲核心技術全解析

引言 本文章基于HarmonyOS NEXT操作系統&#xff0c;API12以上的版本。 在 ArkTS (ArkUI 框架) 中&#xff0c;用戶首選項 (Preferences) 和 持久化存儲 (PersistentStorage) 都用于數據存儲&#xff0c;但它們有不同的應用場景和特點。 1. 用戶首選項 (Preferences) 概念&a…

Leetcode—15. 三數之和(哈希表—基礎算法)

題目&#xff1a; 給你一個整數數組 nums &#xff0c;判斷是否存在三元組 [nums[i], nums[j], nums[k]] 滿足 i ! j、i ! k 且 j ! k &#xff0c;同時還滿足 nums[i] nums[j] nums[k] 0 。請你返回所有和為 0 且不重復的三元組。 注意&#xff1a;答案中不可以包含重復的…

Linux 啟動Jar腳本設置開機自啟【超級詳細】

Linux 啟動Jar腳本&&設置開機自啟【超級詳細】 概要服務器開機自啟服務重啟腳本 概要 最近在Linux服務器中部署了一個項目&#xff08;單機版&#xff09;&#xff0c;每次更新服務的時候需要用到好幾個命令&#xff0c;停止服務&#xff0c;再重啟&#xff0c;并且服…

【第21節】windows sdk編程:網絡編程基礎

目錄 引言&#xff1a;網絡編程基礎 一、socket介紹(套接字) 1.1 Berkeley Socket套接字 1.2 WinSocket套接字 1.3 WSAtartup函數 1.4 socket函數 1.5 字節序轉換 1.6 綁定套接字 1.7 監聽 1.8 連接 1.9 接收數據 1.10 發送數據 1.11 關閉套接字 二、UDP連接流程…

QT 圖表(拆線圖,欄狀圖,餅狀圖 ,動態圖表)

效果 折線圖 // 創建折線數據系列// 創建折線系列QLineSeries *series new QLineSeries;// series->append(0, 6);// series->append(2, 4);// series->append(3, 8);// 創建圖表并添加系列QChart *chart new QChart;chart->addSeries(series);chart->setTit…

vector和list的區別是什么

vector 和 list 都是C 標準模板庫&#xff08;STL&#xff09;中的容器&#xff0c;它們的區別如下&#xff1a; 內存結構 - vector &#xff1a;是連續的內存空間&#xff0c;就像數組一樣&#xff0c;元素在內存中依次排列。 - list &#xff1a;是由節點組成的雙向鏈表…

【AI】【AIGC】降低AIGC檢測率:技術、挑戰與應對策略

引言 隨著生成式人工智能&#xff08;AIGC&#xff09;技術的迅速發展&#xff0c;越來越多的內容開始由人工智能生成。AIGC技術的應用非常廣泛&#xff0c;包括文本生成、圖像生成、音頻生成等。然而&#xff0c;隨著這些技術的普及&#xff0c;如何有效識別并檢測AIGC生成的…

vue3 ts 請求封裝后端接口

一 首頁-廣告區域-小程序 首頁-廣告區域-小程序 GET/home/banner1.1 請求封裝 首頁-廣告區域 home.ts export const getHomeBannerApi (distributionSite 1) > {return http<BannerItem[]>({method: GET,url: /home/banner,data: {distributionSite,},}) }函數定…

響應式CMS架構優化SEO與用戶體驗

內容概要 在數字化內容生態中&#xff0c;響應式CMS架構已成為平衡搜索引擎可見性與終端用戶體驗的核心載體。該系統通過多終端適配技術&#xff0c;確保PC、移動端及平板等設備的內容渲染一致性&#xff0c;直接降低頁面跳出率并延長用戶停留時長。與此同時&#xff0c;智能S…

算法基礎篇(1)(藍橋杯常考點)

算法基礎篇 前言 算法內容還有搜索&#xff0c;數據結構&#xff08;進階&#xff09;&#xff0c;動態規劃和圖論 數學那個的話大家也知道比較難&#xff0c;放在最后講 這期包含的內容可以看目錄 模擬那個算法的話就是題說什么寫什么&#xff0c;就不再分入目錄中了 注意事…

MyBatis一級緩存和二級緩存

介紹 在開發基于 MyBatis 的應用時&#xff0c;緩存是提升性能的關鍵因素之一。MyBatis 提供了一級緩存和二級緩存&#xff0c;合理使用它們可以顯著減少數據庫的訪問次數&#xff0c;提高系統的響應速度和吞吐量。本文將深入探討 MyBatis 一級緩存和二級緩存的工作原理、使用…

C++核心語法快速整理

前言 歡迎來到我的博客 個人主頁:北嶺敲鍵盤的荒漠貓-CSDN博客 本文主要為學過多門語言玩家快速入門C 沒有基礎的就放棄吧。 全部都是精華&#xff0c;看完能直接上手改別人的項目。 輸出內容 std::代表了這里的cout使用的標準庫&#xff0c;避免不同庫中的相同命名導致混亂 …

如何讓自動駕駛汽車“看清”世界?坐標映射與數據融合概述

在自動駕駛領域,多傳感器融合技術是實現車輛環境感知和決策控制的關鍵。其中,坐標系映射和對應是多傳感器融合的重要環節,它涉及到不同傳感器數據在統一坐標系下的轉換和匹配,以實現對車輛周圍環境的準確感知。本文將介紹多傳感器融合中坐標系映射和對應的數學基礎和實際應…

Unity Shader 的編程流程和結構

Unity Shader 的編程流程和結構 Unity Shader 的編程主要由以下三個核心部分組成&#xff1a;Properties&#xff08;屬性&#xff09;、SubShader&#xff08;子著色器&#xff09; 和 Fallback&#xff08;回退&#xff09;。下面是它們的具體作用和結構&#xff1a; 1. Pr…

第十四天- 排序

一、排序的基本概念 排序是計算機科學中一項重要的操作&#xff0c;它將一組數據元素按照特定的順序&#xff08;如升序或降序&#xff09;重新排列。排序算法的性能通常通過時間復雜度和空間復雜度來衡量。在 Python 中&#xff0c;有內置的排序函數&#xff0c;同時也可以手…

移除idea External Liraries 中maven依賴包

問題背景 擴展包里面不停的出現已經在POM文件注釋的包&#xff0c;其實是沒有查詢到根源位置。 在IDEA插件中搜索Maven Helper 點擊pom.xml文件 會出現擴展插件 定位之后在pom中添加exclusions&#xff0c;如下代碼 <dependency><groupId>com.disney.eva.framewo…

AI革命!藍耘攜手海螺AI視頻,打造智能化視頻新紀元

AI革命&#xff01;藍耘攜手海螺AI視頻&#xff0c;打造智能化視頻新紀元 前言 在這個信息爆炸的時代&#xff0c;視頻已經成為我們獲取信息、學習新知識的重要方式。而隨著人工智能&#xff08;AI&#xff09;技術的快速發展&#xff0c;AI與視頻內容的結合為我們帶來了全新的…

dify1.1.1安裝

1、 按照GitHub上操作 下載源碼&#xff0c;沒有安裝git的&#xff0c;可以下載成zip包&#xff0c; unzip 解壓 git clone https://github.com/langgenius/dify.git cd dify cd docker cp .env.example .env2、啟動前 &#xff0c;先改下 docker-compose.yaml&#xff0c;…

ElasticSearch 可觀測性最佳實踐

ElasticSearch 概述 ElasticSearch 是一個開源的高擴展的分布式全文檢索引擎&#xff0c;它可以近乎實時的存儲、檢索數據&#xff1b;本身擴展性很好&#xff0c;可以擴展到上百臺服務器&#xff0c;處理 PB 級別&#xff08;大數據時代&#xff09;的數據。ES 也使用 Java 開…

Excel處理控件Spire.XLS系列教程:C# 在 Excel 中添加或刪除單元格邊框

單元格邊框是指在單元格或單元格區域周圍添加的線條。它們可用于不同的目的&#xff0c;如分隔工作表中的部分、吸引讀者注意重要的單元格或使工作表看起來更美觀。本文將介紹如何使用 Spire.XLS for .NET 在 C# 中添加或刪除 Excel 單元格邊框。 安裝 Spire.XLS for .NET E-…