From ea0ec29c8105bb83f4f0fa748a6897c1706f9e50 Mon Sep 17 00:00:00 2001 From: James Gallagher Date: Fri, 8 Sep 2023 12:43:20 +0100 Subject: [PATCH 1/9] add concurrent processing for inference slicer --- .../detection/tools/inference_slicer.py | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/supervision/detection/tools/inference_slicer.py b/supervision/detection/tools/inference_slicer.py index 1fbd0697..d598c715 100644 --- a/supervision/detection/tools/inference_slicer.py +++ b/supervision/detection/tools/inference_slicer.py @@ -6,6 +6,8 @@ from supervision.detection.core import Detections, validate_inference_callback from supervision.detection.utils import move_boxes from supervision.utils.image import crop_image +import concurrent.futures + def move_detections(detections: Detections, offset: np.array) -> Detections: """ @@ -97,15 +99,34 @@ class InferenceSlicer: overlap_ratio_wh=self.overlap_ratio_wh, ) - for offset in offsets: - image_slice = crop_image(image=image, xyxy=offset) - detections = self.callback(image_slice) - detections = move_detections(detections=detections, offset=offset[:2]) - detections_list.append(detections) + with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor: + futures = [ + executor.submit(self._run_callback, image, offset) for offset in offsets + ] + for future in concurrent.futures.as_completed(futures): + detections_list.append(future.result()) + return Detections.merge(detections_list=detections_list).with_nms( threshold=self.iou_threshold ) + def _run_callback(self, image, offset) -> Detections: + """ + Run the provided callback on a slice of an image. + + Args: + image (np.ndarray): The input image on which inference needs to run + offset (np.ndarray): An array of shape `(4,)` containing coordinates for the slice. + + Returns: + Detections: A collection of detections for the slice. + """ + image_slice = crop_image(image=image, xyxy=offset) + detections = self.callback(image_slice) + detections = move_detections(detections=detections, offset=offset[:2]) + + return detections + @staticmethod def _generate_offset( resolution_wh: Tuple[int, int], From bb42753e2302ede674e2ef363388a611200def69 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 11:46:16 +0000 Subject: [PATCH 2/9] =?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/inference_slicer.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/supervision/detection/tools/inference_slicer.py b/supervision/detection/tools/inference_slicer.py index d598c715..61b8a93e 100644 --- a/supervision/detection/tools/inference_slicer.py +++ b/supervision/detection/tools/inference_slicer.py @@ -1,3 +1,4 @@ +import concurrent.futures from typing import Callable, Optional, Tuple import numpy as np @@ -6,8 +7,6 @@ from supervision.detection.core import Detections, validate_inference_callback from supervision.detection.utils import move_boxes from supervision.utils.image import crop_image -import concurrent.futures - def move_detections(detections: Detections, offset: np.array) -> Detections: """ From 8f583404ce162511c98110c7f42ab722fec95057 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Fri, 8 Sep 2023 15:03:59 +0300 Subject: [PATCH 3/9] =?UTF-8?q?fix(import):=20=F0=9F=93=A6=20import=20only?= =?UTF-8?q?=20=20ThreadPoolExecutor,as=5Fcompleted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- supervision/detection/tools/inference_slicer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/supervision/detection/tools/inference_slicer.py b/supervision/detection/tools/inference_slicer.py index 61b8a93e..67743532 100644 --- a/supervision/detection/tools/inference_slicer.py +++ b/supervision/detection/tools/inference_slicer.py @@ -1,4 +1,4 @@ -import concurrent.futures +from concurrent.futures import ThreadPoolExecutor,as_completed from typing import Callable, Optional, Tuple import numpy as np @@ -98,11 +98,11 @@ class InferenceSlicer: overlap_ratio_wh=self.overlap_ratio_wh, ) - with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor: + with ThreadPoolExecutor(max_workers=8) as executor: futures = [ executor.submit(self._run_callback, image, offset) for offset in offsets ] - for future in concurrent.futures.as_completed(futures): + for future in as_completed(futures): detections_list.append(future.result()) return Detections.merge(detections_list=detections_list).with_nms( From 21e5d5139ce0e1cb65d0df78ac5835f95b0d1d11 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 12:04:17 +0000 Subject: [PATCH 4/9] =?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/inference_slicer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervision/detection/tools/inference_slicer.py b/supervision/detection/tools/inference_slicer.py index 67743532..bb5d6a36 100644 --- a/supervision/detection/tools/inference_slicer.py +++ b/supervision/detection/tools/inference_slicer.py @@ -1,4 +1,4 @@ -from concurrent.futures import ThreadPoolExecutor,as_completed +from concurrent.futures import ThreadPoolExecutor, as_completed from typing import Callable, Optional, Tuple import numpy as np From b9598f54b6df270e537f9a9833c5f048f159f66c Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Fri, 8 Sep 2023 15:06:39 +0300 Subject: [PATCH 5/9] =?UTF-8?q?feat:=20=E2=9C=A8=20control=20thread=20work?= =?UTF-8?q?er=20and=20set=20default=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- supervision/detection/tools/inference_slicer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/supervision/detection/tools/inference_slicer.py b/supervision/detection/tools/inference_slicer.py index bb5d6a36..97a36b6a 100644 --- a/supervision/detection/tools/inference_slicer.py +++ b/supervision/detection/tools/inference_slicer.py @@ -51,11 +51,13 @@ class InferenceSlicer: slice_wh: Tuple[int, int] = (320, 320), overlap_ratio_wh: Tuple[float, float] = (0.2, 0.2), iou_threshold: Optional[float] = 0.5, + thread_workers: int = 1, ): self.slice_wh = slice_wh self.overlap_ratio_wh = overlap_ratio_wh self.iou_threshold = iou_threshold self.callback = callback + self.thread_workers = thread_workers validate_inference_callback(callback=callback) def __call__(self, image: np.ndarray) -> Detections: @@ -98,7 +100,7 @@ class InferenceSlicer: overlap_ratio_wh=self.overlap_ratio_wh, ) - with ThreadPoolExecutor(max_workers=8) as executor: + with ThreadPoolExecutor(max_workers=self.thread_workers) as executor: futures = [ executor.submit(self._run_callback, image, offset) for offset in offsets ] From c6b8fbed31f1159b821ad01a10dcf46e3aa428c7 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Fri, 8 Sep 2023 21:29:37 +0300 Subject: [PATCH 6/9] fix(pre-commit): formatting fix Signed-off-by: Onuralp SEZER --- supervision/detection/tools/inference_slicer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/supervision/detection/tools/inference_slicer.py b/supervision/detection/tools/inference_slicer.py index 97a36b6a..3625eb3e 100644 --- a/supervision/detection/tools/inference_slicer.py +++ b/supervision/detection/tools/inference_slicer.py @@ -117,7 +117,8 @@ class InferenceSlicer: Args: image (np.ndarray): The input image on which inference needs to run - offset (np.ndarray): An array of shape `(4,)` containing coordinates for the slice. + offset (np.ndarray): An array of shape `(4,)` containing coordinates + for the slice. Returns: Detections: A collection of detections for the slice. From 1c1936e411e814c384de00dadec82e207325b0d0 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Wed, 4 Oct 2023 18:15:08 +0200 Subject: [PATCH 7/9] Refactor args description in InferenceSlicer --- supervision/detection/tools/inference_slicer.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/supervision/detection/tools/inference_slicer.py b/supervision/detection/tools/inference_slicer.py index 3625eb3e..3ae8c1f0 100644 --- a/supervision/detection/tools/inference_slicer.py +++ b/supervision/detection/tools/inference_slicer.py @@ -28,15 +28,16 @@ class InferenceSlicer: larger image into smaller slices, performing inference on each slice, and then merging the detections. - Attributes: + Args: 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. + 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. + thread_workers (int): Number of threads for parallel execution. Note: The class ensures that slices do not exceed the boundaries of the original From 440be5d416465720ea95d1d1474cba2bcb15d460 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Wed, 4 Oct 2023 18:36:30 +0200 Subject: [PATCH 8/9] Add color mapping strategy to InferenceSlicer Added a new argument, 'color_map' in InferenceSlicer to provide more options for color coding while annotating. This includes mapping colors by 'index', 'class' or 'track'. Also refactored the way colors are resolved by separating the responsibility into a new function 'resolve_color_idx' and 'resolve_color'. --- supervision/annotators/core.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/supervision/annotators/core.py b/supervision/annotators/core.py index 350ae06b..7f11609b 100644 --- a/supervision/annotators/core.py +++ b/supervision/annotators/core.py @@ -582,6 +582,7 @@ class TraceAnnotator: position: Optional[Position] = Position.CENTER, trace_length: int = 30, thickness: int = 2, + color_map: str = "class" ): """ Args: @@ -592,11 +593,14 @@ class TraceAnnotator: trace_length (int): The maximum length of the trace in terms of historical points. Defaults to `30`. thickness (int): The thickness of the trace lines. Defaults to `2`. + color_map (str): Strategy for mapping colors to annotations. + Options are `index`, `class`, or `track`. """ self.color: Union[Color, ColorPalette] = color self.position = position self.trace = Trace(max_size=trace_length) self.thickness = thickness + self.color_map: ColorMap = ColorMap(color_map) def annotate(self, scene: np.ndarray, detections: Detections) -> np.ndarray: """ @@ -629,15 +633,14 @@ class TraceAnnotator: """ self.trace.put(detections) - for i, (xyxy, mask, confidence, class_id, tracker_id) in enumerate(detections): - class_id = detections.class_id[i] if class_id is not None else None - idx = class_id if class_id is not None else i - color = ( - self.color.by_idx(idx) - if isinstance(self.color, ColorPalette) - else self.color + for detection_idx in range(len(detections)): + tracker_id = int(detections.tracker_id[detection_idx]) + idx = resolve_color_idx( + detections=detections, + detection_idx=detection_idx, + color_map=self.color_map, ) - + color = resolve_color(color=self.color, idx=idx) xy = self.trace.get(tracker_id=tracker_id) if len(xy) > 1: scene = cv2.polylines( From a66d835a57e28afd1335e662a88c77ed23a01cb9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 16:37:40 +0000 Subject: [PATCH 9/9] =?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/annotators/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervision/annotators/core.py b/supervision/annotators/core.py index 7f11609b..a820a0fa 100644 --- a/supervision/annotators/core.py +++ b/supervision/annotators/core.py @@ -582,7 +582,7 @@ class TraceAnnotator: position: Optional[Position] = Position.CENTER, trace_length: int = 30, thickness: int = 2, - color_map: str = "class" + color_map: str = "class", ): """ Args: