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網絡中
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPn5EMZR0T5tGROBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLxkTN0QTMxATM3IjMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
2.最小框iou
如果是大框包含小框的情況,就要用最小框iou,最小框iou表示交集面積除以最小框的面積,用在MTCNN中的O網絡中
二.非極大值抑制NMS
如下圖所示,NMS顧名思義,就是把正确框到人臉的框留下來,誤差比較大的框删去
原理
下面的圖檔裡有兩張人臉,每張人臉上面有框,數字是置信度C,置信度C越大說明框到整個人臉的機率越大。那有人說直接留下兩個置信度C最大的不就行了嘛?很明顯,0.9和0.81最大,但是是在一張人臉上,這個方法不行,那下面介紹正确方法
如下圖所示:
1.先按照置信度C排序
2.從最大的開始,依次往後計算iou,設定一個門檻值,大于該門檻值不保留小于該門檻值保留
3.由2,排除0.81,還剩下0.8,0.7,0.6三個框再以0.8開始,往後依次計算iou
4.最後隻剩下了0.9,0.8兩個框
三.程式實作
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]]
随便找一張背景圖,并把框畫在圖上
定義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()
結果:有一個框沒有排除掉,不知道是哪裡的問題,你知道的話可以留言
所有程式
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()
轉載或引用請注明來源!