一、经典NMS 非极大值抑制(Non-Maximum Suppression,NMS)的思想是搜索局部极大值,抑制非极大值元素。
经典NMS最初第一次应用到目标检测中是在R-CNN算法中,其实现严格按照搜索局部极大值,抑制非极大值元素的思想来实现的,具体的实现步骤如下:
先假设有6个输出的矩形框(即proposal_clip_box),根据分类器类别分类概率做排序,从小到大分别属于车辆的概率(scores)分别为A、B、C、D、E、F。 (1) 从最大概率矩形框F开始,分别判断A~E与F的重叠度IOU是否大于某个设定的阈值; (2) 假设B、D与F的重叠度超过阈值,那么就扔掉B、D;并标记第一个矩形框F,是我们保留下来的。 (3) 从剩下的矩形框A、C、E中,选择概率最大的E,然后判断E与A、C的重叠度,重叠度大于一定的阈值,那么就扔掉;并标记E是我们保留下来的第二个矩形框。 就这样一直重复,找到所有被保留下来的矩形框。
如上图F与BD重合度较大,可以去除BD。AE重合度较大,我们删除A,保留scores较大的E。C和其他重叠都小保留C。最终留下了C、E、F三个。
# -*- coding: utf-8 -*- """ Created on Fri Sep 4 15:35:06 2020 @author: zqq """ import numpy as np boxes=np.array([[100,100,210,210,0.72], [250,250,420,420,0.8], [220,220,320,330,0.92], [100,100,190,200,0.71], [230,240,325,330,0.81], [220,230,315,340,0.9]]) def py_cpu_nms(dets, thresh): "Pure Python NMS baseline" # x1、y1、x2、y2以及score赋值 x1 = dets[:,0] y1 = dets[:,1] x2 = dets[:,2] y2 = dets[:,3] scores = dets[:, 4] # 每一个检测框的面积 areas = (y2-y1+1) * (x2-x1+1) print(areas) # 按照score置信度降序排序 order = scores.argsort()[::-1] keep = [] # 保留的结果框集合 while order.size >0: i = order[0] # every time the first is the biggst, and add it directly keep.append(i) # 保留该类剩余box中得分最高的一个 # 得到相交区域,左上及右下 xx1 = np.maximum(x1[i], x1[order[1:]]) yy1 = np.maximum(y1[i], y1[order[1:]]) xx2 = np.minimum(x2[i], x2[order[1:]]) yy2 = np.minimum(y2[i], y2[order[1:]]) # 计算相交的面积,不重叠时面积为0 w = np.maximum(0, xx2-xx1+1) # the weights of overlap h = np.maximum(0, yy2-yy1+1) # the height of overlap inter = w*h # 计算IoU:重叠面积 /(面积1+面积2-重叠面积) ovr = inter / (areas[i]+areas[order[1:]] - inter) # 保留IoU小于阈值的box indx = np.where(ovr<=thresh)[0] order = order[indx+1] # 因为ovr数组的长度比order数组少一个,所以这里要将所有下标后移一位 return keep import matplotlib.pyplot as plt def plot_bbox(dets, c='k'): x1 = dets[:,0] y1 = dets[:,1] x2 = dets[:,2] y2 = dets[:,3] plt.plot([x1,x2], [y1,y1], c) plt.plot([x1,x1], [y1,y2], c) plt.plot([x1,x2], [y2,y2], c) plt.plot([x2,x2], [y1,y2], c) #plt.title(" nms") #plt.show() plt.figure(1) ax1 = plt.subplot(1,2,1) ax1.set_title('before nms') ax2 = plt.subplot(1,2,2) ax2.set_title('after nms') plt.sca(ax1) plot_bbox(boxes,'k') # before nms keep = py_cpu_nms(boxes, thresh=0.7) plt.sca(ax2) plot_bbox(boxes[keep], 'b')# after nms plt.show()二、Soft-NMS 论文: http://cn.arxiv.org/abs/1704.04503 代码: https://github.com/bharatsingh430/soft-nms
soft NMS提出尤其对密集物体检测的检测效果有一定的提升作用.
绝大部分目标检测方法,最后都要用到 NMS-非极大值抑制进行后处理。 通常的做法是将检测框按得分排序,然后保留得分最高的框,同时删除与该框重叠面积大于一定比例的其它框。
这种贪心式方法存在如下图所示的问题: 红色框和绿色框是当前的检测结果,二者的得分分别是0.95和0.80。如果按照传统的NMS进行处理,首先选中得分最高的红色框,然后绿色框就会因为与之重叠面积过大而被删掉。
思路:不要粗鲁地删除所有IOU大于阈值的框,而是降低其置信度。
soft NMS算法的大致思路为:M为当前得分最高框,bi 为待处理框,bi 和M的IOU越大,bi 的得分si 就下降的越厉害。
算法结构如图所示:
NMS中:
Soft-NMS中: (1)线性加权: (2)高斯加权: soft NMS仍然有问题:其阈值仍然需要手工设定 Soft-NMS代码:
# coding:utf-8 import numpy as np def soft_nms(boxes, sigma=0.5, Nt=0.1, threshold=0.001, method=1): N = boxes.shape[0] pos = 0 maxscore = 0 maxpos = 0 for i in range(N): maxscore = boxes[i, 4] maxpos = i tx1 = boxes[i,0] ty1 = boxes[i,1] tx2 = boxes[i,2] ty2 = boxes[i,3] ts = boxes[i,4] pos = i + 1 # get max box while pos < N: if maxscore < boxes[pos, 4]: maxscore = boxes[pos, 4] maxpos = pos pos = pos + 1 # add max box as a detection boxes[i,0] = boxes[maxpos,0] boxes[i,1] = boxes[maxpos,1] boxes[i,2] = boxes[maxpos,2] boxes[i,3] = boxes[maxpos,3] boxes[i,4] = boxes[maxpos,4] # swap ith box with position of max box boxes[maxpos,0] = tx1 boxes[maxpos,1] = ty1 boxes[maxpos,2] = tx2 boxes[maxpos,3] = ty2 boxes[maxpos,4] = ts tx1 = boxes[i,0] ty1 = boxes[i,1] tx2 = boxes[i,2] ty2 = boxes[i,3] ts = boxes[i,4] pos = i + 1 # NMS iterations, note that N changes if detection boxes fall below threshold while pos < N: x1 = boxes[pos, 0] y1 = boxes[pos, 1] x2 = boxes[pos, 2] y2 = boxes[pos, 3] s = boxes[pos, 4] area = (x2 - x1 + 1) * (y2 - y1 + 1) iw = (min(tx2, x2) - max(tx1, x1) + 1) if iw > 0: ih = (min(ty2, y2) - max(ty1, y1) + 1) if ih > 0: ua = float((tx2 - tx1 + 1) * (ty2 - ty1 + 1) + area - iw * ih) ov = iw * ih / ua #iou between max box and detection box if method == 1: # linear if ov > Nt: weight = 1 - ov else: weight = 1 elif method == 2: # gaussian weight = np.exp(-(ov * ov)/sigma) else: # original NMS if ov > Nt: weight = 0 else: weight = 1 boxes[pos, 4] = weight*boxes[pos, 4] print(boxes[:, 4]) # if box score falls below threshold, discard the box by swapping with last box # update N if boxes[pos, 4] < threshold: boxes[pos,0] = boxes[N-1, 0] boxes[pos,1] = boxes[N-1, 1] boxes[pos,2] = boxes[N-1, 2] boxes[pos,3] = boxes[N-1, 3] boxes[pos,4] = boxes[N-1, 4] N = N - 1 pos = pos - 1 pos = pos + 1 keep = [i for i in range(N)] return keep boxes = np.array([[100, 100, 150, 168, 0.63],[166, 70, 312, 190, 0.55],[221, 250, 389, 500, 0.79],[12, 190, 300, 399, 0.9],[28, 130, 134, 302, 0.3]]) keep = soft_nms(boxes) print(keep)参考: (写的很好) https://zhuanlan.zhihu.com/p/42018282
https://zhuanlan.zhihu.com/p/54709759
https://zhuanlan.zhihu.com/p/64423753
https://blog.csdn.net/lz867422770/article/details/100019587
https://blog.csdn.net/Blateyang/article/details/79113030
https://blog.csdn.net/a1103688841/article/details/89711120
(这篇写的不错) https://www.cnblogs.com/makefile/p/nms.html
https://github.com/rbgirshick/py-faster-rcnn/blob/master/lib/nms/py_cpu_nms.py
https://www.cnblogs.com/zf-blog/p/8532228.html
