非極大值抑制(NMS)及其變種實(shí)現(xiàn)

非極大值抑制(NMS)及其變種實(shí)現(xiàn)

NMS(Non Maximum Suppression),又名非極大值抑制,是目標(biāo)檢測框架中的后處理模塊饿这,主要用于刪除高度冗余的bbox菩颖,先用圖示直觀看看NMS的工作機(jī)制:
從下圖處理的結(jié)果可以看出样漆,在目標(biāo)檢測過程中,對于每個(gè)obj在檢測的時(shí)候會產(chǎn)生多個(gè)bbox晦闰,NMS本質(zhì)就是對每個(gè)obj的多個(gè)bbox去冗余放祟,得到最終的檢測結(jié)果.


在這里插入圖片描述

NMS各大變種

在這里鳍怨,主要是針對文本檢測中的NMS進(jìn)行詳細(xì)闡述.文本檢測是一種特殊的目標(biāo)檢測,但它與通用的目標(biāo)檢測又存在一定的區(qū)別:通用目標(biāo)檢測一般采用的水平矩形框跪妥,而文本檢測中文本行存在方向不確定性(水平鞋喇、垂直、傾斜眉撵、彎曲)侦香,針對多方向文本一般采用帶方向矩形框、四邊形及多邊形.因?yàn)榫匦慰虻谋碚鞣绞讲煌ε保脱苌瞬煌姹镜腘MS罐韩,主要包括:標(biāo)準(zhǔn)NMS、locality-aware NMS(簡稱LNMS)污朽、inclined NMS(簡稱INMS)散吵、Mask NMS(簡稱MNMS)、polygonal NMS(簡稱PNMS)

標(biāo)準(zhǔn)NMS

基本步驟
1.將所有檢出的output bbox按cls score劃分(如文本檢測僅包含文1類蟆肆,即將output bbox按照其對應(yīng)的cls score劃分為2個(gè)集合矾睦,1個(gè)為bg類,bg類不需要做NMS而已)

2.在每個(gè)集合內(nèi)根據(jù)各個(gè)bbox的cls score做降序排列炎功,得到一個(gè)降序的list_k

3.從list_k中top1 cls score開始顷锰,計(jì)算該bbox_x與list中其他bbox_y的IoU,若IoU大于閾值T亡问,則剔除該bbox_y官紫,最終保留bbox_x,從list_k中取出

4.對剩余的bbox_x州藕,重復(fù)step-3中的迭代操作束世,直至list_k中所有bbox都完成篩選;

5.對每個(gè)集合的list_k床玻,重復(fù)step-3毁涉、4中的迭代操作,直至所有l(wèi)ist_k都完成篩選锈死;

代碼示例


import numpy as np

def nms(dets,thresh):
    #首先為x1,y1,x2,y2,score賦值
    x1 = dets[:,0]              #取所有行第一列的數(shù)據(jù)
    y1 = dets[:,1]
    x2 = dets[:,2]
    y2 = dets[:,3]
    scores = dets[:,4]

    #按照score的置信度將其排序,argsort()函數(shù)是將x中的元素從小到大排列贫堰,提取其對應(yīng)的index(索引)
    order = scores.argsort()[::-1]
    #計(jì)算面積
    areas = (x2-x1+1)*(y2-y1+1)

    #保留最后需要保留的邊框的索引
    keep = []
    while order.size > 0:
        #order[0]是目前置信度最大的,肯定保留
        i = order[0]
        keep.append(i)
        #計(jì)算窗口i與其他窗口的交疊的面積待牵,此處的maximum是np中的廣播機(jī)制
        xx1 = np.maximum(x1[i],x1[order[1:]])
        yy1 = np.maximum(y1[i],y1[order[1:]])
        xx2 = np.maximum(x2[i],x2[order[1:]])
        yy2 = np.maximum(y2[i],y2[order[1:]])

        #計(jì)算相交框的面積,左上右下其屏,畫圖理解。注意矩形框不相交時(shí)w或h算出來會是負(fù)數(shù)缨该,用0代替
        w = np.maximum(0.0,xx2-xx1+1)
        h = np.maximum(0.0,yy2-yy1+1)
        inter = w*h

        #計(jì)算IOU:相交的面積/相并的面積
        ovr = inter/(areas[i]+areas[order[1:]]-inter)

        #inds為所有與窗口i的iou值小于threshold值的窗口的index偎行,其他窗口此次都被窗口i吸收
        inds = np.where(ovr<thresh)[0]                  #np.where就可以得到索引值(3,0,8)之類的,再取第一個(gè)索引
        #將order序列更新,由于前面得到的矩形框索引要比矩形框在原order序列中的索引小1(因?yàn)橛?jì)算inter時(shí)是少了1的)蛤袒,所以要把這個(gè)1加回來
        order = order[inds+1]

    return keep

# test
if __name__ == "__main__":
    dets = np.array([[30, 20, 230, 200, 1],
                     [50, 50, 260, 220, 0.9],
                     [210, 30, 420, 5, 0.8],
                     [430, 280, 460, 360, 0.7]])
    thresh = 0.35
    keep_dets = nms(dets, thresh)
    print(keep_dets)
    print(dets[keep_dets])


適用范圍
適應(yīng)范圍:標(biāo)準(zhǔn)的NMS一般用于軸對齊的矩形框(即水平bbox)

局部感知NMS(LNMS)

LNMS是在EAST文本檢測中提出的.主要原因:文本檢測面臨的是成千上萬個(gè)幾何體熄云,如果用普通的NMS,其計(jì)算復(fù)雜度妙真,n是幾何體的個(gè)數(shù)缴允,這是不可接受的.對上述時(shí)間復(fù)雜度問題,EAST提出了基于行合并幾何體的方法珍德,當(dāng)然這是基于鄰近幾個(gè)幾何體是高度相關(guān)的假設(shè).注意:這里合并的四邊形坐標(biāo)是通過兩個(gè)給定四邊形的得分進(jìn)行加權(quán)平均的练般,也就是說這里是“平均”而不是”選擇”幾何體*,目的是減少計(jì)算量.

基本步驟
1.先對所有的output box集合結(jié)合相應(yīng)的閾值(大于閾值則進(jìn)行合并,小于閾值則不和并)菱阵,依次遍歷進(jìn)行加權(quán)合并踢俄,得到合并后的bbox集合缩功;
2.對合并后的bbox集合進(jìn)行標(biāo)準(zhǔn)的NMS操作

實(shí)現(xiàn)源碼

import numpy as np
from shapely.geometry import Polygon

def intersection(g, p):
    #取g,p中的幾何體信息組成多邊形
    g = Polygon(g[:8].reshape((4, 2)))
    p = Polygon(p[:8].reshape((4, 2)))

    # 判斷g,p是否為有效的多邊形幾何體
    if not g.is_valid or not p.is_valid:
        return 0

    # 取兩個(gè)幾何體的交集和并集
    inter = Polygon(g).intersection(Polygon(p)).area
    union = g.area + p.area - inter
    if union == 0:
        return 0
    else:
        return inter/union

def weighted_merge(g, p):
    # 取g,p兩個(gè)幾何體的加權(quán)(權(quán)重根據(jù)對應(yīng)的檢測得分計(jì)算得到)
    g[:8] = (g[8] * g[:8] + p[8] * p[:8])/(g[8] + p[8])

    #合并后的幾何體的得分為兩個(gè)幾何體得分的總和
    g[8] = (g[8] + p[8])
    return g

def standard_nms(S, thres):
    #標(biāo)準(zhǔn)NMS
    order = np.argsort(S[:, 8])[::-1]
    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)
        ovr = np.array([intersection(S[i], S[t]) for t in order[1:]])
        inds = np.where(ovr <= thres)[0]
        order = order[inds+1]

    return S[keep]

def nms_locality(polys, thres=0.3):
    '''
    locality aware nms of EAST
    :param polys: a N*9 numpy array. first 8 coordinates, then prob
    :return: boxes after nms
    '''
    S = []    #合并后的幾何體集合
    p = None   #合并后的幾何體
    for g in polys:
        if p is not None and intersection(g, p) > thres:    #若兩個(gè)幾何體的相交面積大于指定的閾值晴及,則進(jìn)行合并
            p = weighted_merge(g, p)
        else:    #反之,則保留當(dāng)前的幾何體
            if p is not None:
                S.append(p)
            p = g
    if p is not None:
        S.append(p)
    if len(S) == 0:
        return np.array([])
    return standard_nms(np.array(S), thres)

if __name__ == '__main__':
    # 343,350,448,135,474,143,369,359
    print(Polygon(np.array([[343, 350], [448, 135],
                            [474, 143], [369, 359]])).area)

適用范圍
適應(yīng)范圍:LNMS一般用于軸對齊的矩形框(即水平bbox)嫡锌,特別是離得很近的傾斜文本
當(dāng)圖像中有很多文本時(shí)候虑稼,就會產(chǎn)生大量的檢測框(即下圖中中間圖中綠色的框,這里總共會產(chǎn)生1400多個(gè)綠色框势木,這里我圖片壓縮過了蛛倦,比較模糊);經(jīng)過LNMS后啦桌,得到最終的結(jié)果(即下述中的右圖溯壶,即藍(lán)色框)

傾斜NMS(INMS)

INMS是在2018的文章中提出的,主要是解決傾斜的文本行檢測.

基本步驟
(rbox代表旋轉(zhuǎn)矩形框)
1.對輸出的檢測框rbox按照得分進(jìn)行降序排序rbox_lists甫男;
2.依次遍歷上述的rbox_lists.具體的做法是:將當(dāng)前遍歷的rbox與剩余的rbox進(jìn)行交集運(yùn)算得到相應(yīng)的相交點(diǎn)集合且改,并根據(jù)判斷相交點(diǎn)集合組成的凸邊形的面積,計(jì)算每兩個(gè)rbox的IOU板驳;對于大于設(shè)定閾值的rbox進(jìn)行濾除又跛,保留小于設(shè)定閾值的rbox;
3.得到最終的檢測框

實(shí)現(xiàn)代碼

#coding=utf-8
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np
import cv2
import tensorflow as tf

def nms_rotate(decode_boxes, scores, iou_threshold, max_output_size,
               use_angle_condition=False, angle_threshold=0, use_gpu=False, gpu_id=0):
    """
    :param boxes: format [x_c, y_c, w, h, theta]
    :param scores: scores of boxes
    :param threshold: iou threshold (0.7 or 0.5)
    :param max_output_size: max number of output
    :return: the remaining index of boxes
    """
    if use_gpu:
        #采用gpu方式
        keep = nms_rotate_gpu(boxes_list=decode_boxes,
                              scores=scores,
                              iou_threshold=iou_threshold,
                              angle_gap_threshold=angle_threshold,
                              use_angle_condition=use_angle_condition,
                              device_id=gpu_id)

        keep = tf.cond(
            tf.greater(tf.shape(keep)[0], max_output_size),
            true_fn=lambda: tf.slice(keep, [0], [max_output_size]),
            false_fn=lambda: keep)
    else: #采用cpu方式
        keep = tf.py_func(nms_rotate_cpu,
                          inp=[decode_boxes, scores, iou_threshold, max_output_size],
                          Tout=tf.int64)
    return keep

def nms_rotate_cpu(boxes, scores, iou_threshold, max_output_size):
    keep = [] #保留框的結(jié)果集合
    order = scores.argsort()[::-1] #對檢測結(jié)果得分進(jìn)行降序排序
    num = boxes.shape[0] #獲取檢測框的個(gè)數(shù)

    suppressed = np.zeros((num), dtype=np.int)
    for _i in range(num):
        if len(keep) >= max_output_size: ∪糁巍#若當(dāng)前保留框集合中的個(gè)數(shù)大于max_output_size時(shí)慨蓝,直接返回
            break

        i = order[_i]
        if suppressed[i] == 1: #對于抑制的檢測框直接跳過
            continue
        keep.append(i)  #保留當(dāng)前框的索引
        r1 = ((boxes[i, 1], boxes[i, 0]), (boxes[i, 3], boxes[i, 2]), boxes[i, 4])  #根據(jù)box信息組合成opencv中的旋轉(zhuǎn)bbox
        print("r1:{}".format(r1))
        area_r1 = boxes[i, 2] * boxes[i, 3] 《擞住#計(jì)算當(dāng)前檢測框的面積
        for _j in range(_i + 1, num): ±窳摇#對剩余的而進(jìn)行遍歷
            j = order[_j]
            if suppressed[i] == 1:
                continue
            r2 = ((boxes[j, 1], boxes[j, 0]), (boxes[j, 3], boxes[j, 2]), boxes[j, 4])
            area_r2 = boxes[j, 2] * boxes[j, 3]
            inter = 0.0

            int_pts = cv2.rotatedRectangleIntersection(r1, r2)[1] #求兩個(gè)旋轉(zhuǎn)矩形的交集婆跑,并返回相交的點(diǎn)集合
            if int_pts is not None:
                order_pts = cv2.convexHull(int_pts, returnPoints=True) #求點(diǎn)集的凸邊形
                int_area = cv2.contourArea(order_pts)  #計(jì)算當(dāng)前點(diǎn)集合組成的凸邊形的面積
                inter = int_area * 1.0 / (area_r1 + area_r2 - int_area + 0.0000001)

            if inter >= iou_threshold:  #對大于設(shè)定閾值的檢測框進(jìn)行濾除
                suppressed[j] = 1

    return np.array(keep, np.int64)

# gpu的實(shí)現(xiàn)方式
def nms_rotate_gpu(boxes_list, scores, iou_threshold, use_angle_condition=False, angle_gap_threshold=0, device_id=0):
    if use_angle_condition:
        y_c, x_c, h, w, theta = tf.unstack(boxes_list, axis=1)
        boxes_list = tf.transpose(tf.stack([x_c, y_c, w, h, theta]))
        det_tensor = tf.concat([boxes_list, tf.expand_dims(scores, axis=1)], axis=1)
        keep = tf.py_func(rotate_gpu_nms,
                          inp=[det_tensor, iou_threshold, device_id],
                          Tout=tf.int64)
        return keep
    else:
        y_c, x_c, h, w, theta = tf.unstack(boxes_list, axis=1)
        boxes_list = tf.transpose(tf.stack([x_c, y_c, w, h, theta]))
        det_tensor = tf.concat([boxes_list, tf.expand_dims(scores, axis=1)], axis=1)
        keep = tf.py_func(rotate_gpu_nms,
                          inp=[det_tensor, iou_threshold, device_id],
                          Tout=tf.int64)
        keep = tf.reshape(keep, [-1])
        return keep

if __name__ == '__main__':
    boxes = np.array([[50, 40, 100, 100, 0],
                      [60, 50, 100, 100, 0],
                      [50, 30, 100, 100, -45.],
                      [200, 190, 100, 100, 0.]])

    scores = np.array([0.99, 0.88, 0.66, 0.77])
    keep = nms_rotate(tf.convert_to_tensor(boxes, dtype=tf.float32), tf.convert_to_tensor(scores, dtype=tf.float32),
                      0.7, 5)
    import os
    os.environ["CUDA_VISIBLE_DEVICES"] = '0'
    with tf.Session() as sess:
        print(sess.run(keep))

適用范圍
適用范圍:一般適用于傾斜文本檢測(即帶方向的文本)

多邊形NMS(PNMS)

Polygon NMS是在2017年Detecting Curve Text in the Wild: New Dataset and New Solution文章提出的济丘,主要是針對曲線文本提出的.

基本步驟
其思路和標(biāo)準(zhǔn)NMS一致,將標(biāo)準(zhǔn)NMS中的矩形替換成多邊形即可,這里就就不展開詳細(xì)說明了

實(shí)現(xiàn)代碼

#coding=utf-8
import numpy as np
from shapely.geometry import *

def py_cpu_pnms(dets, thresh):
    # 獲取檢測坐標(biāo)點(diǎn)及對應(yīng)的得分
    bbox = dets[:, :4]
    scores = dets[:, 4] 

    #這里文本的標(biāo)注采用14個(gè)點(diǎn)摹迷,這里獲取的是這14個(gè)點(diǎn)的偏移
    info_bbox = dets[:, 5:33]   

    #保存最終點(diǎn)坐標(biāo)
    pts = []
    for i in xrange(dets.shape[0]):
        pts.append([[int(bbox[i, 0]) + info_bbox[i, j], int(bbox[i, 1]) + info_bbox[i, j+1]] for j in xrange(0,28,2)])

    areas = np.zeros(scores.shape)
    #得分降序
    order = scores.argsort()[::-1]
    inter_areas = np.zeros((scores.shape[0], scores.shape[0]))

    for il in xrange(len(pts)):
        #當(dāng)前點(diǎn)集組成多邊形疟赊,并計(jì)算該多邊形的面積
        poly = Polygon(pts[il])
        areas[il] = poly.area

        #多剩余的進(jìn)行遍歷
        for jl in xrange(il, len(pts)):
            polyj = Polygon(pts[jl])

            #計(jì)算兩個(gè)多邊形的交集,并計(jì)算對應(yīng)的面積
            inS = poly.intersection(polyj)
            inter_areas[il][jl] = inS.area
            inter_areas[jl][il] = inS.area

    #下面做法和nms一樣
    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)
        ovr = inter_areas[i][order[1:]] / (areas[i] + areas[order[1:]] - inter_areas[i][order[1:]])
        inds = np.where(ovr <= thresh)[0]
        order = order[inds + 1]

    return keep

適用范圍
適用范圍:一般適用于不規(guī)則形狀文本的檢測(如曲線文本)

掩膜NMS(MNMS)

MNMS是在FTSN文本檢測文章中提出的峡碉,基于分割掩膜圖的基礎(chǔ)上進(jìn)行IOU計(jì)算.如果文本檢測采用的是基于分割的方法來的話近哟,個(gè)人建議采用該方法:1).它可以很好地區(qū)分相近實(shí)例文本;2)它可以處理任意形狀的文本實(shí)例

具體步驟
1.先將所有的檢測按照得分進(jìn)行降序排序box_lists鲫寄;
2.對box_lists進(jìn)行遍歷吉执,每次遍歷當(dāng)前box與剩余box的IOU(它是在掩膜的基礎(chǔ)上進(jìn)行計(jì)算的,具體計(jì)算公式為
M M I=\max \left(\frac{I}{I_{A}}, \frac{I}{I_{B}}\right)
)地来,對于大于設(shè)定閾值的box進(jìn)行濾除戳玫;
3.得到最終的檢測框

實(shí)現(xiàn)代碼


#coding=utf-8
#############################################
# mask nms 實(shí)現(xiàn)
#############################################
import cv2
import numpy as np
import imutils
import copy

EPS=0.00001

def get_mask(box,mask):
    """根據(jù)box獲取對應(yīng)的掩膜"""
    tmp_mask=np.zeros(mask.shape,dtype="uint8")
    tmp=np.array(box.tolist(),dtype=np.int32).reshape(-1,2)
    cv2.fillPoly(tmp_mask, [tmp], (255))
    tmp_mask=cv2.bitwise_and(tmp_mask,mask)
    return tmp_mask,cv2.countNonZero(tmp_mask)


def comput_mmi(area_a,area_b,intersect):
    """
    計(jì)算MMI,2018.11.23 add
    :param mask_a: 實(shí)例文本a的mask的面積
    :param mask_b: 實(shí)例文本b的mask的面積
    :param intersect: 實(shí)例文本a和實(shí)例文本b的相交面積
    :return:
    """
    if area_a==0 or area_b==0:
        area_a+=EPS
        area_b+=EPS
        print("the area of text is 0")
    return max(float(intersect)/area_a,float(intersect)/area_b)


def mask_nms(dets, mask, thres=0.3):
    """
    mask nms 實(shí)現(xiàn)函數(shù)
    :param dets: 檢測結(jié)果,是一個(gè)N*9的numpy,
    :param mask: 當(dāng)前檢測的mask
    :param thres: 檢測的閾值
    """
    # 獲取bbox及對應(yīng)的score
    bbox_infos=dets[:,:8]
    scores=dets[:,8]

    keep=[]
    order=scores.argsort()[::-1]
    print("order:{}".format(order))
    nums=len(bbox_infos)
    suppressed=np.zeros((nums), dtype=np.int)
    print("lens:{}".format(nums))

    # 循環(huán)遍歷
    for i in range(nums):
        idx=order[i]
        if suppressed[idx]==1:
            continue
        keep.append(idx)
        mask_a,area_a=get_mask(bbox_infos[idx],mask)
        for j in range(i,nums):
            idx_j=order[j]
            if suppressed[idx_j]==1:
                continue
            mask_b, area_b =get_mask(bbox_infos[idx_j],mask)

            # 獲取兩個(gè)文本的相交面積
            merge_mask=cv2.bitwise_and(mask_a,mask_b)
            area_intersect=cv2.countNonZero(merge_mask)

            #計(jì)算MMI
            mmi=comput_mmi(area_a,area_b,area_intersect)
            # print("area_a:{},area_b:{},inte:{},mmi:{}".format(area_a,area_b,area_intersect,mmi))

            if mmi >= thres:
                suppressed[idx_j] = 1

    return dets[keep]

適用范圍
適用范圍:采用分割路線的文本檢測未斑,都可以適用該方法

總結(jié)

在文本檢測中咕宿,考慮到文本方向的多樣化.

  • 針對水平文本檢測:標(biāo)準(zhǔn)的NMS就可以

  • 針對基于分割方法的多方向文本檢測,優(yōu)先推薦Mask NMS蜡秽,當(dāng)然也可以采用Polygon NMS和Inclined NMS

  • 針對基于檢測方法的多方向文本檢測府阀,優(yōu)先推薦Polygon NMS和Inclined NMS

Soft-NMS

Motivation

絕大部分目標(biāo)檢測方法,最后都要用到 NMS-非極大值抑制進(jìn)行后處理芽突。 通常的做法是將檢測框按得分排序试浙,然后保留得分最高的框,同時(shí)刪除與該框重疊面積大于一定比例的其它框寞蚌。

這種貪心式方法存在如下圖所示的問題: 紅色框和綠色框是當(dāng)前的檢測結(jié)果田巴,二者的得分分別是0.95和0.80。如果按照傳統(tǒng)的NMS進(jìn)行處理挟秤,首先選中得分最高的紅色框壹哺,然后綠色框就會因?yàn)榕c之重疊面積過大而被刪掉。

另一方面煞聪,NMS的閾值也不太容易確定斗躏,設(shè)小了會出現(xiàn)下圖的情況(綠色框因?yàn)楹图t色框重疊面積較大而被刪掉),設(shè)置過高又容易增大誤檢昔脯。

思路:不要粗魯?shù)貏h除所有IOU大于閾值的框啄糙,而是降低其置信度。

Method

如下圖:如文章題目而言云稚,就是用一行代碼來替換掉原來的NMS隧饼。按照下圖整個(gè)處理一遍之后,指定一個(gè)置信度閾值静陈,然后最后得分大于該閾值的檢測框得以保留.


在這里插入圖片描述

原來的NMS可以描述如下:將IOU大于閾值的窗口的得分全部置為0燕雁。
s_{i}=\left\{\begin{array}{ll}{s_{i},} & {\operatorname{iou}\left(\mathcal{M}, b_{i}\right)<N_{t}} \\ {0,} & {\operatorname{iou}\left(\mathcal{M}, b_{i}\right) \geq N_{t}}\end{array}\right.

文章的改進(jìn)有兩種形式诞丽,一種是線性加權(quán)的:
s_{i}=\left\{\begin{array}{ll}{s_{i},} & {\text { iou }\left(\mathcal{M}, b_{i}\right)<N_{t}} \\ {s_{i}\left(1-\operatorname{iou}\left(\mathcal{M}, b_{i}\right)\right),} & {\text { iou }\left(\mathcal{M}, b_{i}\right) \geq N_{t}}\end{array}\right.

一種是高斯加權(quán)的:
s_{i}=s_{i} e^{-\frac{\operatorname{iou}\left(\mathcal{M}, b_{i}\right)^{2}}{\sigma}}, \forall b_{i} \notin \mathcal{D}

分析上面的兩種改進(jìn)形式,思想都是:M為當(dāng)前得分最高框拐格,b_{i}為待處理框僧免,b_{i}和M的IOU越大,b_{i}得分就下降的越厲害捏浊。
下面是作者給出的代碼:

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

這么做的解釋如下:


在這里插入圖片描述

如上圖:

假如還檢測出了3號框懂衩,而我們的最終目標(biāo)是檢測出1號和2號框,并且剔除3號框金踪,原始的nms只會檢測出一個(gè)1號框并剔除2號框和3號框浊洞,而softnms算法可以對1、2胡岔、3號檢測狂進(jìn)行置信度排序法希,可以知道這三個(gè)框的置信度從大到小的順序依次為:1-》2-》3(由于是使用了懲罰,所有可以獲得這種大小關(guān)系)靶瘸,如果我們再選擇了合適的置信度閾值苫亦,就可以保留1號和2號,同時(shí)剔除3號奕锌,實(shí)現(xiàn)我們的功能著觉。

但是村生,這里也有一個(gè)問題就是置信度的閾值如何選擇惊暴,作者在這里依然使用手工設(shè)置的值,依然存在很大的局限性趁桃,所以該算法依然存在改進(jìn)的空間辽话。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市卫病,隨后出現(xiàn)的幾起案子油啤,更是在濱河造成了極大的恐慌,老刑警劉巖高氮,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袋马,死亡現(xiàn)場離奇詭異古掏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)幽告,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裆甩,“玉大人冗锁,你說我怎么就攤上這事∴退ǎ” “怎么了冻河?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵箍邮,是天一觀的道長。 經(jīng)常有香客問我叨叙,道長锭弊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任擂错,我火速辦了婚禮廷蓉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘马昙。我一直安慰自己桃犬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布行楞。 她就那樣靜靜地躺著攒暇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪子房。 梳的紋絲不亂的頭發(fā)上形用,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機(jī)與錄音证杭,去河邊找鬼田度。 笑死,一個(gè)胖子當(dāng)著我的面吹牛解愤,可吹牛的內(nèi)容都是我干的镇饺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼送讲,長吁一口氣:“原來是場噩夢啊……” “哼奸笤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起哼鬓,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤监右,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后异希,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體健盒,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年称簿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扣癣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡予跌,死狀恐怖搏色,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情券册,我是刑警寧澤频轿,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布垂涯,位于F島的核電站,受9級特大地震影響航邢,放射性物質(zhì)發(fā)生泄漏耕赘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一膳殷、第九天 我趴在偏房一處隱蔽的房頂上張望操骡。 院中可真熱鬧,春花似錦赚窃、人聲如沸册招。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽是掰。三九已至,卻和暖如春辱匿,著一層夾襖步出監(jiān)牢的瞬間键痛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工匾七, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留絮短,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓昨忆,卻偏偏與公主長得像丁频,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子扔嵌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容