37從傳統(tǒng)算法到深度學(xué)習(xí):目標(biāo)檢測(cè)入門(mén)實(shí)戰(zhàn) --非極大值抑制

非極大值抑制的作用

在進(jìn)行目標(biāo)檢測(cè)過(guò)程中溶推,我們的分類(lèi)器會(huì)對(duì)每一個(gè)滑動(dòng)窗口的內(nèi)容進(jìn)行分類(lèi)猜扮,而滑動(dòng)窗口是按照設(shè)定的步長(zhǎng)在圖像金字塔的每個(gè)圖層中從上到下怜瞒、從左向右移動(dòng)鸯屿,這樣一個(gè)目標(biāo)就會(huì)出現(xiàn)在多個(gè)滑動(dòng)窗口中盏档,最后我們就會(huì)獲得多個(gè)相交凶掰、重疊的矩形框。如下圖在目標(biāo)檢測(cè)過(guò)程中目標(biāo)上會(huì)產(chǎn)生多個(gè)矩形框蜈亩,我們希望從這些矩形框中挑選出一個(gè)最合適的矩形框且剔除多余的矩形框懦窘,使得每個(gè)目標(biāo)只被一個(gè)矩形框標(biāo)記。


image.png

非極大值抑制(Non Maximum Suppression)以下簡(jiǎn)稱(chēng) NMS勺拣,的主要作用是去除目標(biāo)檢測(cè)過(guò)程中產(chǎn)生的冗余矩形框奶赠。要實(shí)現(xiàn) NMS 首先需要計(jì)算矩形框之間的交并比(Intersection over Union),以下簡(jiǎn)稱(chēng) IoU药有。下圖以直觀的例子展示計(jì)算 IoU 的方法毅戈,左圖中的目標(biāo)(人)同時(shí)被兩個(gè)矩形框標(biāo)記,為了剔除多余的矩形框需要計(jì)算兩個(gè)矩形框的 IoU愤惰。IoU 的計(jì)算的方法如下圖中間的公式所示苇经,即兩個(gè)框的交集(紅色區(qū)域)與兩個(gè)框的并集(綠色區(qū)域)的比值。如果計(jì)算后的 IoU 大于事先設(shè)定的閾值宦言,則剔除較小的矩形框(下圖中最右邊圖片所示)扇单,通過(guò)這個(gè)過(guò)程我們就達(dá)到了剔除冗余的矩形框的目的。接下來(lái)我們將通過(guò)代碼來(lái)實(shí)現(xiàn)一個(gè) NMS 函數(shù)奠旺。


image.png

構(gòu)建非極大值抑制函數(shù)

通過(guò)前面一節(jié)的介紹我們已經(jīng)知道 NMS 的作用和原理蜘澜,接下來(lái)我們一步一步教會(huì)大家實(shí)現(xiàn)一個(gè) NMS 函數(shù)施流。首先在終端中輸入下面兩行命令下載本節(jié)實(shí)驗(yàn)所需圖片。

!wget https://labfile.oss.aliyuncs.com/courses/3096/man.jpg
!wget https://labfile.oss.aliyuncs.com/courses/3096/people.jpg

然后使用下面兩行命令下載實(shí)驗(yàn) 5 的代碼和訓(xùn)練好的模型鄙信。

!wget https://labfile.oss.aliyuncs.com/courses/3096/hog_detection.py
!wget https://labfile.oss.aliyuncs.com/courses/3096/model

首先我們導(dǎo)入 NumPy瞪醋、OpenCV 和下載好的代碼模塊 hog_detection.py 中的 run 方法。

import numpy as np
import cv2
from hog_detection import run

然后我們定義一個(gè)名為 NMS 的函數(shù)(見(jiàn)下面代碼)装诡。該函數(shù)有兩個(gè)參數(shù)银受,第一個(gè)參數(shù) boxes 表示目標(biāo)檢測(cè)過(guò)程中獲得的所有矩形框。第二個(gè)參數(shù) threshold 表示事先定義的一個(gè)閾值鸦采,當(dāng)兩個(gè)矩形框重疊的面積超過(guò)這個(gè)閾值時(shí)我們將剔除其中一個(gè)矩形框宾巍。

def NMS(boxes, threshold):
    if len(boxes) == 0:
        return []
    
    boxes = np.array(boxes).astype("float")

    x1 = boxes[:,0]
    y1 = boxes[:,1]
    w1 = boxes[:,2]
    h1 = boxes[:,3]
    x2 = x1 + w1
    y2 = y1 + h1
    
    area = (w1 + 1) * (h1 + 1)
    temp = []
    
    idxs = np.argsort(h1)
    
    while len(idxs) > 0:
        last = len(idxs) - 1
        i = idxs[last]
        temp.append(i)   
        
        x1_m = np.maximum(x1[i], x1[idxs[:last]])
        y1_m = np.maximum(y1[i], y1[idxs[:last]])
        
        x2_m = np.minimum(x2[i], x2[idxs[:last]])
        y2_m = np.minimum(y2[i], y2[idxs[:last]])
        
        w = np.maximum(0, x2_m - x1_m + 1)
        h = np.maximum(0, y2_m - y1_m + 1)
        
        over = (w * h) / area[idxs[:last]]
        
        idxs = np.delete(idxs, np.concatenate(([last],  
            np.where(over > threshold)[0])))  

    return boxes[temp].astype("int")

在目標(biāo)檢測(cè)過(guò)程中我們的算法有可能沒(méi)有檢測(cè)到任何目標(biāo),那么這就表示在圖片中沒(méi)有用于標(biāo)記目標(biāo)的矩形框渔伯。所以下面的代碼第 2 行我們將用一個(gè) if 語(yǔ)句來(lái)判斷輸入的 boxes 的數(shù)量是否為 0顶霞,如果矩形框的數(shù)量為 0 則函數(shù)返回一個(gè)空列表。然后我們還需要將 boxes 轉(zhuǎn)換為 NumPy 數(shù)組并且將其中每個(gè)元素轉(zhuǎn)換為 float 浮點(diǎn)類(lèi)型(見(jiàn)代碼第 5 行)咱旱,因?yàn)楹竺嫖覀冃枰眠@些元素進(jìn)行算術(shù)運(yùn)算确丢。
代碼第 7 到 10 行,我們使用切片方法獲取每一個(gè) boxes 內(nèi)的元素并將其分別保存在 x1吐限、y1鲜侥、w1、h1 這四個(gè)數(shù)組中诸典。這四個(gè)數(shù)組中分別保存了每一個(gè) boxes 中的第一至四元素描函。x1 表示矩形框左上角頂點(diǎn)的橫坐標(biāo),y1 表示矩形框左上角頂點(diǎn)的縱坐標(biāo)狐粱,w1 是矩形框的寬舀寓,h1 是矩形框的高。 代碼第 11肌蜻、12 行我們使用這四個(gè)數(shù)組計(jì)算得出每個(gè)矩形框的右下角頂點(diǎn)橫坐標(biāo)的集合 x2 和 縱坐標(biāo)的集合 y2互墓。
代碼第 14 行表示我們需要計(jì)算每個(gè)矩形框的面積。這里分別將 w1 和 h1 加 1 是為了避免使用 area 計(jì)算 IoU 時(shí)分母為零的情況發(fā)生蒋搜。我們還初始化了代碼 15 行中的 temp 列表用于臨時(shí)存儲(chǔ)值篡撵。
代碼 17 行我們使用 NumPy 的 argsort 方法將 h1 中的元素從小到大排序并返回每個(gè)元素在 h1 中的下標(biāo),需要注意的是 idxs 中的元素是 h1 中元素的下標(biāo)豆挽,這些下標(biāo)排列的順序是按照其對(duì)應(yīng) h1 中元素的大小排列的育谬。
接下來(lái)我們使用 while 循環(huán)遍歷 idxs,當(dāng) idxs 中沒(méi)有元素時(shí)終止循環(huán)帮哈。代碼 20 到 22 行我們獲取 idxs 中最后一個(gè)元素并將其添加到 temp 中膛檀。
代碼 24 行我們使用 np.maximum 方法將 x1[i] 與 boxes 中其他矩形框的左上角橫坐標(biāo)兩兩比較, 將較大的值保存在數(shù)組 x1_m 中。同樣代碼 25 行將 y1[i] 與 boxes 中其他矩形框的左上角縱坐標(biāo)兩兩比較咖刃,將較大的值保存在數(shù)組 y1_m 中泳炉。兩個(gè)矩形框重疊的部分是矩形,所以這一步的目的是為了找到這個(gè)重疊矩形的左上角頂點(diǎn)嚎杨。同理胡桃,27、28 兩行代碼的目的是為了找出這個(gè)重疊矩形的右下角頂點(diǎn)磕潮。我們使用 np.minimum 將 x2[i] 與 boxes 中其他矩形框的右下角橫坐標(biāo)兩兩比較, 將較小的值保存在數(shù)組 x2_m 中容贝。同樣的再將 y2[i] 與 boxes 中其他矩形框的右下角縱坐標(biāo)兩兩比較自脯,將較小的值保存在數(shù)組 y2_m 中。
有了重疊矩形的兩個(gè)頂點(diǎn)坐標(biāo)斤富,我們就可以計(jì)算矩形的寬和高膏潮,進(jìn)而可以計(jì)算矩形的面積。第 30满力,31 行代碼是分別計(jì)算矩形的寬和高焕参,我們使用 np.maximum 方法來(lái)剔除掉沒(méi)有相交的矩形。如果兩個(gè)矩形框相交油额,則 x2_m - x1_m + 1 和 y2_m - y1_m + 1 大于零叠纷,如果兩個(gè)矩形框不相交則這兩個(gè)值小于零。
33 行代碼表示計(jì)算重疊矩形面積和 area 中的面積的比值 over潦嘶,這一步和計(jì)算 IoU 是等效的涩嚣。35 行代碼的目的是為了剔除重疊的矩形框。我們使用 np.where 判斷 over 中的元素是否大于設(shè)定的閾值 threshold掂僵,如果大于這個(gè)閾值則返回這個(gè)元素的下標(biāo)航厚。接著使用 np.concatenate 方法將 idxs 中最后的元素和返回的下標(biāo)拼接在一起。最后通過(guò) np.delete 方法從 idxs 中刪除這些下標(biāo)對(duì)應(yīng)的元素锰蓬。
通過(guò)上一步我們刪除了與 idxs 中 last 對(duì)應(yīng)的矩形框相互重疊且面積大于閾值的矩形框(同時(shí)也從 idxs 中刪除最后一個(gè)元素)幔睬,然后進(jìn)入下一個(gè)循環(huán)直到 idxs 中的元素個(gè)數(shù)為 0,最后我們通過(guò)下面一行代碼返回挑選后的矩形框芹扭,同時(shí)我們需要使用 astype 方法將 boxes 中的浮點(diǎn)類(lèi)型轉(zhuǎn)換為整數(shù)類(lèi)型麻顶。
接下來(lái),我們將使用非極大值抑制的函數(shù)來(lái)剔除檢測(cè)結(jié)果中多余的窗口冯勉。首先我們創(chuàng)建一個(gè) img_path 變量用于保存圖片路徑澈蚌,然后調(diào)用 run 函數(shù),run 函數(shù)將返回 2 個(gè)值 roi_loc 和 image灼狰。roi_loc 里保存了一個(gè)或多個(gè)矩形框的頂點(diǎn)坐標(biāo)宛瞄、寬和高,這些矩形框內(nèi)的區(qū)域被模型認(rèn)為是有人存在的。image 是縮放后輸入圖片份汗,我們將在這張圖片上用矩形框標(biāo)記出行人盈电。

img_path = "man.jpg"
roi_loc, image = run(img_path=img_path)

接下來(lái)我們將使用 cv2.rectangle 方法在 image 上畫(huà)出矩形框,這些矩形框是通過(guò) NMS 方法獲得的沒(méi)有重疊的矩形框杯活。

for (x, y, w, h) in NMS(roi_loc, threshold=0.3):
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)

最后我們通過(guò)下面的代碼來(lái)顯示檢測(cè)結(jié)果匆帚,首先從 matplotlib 導(dǎo)入 pyplot,然后我們使用 %matplotlib inline 魔法函數(shù)讓圖片在頁(yè)面中顯示旁钧。我們使用 plt.figure(figsize = (10,10)) 創(chuàng)建一個(gè)寬和高都是 10 英寸的圖像實(shí)例吸重。然后使用 resized[:,:,::-1] 切片方法將圖片通道的順序調(diào)轉(zhuǎn),最后使用 plt.imshow 在頁(yè)面中呈現(xiàn)繪圖后的結(jié)果歪今。

from matplotlib import pyplot as plt
%matplotlib inline

plt.figure(figsize = (10,10))
image = image[:,:,::-1]
plt.imshow(image)

如果腳本運(yùn)行正常我們能看到類(lèi)似下圖結(jié)果嚎幸,相較于左邊沒(méi)有使用 NMS 方法獲得的檢測(cè)圖片,我們剔除了冗余的矩形框使得目標(biāo)只被一個(gè)矩形框標(biāo)記寄猩。


image.png

將 img_path = "man.jpg" 修改為 img_path = "people.jpg" 我們將得到類(lèi)似下圖結(jié)果嫉晶,同樣的通過(guò)使用 NMS 方法我們剔除了大部分的矩形框。


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末田篇,一起剝皮案震驚了整個(gè)濱河市替废,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌泊柬,老刑警劉巖椎镣,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異彬呻,居然都是意外死亡衣陶,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)闸氮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)剪况,“玉大人,你說(shuō)我怎么就攤上這事蒲跨∫攵希” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵或悲,是天一觀的道長(zhǎng)孙咪。 經(jīng)常有香客問(wèn)我,道長(zhǎng)巡语,這世上最難降的妖魔是什么翎蹈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮男公,結(jié)果婚禮上荤堪,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好澄阳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布拥知。 她就那樣靜靜地躺著,像睡著了一般碎赢。 火紅的嫁衣襯著肌膚如雪低剔。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天肮塞,我揣著相機(jī)與錄音襟齿,去河邊找鬼。 笑死枕赵,一個(gè)胖子當(dāng)著我的面吹牛蕊唐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播烁设,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼钓试!你這毒婦竟也來(lái)了装黑?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤弓熏,失蹤者是張志新(化名)和其女友劉穎恋谭,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體挽鞠,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疚颊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了信认。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片材义。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嫁赏,靈堂內(nèi)的尸體忽然破棺而出其掂,到底是詐尸還是另有隱情,我是刑警寧澤潦蝇,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布款熬,位于F島的核電站,受9級(jí)特大地震影響攘乒,放射性物質(zhì)發(fā)生泄漏贤牛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一则酝、第九天 我趴在偏房一處隱蔽的房頂上張望殉簸。 院中可真熱鬧,春花似錦、人聲如沸喂链。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)椭微。三九已至洞坑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蝇率,已是汗流浹背迟杂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留本慕,地道東北人排拷。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像锅尘,于是被迫代替她去往敵國(guó)和親监氢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容