一.重叠度iou
从在一张照片上框人脸时,因为图像金字塔的原因可能会把人脸框两次以上,每个框的坐标为[X1,Y1X2,Y2,C],其中X1,Y1是框左上角的点,X2,Y2是框右下角的点,C是置信度:这个框正确框到人脸的程度。在MTCNN中,iou包含两种:最小框iou、并集iou。
原理
1.并集iou
如下图所示,并集iou为交集面积除以并集面积,并集iou用在MTCNN中的P、R网络中
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()
转载或引用请注明来源!