Skip to content

Commit 5ef882c

Browse files
Labelling issue and Bounding box drawing issue. (#8)
* Fallback to normal labelling if out of frame * Added test images and test.py files * Handle bounding boxes on the edge properly * Made requested changes according to PR --------- Co-authored-by: Shoumik Chowdhury <shoumikchow@gmail.com>
1 parent d40b138 commit 5ef882c

File tree

8 files changed

+262
-60
lines changed

8 files changed

+262
-60
lines changed

bbox_visualizer/bbox_visualizer.py

Lines changed: 131 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,46 @@
11
import cv2
2+
import logging
23

34
font = cv2.FONT_HERSHEY_SIMPLEX
45

56

6-
def draw_rectangle(img,
7-
bbox,
8-
bbox_color=(255, 255, 255),
9-
thickness=3,
10-
is_opaque=False,
11-
alpha=0.5):
7+
def check_and_modify_bbox(bbox, img_size, margin=0):
8+
"""
9+
Checks if the bounding box is inside the given image.
10+
If not, the coordinates are trimmed inorder to fit inside the image.
11+
12+
Trimming criteria:
13+
- for xmin and ymin:
14+
- xmin and ymin are trimmed to 0, if their value is negative i.e. out of the
15+
scope of image.
16+
- for xmax and ymax:
17+
- xmax and ymax are trimmed to image_width and image_height respectivel
18+
19+
20+
Parameters
21+
----------
22+
bbox : list
23+
a list containing x_min, y_min, x_max and y_max of the rectangle positions
24+
img_size: tuple
25+
a tuple containing image width, image height and color channel.
26+
margin: integer (default = 0)
27+
space between edge of the image and bounding box incase the box is trimmed such that
28+
it's size is exactly as the image.
29+
30+
Returns
31+
-------
32+
list
33+
bounding box coordinates (xmin, ymin, xmax, ymax)
34+
"""
35+
bbox = [value if value > 0 else margin for value in bbox]
36+
bbox[2] = bbox[2] if bbox[2] < img_size[1] else img_size[1] - margin
37+
bbox[3] = bbox[3] if bbox[3] < img_size[0] else img_size[0] - margin
38+
return bbox
39+
40+
41+
def draw_rectangle(
42+
img, bbox, bbox_color=(255, 255, 255), thickness=3, is_opaque=False, alpha=0.5
43+
):
1244
"""Draws the rectangle around the object
1345
1446
Parameters
@@ -31,30 +63,37 @@ def draw_rectangle(img,
3163
ndarray
3264
the image with the bounding box drawn
3365
"""
66+
bbox = check_and_modify_bbox(bbox, img.shape)
67+
3468
output = img.copy()
3569
if not is_opaque:
36-
cv2.rectangle(output, (bbox[0], bbox[1]), (bbox[2], bbox[3]),
37-
bbox_color, thickness)
70+
cv2.rectangle(
71+
output, (bbox[0], bbox[1]), (bbox[2], bbox[3]), bbox_color, thickness
72+
)
3873
else:
3974
overlay = img.copy()
4075

41-
cv2.rectangle(overlay, (bbox[0], bbox[1]), (bbox[2], bbox[3]),
42-
bbox_color, -1)
76+
cv2.rectangle(overlay, (bbox[0], bbox[1]), (bbox[2], bbox[3]), bbox_color, -1)
4377
cv2.addWeighted(overlay, alpha, output, 1 - alpha, 0, output)
4478

4579
return output
4680

4781

48-
def add_label(img,
49-
label,
50-
bbox,
51-
size=1,
52-
thickness=2,
53-
draw_bg=True,
54-
text_bg_color=(255, 255, 255),
55-
text_color=(0, 0, 0),
56-
top=True):
57-
"""adds label, inside or outside the rectangle
82+
def add_label(
83+
img,
84+
label,
85+
bbox,
86+
size=1,
87+
thickness=2,
88+
draw_bg=True,
89+
text_bg_color=(255, 255, 255),
90+
text_color=(0, 0, 0),
91+
top=True,
92+
):
93+
"""
94+
adds label, inside or outside the rectangle.
95+
if label cannot be drawn on outside of the box, it puts label inside the box.
96+
5897
5998
Parameters
6099
----------
@@ -83,24 +122,25 @@ def add_label(img,
83122
the image with the label written
84123
"""
85124

86-
(label_width, label_height), baseline = cv2.getTextSize(label, font, size, thickness)
87125

88-
if top:
89-
label_bg = [bbox[0], bbox[1], bbox[0] + label_width, bbox[1] - label_height - (15 * size)]
126+
(text_width, text_height), baseline = cv2.getTextSize(label, font, size, thickness)
127+
128+
if top and bbox[1] - text_height > text_height:
129+
label_bg = [bbox[0], bbox[1], bbox[0] + text_width, bbox[1] - text_height - (15 * size)]
90130
if draw_bg:
91131
cv2.rectangle(img, (label_bg[0], label_bg[1]),
92132
(label_bg[2] + 5, label_bg[3]), text_bg_color, -1)
93-
133+
94134
cv2.putText(img, label, (bbox[0] + 5, bbox[1] - (15 * size)), font, size, text_color, thickness)
95135
else:
96136
label_bg = [bbox[0], bbox[1], bbox[0] + label_width, bbox[1] + label_height + (15 * size)]
97137
if draw_bg:
138+
98139
cv2.rectangle(img, (label_bg[0], label_bg[1]),
99140
(label_bg[2] + 5, label_bg[3]), text_bg_color, -1)
100141
cv2.putText(img, label, (bbox[0] + 5, bbox[1] + (16 * size) + (4 * thickness)), font, size, text_color, thickness)
101142
return img
102143

103-
104144
def add_T_label(img,
105145
label,
106146
bbox,
@@ -109,6 +149,7 @@ def add_T_label(img,
109149
draw_bg=True,
110150
text_bg_color=(255, 255, 255),
111151
text_color=(0, 0, 0)):
152+
112153
"""adds a T label to the rectangle, originating from the top of the rectangle
113154
114155
Parameters
@@ -139,19 +180,28 @@ def add_T_label(img,
139180
(label_width, label_height), baseline = cv2.getTextSize(label, font, size, thickness)
140181
# draw vertical line
141182
x_center = (bbox[0] + bbox[2]) // 2
142-
y_top = bbox[1] - 50
143-
cv2.line(img, (x_center, bbox[1]), (x_center, y_top), text_bg_color, 3)
183+
line_top = y_top = bbox[1] - 50
144184

145185
# draw rectangle with label
146186
y_bottom = y_top
147-
y_top = y_bottom - label_height - 5
148-
x_left = x_center - (label_width // 2) - 5
149-
x_right = x_center + (label_width // 2) + 5
187+
y_top = y_bottom - text_height - 5
188+
189+
if y_top < 0:
190+
logging.warning(
191+
"Labelling style 'T' going out of frame. Falling back to normal labeling."
192+
)
193+
return add_label(img, label, bbox)
194+
195+
cv2.line(img, (x_center, bbox[1]), (x_center, line_top), text_bg_color, 3)
196+
x_left = x_center - (text_width // 2) - 5
197+
x_right = x_center + (text_width // 2) + 5
150198
if draw_bg:
151199
cv2.rectangle(img, (x_left, y_top - 30), (x_right, y_bottom),
152200
text_bg_color, -1)
153201
cv2.putText(img, label, (x_left + 5, y_bottom - (8 * size)),
154202
font, size, text_color, thickness)
203+
)
204+
155205

156206
return img
157207

@@ -166,6 +216,7 @@ def draw_flag_with_label(img,
166216
text_bg_color=(255, 255, 255),
167217
text_color=(0, 0, 0),
168218
):
219+
169220
"""draws a pole from the middle of the object that is to be labeled and adds the label to the flag
170221
171222
Parameters
@@ -199,8 +250,14 @@ def draw_flag_with_label(img,
199250
(label_width, label_height), baseline = cv2.getTextSize(label, font, size, thickness)
200251

201252
x_center = (bbox[0] + bbox[2]) // 2
202-
y_bottom = int((bbox[1] * .75 + bbox[3] * .25))
253+
y_bottom = int((bbox[1] * 0.75 + bbox[3] * 0.25))
203254
y_top = bbox[1] - (y_bottom - bbox[1])
255+
if y_top < 0:
256+
logging.warning(
257+
"Labelling style 'Flag' going out of frame. Falling back to normal labeling."
258+
)
259+
img = draw_rectangle(img, bbox, bbox_color=line_color)
260+
return add_label(img, label, bbox)
204261

205262
start_point = (x_center, y_top)
206263
end_point = (x_center, y_bottom)
@@ -210,14 +267,28 @@ def draw_flag_with_label(img,
210267
# write label
211268

212269
if write_label:
270+
271+
text_width = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 1, 2)[0][0]
213272
label_bg = [
214-
start_point[0], start_point[1], start_point[0] + label_width,
215-
start_point[1] - label_height - (10 * size)
273+
start_point[0],
274+
start_point[1],
275+
start_point[0] + text_width,
276+
start_point[1] + 30,
216277
]
217-
cv2.rectangle(img, (label_bg[0], label_bg[1]),
218-
(label_bg[2] + 5, label_bg[3]), text_bg_color, -1)
219-
cv2.putText(img, label, (start_point[0] + 7, start_point[1] - (13 * size) + 20),
220-
font, size, text_color, thickness)
278+
cv2.rectangle(
279+
img,
280+
(label_bg[0], label_bg[1]),
281+
(label_bg[2] + 5, label_bg[3]),
282+
text_bg_color,
283+
-1,
284+
)
285+
cv2.putText(
286+
img,
287+
label,
288+
(start_point[0] + 5, start_point[1] - 5 + 30),
289+
font, size, text_color, thickness
290+
)
291+
221292
return img
222293

223294

@@ -226,12 +297,9 @@ def draw_flag_with_label(img,
226297
# INSTEAD OF THE FUNCTIONS BELOW
227298

228299

229-
def draw_multiple_rectangles(img,
230-
bboxes,
231-
bbox_color=(255, 255, 255),
232-
thickness=3,
233-
is_opaque=False,
234-
alpha=0.5):
300+
def draw_multiple_rectangles(
301+
img, bboxes, bbox_color=(255, 255, 255), thickness=3, is_opaque=False, alpha=0.5
302+
):
235303
"""draws multiple rectangles
236304
237305
img : ndarray
@@ -252,10 +320,8 @@ def draw_multiple_rectangles(img,
252320
ndarray
253321
the image with the bounding boxes drawn
254322
"""
255-
256323
for bbox in bboxes:
257-
img = draw_rectangle(img, bbox, bbox_color, thickness, is_opaque,
258-
alpha)
324+
img = draw_rectangle(img, bbox, bbox_color, thickness, is_opaque, alpha)
259325
return img
260326

261327

@@ -268,6 +334,7 @@ def add_multiple_labels(img,
268334
text_bg_color=(255, 255, 255),
269335
text_color=(0, 0, 0),
270336
top=True):
337+
271338
"""add labels, inside or outside the rectangles
272339
273340
Parameters
@@ -295,16 +362,17 @@ def add_multiple_labels(img,
295362
for label, bbox in zip(labels, bboxes):
296363
img = add_label(img, label, bbox, size, thickness, draw_bg, text_bg_color, text_color,
297364
top)
298-
299365
return img
300366

301367

302-
def add_multiple_T_labels(img,
303-
labels,
304-
bboxes,
305-
draw_bg=True,
306-
text_bg_color=(255, 255, 255),
307-
text_color=(0, 0, 0)):
368+
def add_multiple_T_labels(
369+
img,
370+
labels,
371+
bboxes,
372+
draw_bg=True,
373+
text_bg_color=(255, 255, 255),
374+
text_color=(0, 0, 0),
375+
):
308376
"""adds T labels to the rectangles, each originating from the top of the rectangle
309377
310378
Parameters
@@ -336,13 +404,15 @@ def add_multiple_T_labels(img,
336404
return img
337405

338406

339-
def draw_multiple_flags_with_labels(img,
340-
labels,
341-
bboxes,
342-
write_label=True,
343-
line_color=(255, 255, 255),
344-
text_bg_color=(255, 255, 255),
345-
text_color=(0, 0, 0)):
407+
def draw_multiple_flags_with_labels(
408+
img,
409+
labels,
410+
bboxes,
411+
write_label=True,
412+
line_color=(255, 255, 255),
413+
text_bg_color=(255, 255, 255),
414+
text_color=(0, 0, 0),
415+
):
346416
"""draws poles from the middle of the objects that are to be labeled and adds the labels to the flags
347417
348418
Parameters
@@ -369,6 +439,7 @@ def draw_multiple_flags_with_labels(img,
369439
"""
370440

371441
for label, bbox in zip(labels, bboxes):
442+
372443
img = draw_flag_with_label(img, label, bbox, size=1, thickness=2,
373444
write_label=write_label, line_color=line_color,
374445
text_bg_color=text_bg_color,

images/test_images/source_bird.jpg

111 KB
Loading

images/test_images/source_bird.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"shapes": [
3+
{
4+
"label": "bird",
5+
"points": [
6+
[470, 30],
7+
[655, 200]
8+
],
9+
"shape_type": "rectangle",
10+
"flags": {}
11+
}
12+
],
13+
"imagePath": "source_bird.jpg",
14+
"imageHeight": 900,
15+
"imageWidth": 675
16+
}
117 KB
Loading
79.7 KB
Loading

0 commit comments

Comments
 (0)