非極大值抑制(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ì)算公式為
)地来,對于大于設(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燕雁。
文章的改進(jìn)有兩種形式诞丽,一種是線性加權(quán)的:
一種是高斯加權(quán)的:
分析上面的兩種改進(jìn)形式,思想都是:M為當(dāng)前得分最高框拐格,為待處理框僧免,
和M的IOU越大,
得分就下降的越厲害捏浊。
下面是作者給出的代碼:
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)的空間辽话。