Soft—NMS/非极大值抑制

传统的NMS算法问题:可以看Figure1在Fiugre1中,检测算法本来应该输出两个框,但是传统的NMS算法可能会把score较低的绿框过滤掉(如果绿框和红框的IOU大于设定的阈值就会被过滤掉),导致只检测出一个object(一个马),显然这样object的recall就比较低了。

Figure2是Soft NMS算法的伪代码。首先是关于三个输入B、S、Nt,在FIgure2中已经介绍很清楚了。D集合用来放最终的box,在boxes集合B非空的前提下,搜索score集合S中数值最大的数,假设其下标为m,那么bm(也是M)就是对应的box。然后将M和D集合合并,并从B集合中去除M。再循环集合B中的每个box,这个时候就有差别了,如果是传统的NMS操作,那么当B中的box bi和M的IOU值大于阈值Nt,那么就从B和S中去除该box;如果是Soft NMS,则对于B中的box bi也是先计算其和M的IOU,然后该IOU值作为函数f()的输入,最后和box bi的score si相乘作为最后该box bi的score。就是这么简单!

接下来得重点就是如何确定函数f()了。
首先NMS算法可以用下面的式子表示:

为了改变NMS这种hard threshold做法,并遵循iou越大,得分越低的原则(iou越大,越有可能是false positive),自然而然想到可以用下面这个公式来表示Soft NMS 

但是上面这个公式是不连续的,这样会导致box集合中的score出现断层,因此就有了下面这个Soft NMS式子(也是大部分实验中采用的式子): 

 代码解析:

扫描二维码关注公众号,回复: 9472635 查看本文章
  1. for循环遍历每一个物体框
  2. 将该物体框与得分最高的物体框进行交换,交换内容包括:面积、坐标值,得分
  3. 交换后得分最高的物体框排在当前序列的第一位
  4. 计算其他物体框与最高得分物体框的IOU值
  5. 通过不同的方式计算得分权重值
  6. 遍历结束,返回得分值高于thread的物体框的索引
def py_cpu_softnms(dets, sc, Nt=0.3, sigma=0.5, thresh=0.001, method=2):
    """
    py_cpu_softnms
    :param dets:   boexs 坐标矩阵 format [y1, x1, y2, x2]
    :param sc:     每个 boxes 对应的分数
    :param Nt:     iou 交叠门限
    :param sigma:  使用 gaussian 函数的方差
    :param thresh: 最后的分数门限
    :param method: 使用的方法
    :return:       留下的 boxes 的 index
    """

    # indexes concatenate boxes with the last column
    N = dets.shape[0]
    indexes = np.array([np.arange(N)])
    dets = np.concatenate((dets, indexes.T), axis=1)

    # the order of boxes coordinate is [y1,x1,y2,x2]
    y1 = dets[:, 0]
    x1 = dets[:, 1]
    y2 = dets[:, 2]
    x2 = dets[:, 3]
    scores = sc
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)

    for i in range(N):
        # intermediate parameters for later parameters exchange
        #将物体框dets[i,:]与得分最高的物体框进行交换
        tBD = dets[i, :].copy()
        tscore = scores[i].copy()
        tarea = areas[i].copy()
        pos = i + 1
        if i != N-1:
            maxscore = np.max(scores[pos:], axis=0)
            maxpos = np.argmax(scores[pos:], axis=0)   #maxpos为scores[pos:]中最大元素的序号,maxpos + pos(=i+1)为最大元素在scores中的序号
        else:
            maxscore = scores[-1]
            maxpos = 0
        if tscore < maxscore:
            #交换两个框的坐标
            dets[i, :] = dets[maxpos + i + 1, :]
            dets[maxpos + i + 1, :] = tBD
            tBD = dets[i, :]
            #交换两个框的得分
            scores[i] = scores[maxpos + i + 1]
            scores[maxpos + i + 1] = tscore
            tscore = scores[i]
            # 交换两个框的面积
            areas[i] = areas[maxpos + i + 1]
            areas[maxpos + i + 1] = tarea
            tarea = areas[i]

        # IoU calculate
        xx1 = np.maximum(dets[i, 1], dets[pos:, 1])
        yy1 = np.maximum(dets[i, 0], dets[pos:, 0])
        xx2 = np.minimum(dets[i, 3], dets[pos:, 3])
        yy2 = np.minimum(dets[i, 2], dets[pos:, 2])

        w = np.maximum(0.0, xx2 - xx1 + 1)
        h = np.maximum(0.0, yy2 - yy1 + 1)
        inter = w * h
        ovr = inter / (areas[i] + areas[pos:] - inter)

        # Three methods: 1.linear 2.gaussian 3.original NMS
        if method == 1:  # linear
            weight = np.ones(ovr.shape)
            weight[ovr > Nt] = weight[ovr > Nt] - ovr[ovr > Nt]
        elif method == 2:  # gaussian
            weight = np.exp(-(ovr * ovr) / sigma)
        else:  # original NMS
            weight = np.ones(ovr.shape)
            weight[ovr > Nt] = 0

        scores[pos:] = weight * scores[pos:]

    # select the boxes and keep the corresponding indexes
    inds = dets[:, 4][scores > thresh]
    keep = inds.astype(int)

    return keep
发布了109 篇原创文章 · 获赞 22 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42233538/article/details/103652402