# ---------------------------------------------------------- # Soft-NMS: Improving Object Detection With One Line of Code # Copyright (c) University of Maryland, College Park # Licensed under The MIT License [see LICENSE for details] # Written by Navaneeth Bodla and Bharat Singh # Modified by Kai Chen # ---------------------------------------------------------- # cython: language_level=3, boundscheck=False import numpy as np cimport numpy as np cdef inline np.float32_t max(np.float32_t a, np.float32_t b): return a if a >= b else b cdef inline np.float32_t min(np.float32_t a, np.float32_t b): return a if a <= b else b def soft_nms_cpu( np.ndarray[float, ndim=2] boxes_in, float iou_thr, unsigned int method=1, float sigma=0.5, float min_score=0.001, ): boxes = boxes_in.copy() cdef 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 inds = np.arange(N) 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] ti = inds[i] 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] inds[i] = inds[maxpos] # 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 inds[maxpos] = ti 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 > iou_thr: weight = 1 - ov else: weight = 1 elif method == 2: # gaussian weight = np.exp(-(ov * ov) / sigma) else: # original NMS if ov > iou_thr: 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] < min_score: 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] inds[pos] = inds[N - 1] N = N - 1 pos = pos - 1 pos = pos + 1 return boxes[:N], inds[:N]