Skip to content

Commit ee40e7a

Browse files
Add bounding box primitive (#251)
* Add bounding box primitive Signed-off-by: Ashwin Vaidya <[email protected]> * Add docstring Signed-off-by: Ashwin Vaidya <[email protected]> --------- Signed-off-by: Ashwin Vaidya <[email protected]>
1 parent 655f282 commit ee40e7a

File tree

3 files changed

+69
-5
lines changed

3 files changed

+69
-5
lines changed
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"""Visualizer."""
22

3-
# Copyright (C) 2024 Intel Corporation
3+
# Copyright (C) 2024-2025 Intel Corporation
44
# SPDX-License-Identifier: Apache-2.0
55

66
from .layout import Flatten, HStack, Layout
7-
from .primitive import Overlay
7+
from .primitive import BoundingBox, Overlay
88
from .scene import Scene
99
from .visualizer import Visualizer
1010

11-
__all__ = ["Overlay", "Scene", "Visualizer", "Layout", "Flatten", "HStack"]
11+
__all__ = ["BoundingBox", "Overlay", "Scene", "Visualizer", "Layout", "Flatten", "HStack"]

model_api/python/model_api/visualizer/primitive.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,70 @@
99

1010
import numpy as np
1111
import PIL
12+
from PIL import Image, ImageDraw
1213

1314

1415
class Primitive(ABC):
1516
"""Primitive class."""
1617

1718
@abstractmethod
18-
def compute(self, image: PIL.Image) -> PIL.Image:
19+
def compute(self, image: Image) -> Image:
1920
pass
2021

2122

23+
class BoundingBox(Primitive):
24+
"""Bounding box primitive.
25+
26+
Args:
27+
x1 (int): x-coordinate of the top-left corner of the bounding box.
28+
y1 (int): y-coordinate of the top-left corner of the bounding box.
29+
x2 (int): x-coordinate of the bottom-right corner of the bounding box.
30+
y2 (int): y-coordinate of the bottom-right corner of the bounding box.
31+
label (str | None): Label of the bounding box.
32+
color (str | tuple[int, int, int]): Color of the bounding box.
33+
34+
Example:
35+
>>> bounding_box = BoundingBox(x1=10, y1=10, x2=100, y2=100, label="Label Name")
36+
>>> bounding_box.compute(image)
37+
"""
38+
39+
def __init__(
40+
self,
41+
x1: int,
42+
y1: int,
43+
x2: int,
44+
y2: int,
45+
label: str | None = None,
46+
color: str | tuple[int, int, int] = "blue",
47+
) -> None:
48+
self.x1 = x1
49+
self.y1 = y1
50+
self.x2 = x2
51+
self.y2 = y2
52+
self.label = label
53+
self.color = color
54+
self.y_buffer = 5 # Text at the bottom of the text box is clipped. This prevents that.
55+
56+
def compute(self, image: Image) -> Image:
57+
draw = ImageDraw.Draw(image)
58+
# draw rectangle
59+
draw.rectangle((self.x1, self.y1, self.x2, self.y2), outline=self.color, width=2)
60+
# add label
61+
if self.label:
62+
# draw the background of the label
63+
textbox = draw.textbbox((0, 0), self.label)
64+
label_image = Image.new(
65+
"RGB",
66+
(textbox[2] - textbox[0], textbox[3] + self.y_buffer - textbox[1]),
67+
self.color,
68+
)
69+
draw = ImageDraw.Draw(label_image)
70+
# write the label on the background
71+
draw.text((0, 0), self.label, fill="white")
72+
image.paste(label_image, (self.x1, self.y1))
73+
return image
74+
75+
2276
class Overlay(Primitive):
2377
"""Overlay primitive.
2478

tests/python/unit/visualizer/test_primitive.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55

66
import numpy as np
77
import PIL
8+
from PIL import ImageDraw
89

9-
from model_api.visualizer import Overlay
10+
from model_api.visualizer import BoundingBox, Overlay
1011

1112

1213
def test_overlay(mock_image: PIL.Image):
@@ -22,3 +23,12 @@ def test_overlay(mock_image: PIL.Image):
2223
data *= 255
2324
overlay = Overlay(data)
2425
assert overlay.compute(empty_image) == expected_image
26+
27+
28+
def test_bounding_box(mock_image: PIL.Image):
29+
"""Test if the bounding box is created correctly."""
30+
expected_image = mock_image.copy()
31+
draw = ImageDraw.Draw(expected_image)
32+
draw.rectangle((10, 10, 100, 100), outline="blue", width=2)
33+
bounding_box = BoundingBox(x1=10, y1=10, x2=100, y2=100)
34+
assert bounding_box.compute(mock_image) == expected_image

0 commit comments

Comments
 (0)