fix uti tests for `InferenceSlicer._generate_offset`
This commit is contained in:
parent
fae1bc2661
commit
ce28ab7fc7
|
|
@ -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}."
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
)
|
||||
)
|
||||
Loading…
Reference in New Issue