Skip to content

Commit 00896ea

Browse files
authored
fix 457 visualizer error handling (#464)
1 parent d1bf4a8 commit 00896ea

5 files changed

Lines changed: 59 additions & 12 deletions

File tree

src/model_api/visualizer/primitive/polygon.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ def _get_points(self, points: list[tuple[int, int]] | None, mask: np.ndarray | N
6969
elif mask is not None:
7070
points_ = self._get_points_from_mask(mask)
7171
else:
72-
msg = "Either points or mask should be provided."
73-
raise ValueError(msg)
72+
logger.warning("Neither points nor mask provided. Skipping polygon drawing.")
73+
return []
7474
return points_
7575

7676
def _get_points_from_mask(self, mask: np.ndarray) -> list[tuple[int, int]]:
@@ -88,8 +88,8 @@ def _get_points_from_mask(self, mask: np.ndarray) -> list[tuple[int, int]]:
8888
logger.warning("Multiple contours found in the mask. Using the largest one.")
8989
contours = sorted(contours, key=cv2.contourArea, reverse=True)
9090
if len(contours) == 0:
91-
msg = "No contours found in the mask."
92-
raise ValueError(msg)
91+
logger.warning("No contours found in the mask. Skipping polygon drawing.")
92+
return []
9393
points_ = contours[0].squeeze().tolist()
9494
return [tuple(point) for point in points_]
9595

@@ -102,6 +102,9 @@ def compute(self, image: Image) -> Image:
102102
Returns:
103103
Image with the polygon drawn on it.
104104
"""
105+
if len(self.points) == 0:
106+
return image
107+
105108
draw = ImageDraw.Draw(image, "RGBA")
106109
# Draw polygon with darker edge and a semi-transparent fill.
107110
ink = ImageColor.getrgb(self.color)

src/model_api/visualizer/scene/detection.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313
from model_api.visualizer.primitive import BoundingBox, Label, Overlay
1414

1515
from .scene import Scene
16+
from .utils import get_label_color_mapping
1617

1718

1819
class DetectionScene(Scene):
1920
"""Detection Scene."""
2021

2122
def __init__(self, image: Image, result: DetectionResult, layout: Union[Layout, None] = None) -> None:
23+
self.color_per_label = get_label_color_mapping(result.label_names)
2224
super().__init__(
2325
base=image,
2426
bounding_box=self._get_bounding_boxes(result),
@@ -43,7 +45,9 @@ def _get_bounding_boxes(self, result: DetectionResult) -> list[BoundingBox]:
4345
for score, label_name, bbox in zip(result.scores, result.label_names, result.bboxes):
4446
x1, y1, x2, y2 = bbox
4547
label = f"{label_name} ({score:.2f})"
46-
bounding_boxes.append(BoundingBox(x1=x1, y1=y1, x2=x2, y2=y2, label=label))
48+
bounding_boxes.append(
49+
BoundingBox(x1=x1, y1=y1, x2=x2, y2=y2, label=label, color=self.color_per_label[label_name]),
50+
)
4751
return bounding_boxes
4852

4953
@property

src/model_api/visualizer/scene/segmentation/instance_segmentation.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# Copyright (C) 2025 Intel Corporation
44
# SPDX-License-Identifier: Apache-2.0
55

6-
import random
76
from typing import Union
87

98
import cv2
@@ -13,15 +12,14 @@
1312
from model_api.visualizer.layout import Flatten, HStack, Layout
1413
from model_api.visualizer.primitive import BoundingBox, Label, Overlay, Polygon
1514
from model_api.visualizer.scene import Scene
15+
from model_api.visualizer.scene.utils import get_label_color_mapping
1616

1717

1818
class InstanceSegmentationScene(Scene):
1919
"""Instance Segmentation Scene."""
2020

2121
def __init__(self, image: Image, result: InstanceSegmentationResult, layout: Union[Layout, None] = None) -> None:
22-
# nosec as random is used for color generation
23-
g = random.Random(0) # noqa: S311 # nosec B311
24-
self.color_per_label = {label: f"#{g.randint(0, 0xFFFFFF):06x}" for label in set(result.label_names)} # nosec B311
22+
self.color_per_label = get_label_color_mapping(result.label_names)
2523
super().__init__(
2624
base=image,
2725
label=self._get_labels(result),
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Visualizer utilities."""
2+
3+
# Copyright (C) 2026 Intel Corporation
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
COLOR_PALETTE = [
7+
"#FF6B6B", # Red
8+
"#4ECDC4", # Teal
9+
"#45B7D1", # Blue
10+
"#FFA07A", # Light Salmon
11+
"#98D8C8", # Mint
12+
"#F7DC6F", # Yellow
13+
"#BB8FCE", # Purple
14+
"#85C1E2", # Sky Blue
15+
"#F8B739", # Orange
16+
"#52BE80", # Green
17+
"#EC7063", # Coral
18+
"#5DADE2", # Light Blue
19+
"#F39C12", # Dark Orange
20+
"#8E44AD", # Dark Purple
21+
"#16A085", # Dark Teal
22+
"#E74C3C", # Dark Red
23+
"#3498DB", # Dodger Blue
24+
"#2ECC71", # Emerald
25+
"#F1C40F", # Sun Yellow
26+
"#E67E22", # Carrot Orange
27+
]
28+
29+
30+
def get_label_color_mapping(labels: list[str]) -> dict[str, str]:
31+
"""Generate a consistent color mapping for a list of labels.
32+
33+
Args:
34+
labels: List of label names.
35+
36+
Returns:
37+
Dictionary mapping each label to a hex color string.
38+
"""
39+
unique_labels = sorted(set(labels))
40+
return {label: COLOR_PALETTE[i % len(COLOR_PALETTE)] for i, label in enumerate(unique_labels)}

tests/unit/visualizer/test_primitive.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import numpy as np
77
import PIL
8-
import pytest
98
from PIL import ImageDraw
109

1110
from model_api.visualizer import BoundingBox, Keypoint, Label, Overlay, Polygon
@@ -58,8 +57,11 @@ def test_polygon(mock_image: PIL.Image):
5857
polygon = Polygon(mask=mask, color="red", opacity=1, outline_width=1)
5958
assert polygon.compute(mock_image) == expected_image
6059

61-
with pytest.raises(ValueError, match="No contours found in the mask."):
62-
Polygon(mask=np.zeros((100, 100), dtype=np.uint8)).compute(mock_image)
60+
# Test with empty mask - should not raise, just return image unchanged
61+
empty_mask = np.zeros((100, 100), dtype=np.uint8)
62+
polygon_empty = Polygon(mask=empty_mask)
63+
result = polygon_empty.compute(mock_image)
64+
assert result == mock_image
6365

6466

6567
def test_label(mock_image: PIL.Image):

0 commit comments

Comments
 (0)