非极大值抑制(nms)算法详解[python]
一、起源
目标检测在使用了基于深度学习的端到端模型后效果斐然。目前,常用的目标检测算法,无论是One-stage的SSD系列算法、YOLO系列算法还是Two-stage的基于RCNN系列的算法,非极大值抑制都是其中必不可少的一个组件。在现有的基于anchor的目标检测算法中,都会产生数量巨大的候选矩形框,这些矩形框有很多是指向同一目标,因此就存在大量冗余的候选矩形框。非极大值抑制算法的目的正在于此,它可以消除多余的框,找到最佳的物体检测位置。
非极大值抑制(Non-Maximum Suppression,以下简称NMS算法)的思想是搜索局部极大值,抑制非极大值元素。针对不同的应用场景和检测算法,由于矩形框的表征方式不同,NMS算法具有各种变体,本文针对NMS及其各种变体(主要是本文检测相关)进行介绍。
二、经典NMS
经典NMS最初第一次应用到目标检测中是在RCNN算法中,其实现严格按照搜索局部极大值,抑制非极大值元素的思想来实现的,具体的实现步骤如下:
设定目标框的置信度阈值,常用的阈值是0.5左右
根据置信度降序排列候选框列表
选取置信度最高的框A添加到输出列表,并将其从候选框列表中删除
计算A与候选框列表中的所有框的IoU值,删除大于阈值的候选框
重复上述过程,直到候选框列表为空,返回输出列表
其中IoU(Intersection over Union)为交并比,如图1所示,IoU相当于两个区域交叉的部分除以两个区域的并集部分得出的结果。图2是IoU为各个取值时的情况展示,一般来说,这个score > 0.5 就可以被认为一个不错的结果了。
图1
图2
下面,通过一个具体例子来说明经典NMS究竟做了什么。图3的左图是包含一个检测目标(王闹海)的实例图片。其中的绿色矩形框代表了经过目标检测算法后,生成的大量的带置信度的Bounding box,矩形框左下角的浮点数即代表该Bounding box的置信度。在这里,使用Python对经典NMS算法实现,并应用到该实例中去。当NMS的阈值设为0.2时,最后的效果如图3中右图所示。
图3
Python代码如下:
本文所有代码和数据的github地址:https://github.com/lzneu/letGo/tree/master/nms
def nms(bounding_boxes, Nt):if len(bounding_boxes) == 0:return [], []bboxes = np.array(bounding_boxes)
# 计算 n 个候选框的面积大小x1 = bboxes[:, 0]y1 = bboxes[:, 1]x2 = bboxes[:, 2]y2 = bboxes[:, 3]scores = bboxes[:, 4]areas = (x2 - x1 + 1) * (y2 - y1 + 1)
# 对置信度进行排序, 获取排序后的下标序号, argsort 默认从小到大排序order = np.argsort(scores)
picked_boxes = [] # 返回值while order.size > 0:# 将当前置信度最大的框加入返回值列表中index = order[-1]picked_boxes.append(bounding_boxes[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.0, x22 - x11 + 1)h = np.maximum(0.0, y22 - y11 + 1)intersection = w * h
# 利用相交的面积和两个框自身的面积计算框的交并比, 将交并比大于阈值的框删除ious = intersection / (areas[index] + areas[order[:-1]] - intersection)left = np.where(ious < Nt)order = order[left]return picked_boxes
三、soft-NMS
经典NMS是为了去除重复的预测框,这种算法在图片中只有单个物体被检测的情况下具有很好的效果,如上图所示,“腰子”王闹海被成功的检测出来。然而,经典NMS算法存在着一些问题:对于重叠物体无法很好的检测。当图像中存在两个重叠度很高的物体时,经典NMS会过滤掉其中置信度较低的一个。如下图所示,图中存在两个王闹海,经典NMS过滤后的结果如下图4所示:
图4
而我们期望的结果是两个腰子都被算法成功检测出来。
为了解决这类问题,Jan Hosang,等人提出了Soft-NMS算法。Soft-NMS的算法伪代码如图5所示。其中红色框为经典NMS的步骤,而绿色框中的内容为Soft-NMS改进的步骤。可以看出,相对于经典NMS算法,Soft-NMS仅仅修改了一行代码。当选取了最大置信度的Bounding box之后,计算其余每个Bounding box与Bounding box的I ou值,经典NMS算法的做法是直接删除Iou大于阈值的Bounding box;而Soft-NMS则是使用一个基于Iou的衰减函数,降低Iou大于阈值Nt的Bounding box的置信度,IoU越大,衰减程度越大。
换一个角度来理解,经典NMS同样可以用一个基于Iou的衰减函数来表示,只是这个衰减函数是个0-1函数,当Iou大于阈值Nt,直接降低该Bounding box的置信度到0。关于经典NMS和Soft-NMS的衰减函数区别,可以通过如下的公式来表示[1]:
经典NMS:
Soft-NMS-线性:
Soft-NMS-高斯:
图5
接下来, 继续使用图4的实例来验证Soft-NMS的效果,使用Python对Soft-NMS算法的实现如下
Python代码:
def soft_nms(bboxes, Nt=0.3, sigma2=0.5, score_thresh=0.3, method=2):# 在 bboxes 之后添加对于的下标[0, 1, 2...], 最终 bboxes 的 shape 为 [n, 5], 前四个为坐标, 后一个为下标res_bboxes = deepcopy(bboxes)N = bboxes.shape[0] # 总的 box 的数量indexes = np.array([np.arange(N)]) # 下标: 0, 1, 2, ..., n-1bboxes = np.concatenate((bboxes, indexes.T), axis=1) # concatenate 之后, bboxes 的操作不会对外部变量产生影响# 计算每个 box 的面积x1 = bboxes[:, 0]y1 = bboxes[:, 1]x2 = bboxes[:, 2]y2 = bboxes[:, 3]scores = bboxes[:, 4]areas = (x2 - x1 + 1) * (y2 - y1 + 1)
for i in range(N):# 找出 i 后面的最大 score 及其下标pos = i + 1if i != N - 1:maxscore = np.max(scores[pos:], axis=0)maxpos = np.argmax(scores[pos:], axis=0)else:maxscore = scores[-1]maxpos = 0# 如果当前 i 的得分小于后面的最大 score, 则与之交换, 确保 i 上的 score 最大if scores[i] < maxscore:bboxes[[i, maxpos + i + 1]] = bboxes[[maxpos + i + 1, i]]scores[[i, maxpos + i + 1]] = scores[[maxpos + i + 1, i]]areas[[i, maxpos + i + 1]] = areas[[maxpos + i + 1, i]]# IoU calculatexx1 = np.maximum(bboxes[i, 0], bboxes[pos:, 0])yy1 = np.maximum(bboxes[i, 1], bboxes[pos:, 1])xx2 = np.minimum(bboxes[i, 2], bboxes[pos:, 2])yy2 = np.minimum(bboxes[i, 3], bboxes[pos:, 3])w = np.maximum(0.0, xx2 - xx1 + 1)h = np.maximum(0.0, yy2 - yy1 + 1)intersection = w * hiou = intersection / (areas[i] + areas[pos:] - intersection)# Three methods: 1.linear 2.gaussian 3.original NMSif method == 1: # linearweight = np.ones(iou.shape)weight[iou > Nt] = weight[iou > Nt] - iou[iou > Nt]elif method == 2: # gaussianweight = np.exp(-(iou * iou) / sigma2)else: # original NMSweight = np.ones(iou.shape)weight[iou > Nt] = 0scores[pos:] = weight * scores[pos:]# select the boxes and keep the corresponding indexesinds = bboxes[:, 5][scores > score_thresh]keep = inds.astype(int)return res_bboxes[keep]
图6
四、Locality-Aware NMS
以上的NMS或相关算法均为针对目标检测问题而提出的,而文本检测作为目标检测的一个特殊领域,具有其特殊之处,比如文本检测中面临的成千上万的候选几何体,如果使用计算复杂度为 的经典NMS类算法,由于n的数量过大,这是不可接受的。为此旷世公司于2017在EAST(一种非常好用的One-stage的文本检测算法,敬请关注交易只能团队下期文章!)论文[2]中提出了Locality-Aware NMS算法。
Locality-Aware NMS算法考虑了文本检测任务中临近像素高度相关的特征,提出了一种基于行的几何体合并方法,它的步骤主要是两个:
合并同一行区域中的几何体积
对合并后的Bounding box集合进行标准的NMS操作
Locality-Aware NMS提升了算法的时间复杂度,在最优情况下可以提升至 。即使是最坏情况,也不会比经典NMS的计算量更大。
Python代码如下:
import numpy as np
from shapely.geometry import Polygon
def intersection(g, p):# 取g,p中的几何体信息组成多边形g = Polygon(g[:8].reshape((4, 2)))p = Polygon(p[:8].reshape((4, 2)))# 判断g,p是否为有效的多边形几何体if not g.is_valid or not p.is_valid:return 0# 取两个几何体的交集和并集inter = Polygon(g).intersection(Polygon(p)).areaunion = g.area + p.area - interif union == 0:return 0else:return inter / union
def weighted_merge(g, p):# 取g,p两个几何体的加权(权重根据对应的检测得分计算得到)g[:8] = (g[8] * g[:8] + p[8] * p[:8]) / (g[8] + p[8])# 合并后的几何体的得分为两个几何体得分的总和g[8] = (g[8] + p[8])return g
def standard_nms(S, thres):# 标准NMSorder = np.argsort(S[:, 8])[::-1]keep = []while order.size > 0:i = order[0]keep.append(i)ovr = np.array([intersection(S[i], S[t]) for t in order[1:]])inds = np.where(ovr <= thres)[0]order = order[inds + 1]return S[keep]
def nms_locality(polys, thres=0.3):'''locality aware nms of EAST:param polys: a N*9 numpy array. first 8 coordinates, then prob:return: boxes after nms'''S = [] # 合并后的几何体集合p = None # 合并后的几何体for g in polys:if p is not None and intersection(g, p) > thres: # 若两个几何体的相交面积大于指定的阈值,则进行合并p = weighted_merge(g, p)else: # 反之,则保留当前的几何体if p is not None:S.append(p)p = gif p is not None:S.append(p)if len(S) == 0:return np.array([])return standard_nms(np.array(S), thres)
五、Softer-NMS
以上的NMS算法,无论是经典NMS还是Soft-NMS算法,都是在一种假设前提下:置信度最高的Bounding box就是目标的候选位置最精确的物体位置。但是事实上,这个假设可能并不成立,或者说并不那么精确。针对这个问题,来自卡内基梅隆大学与旷视科技的研究人员在文中提出了一种新的非极大抑制算法Softer-NMS,显著改进了目标检测的定位精度,代码已经开源,目前Github上的Star已超100,可谓短短两天已经引起了不小的关注。作者关注了两种目前NMS会出现的问题:
所有的候选Bounding box都够精确,无法确定该选择哪一个,见图7a
具有高置信度的Bounding box不够精确,见图7b
图7
Softer-NMS正是为了这两个问题而设计的。关于Softer-NMS的相关解析,敬请关注交易智能团队后续分享。
参考文献:
[1] Bodla N , Singh B , Chellappa R , et al. Improving Object Detection With One Line of Code[J]. 2017.
[2] Zhou X , Yao C , Wen H , et al. EAST: An Efficient and Accurate Scene Text Detector[J]. 2017.
[3] He Y , Zhu C , Wang J , et al. Bounding Box Regression with Uncertainty for Accurate Object Detection[J]. 2018.
[4] 非极大值抑制(Non-Maximum Suppression,NMS) - 康行天下 - 博客园
[5] 非极大值抑制(Non-Maximum Suppression) - 云+社区 - 腾讯云
[6] Intersection over Union (IoU) for object detection - PyImageSearch
[7] NMS 算法源码实现 | 从零开始的BLOG
[8] https://www.52cv.net/?p=1434
[9] https://www.jishuwen.com/d/24Tx#tuit
非极大值抑制(nms)算法详解[python]相关推荐
- NMS(非极大值抑制)算法详解与示例
一.NMS是什么? NMS(non maximum suppression)即非极大值抑制,广泛应用于传统的特征提取和深度学习的目标检测算法中. NMS原理是通过筛选出局部极大值得到最优解. 在2维边 ...
- YOLOv5改进之八:非极大值抑制NMS算法改进Soft-nms
前 言:作为当前先进的深度学习目标检测算法YOLOv5,已经集合了大量的trick,但是还是有提高和改进的空间,针对具体应用场景下的检测难点,可以不同的改进方法.此后的系列文章,将重点对YOLOv5 ...
- MTCNN中的重叠度IOU和非极大值抑制NMS原理及Python实现
MTCNN中的重叠度IOU和非极大值抑制NMS原理及Python实现 一.重叠度iou 从在一张照片上框人脸时,因为图像金字塔的原因可能会把人脸框两次以上,每个框的坐标为[X1,Y1X2,Y2,C], ...
- PyTorch实现非极大值抑制(NMS)
NMS即non maximum suppression即非极大抑制,顾名思义就是抑制不是极大值的元素,搜索局部的极大值.在最近几年常见的物体检测算法(包括rcnn.sppnet.fast-rcnn.f ...
- 数学建模——主成分分析算法详解Python代码
数学建模--主成分分析算法详解Python代码 import matplotlib.pyplot as plt #加载matplotlib用于数据的可视化 from sklearn.decomposi ...
- 交并比 (IoU), mAP (mean Average Precision), 非极大值抑制 (NMS, Soft NMS, Softer NMS, IoU-Net)
目录 目标检测的评价指标 交并比 (Intersection of Union, IoU) mAP (mean Average Precision) 其他指标 非极大值抑制 (Non-Maximum ...
- 【目标检测系列】非极大值抑制(NMS)的各类变体汇总
关注上方"深度学习技术前沿",选择"星标公众号", 技术干货,第一时间送达! [导读]前面已经先后为大家详细介绍过了目标检测领域的基础知识:[目标检测基础积累] ...
- 风机桨叶故障诊断(七) 滑动窗与非极大值抑制NMS
风机桨叶故障诊断(七)滑动窗与非极大值一直NMS 到目前为止,我已经利用自编码神经网络提取特征后训练得到了BP神经网络(参见:点击打开链接),且在测试样本集上表现不错.下面我们就要应用到实际中来检验算 ...
- Opencv学习笔记(二十六)文本检测mser+非极大值抑制nms
文章目录 mser检测 nms非极大值抑制 代码 mser检测 nms非极大值抑制 代码 import cv2 import matplotlib.pyplot as plt import numpy ...
最新文章
- FPGA从Xilinx 的7系列学起(5)
- 我用 MySQL 干掉了一摞简历
- [渝粤教育] 南京信息职业技术学院 电工电子技术基础 参考 资料
- java中sofa并发访问,云上的日子:用块存储、文件存储还是对象存储?
- python从入门到实践答案第四章_python从入门到实践课后习题第四章
- 解决忽略vscode中pylint报错的问题
- HTML5 Canvas渐进填充与透明(摘自 http://blog.csdn.net/jia20003/article/details/9251893)
- 前缀树(字典树,单词查找树,Trie树)
- JavaScript 优先队列
- Cisco Guard BGP流量牵引配置
- python古诗词生成_唐诗生成器
- 经验| 张家口交通综合运行协调与应急指挥中心建设
- Unity 3D涂色
- 面向过程(PO)和面向对象(OO)的区别(思维导图)
- 怎么把视频压缩到100MB以内
- Android模拟器优化加速
- 舞台灯光计算机控制,舞台灯光的操作
- 全志H616安装Python
- imx6芯片通过EIM总线外扩多路sja1000 CAN控制器
- 揭秘:如何只用一个软件分析股市数据?
热门文章
- 计算机英语新词的认知语义阐释论文,计算机英文专业论文题目 计算机英文论文题目怎样定...
- 阿里云——大神建个人网站分享(转载)
- Ubuntu20.04 运行 EasyConnect
- Visual C++ 使用x64编译器
- 北京一行(三)——北大、清华、香山
- 解决ps打开图片错误:无法完成请求,找不到不知名或无效的JPEG标志符类型
- A2B芯片AD2428给DAC提供主时钟的配置方法
- SQL中累加的三种方法
- 制造业ERP系统如何解决每个企业都存在的管理困境
- 月薪30K的软件测试简历怎么包装,软件测试项目经验怎么写?软件测试简历编写,软件测试简历模板