非极大值抑制(Non-Maximum Suppression,NMS),顾名思义就是抑制不是极大值的元素,可以理解为局部最大搜索。这个局部代表的是一个邻域,邻域有两个参数可变,一是邻域的维数,二是邻域的大小。这里主要介绍计算机视觉中常用的NMS,不讨论通用NMS算法(可以参考论文《Efficient Non-Maximum Suppression》对1维和2维数据进行非极大值抑制,比如在传统的边缘检测算法会用到)。
NMS原理
NMS算法常用于计算机视觉目标检测任务中来解决候选检测框中重叠候选框过多的问题,比如人脸检测中同一个人脸,检测器最终会给出多个检测框,每个检测框的得分阈值都满足最低阈值要求,而且无法通过检测器进一步降低重叠候选框的个数,这时需要通过nms算法进一步过滤掉重叠候选框,得到最终的检测框。
基本思想:
对一系列Bounding box组成的列表B,以及bbox对应的置信度得分score组成的列表S,首先选择B中具有最大得分score的检测框M,将其从列表B中移除并加入最终的检测框列表D中,然后依次计算B中剩余检测框与M的IOU值,将IOU值大于设定阈值(一般设置为03~0.5)的框从B中移除,重复这个过程,直到列表B为空。
具体步骤:
以上面图片中车辆的定位为例(上图涉及到多类别的NMS),检测算法回归了一系列候选框,而且大部分都是重叠的,这时需要从这些重叠的候选框中选出最佳候选框(这里的最佳只是NMS算法认为的最佳,如果检测器出现误检或者检测不准,经过NMS后最终检测框还是不准或者误检,简单点说NMS只是从一堆候选框中筛选出它认为最佳的候选框,最终候选框是否准确还是由检测算法决定的),具体步骤如下:
先假设有6个候选框,根据分类器的类别分类概率做排序,假设从小到大属于车辆的概率 分别为A、B、C、D、E、F。
- 从最大概率候选框F开始,分别判断A~E与F的重叠度IOU是否大于某个设定的阈值;
- 假设B、D与F的重叠度超过阈值,那么就扔掉B、D,也就是说在B,D,F三个候选框中选择保留F,而B、D候选框丢弃。
- 从剩下的候选框A、C、E中,选择概率最大的E,然后判断E与A、C的重叠度IOU,重叠度大于一定的阈值的候选框则丢掉,并保留候选框E。
- 就这样一直重复,找到所有被保留下来的候选框。
NMS算法代码实现
def py_cpu_nms(dets, thresh):
# dets:(m,5) thresh:scaler
x1 = dets[:,0]
y1 = dets[:,1]
x2 = dets[:,2]
y2 = dets[:,3]
areas = (y2-y1+1) * (x2-x1+1)
scores = dets[:,4]
keep = []
index = scores.argsort()[::-1] # 将候选列表的得分按照从大到小进行排序,并返回候选框的索引
while index.size >0:
i = index[0] # 每次选择剩下候选框列表中得分最大的候选框作为最终保留的候选框,并以该候选框为基准候选框剔除剩下候选框中重叠度过高的候选框
keep.append(i)
# 获取所有剩下候选框与当前基准候选框的重叠区域坐标
x11 = np.maximum(x1[i], x1[index[1:]])
y11 = np.maximum(y1[i], y1[index[1:]])
x22 = np.minimum(x2[i], x2[index[1:]])
y22 = np.minimum(y2[i], y2[index[1:]])
w = np.maximum(0, x22-x11+1) # the weights of overlap
h = np.maximum(0, y22-y11+1) # the height of overlap
overlaps = w*h
ious = overlaps / (areas[i]+areas[index[1:]] - overlaps) # 获取所有剩下候选框与当前基准候选框的IOU值
idx = np.where(ious<=thresh)[0] # 获取ious中iou小于阈值的索引,这里的索引其实是相对index[1:],因此在下一步的迭代中需要将idx + 1
index = index[idx+1] # 对剩下iou小于阈值的候选框进行下一次迭代
return keep