88
99logger = Logger (__name__ )
1010
11+
12+ class Shape :
13+ RECTANGLE = "rectangle"
14+ CIRCLE = "circle"
15+
16+
1117# Define a mapping of confidence ranges to colors for bounding boxes
1218CONFIDENCE_MAP = {
1319 (0 , 20 ): "#FF0976" , # Pink
@@ -40,7 +46,7 @@ def _read(file_path: str) -> bytes:
4046
4147
4248def get_image_type (image_bytes : bytes | Image .Image ) -> str | None :
43- """Detect the type of an image from bytes or a PIL Image object.
49+ """Detect the type of image from bytes or a PIL Image object.
4450
4551 Returns:
4652 str: The image type in lowercase (e.g., 'jpeg', 'png').
@@ -60,7 +66,7 @@ def get_image_type(image_bytes: bytes | Image.Image) -> str | None:
6066 return None
6167
6268
63- def get_image_bytes (image : str | Image .Image | bytes ) -> bytes :
69+ def get_image_bytes (image : str | Image .Image | bytes ) -> bytes | None :
6470 """Convert different type of image objects to bytes."""
6571 if image is None :
6672 return None
@@ -78,23 +84,12 @@ def get_image_bytes(image: str | Image.Image | bytes) -> bytes:
7884 return None
7985
8086
81- def draw_colored_dot (draw , x , y , color , size ):
82- """Draws a large colored dot on a PIL Image at the specified coordinate.
83-
84- Args:
85- draw: An ImageDraw object from PIL.
86- x: The x-coordinate of the center of the dot.
87- y: The y-coordinate of the center of the dot.
88- color: A color value that PIL understands (e.g., "red", (255, 0, 0), "#FF0000").
89- size: The radius of the dot (in pixels).
90- """
91- # Calculate the bounding box for the circle
92- bounding_box = (x - size , y - size , x + size , y + size )
93- # Draw a filled ellipse (which looks like a circle if the bounding box is a square)
94- draw .ellipse (bounding_box , fill = color )
95-
96-
97- def draw_bounding_boxes (image : Image .Image | bytes , detection : dict , draw : ImageDraw .ImageDraw = None ) -> Image .Image | None :
87+ def draw_bounding_boxes (
88+ image : Image .Image | bytes ,
89+ detection : dict ,
90+ draw : ImageDraw .ImageDraw = None ,
91+ shape : Shape = Shape .RECTANGLE ,
92+ ) -> Image .Image | None :
9893 """Draw bounding boxes on an image using PIL.
9994
10095 The thickness of the box and font size are scaled based on image size.
@@ -104,6 +99,8 @@ def draw_bounding_boxes(image: Image.Image | bytes, detection: dict, draw: Image
10499 detection (dict): A dictionary containing detection results with keys 'class_name', 'bounding_box_xyxy', and
105100 'confidence'.
106101 draw (ImageDraw.ImageDraw, optional): An existing ImageDraw object to use. If None, a new one is created.
102+ shape (Shape, optional): Shape of the bounding box. Defaults to rectangle.
103+ itself. Defaults to False.
107104 """
108105 if isinstance (image , bytes ):
109106 image_box = Image .open (io .BytesIO (image ))
@@ -116,6 +113,10 @@ def draw_bounding_boxes(image: Image.Image | bytes, detection: dict, draw: Image
116113 if not detection or "detection" not in detection :
117114 return None
118115
116+ if shape not in (Shape .RECTANGLE , Shape .CIRCLE ):
117+ logger .warning (f"Unsupported shape '{ shape } '. Defaulting to rectangle." )
118+ shape = Shape .RECTANGLE
119+
119120 detection = detection ["detection" ]
120121
121122 # Scale font size and box thickness based on image size and number of detections
@@ -163,12 +164,19 @@ def draw_bounding_boxes(image: Image.Image | bytes, detection: dict, draw: Image
163164 x2_text = x1 + text_width + label_hpad * 2
164165
165166 # Draw bounding box
166- draw .rectangle ([x1 , y1 , x2 , y2 ], outline = box_color , width = box_thickness )
167+ if shape == Shape .CIRCLE :
168+ center_x = int ((x1 + x2 ) / 2 )
169+ center_y = int ((y1 + y2 ) / 2 )
170+ radius = 10
171+ bounding_box = (center_x - radius , center_y - radius , center_x + radius , center_y + radius )
172+ draw .ellipse (bounding_box , outline = box_color , width = 2 )
173+ else :
174+ draw .rectangle ((x1 , y1 , x2 , y2 ), outline = box_color , width = box_thickness )
167175 # Draw label background (dark gray, semi-transparent) on overlay
168176 label_bg_color = (0 , 0 , 0 , 128 )
169177 overlay = Image .new ("RGBA" , image_box .size , (0 , 0 , 0 , 0 ))
170178 overlay_draw = ImageDraw .Draw (overlay )
171- overlay_draw .rectangle ([ x1 , y1_text , x2_text , y2_text ] , fill = label_bg_color , outline = None )
179+ overlay_draw .rectangle (( x1 , y1_text , x2_text , y2_text ) , fill = label_bg_color , outline = None )
172180 image_box = image_box .convert ("RGBA" )
173181 image_box = Image .alpha_composite (image_box , overlay )
174182 draw = ImageDraw .Draw (image_box )
0 commit comments