非最大抑制算法是如何工作的?
簡介
你曾經(jīng)使用過物體檢測算法嗎?如果是,則很有可能你已經(jīng)使用了非最大抑制算法。也許這是你使用的深度學(xué)習(xí)模式的一部分,你甚至沒有注意到。因為即使是非常復(fù)雜的算法也會面臨這個問題,它們會多次識別同一個對象。今天,想向你展示非最大抑制算法是如何工作的,并提供一個python實現(xiàn)。首先向你展示,邊界框是包圍圖像中檢測到的對象的矩形。然后我將介紹非最大抑制的代碼。該算法逐個刪除冗余的邊界框。它通過移除重疊大于閾值的框來實現(xiàn)。邊界框我們使用邊界框來標(biāo)記圖像中已識別出感興趣對象的部分。
在本例中,要識別的對象是方塊A中的大方塊。邊界框始終是垂直的矩形。因此,我們只需要存儲所有邊界框的左上角和右下角。
當(dāng)使用目標(biāo)檢測方法時,同一個目標(biāo)在稍有不同的區(qū)域被多次檢測到的情況經(jīng)常發(fā)生。
大多數(shù)情況下,我們只想檢測一次對象。為了實現(xiàn)這一點,我們通過應(yīng)用非最大值抑制來刪除冗余的邊界框。非最大抑制現(xiàn)在,向你展示了執(zhí)行非最大抑制的完整功能代碼,這樣你就有了一個概覽。但別擔(dān)心,我會帶你看一下代碼。
def NMS(boxes, overlapThresh = 0.4):
# 返回一個空列表,如果沒有給出框
if len(boxes) == 0:
return []
x1 = boxes[:, 0] # x左上角的坐標(biāo)
y1 = boxes[:, 1] # y左上角的坐標(biāo)
x2 = boxes[:, 2] # x右下角的坐標(biāo)
y2 = boxes[:, 3] # y右下角的坐標(biāo)
# 計算邊界框的面積,并對邊界進行排序
# 邊框的右下角y坐標(biāo)
areas = (x2 - x1 + 1) * (y2 - y1 + 1) # 需要加1
# 開始時所有框的索引。
indices = np.a(chǎn)range(len(x1))
for i,box in enumerate(boxes):
# 創(chuàng)建臨時索引
temp_indices = indices[indices。絠]
# 找出相交方塊的坐標(biāo)
xx1 = np.maximum(box[0], boxes[temp_indices,0])
yy1 = np.maximum(box[1], boxes[temp_indices,1])
xx2 = np.minimum(box[2], boxes[temp_indices,2])
yy2 = np.minimum(box[3], boxes[temp_indices,3])
# 找出交叉框的寬度和高度
w = np.maximum(0, xx2 - xx1 + 1)
h = np.maximum(0, yy2 - yy1 + 1)
# 計算重疊的比例
overlap = (w * h) / areas[temp_indices]
# 如果實際的邊界框與其他框的重疊部分大于閾值,刪除它的索引
if np.a(chǎn)ny(overlap) > treshold:
indices = indices[indices 。 i]
# 只返回其余索引的方框
return boxes[indices].a(chǎn)stype(int)
非最大抑制(NMS)函數(shù)接收一組框,閾值默認值0.4。
def NMS(boxes, overlapThresh = 0.4):
框的數(shù)組必須進行組織,以便每行包含不同的邊界框。
如果它們重疊更多,則兩個中的一個將被丟棄。重疊樹閾值為0.4意味著兩個矩形可以共享其40%的面積。矩形的面積是用它的寬度乘以它的高度來計算的。我們增加1,因為邊界框在起點坐標(biāo)和終點坐標(biāo)上都有一個像素。
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
然后,我們?yōu)樗锌騽?chuàng)建索引。稍后,我們將逐個刪除索引,直到只有對應(yīng)于非重疊框的索引。
indices = np.a(chǎn)range(len(x1))
在循環(huán)中,我們迭代所有框。對于每個框,我們檢查它與任何其他框的重疊是否大于閾值。如果是這樣,我們將從索引列表中刪除該框的索引。
我們創(chuàng)建包含方框索引的索引,其中不包含box[i]的索引。
temp_indices = indices[indices!=i]
為了計算重疊,我們首先計算相交框的坐標(biāo)。這段代碼是矢量化的,以加快速度,我們計算長方體[i]與其他長方體的交點。
xx1 = np.maximum(box[0], boxes[temp_indices,0])
yy1 = np.maximum(box[1], boxes[temp_indices,1])
xx2 = np.minimum(box[2], boxes[temp_indices,2])
yy2 = np.minimum(box[3], boxes[temp_indices,3])
這可能有點混亂,但零點在左上角。因此,我們通過選擇????1及????1的最小值,????2及????2的最大值來獲得相交框的坐標(biāo)。
然后計算相交框的寬度和高度。我們?nèi)∽畲笾?和計算的寬度和高度,因為負的寬度和高度會擾亂重疊的計算。
w = np.maximum(0, xx2 - xx1 + 1)
h = np.maximum(0, yy2 - yy1 + 1)
重疊就是相交框的面積除以邊界框的面積。在我們的例子中,所有邊界框的大小都相同,但該算法也適用于大小不同的情況。
overlap = (w * h) / areas[temp_indices]
然后,如果box[i]與任何其他框的重疊大于treshold,則我們從剩余的索引中排除索引i。
if np.a(chǎn)ny(overlap) > treshold:
indices = indices[indices 。 i]
然后,我們返回帶有未刪除索引的框。像素坐標(biāo)必須是整數(shù),所以我們轉(zhuǎn)換它們只是為了安全。
return boxes[indices].a(chǎn)stype(int)
基于模板匹配的目標(biāo)檢測你可能會問自己,我最初是如何得到這些邊界框的。我使用了一種叫做模板匹配的簡單技術(shù)。你只需要一個圖像和一個模板,即你要搜索的對象。我們的形象將是方塊A。
我們的模板將是圖像中間的方塊。
請注意,模板的方向和大。ㄒ韵袼貫閱挝唬┍仨毰c要在圖像中檢測的對象大致相同。
我們需要opencv。如果你還沒有,可以在終端中安裝。
pip install opencv-python
我們導(dǎo)入cv2。
import cv2
要執(zhí)行模板匹配并從中生成邊界框,我們可以使用以下函數(shù)。
def bounding_boxes(image, template):
(tH, tW) = template.shape[:2] # 獲取模板的高度和寬度
imageGray = cv2.cvtColor(image, 0) # 將圖像轉(zhuǎn)換為灰度
templateGray = cv2.cvtColor(template, 0) # 將模板轉(zhuǎn)換為灰度
result = cv2.matchTemplate(imageGray, templateGray, cv2.TM_CCOEFF_NORMED) # 模板匹配返回相關(guān)性
(y1, x1) = np.where(result >= treshold) # 對象被檢測到,其中相關(guān)性高于閾值
boxes = np.zeros((len(y1), 4)) # 構(gòu)造一個零數(shù)組
x2 = x1 + tW # 用模板的寬度計算x2
y2 = y1 + tH # 計算y2與模板的高度
# 填充邊框數(shù)組
boxes[:, 0] = x1
boxes[:, 1] = y1
boxes[:, 2] = x2
boxes[:, 3] = y2
return boxes.a(chǎn)stype(int)
cv2.matchTemplate函數(shù)返回圖像不同部分與模板的相關(guān)性。
然后,我們選擇圖像的部分,其中相關(guān)性在閾值之上。
(y1, x1) = np.where(result >= treshold)
我們還需要一個函數(shù)將邊界框繪制到圖像上。
def draw_bounding_boxes(image,boxes):
for box in boxes:
image = cv2.rectangle(copy.deepcopy(image),box[:2], box[2:], (255,0,0), 3)
return image
完整代碼
import cv2
import pyautogui
import pyautogui
import cv2
import numpy as np
import os
import time
import matplotlib.pyplot as plt
import copy
def NMS(boxes, overlapThresh = 0.4):
# 返回一個空列表,如果沒有給出框
if len(boxes) == 0:
return []
x1 = boxes[:, 0] # x左上角的坐標(biāo)
y1 = boxes[:, 1] # y左上角的坐標(biāo)
x2 = boxes[:, 2] # x右下角的坐標(biāo)
y2 = boxes[:, 3] # y右下角的坐標(biāo)
# 計算邊界框的面積,并對邊界進行排序
# 邊框的右下角y坐標(biāo)
areas = (x2 - x1 + 1) * (y2 - y1 + 1) # 需要加1
# 開始時所有框的索引。
indices = np.a(chǎn)range(len(x1))
for i,box in enumerate(boxes):
# 創(chuàng)建臨時索引
temp_indices = indices[indices!=i]
# 找出相交方塊的坐標(biāo)
xx1 = np.maximum(box[0], boxes[temp_indices,0])
yy1 = np.maximum(box[1], boxes[temp_indices,1])
xx2 = np.minimum(box[2], boxes[temp_indices,2])
yy2 = np.minimum(box[3], boxes[temp_indices,3])
# 找出交叉框的寬度和高度
w = np.maximum(0, xx2 - xx1 + 1)
h = np.maximum(0, yy2 - yy1 + 1)
# 計算重疊的比例
overlap = (w * h) / areas[temp_indices]
# 如果實際的邊界框與其他框的重疊部分大于閾值,刪除它的索引
if np.a(chǎn)ny(overlap) > treshold:
indices = indices[indices 。 i]
# 只返回其余索引的方框
return boxes[indices].a(chǎn)stype(int)
def bounding_boxes(image, template):
(tH, tW) = template.shape[:2] # 獲取模板的高度和寬度
imageGray = cv2.cvtColor(image, 0) # 將圖像轉(zhuǎn)換為灰度
templateGray = cv2.cvtColor(template, 0) # 將模板轉(zhuǎn)換為灰度
result = cv2.matchTemplate(imageGray, templateGray, cv2.TM_CCOEFF_NORMED) # 模板匹配返回相關(guān)性
(y1, x1) = np.where(result >= treshold) # 對象被檢測到,其中相關(guān)性高于閾值
boxes = np.zeros((len(y1), 4)) # 構(gòu)造一個零數(shù)組
x2 = x1 + tW # 用模板的寬度計算x2
y2 = y1 + tH # 計算y2與模板的高度
# 填充邊框數(shù)組
boxes[:, 0] = x1
boxes[:, 1] = y1
boxes[:, 2] = x2
boxes[:, 3] = y2
return boxes.a(chǎn)stype(int)
def draw_bounding_boxes(image,boxes):
for box in boxes:
image = cv2.rectangle(copy.deepcopy(image),box[:2], box[2:], (255,0,0), 3)
return image
if __name__ == "__main__":
time.sleep(2)
treshold = 0.8837 # 關(guān)聯(lián)閾值,以便識別一個對象
template_diamonds = plt.imread(r"templates/ace_diamonds_plant_template.jpg")
ace_diamonds_rotated = plt.imread(r"images/ace_diamonds_table_rotated.jpg")
boxes_redundant = bounding_boxes(ace_diamonds_rotated, template_diamonds) # 計算邊界盒
boxes = NMS(boxes_redundant) # 刪除多余的包圍框
overlapping_BB_image = draw_bounding_boxes(ace_diamonds_rotated,
boxes_redundant) # 使用所有多余的邊框繪制圖像
segmented_image = draw_bounding_boxes(ace_diamonds_rotated,boxes) # 在圖像上繪制邊界框
plt.imshow(overlapping_BB_image)
plt.show()
plt.imshow(segmented_image)
plt.show()
結(jié)論
我們可以使用非最大值抑制來刪除冗余的邊界框。它們是多余的,因為它們多次標(biāo)記同一對象。
NMS算法利用相交三角形的面積計算三角形之間的重疊。如果邊界框與任何其他邊界框的重疊高于閾值,則將刪除該邊界框。
?原文標(biāo)題:非最大抑制?

請輸入評論內(nèi)容...
請輸入評論/評論長度6~500個字
最新活動更多
推薦專題
- 1 AI 眼鏡讓百萬 APP「集體失業(yè)」?
- 2 大廠紛紛入局,百度、阿里、字節(jié)搶奪Agent話語權(quán)
- 3 深度報告|中國AI產(chǎn)業(yè)正在崛起成全球力量,市場潛力和關(guān)鍵挑戰(zhàn)有哪些?
- 4 一文看懂視覺語言動作模型(VLA)及其應(yīng)用
- 5 上海跑出80億超級獨角獸:獲上市公司戰(zhàn)投,干人形機器人
- 6 國家數(shù)據(jù)局局長劉烈宏調(diào)研格創(chuàng)東智
- 7 下一代入口之戰(zhàn):大廠為何紛紛押注智能體?
- 8 百億AI芯片訂單,瘋狂傾銷中東?
- 9 Robotaxi新消息密集釋放,量產(chǎn)元年誰在領(lǐng)跑?
- 10 格斗大賽出圈!人形機器人致命短板曝光:頭腦過于簡單