NMS

解决问题:多个建议框指向了同一个物体,我们只需要该物体IoU最大的建议框

基本思路:多个类别分开处理;每个列别中 iou 较大的删除,iou 较小的 保留

具体操作

1. 输入为2000x20矩阵,2000代表2000个建议框,20代表20个类别

2. 对每个类别进行排序,从大到小

3. 首先取得分最高的建议框,设为物体1,然后遍历后面所有的建议框,如果建议框和物体1的建议框IoU大于阈值,则认为是一个物体,把这些建议框删除,

如果小于阈值,则认为是另一个物体,暂时保留,保留下来的建议框仍然是有序的

4. 去掉该列被认定的建议框,如物体1,将剩下的建议框进行步骤3操作,直到认定完所有物体

5. 对每列进行上述操作

 

 

如上图F与BD重合度较大,可以去除BD。AE重合度较大,我们删除A,保留scores较大的E。C和其他重叠都小保留C。最终留下了C、E、F三个 

 

存在问题

1.如果 IOU 大于阈值,直接删除,忽略了 该 bbox 的 score,试想 如果 score 很高,很可能存在一个物体的,但被删除了,这对于密集物体十分不友好;

2.阈值 不太好确定

 

 

NMS 下 绿框 会被删除 

 

demo

针对 单类别

# coding:utf-8
import numpy as np
def py_cpu_nms(dets, thresh):
    """Pure Python NMS baseline."""
    # 所有图片的坐标信息,字典形式储存??
    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]  # i 是还未处理的图片中的最大评分
        keep.append(i)  # 保留改图片的值
        # 矩阵操作,下面计算的是图片i分别与其余图片相交的矩形的坐标
        tmp=x1[order[1:]]
        xxxx = x1[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(0.0, xx2 - xx1 + 1)
        h = np.maximum(0.0, yy2 - yy1 + 1)
        inter = w * h
        # 计算重叠比例
        ovr = inter / (areas[i] + areas[order[1:]] - inter)

        # 只保留比例小于阙值的图片,然后继续处理
        inds = np.where(ovr <= thresh)[0]
        indsd= inds+1
        order = order[inds + 1]

    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]])
thresh = 0.1
keep = py_cpu_nms(boxes, thresh)
print(keep)

 

soft-NMS

soft-nms 优化点在于:并非暴力删除 iou 较大的 框,而是 降低 这个框的 score

 

 

 

存在问题

阈值仍需手工设定

优势

1、Soft-NMS可以很方便地引入到object detection算法中,不需要重新训练原有的模型、代码容易实现,不增加计算量(计算量相比整个object detection算法可忽略)。并且很容易集成到目前所有使用NMS的目标检测算法。

2、soft-NMS在训练中采用传统的NMS方法,仅在推断代码中实现soft-NMS

3、NMS是Soft-NMS特殊形式,当得分重置函数采用二值化函数时,Soft-NMS和NMS是相同的。soft-NMS算法是一种更加通用的非最大抑制算法。

 

demo

def my_soft_nms(bboxes, scores, iou_thresh=0.5, sigma=0.5, score_threshold=0.25):

    bboxes = bboxes.contiguous()

    x1 = bboxes[:, 0]
    y1 = bboxes[:, 1]
    x2 = bboxes[:, 2]
    y2 = bboxes[:, 3]
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)
    order = scores.sort(0, descending=True)
    keep = []

    while order.numel() > 0:
        if order.numel() == 1: 
            i = order.item()
            break
        else:
            i = order[0].item()
            keep.append(i)
        xx1 = x1[order[1:]].clamp(min=x1[i])
        yy1 = y1[order[1:]].clamp(min=y1[i])
        xx2 = x2[order[1:]].clamp(max=x2[i])
        yy2 = y2[order[1:]].clamp(max=y2[i])
        inter = (xx2 - xx1).clamp(min=0) * (yy2 - yy1).clamp(min=0)

        idx = (iou > iou_thresh).nonzero().squeeze()  
        if idx.numel() > 0:
            iou = iou[idx]
            newScores = torch.exp(-torch.pow(iou, 2) / sigma)  
            scores[order[idx + 1]] *= newScores  

        newOrder = (scores[order[1:]] > score_threshold).nonzero().squeeze()
        if newOrder.numel() == 0:
            break
        else:
            maxScoreIndex = torch.argmax(newScores)

            if maxScoreIndex != 0:
               newOrder[[0, maxScoreIndex],] = newOrder[[maxScoreIndex, 0],]

            order = order[newOrder + 1]

    return torch.LongTensor(keep)

有人在多个数据集上做了大量实验,针对不同的数据集效果不同,有轻微的提升作用。

 

softer-NMS

未完待续...

 

 

 

 

参考资料:

https://mp.weixin.qq.com/s?__biz=MzUyODY5ODg5NQ==&mid=2247483782&idx=1&sn=bae1d8b8c56c2e5fe82886ca2f418f9e&chksm=fa6d1ae6cd1a93f0adaa3cae6458e238d32e6d5436b0e3b70e35cd0881b56403f99be48d0443&scene=132#wechat_redirect  YOLOv5改进之八:非极大值抑制NMS算法改进Soft-nms

https://zhuanlan.zhihu.com/p/42018282  NMS与soft NMS

https://zhuanlan.zhihu.com/p/89426063  NMS、 soft-nms、softer-nms