Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hackassin
GitHub Repository: hackassin/learnopencv
Path: blob/master/Face-Recognition-with-ArcFace/align/box_utils.py
3142 views
1
import numpy as np
2
from PIL import Image
3
4
5
def nms(boxes, overlap_threshold = 0.5, mode = 'union'):
6
"""Non-maximum suppression.
7
8
Arguments:
9
boxes: a float numpy array of shape [n, 5],
10
where each row is (xmin, ymin, xmax, ymax, score).
11
overlap_threshold: a float number.
12
mode: 'union' or 'min'.
13
14
Returns:
15
list with indices of the selected boxes
16
"""
17
18
# if there are no boxes, return the empty list
19
if len(boxes) == 0:
20
return []
21
22
# list of picked indices
23
pick = []
24
25
# grab the coordinates of the bounding boxes
26
x1, y1, x2, y2, score = [boxes[:, i] for i in range(5)]
27
28
area = (x2 - x1 + 1.0)*(y2 - y1 + 1.0)
29
ids = np.argsort(score) # in increasing order
30
31
while len(ids) > 0:
32
33
# grab index of the largest value
34
last = len(ids) - 1
35
i = ids[last]
36
pick.append(i)
37
38
# compute intersections
39
# of the box with the largest score
40
# with the rest of boxes
41
42
# left top corner of intersection boxes
43
ix1 = np.maximum(x1[i], x1[ids[:last]])
44
iy1 = np.maximum(y1[i], y1[ids[:last]])
45
46
# right bottom corner of intersection boxes
47
ix2 = np.minimum(x2[i], x2[ids[:last]])
48
iy2 = np.minimum(y2[i], y2[ids[:last]])
49
50
# width and height of intersection boxes
51
w = np.maximum(0.0, ix2 - ix1 + 1.0)
52
h = np.maximum(0.0, iy2 - iy1 + 1.0)
53
54
# intersections' areas
55
inter = w * h
56
if mode == 'min':
57
overlap = inter/np.minimum(area[i], area[ids[:last]])
58
elif mode == 'union':
59
# intersection over union (IoU)
60
overlap = inter/(area[i] + area[ids[:last]] - inter)
61
62
# delete all boxes where overlap is too big
63
ids = np.delete(
64
ids,
65
np.concatenate([[last], np.where(overlap > overlap_threshold)[0]])
66
)
67
68
return pick
69
70
71
def convert_to_square(bboxes):
72
"""Convert bounding boxes to a square form.
73
74
Arguments:
75
bboxes: a float numpy array of shape [n, 5].
76
77
Returns:
78
a float numpy array of shape [n, 5],
79
squared bounding boxes.
80
"""
81
82
square_bboxes = np.zeros_like(bboxes)
83
x1, y1, x2, y2 = [bboxes[:, i] for i in range(4)]
84
h = y2 - y1 + 1.0
85
w = x2 - x1 + 1.0
86
max_side = np.maximum(h, w)
87
square_bboxes[:, 0] = x1 + w*0.5 - max_side*0.5
88
square_bboxes[:, 1] = y1 + h*0.5 - max_side*0.5
89
square_bboxes[:, 2] = square_bboxes[:, 0] + max_side - 1.0
90
square_bboxes[:, 3] = square_bboxes[:, 1] + max_side - 1.0
91
return square_bboxes
92
93
94
def calibrate_box(bboxes, offsets):
95
"""Transform bounding boxes to be more like true bounding boxes.
96
'offsets' is one of the outputs of the nets.
97
98
Arguments:
99
bboxes: a float numpy array of shape [n, 5].
100
offsets: a float numpy array of shape [n, 4].
101
102
Returns:
103
a float numpy array of shape [n, 5].
104
"""
105
x1, y1, x2, y2 = [bboxes[:, i] for i in range(4)]
106
w = x2 - x1 + 1.0
107
h = y2 - y1 + 1.0
108
w = np.expand_dims(w, 1)
109
h = np.expand_dims(h, 1)
110
111
# this is what happening here:
112
# tx1, ty1, tx2, ty2 = [offsets[:, i] for i in range(4)]
113
# x1_true = x1 + tx1*w
114
# y1_true = y1 + ty1*h
115
# x2_true = x2 + tx2*w
116
# y2_true = y2 + ty2*h
117
# below is just more compact form of this
118
119
# are offsets always such that
120
# x1 < x2 and y1 < y2 ?
121
122
translation = np.hstack([w, h, w, h])*offsets
123
bboxes[:, 0:4] = bboxes[:, 0:4] + translation
124
return bboxes
125
126
127
def get_image_boxes(bounding_boxes, img, size = 24):
128
"""Cut out boxes from the image.
129
130
Arguments:
131
bounding_boxes: a float numpy array of shape [n, 5].
132
img: an instance of PIL.Image.
133
size: an integer, size of cutouts.
134
135
Returns:
136
a float numpy array of shape [n, 3, size, size].
137
"""
138
139
num_boxes = len(bounding_boxes)
140
width, height = img.size
141
142
[dy, edy, dx, edx, y, ey, x, ex, w, h] = correct_bboxes(bounding_boxes, width, height)
143
img_boxes = np.zeros((num_boxes, 3, size, size), 'float32')
144
145
for i in range(num_boxes):
146
img_box = np.zeros((h[i], w[i], 3), 'uint8')
147
148
img_array = np.asarray(img, 'uint8')
149
img_box[dy[i]:(edy[i] + 1), dx[i]:(edx[i] + 1), :] =\
150
img_array[y[i]:(ey[i] + 1), x[i]:(ex[i] + 1), :]
151
152
# resize
153
img_box = Image.fromarray(img_box)
154
img_box = img_box.resize((size, size), Image.BILINEAR)
155
img_box = np.asarray(img_box, 'float32')
156
157
img_boxes[i, :, :, :] = _preprocess(img_box)
158
159
return img_boxes
160
161
162
def correct_bboxes(bboxes, width, height):
163
"""Crop boxes that are too big and get coordinates
164
with respect to cutouts.
165
166
Arguments:
167
bboxes: a float numpy array of shape [n, 5],
168
where each row is (xmin, ymin, xmax, ymax, score).
169
width: a float number.
170
height: a float number.
171
172
Returns:
173
dy, dx, edy, edx: a int numpy arrays of shape [n],
174
coordinates of the boxes with respect to the cutouts.
175
y, x, ey, ex: a int numpy arrays of shape [n],
176
corrected ymin, xmin, ymax, xmax.
177
h, w: a int numpy arrays of shape [n],
178
just heights and widths of boxes.
179
180
in the following order:
181
[dy, edy, dx, edx, y, ey, x, ex, w, h].
182
"""
183
184
x1, y1, x2, y2 = [bboxes[:, i] for i in range(4)]
185
w, h = x2 - x1 + 1.0, y2 - y1 + 1.0
186
num_boxes = bboxes.shape[0]
187
188
# 'e' stands for end
189
# (x, y) -> (ex, ey)
190
x, y, ex, ey = x1, y1, x2, y2
191
192
# we need to cut out a box from the image.
193
# (x, y, ex, ey) are corrected coordinates of the box
194
# in the image.
195
# (dx, dy, edx, edy) are coordinates of the box in the cutout
196
# from the image.
197
dx, dy = np.zeros((num_boxes,)), np.zeros((num_boxes,))
198
edx, edy = w.copy() - 1.0, h.copy() - 1.0
199
200
# if box's bottom right corner is too far right
201
ind = np.where(ex > width - 1.0)[0]
202
edx[ind] = w[ind] + width - 2.0 - ex[ind]
203
ex[ind] = width - 1.0
204
205
# if box's bottom right corner is too low
206
ind = np.where(ey > height - 1.0)[0]
207
edy[ind] = h[ind] + height - 2.0 - ey[ind]
208
ey[ind] = height - 1.0
209
210
# if box's top left corner is too far left
211
ind = np.where(x < 0.0)[0]
212
dx[ind] = 0.0 - x[ind]
213
x[ind] = 0.0
214
215
# if box's top left corner is too high
216
ind = np.where(y < 0.0)[0]
217
dy[ind] = 0.0 - y[ind]
218
y[ind] = 0.0
219
220
return_list = [dy, edy, dx, edx, y, ey, x, ex, w, h]
221
return_list = [i.astype('int32') for i in return_list]
222
223
return return_list
224
225
226
def _preprocess(img):
227
"""Preprocessing step before feeding the network.
228
229
Arguments:
230
img: a float numpy array of shape [h, w, c].
231
232
Returns:
233
a float numpy array of shape [1, c, h, w].
234
"""
235
img = img.transpose((2, 0, 1))
236
img = np.expand_dims(img, 0)
237
img = (img - 127.5) * 0.0078125
238
return img
239
240