soft_nms_cpu.pyx 3.89 KB
Newer Older
Kai Chen's avatar
Kai Chen committed
1
2
3
4
5
# ----------------------------------------------------------
# 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
6
# Modified by Kai Chen
Kai Chen's avatar
Kai Chen committed
7
8
# ----------------------------------------------------------

Kai Chen's avatar
Kai Chen committed
9
10
# cython: language_level=3, boundscheck=False

Kai Chen's avatar
Kai Chen committed
11
12
13
14
15
16
17
18
19
20
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

21

22
def soft_nms_cpu(
Kai Chen's avatar
Kai Chen committed
23
    np.ndarray[float, ndim=2] boxes_in,
24
25
    float iou_thr,
    unsigned int method=1,
Kai Chen's avatar
Kai Chen committed
26
    float sigma=0.5,
27
    float min_score=0.001,
Kai Chen's avatar
Kai Chen committed
28
29
):
    boxes = boxes_in.copy()
30
    cdef int N = boxes.shape[0]
Kai Chen's avatar
Kai Chen committed
31
32
33
34
35
36
37
38
39
40
41
42
    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

43
44
45
46
47
        tx1 = boxes[i, 0]
        ty1 = boxes[i, 1]
        tx2 = boxes[i, 2]
        ty2 = boxes[i, 3]
        ts = boxes[i, 4]
Kai Chen's avatar
Kai Chen committed
48
49
50
51
52
53
54
55
56
57
58
        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
59
60
61
62
63
        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]
Kai Chen's avatar
Kai Chen committed
64
65
66
        inds[i] = inds[maxpos]

        # swap ith box with position of max box
67
68
69
70
71
        boxes[maxpos, 0] = tx1
        boxes[maxpos, 1] = ty1
        boxes[maxpos, 2] = tx2
        boxes[maxpos, 3] = ty2
        boxes[maxpos, 4] = ts
Kai Chen's avatar
Kai Chen committed
72
73
        inds[maxpos] = ti

74
75
76
77
78
        tx1 = boxes[i, 0]
        ty1 = boxes[i, 1]
        tx2 = boxes[i, 2]
        ty2 = boxes[i, 3]
        ts = boxes[i, 4]
Kai Chen's avatar
Kai Chen committed
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

        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)
96
                    ov = iw * ih / ua  # iou between max box and detection box
Kai Chen's avatar
Kai Chen committed
97

98
99
                    if method == 1:  # linear
                        if ov > iou_thr:
Kai Chen's avatar
Kai Chen committed
100
101
102
                            weight = 1 - ov
                        else:
                            weight = 1
103
104
105
106
                    elif method == 2:  # gaussian
                        weight = np.exp(-(ov * ov) / sigma)
                    else:  # original NMS
                        if ov > iou_thr:
Kai Chen's avatar
Kai Chen committed
107
108
109
110
                            weight = 0
                        else:
                            weight = 1

111
                    boxes[pos, 4] = weight * boxes[pos, 4]
Kai Chen's avatar
Kai Chen committed
112
113
114

                    # if box score falls below threshold, discard the box by
                    # swapping with last box update N
115
116
117
118
119
120
121
                    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]
Kai Chen's avatar
Kai Chen committed
122
123
124
125
126
                        N = N - 1
                        pos = pos - 1

            pos = pos + 1

127
    return boxes[:N], inds[:N]