From 8baba682aa76d6387ebc09435d255c07be86b051 Mon Sep 17 00:00:00 2001 From: Chandler Supple Date: Thu, 16 Jan 2025 19:09:16 -0800 Subject: [PATCH 1/6] Added percent --- .../transformations/detection_offset/v1.py | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/inference/core/workflows/core_steps/transformations/detection_offset/v1.py b/inference/core/workflows/core_steps/transformations/detection_offset/v1.py index 1a9c4753d..28993a5d0 100644 --- a/inference/core/workflows/core_steps/transformations/detection_offset/v1.py +++ b/inference/core/workflows/core_steps/transformations/detection_offset/v1.py @@ -75,6 +75,11 @@ class BlockManifest(WorkflowBlockManifest): examples=[10, "$inputs.offset_y"], validation_alias=AliasChoices("offset_height", "offset_y"), ) + units: Literal["Percent (%)", "Pixels"] = Field( + default="Pixels", + description="Units for offset dimensions", + examples=["Pixels", "Percent (%)"], + ) @classmethod def get_parameters_accepting_batches(cls) -> List[str]: @@ -111,13 +116,16 @@ def run( predictions: Batch[sv.Detections], offset_width: int, offset_height: int, + units: str = "Pixels" ) -> BlockResult: + use_percentage = units == "Percent (%)" return [ { "predictions": offset_detections( detections=detections, offset_width=offset_width, offset_height=offset_height, + use_percentage=use_percentage ) } for detections in predictions @@ -126,26 +134,40 @@ def run( def offset_detections( detections: sv.Detections, - offset_width: int, - offset_height: int, + offset_width: Union[int, float], + offset_height: Union[int, float], parent_id_key: str = PARENT_ID_KEY, detection_id_key: str = DETECTION_ID_KEY, + use_percentage: bool = False ) -> sv.Detections: if len(detections) == 0: return detections _detections = deepcopy(detections) image_dimensions = detections.data["image_dimensions"] - _detections.xyxy = np.array( - [ - ( - max(0, x1 - offset_width // 2), - max(0, y1 - offset_height // 2), - min(image_dimensions[i][1], x2 + offset_width // 2), - min(image_dimensions[i][0], y2 + offset_height // 2), - ) - for i, (x1, y1, x2, y2) in enumerate(_detections.xyxy) - ] - ) + if use_percentage: + _detections.xyxy = np.array( + [ + ( + max(0, x1 - int((x2 - x1) * offset_width / 200)), + max(0, y1 - int((y2 - y1) * offset_height / 200)), + min(image_dimensions[i][1], x2 + int((x2 - x1) * offset_width / 200)), + min(image_dimensions[i][0], y2 + int((y2 - y1) * offset_height / 200)), + ) + for i, (x1, y1, x2, y2) in enumerate(_detections.xyxy) + ] + ) + else: + _detections.xyxy = np.array( + [ + ( + max(0, x1 - offset_width // 2), + max(0, y1 - offset_height // 2), + min(image_dimensions[i][1], x2 + offset_width // 2), + min(image_dimensions[i][0], y2 + offset_height // 2), + ) + for i, (x1, y1, x2, y2) in enumerate(_detections.xyxy) + ] + ) _detections[parent_id_key] = detections[detection_id_key].copy() _detections[detection_id_key] = [str(uuid.uuid4()) for _ in detections] return _detections From 5a8dba1b2bbb242ceff6023e33f91d4bef07f856 Mon Sep 17 00:00:00 2001 From: Chandler Supple Date: Thu, 16 Jan 2025 20:06:26 -0800 Subject: [PATCH 2/6] Updated the tests --- .../transformations/detection_offset/v1.py | 19 +++++++----- .../transformations/test_detection_offset.py | 30 +++++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/inference/core/workflows/core_steps/transformations/detection_offset/v1.py b/inference/core/workflows/core_steps/transformations/detection_offset/v1.py index 28993a5d0..85fa9ca10 100644 --- a/inference/core/workflows/core_steps/transformations/detection_offset/v1.py +++ b/inference/core/workflows/core_steps/transformations/detection_offset/v1.py @@ -116,7 +116,7 @@ def run( predictions: Batch[sv.Detections], offset_width: int, offset_height: int, - units: str = "Pixels" + units: str = "Pixels", ) -> BlockResult: use_percentage = units == "Percent (%)" return [ @@ -125,7 +125,7 @@ def run( detections=detections, offset_width=offset_width, offset_height=offset_height, - use_percentage=use_percentage + use_percentage=use_percentage, ) } for detections in predictions @@ -134,11 +134,11 @@ def run( def offset_detections( detections: sv.Detections, - offset_width: Union[int, float], - offset_height: Union[int, float], + offset_width: int, + offset_height: int, parent_id_key: str = PARENT_ID_KEY, detection_id_key: str = DETECTION_ID_KEY, - use_percentage: bool = False + use_percentage: bool = False, ) -> sv.Detections: if len(detections) == 0: return detections @@ -150,8 +150,13 @@ def offset_detections( ( max(0, x1 - int((x2 - x1) * offset_width / 200)), max(0, y1 - int((y2 - y1) * offset_height / 200)), - min(image_dimensions[i][1], x2 + int((x2 - x1) * offset_width / 200)), - min(image_dimensions[i][0], y2 + int((y2 - y1) * offset_height / 200)), + min( + image_dimensions[i][1], x2 + int((x2 - x1) * offset_width / 200) + ), + min( + image_dimensions[i][0], + y2 + int((y2 - y1) * offset_height / 200), + ), ) for i, (x1, y1, x2, y2) in enumerate(_detections.xyxy) ] diff --git a/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py b/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py index f62815361..4efd8adb8 100644 --- a/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py +++ b/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py @@ -158,3 +158,33 @@ def test_offset_detection_when_nothing_predicted() -> None: # then assert len(detections) == 0, "Expected empty detections in output" + + +def test_offset_detection_with_percentage() -> None: + # given + detections = sv.Detections( + xyxy=np.array([[100, 200, 300, 400]], dtype=np.float64), + class_id=np.array([1]), + confidence=np.array([0.5], dtype=np.float64), + data={ + "detection_id": np.array(["three"]), + "class_name": np.array(["truck"]), + "parent_id": np.array(["p3"]), + "image_dimensions": np.array([[640, 640]]), + }, + ) + + # when + result = offset_detections( + detections=detections, + offset_width=10, + offset_height=10, + use_percentage=True + ) + + # then + x1, y1, x2, y2 = result.xyxy[0] + assert x1 == 90, "Left corner should be moved by 10% of width to the left" + assert y1 == 180, "Top corner should be moved by 10% of height to the top" + assert x2 == 310, "Right corner should be moved by 10% of width to the right" + assert y2 == 420, "Bottom corner should be moved by 10% of height to the bottom" From ba018296ad3b2b8c31bc653b1fe07c1bdc1f64e0 Mon Sep 17 00:00:00 2001 From: Chandler Supple Date: Thu, 16 Jan 2025 20:16:22 -0800 Subject: [PATCH 3/6] Corrected the test values --- .../core_steps/transformations/test_detection_offset.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py b/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py index 4efd8adb8..bea6423c1 100644 --- a/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py +++ b/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py @@ -177,14 +177,14 @@ def test_offset_detection_with_percentage() -> None: # when result = offset_detections( detections=detections, - offset_width=10, - offset_height=10, + offset_width=10, # 10% of width + offset_height=10, # 10% of height use_percentage=True ) # then x1, y1, x2, y2 = result.xyxy[0] assert x1 == 90, "Left corner should be moved by 10% of width to the left" - assert y1 == 180, "Top corner should be moved by 10% of height to the top" + assert y1 == 190, "Top corner should be moved by 10% of height to the top" assert x2 == 310, "Right corner should be moved by 10% of width to the right" - assert y2 == 420, "Bottom corner should be moved by 10% of height to the bottom" + assert y2 == 410, "Bottom corner should be moved by 10% of height to the bottom" From 10f2f90f4e132fbf4afae5a39d63242fbbef067d Mon Sep 17 00:00:00 2001 From: Chandler Supple Date: Thu, 16 Jan 2025 20:20:20 -0800 Subject: [PATCH 4/6] Cleaned up the code --- .../core_steps/transformations/test_detection_offset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py b/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py index bea6423c1..c27f14ed7 100644 --- a/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py +++ b/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py @@ -177,8 +177,8 @@ def test_offset_detection_with_percentage() -> None: # when result = offset_detections( detections=detections, - offset_width=10, # 10% of width - offset_height=10, # 10% of height + offset_width=10, + offset_height=10, use_percentage=True ) From 014b733b4fb64c703d17c6b5968a87fef780b48c Mon Sep 17 00:00:00 2001 From: Chandler Supple Date: Fri, 17 Jan 2025 07:16:13 -0800 Subject: [PATCH 5/6] Fixed the padding formula --- .../transformations/detection_offset/v1.py | 13 ++++--------- .../transformations/test_detection_offset.py | 8 ++++---- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/inference/core/workflows/core_steps/transformations/detection_offset/v1.py b/inference/core/workflows/core_steps/transformations/detection_offset/v1.py index 85fa9ca10..5f7cf56e2 100644 --- a/inference/core/workflows/core_steps/transformations/detection_offset/v1.py +++ b/inference/core/workflows/core_steps/transformations/detection_offset/v1.py @@ -148,15 +148,10 @@ def offset_detections( _detections.xyxy = np.array( [ ( - max(0, x1 - int((x2 - x1) * offset_width / 200)), - max(0, y1 - int((y2 - y1) * offset_height / 200)), - min( - image_dimensions[i][1], x2 + int((x2 - x1) * offset_width / 200) - ), - min( - image_dimensions[i][0], - y2 + int((y2 - y1) * offset_height / 200), - ), + max(0, x1 - int(image_dimensions[i][1] * offset_width / 200)), + max(0, y1 - int(image_dimensions[i][0] * offset_height / 200)), + min(image_dimensions[i][1], x2 + int(image_dimensions[i][1] * offset_width / 200)), + min(image_dimensions[i][0], y2 + int(image_dimensions[i][0] * offset_height / 200)), ) for i, (x1, y1, x2, y2) in enumerate(_detections.xyxy) ] diff --git a/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py b/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py index c27f14ed7..3ad3517ad 100644 --- a/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py +++ b/tests/workflows/unit_tests/core_steps/transformations/test_detection_offset.py @@ -184,7 +184,7 @@ def test_offset_detection_with_percentage() -> None: # then x1, y1, x2, y2 = result.xyxy[0] - assert x1 == 90, "Left corner should be moved by 10% of width to the left" - assert y1 == 190, "Top corner should be moved by 10% of height to the top" - assert x2 == 310, "Right corner should be moved by 10% of width to the right" - assert y2 == 410, "Bottom corner should be moved by 10% of height to the bottom" + assert x1 == 68, "Left corner should be moved by 10% of image width to the left" + assert y1 == 168, "Top corner should be moved by 10% of image height to the top" + assert x2 == 332, "Right corner should be moved by 10% of image width to the right" + assert y2 == 432, "Bottom corner should be moved by 10% of image height to the bottom" From 2d5d055e7e9fc2f269a964f06b07cda5b68786e8 Mon Sep 17 00:00:00 2001 From: Chandler Supple Date: Fri, 17 Jan 2025 07:22:07 -0800 Subject: [PATCH 6/6] Made the linter happy --- .../core_steps/transformations/detection_offset/v1.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/inference/core/workflows/core_steps/transformations/detection_offset/v1.py b/inference/core/workflows/core_steps/transformations/detection_offset/v1.py index 5f7cf56e2..fe1a84464 100644 --- a/inference/core/workflows/core_steps/transformations/detection_offset/v1.py +++ b/inference/core/workflows/core_steps/transformations/detection_offset/v1.py @@ -150,8 +150,14 @@ def offset_detections( ( max(0, x1 - int(image_dimensions[i][1] * offset_width / 200)), max(0, y1 - int(image_dimensions[i][0] * offset_height / 200)), - min(image_dimensions[i][1], x2 + int(image_dimensions[i][1] * offset_width / 200)), - min(image_dimensions[i][0], y2 + int(image_dimensions[i][0] * offset_height / 200)), + min( + image_dimensions[i][1], + x2 + int(image_dimensions[i][1] * offset_width / 200), + ), + min( + image_dimensions[i][0], + y2 + int(image_dimensions[i][0] * offset_height / 200), + ), ) for i, (x1, y1, x2, y2) in enumerate(_detections.xyxy) ]