#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jun 16 17:16:31 2021
@author: ledi
"""
# -*- coding:utf-8 -*-
# Author:Richard Fang
"""
This is a Python version used to implement the Soft NMS algorithm.
Original Paper:Improving Object Detection With One Line of Code
"""
import numpy as np
import tensorflow as tf
import tensorflow as K
import time
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] # 5
indexes = np.array([np.arange(N)]) # array([[0, 1, 2, 3, 4, 5]])
'''
dets=array([[200., 200., 400., 400., 0.],
[220., 220., 420., 420., 1.],
[200., 240., 400., 440., 2.],
[240., 200., 440., 400., 3.],
[ 1., 1., 2., 2., 4.]])
'''
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 # array([0.9, 0.5, 0. , 0. , 0. ], dtype=float32)
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
for i in range(N):
# intermediate parameters for later parameters exchange
#当前变量
tBD = dets[i, :].copy()
tscore = scores[i].copy()
tarea = areas[i].copy()
pos = i + 1
#判断是不是最后一个box
if i != N-1:
#除去第一个box 的score 最大score 和其相应的index(位置)
maxscore = np.max(scores[pos:], axis=0)
maxpos = np.argmax(scores[pos:], axis=0)
else:
maxscore = scores[-1]
maxpos = 0
#比较当前的score 和余下的score的最大值
#如果当前的score 比余下的score的最大值小,就交换对应的(x1,y1,x2,y2),score和area
#总之只有一点就是把score 最大的那个box 放在第一个
if tscore < maxscore:
#将首位的box 换成,score最大的那个box
#交换坐标 (x1,y1,x2,y2)
dets[i, :] = dets[maxpos + i + 1, :]
dets[maxpos + i + 1, :] = tBD
tBD = dets[i, :]
#交换score
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
#分别比较当前的box的某个坐标与其他坐标的最大与最小值,
#为计算交并比做准备
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)
# print('ovr=',ovr)
#传统的NMS算法与soft-NMS算法的比较差别就是weight 的计算方式不同,
#NMS算法比较粗暴,直接 kill 掉 交并比大于阈值的box
#而soft-NMS则将大于阈值的box降低一定的权重
# 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:]
print('weight=',weight)
# print('scores=',scores)
# select the boxes and keep the corresponding indexes
inds = dets[:, 4][scores > thresh]
keep = inds.astype(int)
return keep
def speed():
boxes =1000* np.random.rand(1000, 100, 4)
boxscores = np.random.rand(1000, 100)
start = time.time()
for i in range(1000):
py_cpu_softnms(boxes[i], boxscores[i], method=2)
end = time.time()
print("Average run time: %f ms" % (end-start))
def test():
# boxes and scores
boxes = np.array([[200, 200, 400, 400], [220, 220, 420, 420], [200, 240, 400, 440], [240, 200, 440, 400], [1, 1, 2, 2]], dtype=np.float32)
boxscores = np.array([0.9, 0.8, 0.7, 0.6, 0.5], dtype=np.float32)
# tf.image.non_max_suppression 中 boxes 是 [y1,x1,y2,x2] 排序的。
index = py_cpu_softnms(boxes, boxscores, method=2)
selected_boxes = boxes[index]
return selected_boxes
if __name__ == '__main__':
test()
# speed()