From e1cb055f96c60307a757b6cb3cde4d44b3184fe0 Mon Sep 17 00:00:00 2001 From: Adithi Sreenath Date: Tue, 14 Oct 2025 12:47:28 +0530 Subject: [PATCH 1/3] fix: prevent single object from appearing in multiple polygon zones when checking if a detection is inside a polygon zone, the previous implementation would clip the bounding box to fit within each ROI's dimensions before calculating anchor points. This caused the same detection to produce different anchor points for different ROIs, allowing it to be counted as present in multiple zones. --- supervision/detection/tools/polygon_zone.py | 63 +++++++++++++-------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py index 952da5141..b3d43c4ee 100644 --- a/supervision/detection/tools/polygon_zone.py +++ b/supervision/detection/tools/polygon_zone.py @@ -79,39 +79,54 @@ def __init__( def trigger(self, detections: Detections) -> npt.NDArray[np.bool_]: """ - Determines if the detections are within the polygon zone. + Determines if the detections are within the polygon zone. - Parameters: - detections (Detections): The detections - to be checked against the polygon zone + Anchor points are calculated from original (unclipped) detection boxes. + to ensure a single object can only appear in one zone. This prevents + detections spanning multiple non-overlapping ROIs from being counted + in multiple zones simulataneously. - Returns: - np.ndarray: A boolean numpy array indicating - if each detection is within the polygon zone - """ + Parameters: + detections (Detections): The detections + to be checked against the polygon zone - clipped_xyxy = clip_boxes( - xyxy=detections.xyxy, resolution_wh=self.frame_resolution_wh - ) - clipped_detections = replace(detections, xyxy=clipped_xyxy) - all_clipped_anchors = np.array( - [ - np.ceil(clipped_detections.get_anchors_coordinates(anchor)).astype(int) - for anchor in self.triggering_anchors - ] - ) + Returns: + np.ndarray: A boolean numpy array indicating + if each detection is within the polygon zone + """ + if len(detections) == 0: + return np.array([], dtype=bool) + - is_in_zone: npt.NDArray[np.bool_] = ( - self.mask[all_clipped_anchors[:, :, 1], all_clipped_anchors[:, :, 0]] - .transpose() - .astype(bool) - ) + all_anchors = np.array([ + np.ceil(detections.get_anchors_coordinates(anchors)).astype(int) + for anchors in self.triggering_anchors + ]) - is_in_zone: npt.NDArray[np.bool_] = np.all(is_in_zone, axis=1) + is_in_zone: npt.NDArray[np.bool_] = np.zeros(len(detections), dtype=bool) + + for detection_idx in range(len(detections)): + anchors = all_anchors[:, detection_idx, :] + all_in_zone = True + + for anchor in anchors: + x, y = anchor + + if x < 0 or y < 0 or x >= self.mask.shape[1] or y >= self.mask.shape[0]: + all_in_zone = False + break + + if not self.mask[y, x]: + all_in_zone = False + break + + is_in_zone[detection_idx] = all_in_zone + self.current_count = int(np.sum(is_in_zone)) return is_in_zone.astype(bool) + class PolygonZoneAnnotator: """ A class for annotating a polygon-shaped zone within a From 6ce5b834c9672ea35fde512c238c4fb17841901a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:30:03 +0000 Subject: [PATCH 2/3] =?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 | 40 ++++++++++----------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py index b3d43c4ee..728c4e610 100644 --- a/supervision/detection/tools/polygon_zone.py +++ b/supervision/detection/tools/polygon_zone.py @@ -1,14 +1,12 @@ from __future__ import annotations from collections.abc import Iterable -from dataclasses import replace import cv2 import numpy as np import numpy.typing as npt from supervision import Detections -from supervision.detection.utils.boxes import clip_boxes from supervision.detection.utils.converters import polygon_to_mask from supervision.draw.color import Color from supervision.draw.utils import draw_filled_polygon, draw_polygon, draw_text @@ -79,29 +77,30 @@ def __init__( def trigger(self, detections: Detections) -> npt.NDArray[np.bool_]: """ - Determines if the detections are within the polygon zone. + Determines if the detections are within the polygon zone. - Anchor points are calculated from original (unclipped) detection boxes. - to ensure a single object can only appear in one zone. This prevents - detections spanning multiple non-overlapping ROIs from being counted - in multiple zones simulataneously. + Anchor points are calculated from original (unclipped) detection boxes. + to ensure a single object can only appear in one zone. This prevents + detections spanning multiple non-overlapping ROIs from being counted + in multiple zones simulataneously. - Parameters: - detections (Detections): The detections - to be checked against the polygon zone + Parameters: + detections (Detections): The detections + to be checked against the polygon zone - Returns: - np.ndarray: A boolean numpy array indicating - if each detection is within the polygon zone - """ + Returns: + np.ndarray: A boolean numpy array indicating + if each detection is within the polygon zone + """ if len(detections) == 0: return np.array([], dtype=bool) - - all_anchors = np.array([ - np.ceil(detections.get_anchors_coordinates(anchors)).astype(int) - for anchors in self.triggering_anchors - ]) + all_anchors = np.array( + [ + np.ceil(detections.get_anchors_coordinates(anchors)).astype(int) + for anchors in self.triggering_anchors + ] + ) is_in_zone: npt.NDArray[np.bool_] = np.zeros(len(detections), dtype=bool) @@ -121,12 +120,11 @@ def trigger(self, detections: Detections) -> npt.NDArray[np.bool_]: break is_in_zone[detection_idx] = all_in_zone - + self.current_count = int(np.sum(is_in_zone)) return is_in_zone.astype(bool) - class PolygonZoneAnnotator: """ A class for annotating a polygon-shaped zone within a From 2db77532052e59b0eeb6a91d262d2e3bcdb9e696 Mon Sep 17 00:00:00 2001 From: Adithi Sreenath Date: Tue, 14 Oct 2025 17:52:37 +0530 Subject: [PATCH 3/3] fix: typo in polygon zone docstring (simultaneously) --- supervision/detection/tools/polygon_zone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py index 728c4e610..62536cc58 100644 --- a/supervision/detection/tools/polygon_zone.py +++ b/supervision/detection/tools/polygon_zone.py @@ -82,7 +82,7 @@ def trigger(self, detections: Detections) -> npt.NDArray[np.bool_]: Anchor points are calculated from original (unclipped) detection boxes. to ensure a single object can only appear in one zone. This prevents detections spanning multiple non-overlapping ROIs from being counted - in multiple zones simulataneously. + in multiple zones simultaneously. Parameters: detections (Detections): The detections