-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathdraw_boxes.py
88 lines (70 loc) · 3.28 KB
/
draw_boxes.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
"""Draw predicted or ground truth boxes on input image."""
import colorsys
import random
import numpy as np
from PIL import Image, ImageDraw, ImageFont
def get_colors_for_classes(num_classes):
"""Return list of random colors for number of classes given."""
# Use previously generated colors if num_classes is the same.
if (hasattr(get_colors_for_classes, "colors") and
len(get_colors_for_classes.colors) == num_classes):
return get_colors_for_classes.colors
hsv_tuples = [(x / num_classes, 1., 1.) for x in range(num_classes)]
colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))
colors = list(
map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)),
colors))
random.seed(10101) # Fixed seed for consistent colors across runs.
random.shuffle(colors) # Shuffle colors to decorrelate adjacent classes.
random.seed(None) # Reset seed to default.
get_colors_for_classes.colors = colors # Save colors for future calls.
return colors
def draw_boxes(image, boxes, box_classes, class_names, scores=None):
"""Draw bounding boxes on image.
Draw bounding boxes with class name and optional box score on image.
Args:
image: An `array` of shape (width, height, 3) with values in [0, 1].
boxes: An `array` of shape (num_boxes, 4) containing box corners as
(y_min, x_min, y_max, x_max).
box_classes: A `list` of indicies into `class_names`.
class_names: A `list` of `string` class names.
`scores`: A `list` of scores for each box.
Returns:
A copy of `image` modified with given bounding boxes.
"""
image = Image.fromarray(np.floor(image * 255 + 0.5).astype('uint8'))
font = ImageFont.truetype(
font='font/FiraMono-Medium.otf',
size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32'))
thickness = (image.size[0] + image.size[1]) // 300
colors = get_colors_for_classes(len(class_names))
for i, c in list(enumerate(box_classes)):
box_class = class_names[c]
box = boxes[i]
if isinstance(scores, np.ndarray):
score = scores[i]
label = '{} {:.2f}'.format(box_class, score)
else:
label = '{}'.format(box_class)
draw = ImageDraw.Draw(image)
label_size = draw.textsize(label, font)
top, left, bottom, right = box
top = max(0, np.floor(top + 0.5).astype('int32'))
left = max(0, np.floor(left + 0.5).astype('int32'))
bottom = min(image.size[1], np.floor(bottom + 0.5).astype('int32'))
right = min(image.size[0], np.floor(right + 0.5).astype('int32'))
print(label, (left, top), (right, bottom))
if top - label_size[1] >= 0:
text_origin = np.array([left, top - label_size[1]])
else:
text_origin = np.array([left, top + 1])
# My kingdom for a good redistributable image drawing library.
for i in range(thickness):
draw.rectangle(
[left + i, top + i, right - i, bottom - i], outline=colors[c])
draw.rectangle(
[tuple(text_origin), tuple(text_origin + label_size)],
fill=colors[c])
draw.text(text_origin, label, fill=(0, 0, 0), font=font)
del draw
return np.array(image)