From b9807f6d108a26905d449bc659a1866f8ab0137a Mon Sep 17 00:00:00 2001 From: Mohammad Mahdi Hosseini <73922765+MoHoss007@users.noreply.github.com> Date: Thu, 28 Mar 2024 09:53:33 -0400 Subject: [PATCH 1/4] added trigger_overlap to polygan zone --- supervision/detection/tools/polygon_zone.py | 32 +++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py index 00b746aaa..b884ee673 100644 --- a/supervision/detection/tools/polygon_zone.py +++ b/supervision/detection/tools/polygon_zone.py @@ -89,6 +89,38 @@ def trigger(self, detections: Detections) -> npt.NDArray[np.bool_]: self.current_count = int(np.sum(is_in_zone)) return is_in_zone.astype(bool) + def trigger_overlap(self, detections: Detections, overlap_threshold: float) -> npt.NDArray[np.bool_]: + """ + Determines if the detections are within the polygon zone. + + Parameters: + detections (Detections): The detections + to be checked against the polygon zone + + overlap_threshold (float): threshold of overlap + + Returns: + np.ndarray: A boolean numpy array indicating + if each detection overlaps the polygon zone + """ + + clipped_xyxy = clip_boxes( + xyxy=detections.xyxy, resolution_wh=self.frame_resolution_wh + ) + clipped_detections = replace(detections, xyxy=clipped_xyxy) + overlap_results: npt.NDArray[np.bool_] = np.zeros(len(clipped_detections), dtype=bool) + + masks = [] + for i, (x1, y1, x2, y2) in enumerate(detections.xyxy.astype(np.int32)): + bbox_mask = np.zeros_like(self.mask, dtype=bool) + + bbox_mask[y1:y2, x1:x2] = True + masks.append(bbox_mask) + overlap_ratio = np.count_nonzero(np.logical_and(bbox_mask, self.mask)) / detections.area[i] + overlap_results[i] = overlap_ratio > overlap_threshold + + return overlap_results + class PolygonZoneAnnotator: """ From cf1210806a55bf45abfe0637d2b39f721434edb9 Mon Sep 17 00:00:00 2001 From: MoeHosseini <73922765+MoHoss007@users.noreply.github.com> Date: Thu, 28 Mar 2024 10:22:17 -0400 Subject: [PATCH 2/4] modified trigger_overlap function in polygon_zone --- 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 b884ee673..8497c4687 100644 --- a/supervision/detection/tools/polygon_zone.py +++ b/supervision/detection/tools/polygon_zone.py @@ -117,7 +117,7 @@ def trigger_overlap(self, detections: Detections, overlap_threshold: float) -> n bbox_mask[y1:y2, x1:x2] = True masks.append(bbox_mask) overlap_ratio = np.count_nonzero(np.logical_and(bbox_mask, self.mask)) / detections.area[i] - overlap_results[i] = overlap_ratio > overlap_threshold + overlap_results[i] = overlap_ratio >= overlap_threshold return overlap_results From 62a39745171eaf222f84fd1dd6874f34e0ddfd3f Mon Sep 17 00:00:00 2001 From: MoeHosseini <73922765+MoHoss007@users.noreply.github.com> Date: Thu, 28 Mar 2024 10:22:36 -0400 Subject: [PATCH 3/4] added test cases for trigger_overlap function in polygon_zone --- test/detection/test_polygonzone.py | 172 +++++++++++++++++++++-------- 1 file changed, 127 insertions(+), 45 deletions(-) diff --git a/test/detection/test_polygonzone.py b/test/detection/test_polygonzone.py index 1a86a45b4..e3c55ebcb 100644 --- a/test/detection/test_polygonzone.py +++ b/test/detection/test_polygonzone.py @@ -33,62 +33,144 @@ "detections, polygon_zone, expected_results, exception", [ ( - DETECTIONS, - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - triggering_anchors=( - sv.Position.TOP_LEFT, - sv.Position.TOP_RIGHT, - sv.Position.BOTTOM_LEFT, - sv.Position.BOTTOM_RIGHT, - ), - ), - np.array( - [False, False, False, True, True, True, False, False, False], dtype=bool - ), - DoesNotRaise(), + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + triggering_anchors=( + sv.Position.TOP_LEFT, + sv.Position.TOP_RIGHT, + sv.Position.BOTTOM_LEFT, + sv.Position.BOTTOM_RIGHT, + ), + ), + np.array( + [False, False, False, True, True, True, False, False, False], dtype=bool + ), + DoesNotRaise(), ), # Test all four corners ( - DETECTIONS, - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - ), - np.array( - [False, False, True, True, True, True, False, False, False], dtype=bool - ), - DoesNotRaise(), + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + ), + np.array( + [False, False, True, True, True, True, False, False, False], dtype=bool + ), + DoesNotRaise(), ), # Test default behaviour when no anchors are provided ( - DETECTIONS, - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - triggering_anchors=[sv.Position.BOTTOM_CENTER], - ), - np.array( - [False, False, True, True, True, True, False, False, False], dtype=bool - ), - DoesNotRaise(), + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + triggering_anchors=[sv.Position.BOTTOM_CENTER], + ), + np.array( + [False, False, True, True, True, True, False, False, False], dtype=bool + ), + DoesNotRaise(), ), # Test default behaviour with deprecated api. ( - sv.Detections.empty(), - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - ), - np.array([], dtype=bool), - DoesNotRaise(), + sv.Detections.empty(), + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + ), + np.array([], dtype=bool), + DoesNotRaise(), ), # Test empty detections ], ) def test_polygon_zone_trigger( - detections: sv.Detections, - polygon_zone: sv.PolygonZone, - expected_results: np.ndarray, - exception: Exception, + detections: sv.Detections, + polygon_zone: sv.PolygonZone, + expected_results: np.ndarray, + exception: Exception, ) -> None: with exception: in_zone = polygon_zone.trigger(detections) assert np.all(in_zone == expected_results) + + +@pytest.mark.parametrize( + "detections, polygon_zone, overlap_threshold, expected_results, exception", + [ + # Test cases for trigger_overlap function + ( + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + triggering_anchors=( + sv.Position.TOP_LEFT, + sv.Position.TOP_RIGHT, + sv.Position.BOTTOM_LEFT, + sv.Position.BOTTOM_RIGHT, + ), + ), + 0.25, # Overlap threshold + np.array( + [False, False, True, True, True, True, True, False, False], dtype=bool + ), + DoesNotRaise(), + ), # Case with specific overlap threshold + ( + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + ), + 0.5, # Overlap threshold + np.array( + [False, False, False, True, True, True, False, False, False], dtype=bool + ), + DoesNotRaise(), + ), # Another overlap threshold + ( + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + ), + 0.1, # Lower overlap threshold to catch more detections + np.array( + [False, False, True, True, True, True, True, False, False], dtype=bool + ), + DoesNotRaise(), + ), # Lower threshold test + ( + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + ), + 0, # Lower overlap threshold to catch more detections + np.array( + [True, True, True, True, True, True, True, True, True], dtype=bool + ), + DoesNotRaise(), + ), # Lower threshold test + ( + sv.Detections.empty(), + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + ), + 0, + np.array([], dtype=bool), + DoesNotRaise(), + ), # Test empty detections + ], +) +def test_polygon_zone_overlap( + detections: sv.Detections, + polygon_zone: sv.PolygonZone, + overlap_threshold: float, + expected_results: np.ndarray, + exception: Exception, +) -> None: + with exception: + overlaps = polygon_zone.trigger_overlap(detections, overlap_threshold) + assert np.all(overlaps == expected_results) From 756d1105e865ac0d94cb94a7d832a68f6c4a2fa1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 28 Mar 2024 14:29:40 +0000 Subject: [PATCH 4/4] =?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 | 17 +- test/detection/test_polygonzone.py | 208 ++++++++++---------- 2 files changed, 116 insertions(+), 109 deletions(-) diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py index 8497c4687..713b1ec5e 100644 --- a/supervision/detection/tools/polygon_zone.py +++ b/supervision/detection/tools/polygon_zone.py @@ -89,14 +89,16 @@ def trigger(self, detections: Detections) -> npt.NDArray[np.bool_]: self.current_count = int(np.sum(is_in_zone)) return is_in_zone.astype(bool) - def trigger_overlap(self, detections: Detections, overlap_threshold: float) -> npt.NDArray[np.bool_]: + def trigger_overlap( + self, detections: Detections, overlap_threshold: float + ) -> npt.NDArray[np.bool_]: """ Determines if the detections are within the polygon zone. Parameters: detections (Detections): The detections to be checked against the polygon zone - + overlap_threshold (float): threshold of overlap Returns: @@ -108,7 +110,9 @@ def trigger_overlap(self, detections: Detections, overlap_threshold: float) -> n xyxy=detections.xyxy, resolution_wh=self.frame_resolution_wh ) clipped_detections = replace(detections, xyxy=clipped_xyxy) - overlap_results: npt.NDArray[np.bool_] = np.zeros(len(clipped_detections), dtype=bool) + overlap_results: npt.NDArray[np.bool_] = np.zeros( + len(clipped_detections), dtype=bool + ) masks = [] for i, (x1, y1, x2, y2) in enumerate(detections.xyxy.astype(np.int32)): @@ -116,9 +120,12 @@ def trigger_overlap(self, detections: Detections, overlap_threshold: float) -> n bbox_mask[y1:y2, x1:x2] = True masks.append(bbox_mask) - overlap_ratio = np.count_nonzero(np.logical_and(bbox_mask, self.mask)) / detections.area[i] + overlap_ratio = ( + np.count_nonzero(np.logical_and(bbox_mask, self.mask)) + / detections.area[i] + ) overlap_results[i] = overlap_ratio >= overlap_threshold - + return overlap_results diff --git a/test/detection/test_polygonzone.py b/test/detection/test_polygonzone.py index e3c55ebcb..9d5b4896b 100644 --- a/test/detection/test_polygonzone.py +++ b/test/detection/test_polygonzone.py @@ -33,61 +33,61 @@ "detections, polygon_zone, expected_results, exception", [ ( - DETECTIONS, - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - triggering_anchors=( - sv.Position.TOP_LEFT, - sv.Position.TOP_RIGHT, - sv.Position.BOTTOM_LEFT, - sv.Position.BOTTOM_RIGHT, - ), - ), - np.array( - [False, False, False, True, True, True, False, False, False], dtype=bool - ), - DoesNotRaise(), + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + triggering_anchors=( + sv.Position.TOP_LEFT, + sv.Position.TOP_RIGHT, + sv.Position.BOTTOM_LEFT, + sv.Position.BOTTOM_RIGHT, + ), + ), + np.array( + [False, False, False, True, True, True, False, False, False], dtype=bool + ), + DoesNotRaise(), ), # Test all four corners ( - DETECTIONS, - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - ), - np.array( - [False, False, True, True, True, True, False, False, False], dtype=bool - ), - DoesNotRaise(), + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + ), + np.array( + [False, False, True, True, True, True, False, False, False], dtype=bool + ), + DoesNotRaise(), ), # Test default behaviour when no anchors are provided ( - DETECTIONS, - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - triggering_anchors=[sv.Position.BOTTOM_CENTER], - ), - np.array( - [False, False, True, True, True, True, False, False, False], dtype=bool - ), - DoesNotRaise(), + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + triggering_anchors=[sv.Position.BOTTOM_CENTER], + ), + np.array( + [False, False, True, True, True, True, False, False, False], dtype=bool + ), + DoesNotRaise(), ), # Test default behaviour with deprecated api. ( - sv.Detections.empty(), - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - ), - np.array([], dtype=bool), - DoesNotRaise(), + sv.Detections.empty(), + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + ), + np.array([], dtype=bool), + DoesNotRaise(), ), # Test empty detections ], ) def test_polygon_zone_trigger( - detections: sv.Detections, - polygon_zone: sv.PolygonZone, - expected_results: np.ndarray, - exception: Exception, + detections: sv.Detections, + polygon_zone: sv.PolygonZone, + expected_results: np.ndarray, + exception: Exception, ) -> None: with exception: in_zone = polygon_zone.trigger(detections) @@ -99,77 +99,77 @@ def test_polygon_zone_trigger( [ # Test cases for trigger_overlap function ( - DETECTIONS, - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - triggering_anchors=( - sv.Position.TOP_LEFT, - sv.Position.TOP_RIGHT, - sv.Position.BOTTOM_LEFT, - sv.Position.BOTTOM_RIGHT, - ), - ), - 0.25, # Overlap threshold - np.array( - [False, False, True, True, True, True, True, False, False], dtype=bool - ), - DoesNotRaise(), + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + triggering_anchors=( + sv.Position.TOP_LEFT, + sv.Position.TOP_RIGHT, + sv.Position.BOTTOM_LEFT, + sv.Position.BOTTOM_RIGHT, + ), + ), + 0.25, # Overlap threshold + np.array( + [False, False, True, True, True, True, True, False, False], dtype=bool + ), + DoesNotRaise(), ), # Case with specific overlap threshold ( - DETECTIONS, - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - ), - 0.5, # Overlap threshold - np.array( - [False, False, False, True, True, True, False, False, False], dtype=bool - ), - DoesNotRaise(), + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + ), + 0.5, # Overlap threshold + np.array( + [False, False, False, True, True, True, False, False, False], dtype=bool + ), + DoesNotRaise(), ), # Another overlap threshold ( - DETECTIONS, - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - ), - 0.1, # Lower overlap threshold to catch more detections - np.array( - [False, False, True, True, True, True, True, False, False], dtype=bool - ), - DoesNotRaise(), + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + ), + 0.1, # Lower overlap threshold to catch more detections + np.array( + [False, False, True, True, True, True, True, False, False], dtype=bool + ), + DoesNotRaise(), ), # Lower threshold test ( - DETECTIONS, - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - ), - 0, # Lower overlap threshold to catch more detections - np.array( - [True, True, True, True, True, True, True, True, True], dtype=bool - ), - DoesNotRaise(), + DETECTIONS, + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + ), + 0, # Lower overlap threshold to catch more detections + np.array( + [True, True, True, True, True, True, True, True, True], dtype=bool + ), + DoesNotRaise(), ), # Lower threshold test ( - sv.Detections.empty(), - sv.PolygonZone( - POLYGON, - FRAME_RESOLUTION, - ), - 0, - np.array([], dtype=bool), - DoesNotRaise(), + sv.Detections.empty(), + sv.PolygonZone( + POLYGON, + FRAME_RESOLUTION, + ), + 0, + np.array([], dtype=bool), + DoesNotRaise(), ), # Test empty detections ], ) def test_polygon_zone_overlap( - detections: sv.Detections, - polygon_zone: sv.PolygonZone, - overlap_threshold: float, - expected_results: np.ndarray, - exception: Exception, + detections: sv.Detections, + polygon_zone: sv.PolygonZone, + overlap_threshold: float, + expected_results: np.ndarray, + exception: Exception, ) -> None: with exception: overlaps = polygon_zone.trigger_overlap(detections, overlap_threshold)