forked from x4nth055/pythoncode-tutorials
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.py
267 lines (196 loc) · 9.74 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
import time
import torch
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
def boxes_iou(box1, box2):
"""
Returns the IOU between `box1` and `box2` (i.e intersection area divided by union area)
"""
# Get the Width and Height of each bounding box
width_box1 = box1[2]
height_box1 = box1[3]
width_box2 = box2[2]
height_box2 = box2[3]
# Calculate the area of the each bounding box
area_box1 = width_box1 * height_box1
area_box2 = width_box2 * height_box2
# Find the vertical edges of the union of the two bounding boxes
mx = min(box1[0] - width_box1/2.0, box2[0] - width_box2/2.0)
Mx = max(box1[0] + width_box1/2.0, box2[0] + width_box2/2.0)
# Calculate the width of the union of the two bounding boxes
union_width = Mx - mx
# Find the horizontal edges of the union of the two bounding boxes
my = min(box1[1] - height_box1/2.0, box2[1] - height_box2/2.0)
My = max(box1[1] + height_box1/2.0, box2[1] + height_box2/2.0)
# Calculate the height of the union of the two bounding boxes
union_height = My - my
# Calculate the width and height of the area of intersection of the two bounding boxes
intersection_width = width_box1 + width_box2 - union_width
intersection_height = height_box1 + height_box2 - union_height
# If the the boxes don't overlap then their IOU is zero
if intersection_width <= 0 or intersection_height <= 0:
return 0.0
# Calculate the area of intersection of the two bounding boxes
intersection_area = intersection_width * intersection_height
# Calculate the area of the union of the two bounding boxes
union_area = area_box1 + area_box2 - intersection_area
# Calculate the IOU
iou = intersection_area/union_area
return iou
def nms(boxes, iou_thresh):
"""
Performs Non maximal suppression technique to `boxes` using `iou_thresh` threshold
"""
# print(boxes.shape)
# If there are no bounding boxes do nothing
if len(boxes) == 0:
return boxes
# Create a PyTorch Tensor to keep track of the detection confidence
# of each predicted bounding box
det_confs = torch.zeros(len(boxes))
# Get the detection confidence of each predicted bounding box
for i in range(len(boxes)):
det_confs[i] = boxes[i][4]
# Sort the indices of the bounding boxes by detection confidence value in descending order.
# We ignore the first returned element since we are only interested in the sorted indices
_,sortIds = torch.sort(det_confs, descending = True)
# Create an empty list to hold the best bounding boxes after
# Non-Maximal Suppression (NMS) is performed
best_boxes = []
# Perform Non-Maximal Suppression
for i in range(len(boxes)):
# Get the bounding box with the highest detection confidence first
box_i = boxes[sortIds[i]]
# Check that the detection confidence is not zero
if box_i[4] > 0:
# Save the bounding box
best_boxes.append(box_i)
# Go through the rest of the bounding boxes in the list and calculate their IOU with
# respect to the previous selected box_i.
for j in range(i + 1, len(boxes)):
box_j = boxes[sortIds[j]]
# If the IOU of box_i and box_j is higher than the given IOU threshold set
# box_j's detection confidence to zero.
if boxes_iou(box_i, box_j) > iou_thresh:
box_j[4] = 0
return best_boxes
def detect_objects(model, img, iou_thresh, nms_thresh):
# Start the time. This is done to calculate how long the detection takes.
start = time.time()
# Set the model to evaluation mode.
model.eval()
# Convert the image from a NumPy ndarray to a PyTorch Tensor of the correct shape.
# The image is transposed, then converted to a FloatTensor of dtype float32, then
# Normalized to values between 0 and 1, and finally unsqueezed to have the correct
# shape of 1 x 3 x 416 x 416
img = torch.from_numpy(img.transpose(2,0,1)).float().div(255.0).unsqueeze(0)
# Feed the image to the neural network with the corresponding NMS threshold.
# The first step in NMS is to remove all bounding boxes that have a very low
# probability of detection. All predicted bounding boxes with a value less than
# the given NMS threshold will be removed.
list_boxes = model(img, nms_thresh)
# Make a new list with all the bounding boxes returned by the neural network
boxes = list_boxes[0][0] + list_boxes[1][0] + list_boxes[2][0]
# Perform the second step of NMS on the bounding boxes returned by the neural network.
# In this step, we only keep the best bounding boxes by eliminating all the bounding boxes
# whose IOU value is higher than the given IOU threshold
boxes = nms(boxes, iou_thresh)
# Stop the time.
finish = time.time()
# Print the time it took to detect objects
print('\n\nIt took {:.3f}'.format(finish - start), 'seconds to detect the objects in the image.\n')
# Print the number of objects detected
print('Number of Objects Detected:', len(boxes), '\n')
return boxes
def load_class_names(namesfile):
# Create an empty list to hold the object classes
class_names = []
# Open the file containing the COCO object classes in read-only mode
with open(namesfile, 'r') as fp:
# The coco.names file contains only one object class per line.
# Read the file line by line and save all the lines in a list.
lines = fp.readlines()
# Get the object class names
for line in lines:
# Make a copy of each line with any trailing whitespace removed
line = line.rstrip()
# Save the object class name into class_names
class_names.append(line)
return class_names
def print_objects(boxes, class_names):
print('Objects Found and Confidence Level:\n')
for i in range(len(boxes)):
box = boxes[i]
if len(box) >= 7 and class_names:
cls_conf = box[5]
cls_id = box[6]
print('%i. %s: %f' % (i + 1, class_names[cls_id], cls_conf))
def plot_boxes(img, boxes, class_names, plot_labels, color = None):
# Define a tensor used to set the colors of the bounding boxes
colors = torch.FloatTensor([[1,0,1],[0,0,1],[0,1,1],[0,1,0],[1,1,0],[1,0,0]])
# Define a function to set the colors of the bounding boxes
def get_color(c, x, max_val):
ratio = float(x) / max_val * 5
i = int(np.floor(ratio))
j = int(np.ceil(ratio))
ratio = ratio - i
r = (1 - ratio) * colors[i][c] + ratio * colors[j][c]
return int(r * 255)
# Get the width and height of the image
width = img.shape[1]
height = img.shape[0]
# Create a figure and plot the image
fig, a = plt.subplots(1,1)
a.imshow(img)
# Plot the bounding boxes and corresponding labels on top of the image
for i in range(len(boxes)):
# Get the ith bounding box
box = boxes[i]
# Get the (x,y) pixel coordinates of the lower-left and lower-right corners
# of the bounding box relative to the size of the image.
x1 = int(np.around((box[0] - box[2]/2.0) * width))
y1 = int(np.around((box[1] - box[3]/2.0) * height))
x2 = int(np.around((box[0] + box[2]/2.0) * width))
y2 = int(np.around((box[1] + box[3]/2.0) * height))
# Set the default rgb value to red
rgb = (1, 0, 0)
# Use the same color to plot the bounding boxes of the same object class
if len(box) >= 7 and class_names:
cls_conf = box[5]
cls_id = box[6]
classes = len(class_names)
offset = cls_id * 123457 % classes
red = get_color(2, offset, classes) / 255
green = get_color(1, offset, classes) / 255
blue = get_color(0, offset, classes) / 255
# If a color is given then set rgb to the given color instead
if color is None:
rgb = (red, green, blue)
else:
rgb = color
# Calculate the width and height of the bounding box relative to the size of the image.
width_x = x2 - x1
width_y = y1 - y2
# Set the postion and size of the bounding box. (x1, y2) is the pixel coordinate of the
# lower-left corner of the bounding box relative to the size of the image.
rect = patches.Rectangle((x1, y2),
width_x, width_y,
linewidth = 2,
edgecolor = rgb,
facecolor = 'none')
# Draw the bounding box on top of the image
a.add_patch(rect)
# If plot_labels = True then plot the corresponding label
if plot_labels:
# Create a string with the object class name and the corresponding object class probability
conf_tx = class_names[cls_id] + ': {:.1f}'.format(cls_conf)
# Define x and y offsets for the labels
lxc = (img.shape[1] * 0.266) / 100
lyc = (img.shape[0] * 1.180) / 100
# Draw the labels on top of the image
a.text(x1 + lxc, y1 - lyc, conf_tx, fontsize = 12, color = 'k',
bbox = dict(facecolor = rgb, edgecolor = rgb, alpha = 0.6))
plt.axis("off")
plt.savefig("output.jpg")
plt.show()