From 2961045177ac20e88a927c3db3c45e5377331d83 Mon Sep 17 00:00:00 2001 From: Grzegorz Klimaszewski <166530809+grzegorz-roboflow@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:49:30 +0200 Subject: [PATCH 1/6] Add opacity to PolygonZoneAnnotator --- supervision/detection/tools/polygon_zone.py | 26 ++++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py index 5f82cfd87..44d12ff70 100644 --- a/supervision/detection/tools/polygon_zone.py +++ b/supervision/detection/tools/polygon_zone.py @@ -121,6 +121,7 @@ def __init__( text_thickness: int = 1, text_padding: int = 10, display_in_zone_count: bool = True, + opacity: float = 1, ): self.zone = zone self.color = color @@ -132,6 +133,7 @@ def __init__( self.font = cv2.FONT_HERSHEY_SIMPLEX self.center = get_polygon_center(polygon=zone.polygon) self.display_in_zone_count = display_in_zone_count + self.opacity = opacity def annotate(self, scene: np.ndarray, label: Optional[str] = None) -> np.ndarray: """ @@ -145,12 +147,24 @@ def annotate(self, scene: np.ndarray, label: Optional[str] = None) -> np.ndarray Returns: np.ndarray: The image with the polygon zone and count of detected objects """ - annotated_frame = draw_polygon( - scene=scene, - polygon=self.zone.polygon, - color=self.color, - thickness=self.thickness, - ) + if self.opacity == 1: + annotated_frame = draw_polygon( + scene=scene, + polygon=self.zone.polygon, + color=self.color, + thickness=self.thickness, + ) + else: + scene_with_annotations = scene.copy() + annotated_frame = draw_polygon( + scene=scene_with_annotations, + polygon=self.zone.polygon, + color=self.color, + thickness=self.thickness, + ) + cv2.addWeighted( + annotated_frame, self.opacity, scene, 1 - self.opacity, gamma=0, dst=scene + ) if self.display_in_zone_count: annotated_frame = draw_text( From 1c30493ee04738ec31cd798431e0bab5e11ba878 Mon Sep 17 00:00:00 2001 From: Grzegorz Klimaszewski <166530809+grzegorz-roboflow@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:54:04 +0200 Subject: [PATCH 2/6] Docstring --- supervision/detection/tools/polygon_zone.py | 1 + 1 file changed, 1 insertion(+) diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py index 44d12ff70..4e72b19b6 100644 --- a/supervision/detection/tools/polygon_zone.py +++ b/supervision/detection/tools/polygon_zone.py @@ -109,6 +109,7 @@ class PolygonZoneAnnotator: default is cv2.FONT_HERSHEY_SIMPLEX center (Tuple[int, int]): The center of the polygon for text placement display_in_zone_count (bool): Show the label of the zone or not. Default is True + opacity: The opacity of zone when drawn on the scene. Default is 1 (no opacity) """ def __init__( From 79858ad9d301328a8e4194fc11de6fbb0f993523 Mon Sep 17 00:00:00 2001 From: Grzegorz Klimaszewski <166530809+grzegorz-roboflow@users.noreply.github.com> Date: Thu, 19 Sep 2024 11:58:54 +0200 Subject: [PATCH 3/6] Move opacity handling to utils/draw; extend PolygonZoneAnnotator to draw filled polygon when opacity is greater than 0 --- supervision/__init__.py | 1 + supervision/detection/tools/polygon_zone.py | 20 +++--- supervision/draw/utils.py | 61 +++++++++++++++--- test/detection/test_polygon_zone_annotator.py | 64 +++++++++++++++++++ 4 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 test/detection/test_polygon_zone_annotator.py diff --git a/supervision/__init__.py b/supervision/__init__.py index b3e8160aa..ebd820404 100644 --- a/supervision/__init__.py +++ b/supervision/__init__.py @@ -76,6 +76,7 @@ from supervision.draw.utils import ( calculate_optimal_line_thickness, calculate_optimal_text_scale, + draw_filled_polygon, draw_filled_rectangle, draw_image, draw_line, diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py index 4e72b19b6..683ae3b51 100644 --- a/supervision/detection/tools/polygon_zone.py +++ b/supervision/detection/tools/polygon_zone.py @@ -9,7 +9,7 @@ from supervision import Detections from supervision.detection.utils import clip_boxes, polygon_to_mask from supervision.draw.color import Color -from supervision.draw.utils import draw_polygon, draw_text +from supervision.draw.utils import draw_polygon, draw_filled_polygon, draw_text from supervision.geometry.core import Position from supervision.geometry.utils import get_polygon_center from supervision.utils.internal import SupervisionWarnings @@ -109,7 +109,7 @@ class PolygonZoneAnnotator: default is cv2.FONT_HERSHEY_SIMPLEX center (Tuple[int, int]): The center of the polygon for text placement display_in_zone_count (bool): Show the label of the zone or not. Default is True - opacity: The opacity of zone when drawn on the scene. Default is 1 (no opacity) + opacity: The opacity of zone when drawn on the scene. Default is 0 (only zone border will be drawn) """ def __init__( @@ -122,7 +122,7 @@ def __init__( text_thickness: int = 1, text_padding: int = 10, display_in_zone_count: bool = True, - opacity: float = 1, + opacity: float = 0, ): self.zone = zone self.color = color @@ -148,7 +148,7 @@ def annotate(self, scene: np.ndarray, label: Optional[str] = None) -> np.ndarray Returns: np.ndarray: The image with the polygon zone and count of detected objects """ - if self.opacity == 1: + if self.opacity == 0: annotated_frame = draw_polygon( scene=scene, polygon=self.zone.polygon, @@ -156,16 +156,18 @@ def annotate(self, scene: np.ndarray, label: Optional[str] = None) -> np.ndarray thickness=self.thickness, ) else: - scene_with_annotations = scene.copy() + annotated_frame = draw_filled_polygon( + scene=scene.copy(), + polygon=self.zone.polygon, + color=self.color, + opacity=self.opacity, + ) annotated_frame = draw_polygon( - scene=scene_with_annotations, + scene=annotated_frame, polygon=self.zone.polygon, color=self.color, thickness=self.thickness, ) - cv2.addWeighted( - annotated_frame, self.opacity, scene, 1 - self.opacity, gamma=0, dst=scene - ) if self.display_in_zone_count: annotated_frame = draw_text( diff --git a/supervision/draw/utils.py b/supervision/draw/utils.py index 366518116..4c5e30d6a 100644 --- a/supervision/draw/utils.py +++ b/supervision/draw/utils.py @@ -59,7 +59,7 @@ def draw_rectangle( return scene -def draw_filled_rectangle(scene: np.ndarray, rect: Rect, color: Color) -> np.ndarray: +def draw_filled_rectangle(scene: np.ndarray, rect: Rect, color: Color, opacity: float = 1) -> np.ndarray: """ Draws a filled rectangle on an image. @@ -67,17 +67,32 @@ def draw_filled_rectangle(scene: np.ndarray, rect: Rect, color: Color) -> np.nda scene (np.ndarray): The scene on which the rectangle will be drawn rect (Rect): The rectangle to be drawn color (Color): The color of the rectangle + opacity (float): The opacity of rectangle when drawn on the scene. Default is 1 (no opacity) Returns: np.ndarray: The scene with the rectangle drawn on it """ - cv2.rectangle( - scene, - rect.top_left.as_xy_int_tuple(), - rect.bottom_right.as_xy_int_tuple(), - color.as_bgr(), - -1, - ) + if opacity == 1: + cv2.rectangle( + scene, + rect.top_left.as_xy_int_tuple(), + rect.bottom_right.as_xy_int_tuple(), + color.as_bgr(), + -1, + ) + else: + scene_with_annotations = scene.copy() + cv2.rectangle( + scene_with_annotations, + rect.top_left.as_xy_int_tuple(), + rect.bottom_right.as_xy_int_tuple(), + color.as_bgr(), + -1, + ) + cv2.addWeighted( + scene_with_annotations, opacity, scene, 1 - opacity, gamma=0, dst=scene + ) + return scene @@ -153,6 +168,36 @@ def draw_polygon( return scene +def draw_filled_polygon( + scene: np.ndarray, polygon: np.ndarray, color: Color, opacity: float = 1 +) -> np.ndarray: + """Draw a polygon on a scene. + + Parameters: + scene (np.ndarray): The scene to draw the polygon on. + polygon (np.ndarray): The polygon to be drawn, given as a list of vertices. + color (Color): The color of the polygon. + opacity (float): The opacity of rectangle when drawn on the scene. Default is 1 (no opacity) + + Returns: + np.ndarray: The scene with the polygon drawn on it. + """ + if opacity == 1: + cv2.fillPoly( + scene, [polygon], color=color.as_bgr() + ) + else: + scene_with_annotations = scene.copy() + cv2.fillPoly( + scene_with_annotations, [polygon], color=color.as_bgr() + ) + cv2.addWeighted( + scene_with_annotations, opacity, scene, 1 - opacity, gamma=0, dst=scene + ) + + return scene + + def draw_text( scene: np.ndarray, text: str, diff --git a/test/detection/test_polygon_zone_annotator.py b/test/detection/test_polygon_zone_annotator.py new file mode 100644 index 000000000..0408499e8 --- /dev/null +++ b/test/detection/test_polygon_zone_annotator.py @@ -0,0 +1,64 @@ +from contextlib import ExitStack as DoesNotRaise + +import cv2 +import numpy as np +import pytest + +import supervision as sv + + +COLOR = sv.Color(r=255, g=0, b=0) +THICKNESS = 2 +POLYGON = np.array([[100, 100], [200, 100], [200, 200], [100, 200]]) +SCENE = np.random.randint(0, 255, (1000, 1000, 3), dtype=np.uint8) +ANNOTATED_SCENE_NO_OPACITY = sv.draw_polygon( + scene=SCENE.copy(), + polygon=POLYGON, + color=COLOR, + thickness=THICKNESS, +) +ANNOTATED_SCENE_0_5_OPACITY = sv.draw_filled_polygon( + scene=ANNOTATED_SCENE_NO_OPACITY.copy(), + polygon=POLYGON, + color=COLOR, + opacity=0.5, +) + + +@pytest.mark.parametrize( + "scene, polygon_zone_annotator, expected_results", + [ + ( + SCENE, + sv.PolygonZoneAnnotator( + zone=sv.PolygonZone( + POLYGON, + ), + color=COLOR, + thickness=THICKNESS, + display_in_zone_count=False, + ), + ANNOTATED_SCENE_NO_OPACITY, + ), # Test no opacity (default) + ( + SCENE, + sv.PolygonZoneAnnotator( + zone=sv.PolygonZone( + POLYGON, + ), + color=COLOR, + thickness=THICKNESS, + display_in_zone_count=False, + opacity=0.5, + ), + ANNOTATED_SCENE_0_5_OPACITY, + ), # Test 10% opacity + ], +) +def test_polygon_zone_annotator( + scene: np.ndarray, + polygon_zone_annotator: sv.PolygonZoneAnnotator, + expected_results: np.ndarray, +) -> None: + annotated_scene = polygon_zone_annotator.annotate(scene=scene) + assert np.all(annotated_scene == expected_results) From f307f981f6ffcbf5f962f59c04f0dbfa09e81343 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:05:01 +0000 Subject: [PATCH 4/6] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto=20?= =?UTF-8?q?format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- supervision/detection/tools/polygon_zone.py | 2 +- supervision/draw/utils.py | 12 +++++------- test/detection/test_polygon_zone_annotator.py | 4 ---- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py index 683ae3b51..2ca4d0806 100644 --- a/supervision/detection/tools/polygon_zone.py +++ b/supervision/detection/tools/polygon_zone.py @@ -9,7 +9,7 @@ from supervision import Detections from supervision.detection.utils import clip_boxes, polygon_to_mask from supervision.draw.color import Color -from supervision.draw.utils import draw_polygon, draw_filled_polygon, draw_text +from supervision.draw.utils import draw_filled_polygon, draw_polygon, draw_text from supervision.geometry.core import Position from supervision.geometry.utils import get_polygon_center from supervision.utils.internal import SupervisionWarnings diff --git a/supervision/draw/utils.py b/supervision/draw/utils.py index 4c5e30d6a..678f3f664 100644 --- a/supervision/draw/utils.py +++ b/supervision/draw/utils.py @@ -59,7 +59,9 @@ def draw_rectangle( return scene -def draw_filled_rectangle(scene: np.ndarray, rect: Rect, color: Color, opacity: float = 1) -> np.ndarray: +def draw_filled_rectangle( + scene: np.ndarray, rect: Rect, color: Color, opacity: float = 1 +) -> np.ndarray: """ Draws a filled rectangle on an image. @@ -183,14 +185,10 @@ def draw_filled_polygon( np.ndarray: The scene with the polygon drawn on it. """ if opacity == 1: - cv2.fillPoly( - scene, [polygon], color=color.as_bgr() - ) + cv2.fillPoly(scene, [polygon], color=color.as_bgr()) else: scene_with_annotations = scene.copy() - cv2.fillPoly( - scene_with_annotations, [polygon], color=color.as_bgr() - ) + cv2.fillPoly(scene_with_annotations, [polygon], color=color.as_bgr()) cv2.addWeighted( scene_with_annotations, opacity, scene, 1 - opacity, gamma=0, dst=scene ) diff --git a/test/detection/test_polygon_zone_annotator.py b/test/detection/test_polygon_zone_annotator.py index 0408499e8..d28a4eec6 100644 --- a/test/detection/test_polygon_zone_annotator.py +++ b/test/detection/test_polygon_zone_annotator.py @@ -1,12 +1,8 @@ -from contextlib import ExitStack as DoesNotRaise - -import cv2 import numpy as np import pytest import supervision as sv - COLOR = sv.Color(r=255, g=0, b=0) THICKNESS = 2 POLYGON = np.array([[100, 100], [200, 100], [200, 200], [100, 200]]) From 7ad0bc361b3cd9927fc66b908fd36987072f4f45 Mon Sep 17 00:00:00 2001 From: Grzegorz Klimaszewski <166530809+grzegorz-roboflow@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:08:12 +0200 Subject: [PATCH 5/6] Adjust docstrings --- supervision/detection/tools/polygon_zone.py | 2 +- supervision/draw/utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py index 2ca4d0806..af0d1c0c4 100644 --- a/supervision/detection/tools/polygon_zone.py +++ b/supervision/detection/tools/polygon_zone.py @@ -109,7 +109,7 @@ class PolygonZoneAnnotator: default is cv2.FONT_HERSHEY_SIMPLEX center (Tuple[int, int]): The center of the polygon for text placement display_in_zone_count (bool): Show the label of the zone or not. Default is True - opacity: The opacity of zone when drawn on the scene. Default is 0 (only zone border will be drawn) + opacity: The opacity of zone filling when drawn on the scene. Default is 0 """ def __init__( diff --git a/supervision/draw/utils.py b/supervision/draw/utils.py index 678f3f664..b5df0921b 100644 --- a/supervision/draw/utils.py +++ b/supervision/draw/utils.py @@ -69,7 +69,7 @@ def draw_filled_rectangle( scene (np.ndarray): The scene on which the rectangle will be drawn rect (Rect): The rectangle to be drawn color (Color): The color of the rectangle - opacity (float): The opacity of rectangle when drawn on the scene. Default is 1 (no opacity) + opacity (float): The opacity of rectangle when drawn on the scene. Returns: np.ndarray: The scene with the rectangle drawn on it @@ -179,7 +179,7 @@ def draw_filled_polygon( scene (np.ndarray): The scene to draw the polygon on. polygon (np.ndarray): The polygon to be drawn, given as a list of vertices. color (Color): The color of the polygon. - opacity (float): The opacity of rectangle when drawn on the scene. Default is 1 (no opacity) + opacity (float): The opacity of rectangle when drawn on the scene. Returns: np.ndarray: The scene with the polygon drawn on it. From 81ee4f1c2cd74cfcc8c856180db972374729c8c1 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Thu, 19 Sep 2024 22:37:36 +0300 Subject: [PATCH 6/6] Add draw_filled_polygon to docs, fix a few docstring types --- docs/utils/draw.md | 6 ++++++ supervision/draw/utils.py | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/utils/draw.md b/docs/utils/draw.md index f4b86a53f..d881051f3 100644 --- a/docs/utils/draw.md +++ b/docs/utils/draw.md @@ -28,6 +28,12 @@ comments: true :::supervision.draw.utils.draw_polygon +
+

draw_filled_polygon

+
+ +:::supervision.draw.utils.draw_filled_polygon +

draw_text

diff --git a/supervision/draw/utils.py b/supervision/draw/utils.py index b5df0921b..19ce4a258 100644 --- a/supervision/draw/utils.py +++ b/supervision/draw/utils.py @@ -173,13 +173,13 @@ def draw_polygon( def draw_filled_polygon( scene: np.ndarray, polygon: np.ndarray, color: Color, opacity: float = 1 ) -> np.ndarray: - """Draw a polygon on a scene. + """Draw a filled polygon on a scene. Parameters: scene (np.ndarray): The scene to draw the polygon on. polygon (np.ndarray): The polygon to be drawn, given as a list of vertices. color (Color): The color of the polygon. - opacity (float): The opacity of rectangle when drawn on the scene. + opacity (float): The opacity of polygon when drawn on the scene. Returns: np.ndarray: The scene with the polygon drawn on it.