From bddd32dbb5dc3222dff4116b404ad60878633486 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Wed, 30 Aug 2023 15:56:15 +0200 Subject: [PATCH] refactor(inference_slicer): Update class and method docstrings --- .DS_Store | Bin 0 -> 8196 bytes docs/detection/tools/inference_slicer.md | 3 + docs/detection/tools/slicer.md | 3 - docs/utils/image.md | 2 +- mkdocs.yml | 1 + supervision/__init__.py | 2 +- .../tools/{slicer.py => inference_slicer.py} | 78 ++++++++++++++++-- supervision/detection/utils.py | 7 +- 8 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 .DS_Store create mode 100644 docs/detection/tools/inference_slicer.md delete mode 100644 docs/detection/tools/slicer.md rename supervision/detection/tools/{slicer.py => inference_slicer.py} (51%) diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a8d744c9030ddefb3a86f0b3375ef8d4c52a7cf0 GIT binary patch literal 8196 zcmeHMU2GIp6u#edfte|_x6ne_5wg;RA}v_{DioEaKm3XXwxy+j>+a3~6Q(m|XLbt` zV%7Kp_>&mpllT*Tl*AZ~F=FT&(I-tM@S+JOzUYH7>XQkcJ9oCwQWAYJ1m`CAo_o$c zXYQPHzjN>2S;iPTOXh0E8X03MRY7?*H8T`3F6tdci6pa$g6tVHO~2seJQW0Hnj2Q}dpfM|lm zVWB?V145frMAH!+lQcA?XUf|HB2z?H3`lbtPYHFR>4=U=D$NLlj{VcC5ldX_tIWPIvK8pX6VTY8EVPT$D5nsc_4nqB>a0 z+b2qPaEdpnYB790arkKT4`6u)on!U+L83)BkSJ9%@j0~EMhxI<6+){Z? zDz@sY^uFV?-WvC%WVOE5=y!5?%h9+g=V{kB7z1S)TaE9M!cF?)#$dr49wrxk@}O0U z(?+N9w3zwHoFDX#Sgv@haXHpwY%_-ZW4WT$J4%pK`4uVON%C;bXhfLxn0DY5#XD0- zo=O?U0r9{srWrcYxL{$8kumyJ{;f*OW4mRWfKJOU@vb(dSI89#W80K&lblB9*&?=z zPOt6k5VP0_JH^hkx7Y>tA^Vh#v#;4V>^l3N{lIRpAK5SLH})42s6j0_>X1Y;mSY84 zunuW#K__-$5B4I1J{(3K1z0!&4<(dw5~uJ2&f*+i#w&Oe@8Dg$j}LGWm+&PnV;o=M zJ6y#zT*nRkjz91xZYr9hE7eN9(x5C<8kH8MRavieC|&a9l}Z{NoC-droA}!^CfXL1 z6EkH@a_hG3_eRECpBv`Ly!rJFOPZFiT+_ZOJ&7d=z6#!MM{x+l^x%%&+`Vsq_U1nF<1VQyD_A~pH z-GoX2O(KP5Aw*lS676^t8_R7q*0 zxc~Ru{rmrnwy`jdKpcVlBLYZtXSzGdm%Htr;;x;d`V3W3gx;8>p$RoYMaaYJIMK;J j45^=r#?lcTlaxlN{O2D6;`5*V)1RvG`5!{`zHI&mG_y96 literal 0 HcmV?d00001 diff --git a/docs/detection/tools/inference_slicer.md b/docs/detection/tools/inference_slicer.md new file mode 100644 index 00000000..003ed83b --- /dev/null +++ b/docs/detection/tools/inference_slicer.md @@ -0,0 +1,3 @@ +## InferenceSlicer + +:::supervision.detection.tools.inference_slicer.InferenceSlicer diff --git a/docs/detection/tools/slicer.md b/docs/detection/tools/slicer.md deleted file mode 100644 index fbb51b2e..00000000 --- a/docs/detection/tools/slicer.md +++ /dev/null @@ -1,3 +0,0 @@ -## InferenceSlicer - -:::supervision.detection.tools.slicer.InferenceSlicer diff --git a/docs/utils/image.md b/docs/utils/image.md index 1dea2f50..36a833da 100644 --- a/docs/utils/image.md +++ b/docs/utils/image.md @@ -4,4 +4,4 @@ ## crop -:::supervision.utils.image.crop +:::supervision.utils.image.crop_image diff --git a/mkdocs.yml b/mkdocs.yml index 624966fb..fee74016 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -37,6 +37,7 @@ nav: - Utils: detection/utils.md - Tools: - Polygon Zone: detection/tools/polygon_zone.md + - Inference Slicer: detection/tools/inference_slicer.md - Trackers: - Core: tracker/core.md - Dataset: diff --git a/supervision/__init__.py b/supervision/__init__.py index 43918bf8..8fba9308 100644 --- a/supervision/__init__.py +++ b/supervision/__init__.py @@ -16,8 +16,8 @@ from supervision.dataset.core import ( from supervision.detection.annotate import BoxAnnotator, MaskAnnotator from supervision.detection.core import Detections from supervision.detection.line_counter import LineZone, LineZoneAnnotator +from supervision.detection.tools.inference_slicer import InferenceSlicer from supervision.detection.tools.polygon_zone import PolygonZone, PolygonZoneAnnotator -from supervision.detection.tools.slicer import InferenceSlicer from supervision.detection.utils import ( box_iou_batch, filter_polygons_by_area, diff --git a/supervision/detection/tools/slicer.py b/supervision/detection/tools/inference_slicer.py similarity index 51% rename from supervision/detection/tools/slicer.py rename to supervision/detection/tools/inference_slicer.py index ad0ba242..c8c156df 100644 --- a/supervision/detection/tools/slicer.py +++ b/supervision/detection/tools/inference_slicer.py @@ -11,7 +11,8 @@ def move_detections(detections: Detections, offset: np.array) -> Detections: """ Args: detections (sv.Detections): Detections object to be moved. - offset (np.array): An array of shape `(2,)` containing offset values in format is `[dx, dy]`. + offset (np.array): An array of shape `(2,)` containing offset values in format + is `[dx, dy]`. Returns: (sv.Detections) repositioned Detections object. """ @@ -21,7 +22,26 @@ def move_detections(detections: Detections, offset: np.array) -> Detections: class InferenceSlicer: """ - Slicing inference(SAHI) method for small target detection. + InferenceSlicer performs slicing-based inference for small target detection. This + method, often referred to as Slicing Adaptive Inference (SAHI), involves dividing a + larger image into smaller slices, performing inference on each slice, and then + merging the detections. + + Attributes: + slice_wh (Tuple[int, int]): Dimensions of each slice in the format + `(width, height)`. + overlap_ratio_wh (Tuple[float, float]): Overlap ratio between consecutive + slices in the format `(width_ratio, height_ratio)`. + iou_threshold (Optional[float]): Intersection over Union (IoU) threshold used + for non-max suppression. + callback (Callable): A function that performs inference on a given image slice + and returns detections. + + Note: + The class 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. """ def __init__( @@ -38,6 +58,37 @@ class InferenceSlicer: validate_inference_callback(callback=callback) def __call__(self, image: np.ndarray) -> Detections: + """ + Performs slicing-based inference on the provided image using the specified + callback. + + Args: + image (np.ndarray): The input image on which inference needs to be + performed. The image should be in the format + `(height, width, channels)`. + + Returns: + Detections: A collection of detections for the entire image after merging + results from all slices and applying NMS. + + Example: + ```python + >>> import cv2 + >>> import supervision as sv + >>> from ultralytics import YOLO + + >>> image = cv2.imread(SOURCE_IMAGE_PATH) + >>> model = YOLO(...) + + >>> def callback(image_slice: np.ndarray) -> sv.Detections: + ... result = model(image_slice)[0] + ... return sv.Detections.from_ultralytics(result) + + >>> slicer = sv.InferenceSlicer(callback = callback) + + >>> detections = slicer(image) + ``` + """ detections_list = [] resolution_wh = (image.shape[1], image.shape[0]) offsets = self._generate_offset( @@ -62,18 +113,29 @@ class InferenceSlicer: overlap_ratio_wh: Tuple[float, float], ) -> np.ndarray: """ - Generate offset coordinates for slicing an image based on the given resolution, slice dimensions, and overlap ratios. + Generate offset coordinates for slicing an image based on the given resolution, + slice dimensions, and overlap ratios. Args: - resolution_wh (Tuple[int, int]): A tuple representing the width and height of the image to be sliced. - slice_wh (Tuple[int, int]): A tuple representing the desired width and height of each slice. - overlap_ratio_wh (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. + resolution_wh (Tuple[int, int]): A tuple representing the width and height + of the image to be sliced. + slice_wh (Tuple[int, int]): A tuple representing the desired width and + height of each slice. + overlap_ratio_wh (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. Returns: - np.ndarray: An array of shape `(n, 4)` containing coordinates for each slice in the format `[xmin, ymin, xmax, ymax]`. + 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. + 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. """ slice_width, slice_height = slice_wh image_width, image_height = resolution_wh diff --git a/supervision/detection/utils.py b/supervision/detection/utils.py index 18edcd3a..e882173b 100644 --- a/supervision/detection/utils.py +++ b/supervision/detection/utils.py @@ -380,9 +380,10 @@ def process_roboflow_result( def move_boxes(xyxy: np.ndarray, offset: np.ndarray) -> np.ndarray: """ Args: - xyxy (np.ndarray): An array of shape `(n, 4)` containing the bounding boxes coordinates in format - `[x1, y1, x2, y2]` - offset (np.array): An array of shape `(2,)` containing offset values in format is `[dx, dy]`. + xyxy (np.ndarray): An array of shape `(n, 4)` containing the bounding boxes + coordinates in format `[x1, y1, x2, y2]` + offset (np.array): An array of shape `(2,)` containing offset values in format + is `[dx, dy]`. Returns: (np.ndarray) repositioned bounding boxes