From ce28ab7fc7f76bd17fb1f3477233d4276709b3c4 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Fri, 14 Nov 2025 15:49:22 +0100 Subject: [PATCH] fix uti tests for `InferenceSlicer._generate_offset` --- .../detection/tools/inference_slicer.py | 98 ++++++------------- test/detection/tools/test_inference_slicer.py | 53 +--------- 2 files changed, 34 insertions(+), 117 deletions(-) diff --git a/supervision/detection/tools/inference_slicer.py b/supervision/detection/tools/inference_slicer.py index aaecccb3..3aa7c4ba 100644 --- a/supervision/detection/tools/inference_slicer.py +++ b/supervision/detection/tools/inference_slicer.py @@ -13,8 +13,7 @@ from supervision.detection.utils.iou_and_nms import OverlapFilter, OverlapMetric from supervision.detection.utils.masks import move_masks from supervision.utils.image import crop_image from supervision.utils.internal import ( - SupervisionWarnings, - warn_deprecated, + SupervisionWarnings ) @@ -62,15 +61,9 @@ class InferenceSlicer: Args: slice_wh (Tuple[int, int]): Dimensions of each slice measured in pixels. The tuple should be in the format `(width, height)`. - overlap_ratio_wh (Optional[Tuple[float, float]]): [⚠️ Deprecated: please set - to `None` and use `overlap_wh`] A tuple representing the - desired overlap ratio for width and height between consecutive slices. - Each value should be in the range [0, 1), where 0 means no overlap and - a value close to 1 means high overlap. - overlap_wh (Optional[Tuple[int, int]]): A tuple representing the desired + overlap_wh (Tuple[int, int]): A tuple representing the desired overlap for width and height between consecutive slices measured in pixels. - Each value should be greater than or equal to 0. Takes precedence over - `overlap_ratio_wh`. + Each value must be greater than or equal to 0. overlap_filter (Union[OverlapFilter, str]): Strategy for filtering or merging overlapping detections in slices. iou_threshold (float): Intersection over Union (IoU) threshold @@ -91,26 +84,16 @@ class InferenceSlicer: def __init__( self, callback: Callable[[np.ndarray], Detections], - slice_wh: tuple[int, int] = (320, 320), - overlap_ratio_wh: tuple[float, float] | None = (0.2, 0.2), - overlap_wh: tuple[int, int] | None = None, + slice_wh: tuple[int, int] = (640, 640), + overlap_wh: tuple[int, int] = (100, 100), overlap_filter: OverlapFilter | str = OverlapFilter.NON_MAX_SUPPRESSION, iou_threshold: float = 0.5, overlap_metric: OverlapMetric | str = OverlapMetric.IOU, thread_workers: int = 1, ): - if overlap_ratio_wh is not None: - warn_deprecated( - "`overlap_ratio_wh` in `InferenceSlicer.__init__` is deprecated and " - "will be removed in `supervision-0.27.0`. Please manually set it to " - "`None` and use `overlap_wh` instead." - ) - - self._validate_overlap(overlap_ratio_wh, overlap_wh) - self.overlap_ratio_wh = overlap_ratio_wh self.overlap_wh = overlap_wh - self.slice_wh = slice_wh + self._validate_overlap(slice_wh=self.slice_wh, overlap_wh=overlap_wh) self.iou_threshold = iou_threshold self.overlap_metric = OverlapMetric.from_value(overlap_metric) self.overlap_filter = OverlapFilter.from_value(overlap_filter) @@ -146,7 +129,7 @@ class InferenceSlicer: slicer = sv.InferenceSlicer( callback=callback, - overlap_filter_strategy=sv.OverlapFilter.NON_MAX_SUPPRESSION, + overlap_filter=sv.OverlapFilter.NON_MAX_SUPPRESSION, ) detections = slicer(image) @@ -157,7 +140,6 @@ class InferenceSlicer: offsets = self._generate_offset( resolution_wh=resolution_wh, slice_wh=self.slice_wh, - overlap_ratio_wh=self.overlap_ratio_wh, overlap_wh=self.overlap_wh, ) @@ -211,25 +193,20 @@ class InferenceSlicer: def _generate_offset( resolution_wh: tuple[int, int], slice_wh: tuple[int, int], - overlap_ratio_wh: tuple[float, float] | None, - overlap_wh: tuple[int, int] | None, + overlap_wh: tuple[int, int], ) -> np.ndarray: """ Generate offset coordinates for slicing an image based on the given resolution, - slice dimensions, and overlap ratios. + slice dimensions, and pixel overlap. Args: resolution_wh (Tuple[int, int]): A tuple representing the width and height of the image to be sliced. slice_wh (Tuple[int, int]): Dimensions of each slice measured in pixels. The tuple should be in the format `(width, height)`. - overlap_ratio_wh (Optional[Tuple[float, float]]): A tuple representing the - desired overlap ratio for width and height between consecutive slices. - Each value should be in the range [0, 1), where 0 means no overlap and - a value close to 1 means high overlap. - overlap_wh (Optional[Tuple[int, int]]): A tuple representing the desired + overlap_wh (Tuple[int, int]): A tuple representing the desired overlap for width and height between consecutive slices measured in - pixels. Each value should be greater than or equal to 0. + pixels. Each value must be greater than or equal to 0. Returns: np.ndarray: An array of shape `(n, 4)` containing coordinates for each @@ -244,16 +221,7 @@ class InferenceSlicer: """ slice_width, slice_height = slice_wh image_width, image_height = resolution_wh - overlap_width = ( - overlap_wh[0] - if overlap_wh is not None - else int(overlap_ratio_wh[0] * slice_width) - ) - overlap_height = ( - overlap_wh[1] - if overlap_wh is not None - else int(overlap_ratio_wh[1] * slice_height) - ) + overlap_width, overlap_height = overlap_wh width_stride = slice_width - overlap_width height_stride = slice_height - overlap_height @@ -271,29 +239,27 @@ class InferenceSlicer: @staticmethod def _validate_overlap( - overlap_ratio_wh: tuple[float, float] | None, - overlap_wh: tuple[int, int] | None, + slice_wh: tuple[int, int], + overlap_wh: tuple[int, int], ) -> None: - if overlap_ratio_wh is not None and overlap_wh is not None: + if not isinstance(overlap_wh, tuple) or len(overlap_wh) != 2: raise ValueError( - "Both `overlap_ratio_wh` and `overlap_wh` cannot be provided. " - "Please provide only one of them." - ) - if overlap_ratio_wh is None and overlap_wh is None: - raise ValueError( - "Either `overlap_ratio_wh` or `overlap_wh` must be provided. " - "Please provide one of them." + "`overlap_wh` must be a tuple of two non-negative values " + "(overlap_w, overlap_h)." ) - if overlap_ratio_wh is not None: - if not (0 <= overlap_ratio_wh[0] < 1 and 0 <= overlap_ratio_wh[1] < 1): - raise ValueError( - "Overlap ratios must be in the range [0, 1). " - f"Received: {overlap_ratio_wh}" - ) - if overlap_wh is not None: - if not (overlap_wh[0] >= 0 and overlap_wh[1] >= 0): - raise ValueError( - "Overlap values must be greater than or equal to 0. " - f"Received: {overlap_wh}" - ) + overlap_w, overlap_h = overlap_wh + slice_w, slice_h = slice_wh + + if overlap_w < 0 or overlap_h < 0: + raise ValueError( + "Overlap values must be greater than or equal to 0. " + f"Received: {overlap_wh}" + ) + + if overlap_w >= slice_w or overlap_h >= slice_h: + raise ValueError( + "`overlap_wh` must be smaller than `slice_wh` in both dimensions " + f"to keep a positive stride. Received overlap_wh={overlap_wh}, " + f"slice_wh={slice_wh}." + ) diff --git a/test/detection/tools/test_inference_slicer.py b/test/detection/tools/test_inference_slicer.py index 2185b77f..b32d3b20 100644 --- a/test/detection/tools/test_inference_slicer.py +++ b/test/detection/tools/test_inference_slicer.py @@ -19,51 +19,6 @@ def mock_callback(): return callback - -@pytest.mark.parametrize( - "slice_wh, overlap_ratio_wh, overlap_wh, expected_overlap, exception", - [ - # Valid case: explicit overlap_wh in pixels - ((128, 128), None, (26, 26), (26, 26), DoesNotRaise()), - # Valid case: overlap_wh in pixels - ((128, 128), None, (20, 20), (20, 20), DoesNotRaise()), - # Invalid case: negative overlap_wh, should raise ValueError - ((128, 128), None, (-10, 20), None, pytest.raises(ValueError)), - # Invalid case: no overlaps defined - ((128, 128), None, None, None, pytest.raises(ValueError)), - # Valid case: overlap_wh = 50 pixels - ((256, 256), None, (50, 50), (50, 50), DoesNotRaise()), - # Valid case: overlap_wh = 60 pixels - ((200, 200), None, (60, 60), (60, 60), DoesNotRaise()), - # Valid case: small overlap_wh values - ((100, 100), None, (0.1, 0.1), (0.1, 0.1), DoesNotRaise()), - # Invalid case: negative overlap_wh values - ((128, 128), None, (-10, -10), None, pytest.raises(ValueError)), - # Invalid case: overlap_wh greater than slice size - ((128, 128), None, (150, 150), (150, 150), DoesNotRaise()), - # Valid case: zero overlap - ((128, 128), None, (0, 0), (0, 0), DoesNotRaise()), - ], -) -def test_inference_slicer_overlap( - mock_callback, - slice_wh: tuple[int, int], - overlap_ratio_wh: tuple[float, float] | None, - overlap_wh: tuple[int, int] | None, - expected_overlap: tuple[int, int] | None, - exception: Exception, -) -> None: - with exception: - slicer = InferenceSlicer( - callback=mock_callback, - slice_wh=slice_wh, - overlap_ratio_wh=overlap_ratio_wh, - overlap_wh=overlap_wh, - overlap_filter=OverlapFilter.NONE, - ) - assert slicer.overlap_wh == expected_overlap - - @pytest.mark.parametrize( "resolution_wh, slice_wh, overlap_wh, expected_offsets", [ @@ -163,24 +118,20 @@ def test_inference_slicer_overlap( ] ), ), - # Case 6: Overlap_wh is greater than the slice size - ((256, 256), (128, 128), (150, 150), np.array([]).reshape(0, 4)), ], ) def test_generate_offset( resolution_wh: tuple[int, int], slice_wh: tuple[int, int], - overlap_wh: tuple[int, int] | None, + overlap_wh: tuple[int, int], expected_offsets: np.ndarray, ) -> None: offsets = InferenceSlicer._generate_offset( resolution_wh=resolution_wh, slice_wh=slice_wh, - overlap_ratio_wh=None, overlap_wh=overlap_wh, ) - # Verify that the generated offsets match the expected offsets assert np.array_equal(offsets, expected_offsets), ( f"Expected {expected_offsets}, got {offsets}" - ) + ) \ No newline at end of file