非极大值抑制(Non-Maximum Suppression,NMS),顾名思义就是抑制不是极大值的元素。例如在行人检测中,滑动窗口经提取特征,经分类器分类识别后,每个窗口都会得到一个分数。但是滑动窗口会导致很多窗口与其他窗口存在包含或者大部分交叉的情况。这时就需要用到NMS来选取那些邻域里分数最高(是行人的概率最大),并且抑制那些分数低的窗口。 NMS在计算机视觉领域有着非常重要的应用,如视频目标跟踪、数据挖掘、3D重建、目标识别以及纹理分析等 。
在目标检测中,NMS的目的就是要去除冗余的检测框,保留最好的一个,如下图所示:
NMS的具体工作流程是,对于存放预测框的列表B以及预测框对应的置信度S,首先选择具有最大score的检测框M,将其从B集合中移除并加入到最终的检测结果列表D中.通常将B中剩余检测框中与M的IoU(求交并比)大于阈值Nt的框从B中移除.重复这个过程,直到B为空。目的就是去除冗余的检测框。
通过一个例子看些NMS的使用方法,假设定位车辆,算法就找出了一系列的矩形框,我们需要判别哪些矩形框是没用的,需要使用NMS的方法来实现。
设现在检测窗口有:A、B、C、D、E 5个候选框,接下来进行迭代计算:
- 第一轮:因为B是得分最高的,与B的IoU>0.5删除。A,CDE中现在与B计算IoU,DE结果>0.5,剔除DE,B作为一个预测结果,有个检测框留下B,放入集合
- 第二轮:A的得分最高,与A计算IoU,C的结果>0.5,剔除C,A作为一个结果
最终结果为在这个5个中检测出了两个目标为A和B。
代码实现:
import numpy as np
def nums(bboxes,confidence_score,threshold):
# bboxes同类候选框坐标,confidence_score同类候选框得分,threshold:iou阈值
# 传入无候选框的情况
if len(bboxes)==0:
return [],[]
bboxes=np.array(bboxes)
score=np.array(confidence_score)
# 取出几个极坐标点
x1=bboxes[:,0]
y1=bboxes[:,1]
x2=bboxes[:,2]
y2=bboxes[:,3]
# 对候选框进行NMS筛选
# 返回的框坐标和分数
picked_bboxes=[]
picked_score=[]
# 对置信度进行排序, 获取排序后的下标序号, argsort默认从小到大排序
order=np.argsort(score)
areas=(x2-x1)*(y2-y1)
while order.size>0:
# 将当前置信度最大的框加入返回值列表中
index=order[-1]
#保留该类剩余box中得分最高的一个
picked_bboxes.append(bboxes[index])
picked_score.append(confidence_score[index])
# 获取当前置信度最大的候选框与其他任意候选框的相交面积
x11=np.maximum(x1[index],x1[order[:-1]])
y11=np.maximum(y1[index],y1[order[:-1]])
x22=np.minimum(x2[index],x2[order[:-1]])
y22=np.minimum(y2[index],y2[order[:-1]])
# 求相交的面积
w=np.maximum(0,x22-x11)
h=np.maximum(0,y22-y11)
# 求相交的面积
intersection=w*h
# 求交并比
ratio=intersection/(areas[index]+areas[order[:-1]]-intersection)
# 保留iou小于阈值的box,保留的是索引
keep_boxs_indics=np.where(ratio<threshold)
# 保留剩余的框
order=order[keep_boxs_indics]
# 返回NMS后的框及分类的结果
return picked_bboxes,picked_score
bounding = [(187, 82, 337, 317), (150, 67, 305, 282), (246, 121, 368, 304)]
confidence_score = [0.9, 0.65, 0.8]
threshold = 0.3
picked_bboxes,picked_score=nums(bounding,confidence_score,threshold)
print(picked_bboxes)
print(picked_score)