NMS和Soft NMS

NMSone-stagetwo-stage目标检测任务中常用的一种后处理方法,用来过滤无效重叠的检测框。

NMS

NMS全称非极大值抑制,出自ICPR2006的论文《Efficient Non-Maximum Suppression》。其基本思想很简单,就是保留局部最大值而去除局部非最大值。

NMS对所有的类别的检测框进行循环过滤。对于某个类别C,首先对这些矩形框按照概率降序排列,选中概率最大的框作为候选框,对于剩下的框,依次与候选框求IOU,如果IOU大于某个阈值(超参),则将这些框丢弃(置0),并标记保留最大概率框。

以此类推,最终所有的框相互之间的IOU都是小于超参阈值的,或者概率被置为0了。剩下的所有概率非0的框就是最终的检测框。

基于这种计算逻辑的NMS有两个缺点。首先,NMS算法需要一个超参即IOU Threshold,这个阈值在不同任务中很难平衡。其次,NMS会将相邻或者重叠的两个物体对应的两个大概率目标框去掉一个,造成漏检。

实现:

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

    areas = (y2 - y1 + 1) * (x2 - x1 + 1)
    scores = dets[:, 4]
    keep = []
    index = scores.argsort()[::-1]

    while index.size > 0:
        i = index[0]       # every time the first is the biggst, and add it directly
        keep.append(i)

        x11 = np.maximum(x1[i], x1[index[1:]])    # calculate the points of overlap 
        y11 = np.maximum(y1[i], y1[index[1:]])
        x22 = np.minimum(x2[i], x2[index[1:]])
        y22 = np.minimum(y2[i], y2[index[1:]])
        
        w = np.maximum(0, x22 - x11 + 1)    # the weights of overlap
        h = np.maximum(0, y22 - y11 + 1)    # the height of overlap
       
        overlaps = w * h
        ious = overlaps / (areas[i] + areas[index[1:]] - overlaps)
 
        idx = np.where(ious <= threshold)[0]
        index = index[idx + 1]   # because index start from 1
 
    return keep

Soft-NMS

Soft-NMS出自CVPR2017的论文《Improving Object Detection With One Line of Code》,对NMS做了一些改进。

Soft-NMS总体算法流程同NMS相同,主要差别循环过程中对阈值的判断部分。NMS是简单的对IOU大于阈值的检测框进行删除出来,而Soft-NMS则是通过权重来降低检测框原有的置信度。对于有重叠的框,重叠区域越大,置信度衰减越严重。

soft-nms

Soft-NMS计算降低置信度的权重常用两种方法:线性法和高斯法。

线性法:

liner

高斯法:

Gaussian

实现如下,方法1为线性法,方法2为高斯法,其他参数的话Soft-NMS退化为NMS

def soft_nms(dets, sigma=0.5, threshold1=0.7, threshold2=0.1, method=1):
    n = dets.shape[0]

    x1 = dets[:, 0]
    y1 = dets[:, 1]
    x2 = dets[:, 2]
    y2 = dets[:, 3]
    scores = dets[:, 4]
    areas = (y2 - y1 + 1) * (x2 - x1 + 1)

    new_scores = scores.copy()
    index = [i for i in range(n)]
    keep = []

    while len(index) > 0:
        # get max box position of current based new scores
        max_score = 0
        max_pos = -1

        for i in index:
            if new_scores[i] >= max_score:
                max_pos = i
                max_score = new_scores[i]

        if max_pos == -1:
            break

        keep.append(max_pos)
        index.remove(max_pos)

        # calculate ious between current max box and others
        x11 = np.maximum(x1[max_pos], x1[index])
        y11 = np.maximum(y1[max_pos], y1[index])
        x22 = np.minimum(x2[max_pos], x2[index])
        y22 = np.minimum(y2[max_pos], y2[index])

        w = np.maximum(0, x22 - x11 + 1)
        h = np.maximum(0, y22 - y11 + 1)

        overlaps = w * h
        ious = overlaps / (areas[max_pos] + areas[index] - overlaps)

        # adjust score of others
        new_index = []
        for i, ids in enumerate(index):
            iou = ious[i]
            weight = 1
 
            if method == 1:
                # linear
                if iou >= threshold1:
                    weight = 1 - iou
            elif method == 2:
                # gaussian
                weight = np.exp(-(iou * iou) / sigma)
            else:
                # normal nms
                if iou >= threshold1:
                    weight = 0

            new_scores[ids] = new_scores[ids] * weight

            if new_scores[ids] > threshold2:
                new_index.append(ids)
        index = new_index

    return keep

实验

模拟的5个候选框:

box

if __name__ == '__main__':
    boxes = np.array([[100, 100, 210, 210, 0.72],
                  [250, 250, 420, 420, 0.8],
                  [220, 220, 320, 330, 0.92],
                  [100, 100, 210, 210, 0.72],
                  [230, 240, 325, 330, 0.81],
                  [220, 230, 315, 340, 0.9]])  # (x1,y1,x2,y2,score)

    keep = nms(boxes, threshold=0.7)
    print(keep)
 
    keep = soft_nms(boxes, threshold1=0.7, threshold2=0.2, method=0)
    print(keep)

    keep = soft_nms(boxes, threshold1=0.7, threshold2=0.2, method=1)
    print(keep)

    keep = soft_nms(boxes, threshold1=0.7, threshold2=0.2, method=2)
    print(keep)
[2, 1, 3]
[2, 1, 3]
[2, 1, 3, 4]
[2, 1, 3, 4]

实验可以看出,在相同IOU阈值的情况下,Soft-NMS相比NMS保留了一个检测结果,这在同类别物体重叠的情况下能够提升其召回率。但是Soft-NMS又多引入了一个超参,这个参数的设置也会显著影响后处理的结果;而且,由于Soft-NMS在每次迭代都会修改score值,其最大值是在动态变化的需要在每次迭代都寻找一次,因此Soft-NMS相比NMS计算效率有所降低。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容