天天看點

MTCNN中的重疊度IOU和非極大值抑制NMS原理及Python實作

MTCNN中的重疊度IOU和非極大值抑制NMS原理及Python實作

一.重疊度iou

從在一張照片上框人臉時,因為圖像金字塔的原因可能會把人臉框兩次以上,每個框的坐标為[X1,Y1X2,Y2,C],其中X1,Y1是框左上角的點,X2,Y2是框右下角的點,C是置信度:這個框正确框到人臉的程度。在MTCNN中,iou包含兩種:最小框iou、并集iou。

原理

1.并集iou

如下圖所示,并集iou為交集面積除以并集面積,并集iou用在MTCNN中的P、R網絡中

MTCNN中的重疊度IOU和非極大值抑制NMS原理及Python實作

2.最小框iou

如果是大框包含小框的情況,就要用最小框iou,最小框iou表示交集面積除以最小框的面積,用在MTCNN中的O網絡中

MTCNN中的重疊度IOU和非極大值抑制NMS原理及Python實作

二.非極大值抑制NMS

如下圖所示,NMS顧名思義,就是把正确框到人臉的框留下來,誤差比較大的框删去

MTCNN中的重疊度IOU和非極大值抑制NMS原理及Python實作
MTCNN中的重疊度IOU和非極大值抑制NMS原理及Python實作

原理

下面的圖檔裡有兩張人臉,每張人臉上面有框,數字是置信度C,置信度C越大說明框到整個人臉的機率越大。那有人說直接留下兩個置信度C最大的不就行了嘛?很明顯,0.9和0.81最大,但是是在一張人臉上,這個方法不行,那下面介紹正确方法

MTCNN中的重疊度IOU和非極大值抑制NMS原理及Python實作

如下圖所示:

1.先按照置信度C排序

2.從最大的開始,依次往後計算iou,設定一個門檻值,大于該門檻值不保留小于該門檻值保留

3.由2,排除0.81,還剩下0.8,0.7,0.6三個框再以0.8開始,往後依次計算iou

4.最後隻剩下了0.9,0.8兩個框

MTCNN中的重疊度IOU和非極大值抑制NMS原理及Python實作

三.程式實作

1.

iou

首先,我随便畫了幾個框,坐标是

[[100,100,150,150,4],

[50,50,200,200,6],

[120,120,250,250,3],

[300,50,400,150,1],

[380,130,500,250,5],

[380,25,450,95,2]]

随便找一張背景圖,并把框畫在圖上

MTCNN中的重疊度IOU和非極大值抑制NMS原理及Python實作

定義iou函數,計算第一個框和後面所有框的面積:注意切片的用法

def iou(box,boxes,isMin=False):#box格式[x1,y1,x2,y2,c]
    #一個框面積
    box_area=(box[2]-box[0])*(box[3]-box[1])
    #多個框面積
    boxes_area=(boxes[:,2]-boxes[:,0])*(boxes[:,3]-boxes[:,1])
    
           

這是計算交集之前的技巧,計算交集先要找構成交集的點,總計四句話:

較大的左上角的橫坐标,

較大的左下角的縱坐标,

較小的右下角的橫坐标,

較小的右下角的縱坐标。

#找交集
    xx1=np.maximum(box[0],boxes[:,0])
    yy1 = np.maximum(box[1], boxes[:, 1])
    xx2 = np.minimum(box[2], boxes[:, 2])
    yy2 = np.minimum(box[3], boxes[:, 3])
           

計算之前先要判斷是否有交集

#判斷是否有交集
    w=np.maximum(0,xx2-xx1)
    h=np.maximum(0,yy2-yy1)
           

計算交集面積并計算iou,有兩種iou

inter=w*h
    #iou
    if isMin:#最小面積iou
        over=np.true_divide(inter,np.minimum(box_area,boxes_area))
    else:#交集除以并集iou
        over=np.true_divide(inter,(box_area+boxes_area-inter))
    return over
           

2.

NMS

定義NMS函數,設一個門檻值thresh,并按照置信度排序

def nms(boxes,thresh=0.05,isMin=False):
    #根據置信度對框排序
    _boxes=boxes[(-boxes[:,4]).argsort()]
    r_boxes=[]
           

分别取排完序的第一個框和剩餘的框,計算iou,保留較小iou的框

while _boxes.shape[0]>1:
        #取出第一個框
        a_box=_boxes[0]
        #取出剩餘的框
        b_boxes=_boxes[1:]
        #保留第一個框
        r_boxes.append(a_box)
        #比較iou後保留iou小的框
        index=np.where(iou(a_box,b_boxes,isMin)<thresh)
        _boxes=b_boxes[index]
           

下面就是主函數,自己設定了幾個框,a.jpg是背景圖,先把框畫到背景圖上,然後調用前面的NMS函數,計算完成之後得到的框再畫出來,效果在下面

if __name__ == '__main__':
    bs=np.array([[100,100,150,150,4],[50,50,200,200,6],[120,120,250,250,3],
                [300,50,400,150,1],[380,130,500,250,5],[380,25,450,95,2]])
    # NMS之前
    img = image.open("a.jpg")
    draw = imgdraw.Draw(img)

    for i in range(bs.shape[0]):
        ai=bs[i, :4]
        draw.rectangle((ai[0], ai[1], ai[2],ai[3]), outline="red")
    img.show()

    img1 = image.open("a.jpg")
    draw1 = imgdraw.Draw(img1)
    b=nms(bs)
    #NMS之後
    for i in range(b.shape[0]):
        bi=b[i,:4]
        draw1.rectangle((bi[0], bi[1], bi[2], bi[3]), outline="red")
    img1.show()
           

結果:有一個框沒有排除掉,不知道是哪裡的問題,你知道的話可以留言

MTCNN中的重疊度IOU和非極大值抑制NMS原理及Python實作

所有程式

import PIL.ImageDraw as imgdraw
import PIL.Image  as image
import numpy as np
#iou
def iou(box,boxes,isMin=False):#box格式[x1,y1,x2,y2,c]
    #一個框面積
    box_area=(box[2]-box[0])*(box[3]-box[1])
    #多個框面積
    boxes_area=(boxes[:,2]-boxes[:,0])*(boxes[:,3]-boxes[:,1])
    #找交集
    xx1=np.maximum(box[0],boxes[:,0])
    yy1 = np.maximum(box[1], boxes[:, 1])
    xx2 = np.minimum(box[2], boxes[:, 2])
    yy2 = np.minimum(box[3], boxes[:, 3])
    #判斷是否有交集
    w=np.maximum(0,xx2-xx1)
    h=np.maximum(0,yy2-yy1)
    #交集面積
    inter=w*h
    #iou
    if isMin:#最小面積iou
        over=np.true_divide(inter,np.minimum(box_area,boxes_area))
    else:#交集除以并集iou
        over=np.true_divide(inter,(box_area+boxes_area-inter))
    return over

#NMS
def nms(boxes,thresh=0.05,isMin=False):
    #根據置信度對框排序
    _boxes=boxes[(-boxes[:,4]).argsort()]
    r_boxes=[]
    while _boxes.shape[0]>1:
        #取出第一個框
        a_box=_boxes[0]
        #取出剩餘的框
        b_boxes=_boxes[1:]
        #保留第一個框
        r_boxes.append(a_box)
        #比較iou後保留iou小的框
        index=np.where(iou(a_box,b_boxes,isMin)<thresh)
        _boxes=b_boxes[index]
    if _boxes.shape[0]>0:
        r_boxes.append(_boxes[0])
    return np.stack(r_boxes)#stack:組裝為矩陣
if __name__ == '__main__':
    bs=np.array([[100,100,150,150,4],[50,50,200,200,6],[120,120,250,250,3],
                [300,50,400,150,1],[380,130,500,250,5],[380,25,450,95,2]])
    # NMS之前
    img = image.open("a.jpg")
    draw = imgdraw.Draw(img)

    for i in range(bs.shape[0]):
        ai=bs[i, :4]
        draw.rectangle((ai[0], ai[1], ai[2],ai[3]), outline="red")
    img.show()

    img1 = image.open("a.jpg")
    draw1 = imgdraw.Draw(img1)
    b=nms(bs)
    #NMS之後
    for i in range(b.shape[0]):
        bi=b[i,:4]
        draw1.rectangle((bi[0], bi[1], bi[2], bi[3]), outline="red")
    img1.show()






           

轉載或引用請注明來源!

繼續閱讀