1. IOU

交并比(Intersection-over-Union,IoU),目标检测中使用的一个概念,是产生的候选框(candidate bound)与原标记框(ground truth bound)的交叠率,即它们的交集与并集的比值。最理想情况是完全重叠,即比值为1。

目标检测基础知识-IOU,NMS,Soft-NMS_经典算法

计算公式:

目标检测基础知识-IOU,NMS,Soft-NMS_NMS_02

附核心代码:

x1 = max(cx1, gx1)
y1 = max(cy1, gy1)
x2 = min(cx2, gx2)
y2 = min(cy2, gy2)
w = max(0, (x2 - x1))
h = max(0, (y2 - y1))
area = w * h #C∩G的面积
iou = area / (carea + garea - area)

2. NMS

NMS(non maximum suppression),中文名非极大值抑制,在很多计算机视觉任务中都有广泛应用,如:边缘检测、目标检测等。

在物体检测中NMS(Non-maximum suppression)非极大抑制应用十分广泛,其目的是为了消除多余的框,找到最佳的物体检测的位置。

在RCNN系列算法中,会从一张图片中找出很多个候选框(可能包含物体的矩形边框),然后为每个矩形框为做类别分类概率。

目标检测基础知识-IOU,NMS,Soft-NMS_目标检测_03

就像上面的图片一样,定位一个车辆,最后算法就找出了一堆的方框,我们需要判别哪些矩形框是没用的。

非极大值抑制:先假设有6个候选框,根据分类器类别分类概率做排序,从小到大分别属于车辆的概率分别为A、B、C、D、E、F。


  1. 从最大概率矩形框(即面积最大的框)F开始,分别判断A~E与F的重叠度IOU是否大于某个设定的阈值;
  2. 假设B、D与F的重叠度超过阈值,那么就扔掉B、D(因为超过阈值,说明D与F或者B与F,已经有很大部分是重叠的,那我们保留面积最大的F即可,其余小面积的B,D就是多余的,用F完全可以表示一个物体了,所以保留F丢掉B,D);并标记第一个矩形框F,是我们保留下来的。
  3. 从剩下的矩形框A、C、E中,选择概率最大的E,然后判断E与A、C的重叠度,重叠度大于一定的阈值,那么就扔掉;并标记E是我们保留下来的第二个矩形框。
  4. 一直重复这个过程,找到所有曾经被保留下来的矩形框。

附核心代码:

import numpy as np

def nms( dets, thresh ):
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
scores = dets[:, 4]

areas = (x2 - x1 + 1) * (y2 - y1 + 1)
order = scores.argsort()[::-1]
keep = []

while order.size > 0:

i = order[0]
keep.append(i)

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:]] )
w = np.maximum( xx2 - xx1 + 1., 0. )
h = np.maximum( yy2 - yy1 + 1., 0. )

inters = w * h
overlaps = inters / ( areas[i] + areas[order[1:]] - inters )
inds = np.where( overlaps <= thresh )[0]
order = order[inds + 1]

return keep

3. Soft-NMS

绝大部分目标检测方法,最后都要用到 NMS-非极大值抑制进行后处理。 通常的做法是将检测框按得分排序,然后保留得分最高的框,同时删除与该框重叠面积大于一定比例的其它框。

这种贪心式方法存在如下图所示的问题: 红色框和绿色框是当前的检测结果,二者的得分分别是0.95和0.80。如果按照传统的NMS进行处理,首先选中得分最高的红色框,然后绿色框就会因为与之重叠面积过大而被删掉。

另一方面,NMS的阈值也不太容易确定,设小了会出现下图的情况(绿色框因为和红色框重叠面积较大而被删掉),设置过高又容易增大误检。

目标检测基础知识-IOU,NMS,Soft-NMS_物体检测_04

思路:不要粗鲁地删除所有IOU大于阈值的框,而是降低其置信度。

先直接上伪代码,如下图:如文章题目而言,就是用一行代码来替换掉原来的NMS。按照下图整个处理一遍之后,指定一个置信度阈值,然后最后得分大于该阈值的检测框得以保留

目标检测基础知识-IOU,NMS,Soft-NMS_IOU_05

原来的NMS可以描述如下:将IOU大于阈值的窗口的得分全部置为0。

目标检测基础知识-IOU,NMS,Soft-NMS_经典算法_06

soft-nms的改进有两种形式,一种是线性加权的:

目标检测基础知识-IOU,NMS,Soft-NMS_经典算法_07

一种是高斯加权的:

目标检测基础知识-IOU,NMS,Soft-NMS_物体检测_08

分析上面的两种改进形式,思想都是:M为当前得分最高框, b i b_i bi​为待处理框, b i b_i bi​和M的IOU越大, S i S_i Si​的得分 就下降的越厉害。

具体地,下面是作者给出的代码:(当然不止一行T_T)

def cpu_soft_nms(np.ndarray[float, ndim=2] boxes, float sigma=0.5, float Nt=0.3, float threshold=0.001, unsigned int method=0):
cdef unsigned int N = boxes.shape[0]
cdef float iw, ih, box_area
cdef float ua
cdef int pos = 0
cdef float maxscore = 0
cdef int maxpos = 0
cdef float x1,x2,y1,y2,tx1,tx2,ty1,ty2,ts,area,weight,ov

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]

# 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

这么做的解释如下:

目标检测基础知识-IOU,NMS,Soft-NMS_物体检测_09

如上图:

假如还检测出了3号框,而我们的最终目标是检测出1号和2号框,并且剔除3号框,原始的nms只会检测出一个1号框并剔除2号框和3号框,而softnms算法可以对1、2、3号检测狂进行置信度排序,可以知道这三个框的置信度从大到小的顺序依次为:1-》2-》3(由于是使用了惩罚,所有可以获得这种大小关系),如果我们再选择了合适的置信度阈值,就可以保留1号和2号,同时剔除3号,实现我们的功能。

但是,这里也有一个问题就是置信度的阈值如何选择,作者在这里依然使用手工设置的值,依然存在很大的局限性,所以该算法依然存在改进的空间。