NMS算法常常在图像目标检测的各种算法中使用。原始图像中,单个检测目标往往被识别出多个位置、大小都不相同的目标检测框,这些检测框可以是类似于这样的名字,bbox(bounding box), region proposal, anchors等等。可以在诸如faster rcnn或YOLO算法中看到。
举例来讲,原始图中包含两个检测物体,每个检测物体会存在许多检测框。因为是同一个物体,因此这些框的IOU值都很高(相比较不同物体的检测框来说)。针对同一个检测目标,有必要只留下置信度(score)最高的那个检测框作为该物体的检测框,其它的都应该过滤掉。
该算法具体的步骤如下:
1、假定有6个带置信率的region proposals,并预设一个IOU的阈值如0.7。
2、按置信率大小对6个框排序: 0.95, 0.9, 0.9, 0.8, 0.7, 0.7。
3、设定置信率为0.95的region proposals为一个物体框;
4、在剩下5个region proposals中,去掉与0.95物体框IOU大于0.7的。
5、重复2~4的步骤,直到没有region proposals为止。
6、每次获取到的最大置信率的region proposals就是我们筛选出来的目标。
这个操作的直观结果,就是图片上每个待检查物体,仅仅保留一个置信率最大region proposals。
凡是与这个置信率最大的框相交面积大于某个阈值的其它框,统统去掉。
因此,调整这个IOU的阈值,往往是图像目标检测算法的一个调整点。
以下是一段用来实现了NMS算法的代码:
'''
# This file isn't part of faster CNN, just for testing something.
'''
# 验证MNS算法
import numpy as np
dets = np.array([
[204, 102, 358, 250, 0.5],
[257, 118, 380, 250, 0.7],
[280, 135, 400, 250, 0.6],
[255, 118, 360, 235, 0.7]])
thresh = 0.7
def py_cpu_nms(dets, thresh):
"""Pure Python NMS baseline."""
x1 = dets[:, 0] #xmin
y1 = dets[:, 1] #ymin
x2 = dets[:, 2] #xmax
y2 = dets[:, 3] #ymax
scores = dets[:, 4] #confidence
areas = (x2 - x1 + 1) * (y2 - y1 + 1) #the size of bbox
order = scores.argsort()[::-1] #sort bounding boxes by decreasing order, returning array([3, 1, 2, 0])
keep = [] # store the final bounding boxes
while order.size > 0:
i = order[0] # 永远取置信率最高的框作为物体的最佳框
keep.append(i) # 保存起来
# 获取两个框相交矩形的坐标
# 左上角坐标取二者最大;右下角取二者最小
xx1 = np.maximum(x1[i], x1[order[1:]]) #array([ 257., 280., 255.])
yy1 = np.maximum(y1[i], y1[order[1:]]) #array([ 118., 135., 118.])
xx2 = np.minimum(x2[i], x2[order[1:]]) #array([ 360., 360., 358.])
yy2 = np.minimum(y2[i], y2[order[1:]]) #array([ 235., 235., 235.])
w = np.maximum(0.0, xx2 - xx1 + 1) #array([ 104., 81., 104.])
h = np.maximum(0.0, yy2 - yy1 + 1) #array([ 118., 101., 118.])
# 计算相交矩形面积
inter = w * h #array([ 12272., 8181., 12272.])
# 计算IOU
# Cross Area / (bbox + particular area - Cross Area)
ovr = inter / (areas[i] + areas[order[1:]] - inter)
#reserve all the boundingbox whose ovr less than thresh
# 找出与当前置信率最高框相交小于阈值的框的索引
inds = np.where(ovr <= thresh)[0]
# 只保留比例小于阙值的框,然后继续处理
order = order[inds + 1]
return keep
index = py_cpu_nms(dets, thresh)
print(index)
print(dets[index])