improve `InferenceSlicer` implementation lowering the count of generated boxes
This commit is contained in:
parent
ce28ab7fc7
commit
8d464aa1b5
|
|
@ -200,40 +200,58 @@ class InferenceSlicer:
|
|||
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_wh (Tuple[int, int]): A tuple representing the desired
|
||||
overlap for width and height between consecutive slices measured in
|
||||
pixels. Each value must be greater than or equal to 0.
|
||||
resolution_wh (Tuple[int, int]): Width and height of the image to be sliced.
|
||||
slice_wh (Tuple[int, int]): Dimensions of each slice in pixels (width, height).
|
||||
overlap_wh (Tuple[int, int]): Overlap in pixels (overlap_w, overlap_h).
|
||||
|
||||
Returns:
|
||||
np.ndarray: An array of shape `(n, 4)` containing coordinates for each
|
||||
slice in the format `[xmin, ymin, xmax, ymax]`.
|
||||
|
||||
Note:
|
||||
The function ensures that slices do not exceed the boundaries of the
|
||||
original image. As a result, the final slices in the row and column
|
||||
dimensions might be smaller than the specified slice dimensions if the
|
||||
image's width or height is not a multiple of the slice's width or
|
||||
height minus the overlap.
|
||||
np.ndarray: Array of shape (n, 4) with [x_min, y_min, x_max, y_max] slices.
|
||||
"""
|
||||
slice_width, slice_height = slice_wh
|
||||
image_width, image_height = resolution_wh
|
||||
overlap_width, overlap_height = overlap_wh
|
||||
|
||||
width_stride = slice_width - overlap_width
|
||||
height_stride = slice_height - overlap_height
|
||||
stride_x = slice_width - overlap_width
|
||||
stride_y = slice_height - overlap_height
|
||||
|
||||
ws = np.arange(0, image_width, width_stride)
|
||||
hs = np.arange(0, image_height, height_stride)
|
||||
def _compute_axis_starts(
|
||||
image_size: int,
|
||||
slice_size: int,
|
||||
stride: int,
|
||||
) -> list[int]:
|
||||
if image_size <= slice_size:
|
||||
return [0]
|
||||
|
||||
xmin, ymin = np.meshgrid(ws, hs)
|
||||
xmax = np.clip(xmin + slice_width, 0, image_width)
|
||||
ymax = np.clip(ymin + slice_height, 0, image_height)
|
||||
# No overlap case, preserve original behavior, no overlapping tiles
|
||||
if stride == slice_size:
|
||||
return np.arange(0, image_size, stride).tolist()
|
||||
|
||||
offsets = np.stack([xmin, ymin, xmax, ymax], axis=-1).reshape(-1, 4)
|
||||
# Overlap case, ensure last tile touches the border without redundancy
|
||||
last_start = image_size - slice_size
|
||||
starts = np.arange(0, last_start, stride).tolist()
|
||||
if not starts or starts[-1] != last_start:
|
||||
starts.append(last_start)
|
||||
return starts
|
||||
|
||||
x_starts = _compute_axis_starts(
|
||||
image_size=image_width,
|
||||
slice_size=slice_width,
|
||||
stride=stride_x,
|
||||
)
|
||||
y_starts = _compute_axis_starts(
|
||||
image_size=image_height,
|
||||
slice_size=slice_height,
|
||||
stride=stride_y,
|
||||
)
|
||||
|
||||
x_min, y_min = np.meshgrid(x_starts, y_starts)
|
||||
x_max = np.clip(x_min + slice_width, 0, image_width)
|
||||
y_max = np.clip(y_min + slice_height, 0, image_height)
|
||||
|
||||
offsets = np.stack(
|
||||
[x_min, y_min, x_max, y_max],
|
||||
axis=-1,
|
||||
).reshape(-1, 4)
|
||||
|
||||
return offsets
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from contextlib import ExitStack as DoesNotRaise
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.detection.tools.inference_slicer import InferenceSlicer
|
||||
from supervision.detection.utils.iou_and_nms import OverlapFilter
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -22,7 +19,7 @@ def mock_callback():
|
|||
@pytest.mark.parametrize(
|
||||
"resolution_wh, slice_wh, overlap_wh, expected_offsets",
|
||||
[
|
||||
# Case 1: No overlap, exact slices fit within image dimensions
|
||||
# Case 1: Square image, square slices, no overlap
|
||||
(
|
||||
(256, 256),
|
||||
(128, 128),
|
||||
|
|
@ -36,7 +33,7 @@ def mock_callback():
|
|||
]
|
||||
),
|
||||
),
|
||||
# Case 2: Overlap of 64 pixels in both directions
|
||||
# Case 2: Square image, square slices, non-zero overlap
|
||||
(
|
||||
(256, 256),
|
||||
(128, 128),
|
||||
|
|
@ -46,75 +43,137 @@ def mock_callback():
|
|||
[0, 0, 128, 128],
|
||||
[64, 0, 192, 128],
|
||||
[128, 0, 256, 128],
|
||||
[192, 0, 256, 128],
|
||||
[0, 64, 128, 192],
|
||||
[64, 64, 192, 192],
|
||||
[128, 64, 256, 192],
|
||||
[192, 64, 256, 192],
|
||||
[0, 128, 128, 256],
|
||||
[64, 128, 192, 256],
|
||||
[128, 128, 256, 256],
|
||||
[192, 128, 256, 256],
|
||||
[0, 192, 128, 256],
|
||||
[64, 192, 192, 256],
|
||||
[128, 192, 256, 256],
|
||||
[192, 192, 256, 256],
|
||||
]
|
||||
),
|
||||
),
|
||||
# Case 3: Image not perfectly divisible by slice size (no overlap)
|
||||
# Case 3: Rectangle image (horizontal), square slices, no overlap
|
||||
(
|
||||
(300, 300),
|
||||
(128, 128),
|
||||
(192, 128),
|
||||
(64, 64),
|
||||
(0, 0),
|
||||
np.array(
|
||||
[
|
||||
[0, 0, 128, 128],
|
||||
[128, 0, 256, 128],
|
||||
[256, 0, 300, 128],
|
||||
[0, 128, 128, 256],
|
||||
[128, 128, 256, 256],
|
||||
[256, 128, 300, 256],
|
||||
[0, 256, 128, 300],
|
||||
[128, 256, 256, 300],
|
||||
[256, 256, 300, 300],
|
||||
[0, 0, 64, 64],
|
||||
[64, 0, 128, 64],
|
||||
[128, 0, 192, 64],
|
||||
[0, 64, 64, 128],
|
||||
[64, 64, 128, 128],
|
||||
[128, 64, 192, 128],
|
||||
]
|
||||
),
|
||||
),
|
||||
# Case 4: Overlap of 32 pixels, image not perfectly divisible by slice size
|
||||
# Case 4: Rectangle image (horizontal), square slices, non-zero overlap
|
||||
(
|
||||
(300, 300),
|
||||
(128, 128),
|
||||
(192, 128),
|
||||
(64, 64),
|
||||
(32, 32),
|
||||
np.array(
|
||||
[
|
||||
[0, 0, 128, 128],
|
||||
[96, 0, 224, 128],
|
||||
[192, 0, 300, 128],
|
||||
[288, 0, 300, 128],
|
||||
[0, 96, 128, 224],
|
||||
[96, 96, 224, 224],
|
||||
[192, 96, 300, 224],
|
||||
[288, 96, 300, 224],
|
||||
[0, 192, 128, 300],
|
||||
[96, 192, 224, 300],
|
||||
[192, 192, 300, 300],
|
||||
[288, 192, 300, 300],
|
||||
[0, 288, 128, 300],
|
||||
[96, 288, 224, 300],
|
||||
[192, 288, 300, 300],
|
||||
[288, 288, 300, 300],
|
||||
[0, 0, 64, 64],
|
||||
[32, 0, 96, 64],
|
||||
[64, 0, 128, 64],
|
||||
[96, 0, 160, 64],
|
||||
[128, 0, 192, 64],
|
||||
[0, 32, 64, 96],
|
||||
[32, 32, 96, 96],
|
||||
[64, 32, 128, 96],
|
||||
[96, 32, 160, 96],
|
||||
[128, 32, 192, 96],
|
||||
[0, 64, 64, 128],
|
||||
[32, 64, 96, 128],
|
||||
[64, 64, 128, 128],
|
||||
[96, 64, 160, 128],
|
||||
[128, 64, 192, 128],
|
||||
]
|
||||
),
|
||||
),
|
||||
# Case 5: Image smaller than slice size (no overlap)
|
||||
# Case 5: Rectangle image (vertical), square slices, no overlap
|
||||
(
|
||||
(100, 100),
|
||||
(128, 128),
|
||||
(128, 192),
|
||||
(64, 64),
|
||||
(0, 0),
|
||||
np.array(
|
||||
[
|
||||
[0, 0, 100, 100],
|
||||
[0, 0, 64, 64],
|
||||
[64, 0, 128, 64],
|
||||
[0, 64, 64, 128],
|
||||
[64, 64, 128, 128],
|
||||
[0, 128, 64, 192],
|
||||
[64, 128, 128, 192],
|
||||
]
|
||||
),
|
||||
),
|
||||
# Case 6: Rectangle image (vertical), square slices, non-zero overlap
|
||||
(
|
||||
(128, 192),
|
||||
(64, 64),
|
||||
(32, 32),
|
||||
np.array(
|
||||
[
|
||||
[0, 0, 64, 64],
|
||||
[32, 0, 96, 64],
|
||||
[64, 0, 128, 64],
|
||||
[0, 32, 64, 96],
|
||||
[32, 32, 96, 96],
|
||||
[64, 32, 128, 96],
|
||||
[0, 64, 64, 128],
|
||||
[32, 64, 96, 128],
|
||||
[64, 64, 128, 128],
|
||||
[0, 96, 64, 160],
|
||||
[32, 96, 96, 160],
|
||||
[64, 96, 128, 160],
|
||||
[0, 128, 64, 192],
|
||||
[32, 128, 96, 192],
|
||||
[64, 128, 128, 192],
|
||||
]
|
||||
),
|
||||
),
|
||||
# Case 7: Square image, rectangular slices (horizontal), no overlap
|
||||
(
|
||||
(160, 160),
|
||||
(80, 40),
|
||||
(0, 0),
|
||||
np.array(
|
||||
[
|
||||
[0, 0, 80, 40],
|
||||
[80, 0, 160, 40],
|
||||
[0, 40, 80, 80],
|
||||
[80, 40, 160, 80],
|
||||
[0, 80, 80, 120],
|
||||
[80, 80, 160, 120],
|
||||
[0, 120, 80, 160],
|
||||
[80, 120, 160, 160],
|
||||
]
|
||||
),
|
||||
),
|
||||
# Case 8: Square image, rectangular slices (vertical), non-zero overlap
|
||||
(
|
||||
(160, 160),
|
||||
(40, 80),
|
||||
(10, 20),
|
||||
np.array(
|
||||
[
|
||||
[0, 0, 40, 80],
|
||||
[30, 0, 70, 80],
|
||||
[60, 0, 100, 80],
|
||||
[90, 0, 130, 80],
|
||||
[120, 0, 160, 80],
|
||||
[0, 60, 40, 140],
|
||||
[30, 60, 70, 140],
|
||||
[60, 60, 100, 140],
|
||||
[90, 60, 130, 140],
|
||||
[120, 60, 160, 140],
|
||||
[0, 80, 40, 160],
|
||||
[30, 80, 70, 160],
|
||||
[60, 80, 100, 160],
|
||||
[90, 80, 130, 160],
|
||||
[120, 80, 160, 160],
|
||||
]
|
||||
),
|
||||
),
|
||||
|
|
|
|||
Loading…
Reference in New Issue