非极大值抑制算法NMS

非极大值抑制(Non-Maximum Suppression, NMS)是目标检测任务中一个重要的后处理步骤,只要是Anchor-based的检测方法,都需要经过NMS进行后处理。一个图片经过目标检测之后,会得到大量重复的anchor,而NMS就是去除掉这些重复的anchor。

如下图所示,左边是NMS处理之前,右边表示NMS处理后。

TIM截图20190508215650.jpg

NMS主要有两种处理方式,分别为Hard NMS和Soft NMS,下面分布介绍。

Hard NMS

Hard NMS的步骤为

  1. 根据anchor的得分对anchor从高到低进行排序。
  2. 取出anchor列表(假设有100个anchor)中第一个anchor,计算剩下的所有anchor(99个anchor)与该anchor的iou,将该anchor添加到令一个列表中。
  3. 舍弃iou大于iou阈值所对应的anchor(认为是检测同一个物体)
  4. 重复2和3,直至anchor列表为空

代码如下所示

def bbox_iou(anchors1, anchors2):
    lt = np.minimum(anchors1[:, np.newaxis, :2], anchors2[:, :2])
    rb = np.maximum(anchors2[:, np.newaxis, 2:], anchors2[:, 2:])
    inter_area = np.prod(rb - lt, axis=1) * np.all(rb > lt, axis=1)
    area1 = np.prod(anchors1[2:] - anchors1[:2], axis=1)
    area2 = np.prod(anchors2[2:] - anchors2[:2], axis=1)
    return inter_area / (area1[:, np.newaxis] + area2 - inter_area)

def hard_nms(anchors, scores, iou_thresh=0.7, condidates_num=200):
    """
    Params:
        anchors(numpy.array): detection anchors before nms, with shape(N, 4)
        scores(numpy.array): anchor scores before nms, with shape(N, )
        iou_thresh(float): iou thershold
        
    Return:
        keeps(nump.array): keeped anchor indexes
    """
    # if no anchor in anchors
    if anchors.size == 0:
        return 

    # sort by scores
    idxs = scores.argsort(0)[::-1]
    keeps = []
    while len(idxs) > 0:
        current = idxs[0]
        keep.append(current)
    # if keeped num equal with condidates_num or only one anchor left
        if len(idxs) == 1 or len(keeps) == condidates_num:
            break
            
        idxs = idxs[1:]
        ious = bbox_iou(anchors[current][np.newaxis, :], anchors[idxs]).flaten()
        mask = ious <= iou_thresh
        
        idxs = idxs[mask]

     return np.array(keeps)
        

Soft NMS

通常我们选择Hard NMS作为目标检测的后处理。但在存在密集物体的情况下Hard NMS会丢弃掉一些在置信度高的anchor周围的anchor。比如下方图片中的两匹马,红色框置信度最高,将会保留下来,假设Hard NMS的iou阈值为0.5,下面红色框和绿色框的iou可能大于阈值,这个时候绿色框将会被丢弃。但在我们看来,绿色框和红色框其实是在检测不同的两匹马,绿色框不应被丢弃。所以这就是Hard NMS所面临的问题。那有没有解决这个问题的方式呢

我们首先可能会想到修改iou阈值来缓解这个问题,那iou阈值到底设置为多少合适呢,设高了,会有产生误检;设低了,召回率会下降,所以这是一个不好抉择的事。

TIM截图20190519165158.jpg

而Soft NMS刚好就是为了解决这个问题的,Soft NMS的过程可以用下图来表示

TIM截图20190519165236.jpg

从上图可以看出NMS(Hard NMS)和Soft NMS唯一的区别在于,处理iou大于阈值对应的anchor的方式,在Hard NMS中,直接把iou大于阈值的anchor丢弃

$$s_i=\begin{cases} & s_i,\quad Iou(M,b_i)< N_t \\&0 ,\quad Iou(M,b_i)\geq N_t \end{cases}$$

而在Soft-NMS中。

$$s_i=\begin{cases} & s_i,\quad Iou(M,b_i)< N_t \\&s_i(1-Iou(M,b_i)) ,\quad Iou(M,b_i)\geq N_t \end{cases}$$

对于上式,它是一个跳跃性变化的函数(小于阈值,score不变,大于阈值,score乘上一个小于1的系数,相当于在$N_t$的位置发生了突变),作者认为该惩罚函数应该是连续的,否则会导致anchor排序的突变。具体参考

因此对上式进行一些改进,变成如下形式

$$s_i=s_ie^{-\frac{iou(M,b_i)^2}{\sigma}},\forall b_i \notin D$$

对于靠近M的anchor的score给予更大的惩罚(penalty),即乘上一个很小的系数,对于远离M的anchor的分值,给予小的惩罚,iou为0,则惩罚为0。

Soft-NMS代码如下

def soft_nms(anchors, scores, iou_thresh=0.7, condidates_num=200, sigma=0.5):
    """
    Params:
        anchors(numpy.array): detection anchors before nms, with shape(N, 4)
        scores(numpy.array): anchor scores before nms, with shape(N, )
        iou_thresh(float): iou thershold
        sigma(float): soft nms hyper-parameter
        
    Return:
        keeps(nump.array): keeped anchor indexes
    """
    # if no anchor in anchors
    if anchors.size == 0:
        return 

    keeps = []
    while len(scores) > 0:
        # get the maximum score index
        max_idx = np.argmax(scores)

        keep.append(max_idx)
        
        # if keeped num equal with condidates_num or only one anchor left
        if len(scores) == 1 or len(keeps) == condidates_num:
            break
        # get the left score indexes 
        mask = np.arange(len(scores)) != max_idx    
        scores = scores[mask]
        
        # calculate iou
        ious = bbox_iou(anchors[max_idx][np.newaxis, :], anchors[mask]).flaten()
       
        # re-asign value, scores decay
        scores = scores * np.exp(-ious * ious / sigma)
        scores = scores[scores > iou_thresh]
     return np.array(keeps)

总结

Soft-NMS是非最大抑制的广义版本,传统NMS是具有不连续二进制加权功能的特殊情况。

从下图可以看出Soft NMS相比于Hard NMS在VOC以及Coco数据集上都有一定提升, 并且Soft NMS并没有增加太多的运算量和超参数,因此还是比较推荐使用的。

image-20200321160530614.png

Reference

手撕非极大值抑制算法NMS与soft-NMS

Improving Object Detection With One Line of Code


mhxin
84 声望15 粉丝