From 6842cee3371b58808461c37d878d54082e8a2f32 Mon Sep 17 00:00:00 2001 From: Grzegorz Klimaszewski <166530809+grzegorz-roboflow@users.noreply.github.com> Date: Wed, 31 Jul 2024 13:09:40 +0200 Subject: [PATCH 1/5] Allow zone defined as input parameter to be passed to perspective_correction --- .../core_steps/transformations/perspective_correction.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/inference/core/workflows/core_steps/transformations/perspective_correction.py b/inference/core/workflows/core_steps/transformations/perspective_correction.py index 1edd5706a..948f563ac 100644 --- a/inference/core/workflows/core_steps/transformations/perspective_correction.py +++ b/inference/core/workflows/core_steps/transformations/perspective_correction.py @@ -72,7 +72,7 @@ class PerspectiveCorrectionManifest(WorkflowBlockManifest): examples=["$inputs.image", "$steps.cropping.crops"], validation_alias=AliasChoices("images", "image"), ) - perspective_polygons: Union[list, StepOutputSelector(kind=[LIST_OF_VALUES_KIND])] = Field( # type: ignore + perspective_polygons: Union[list, StepOutputSelector(kind=[LIST_OF_VALUES_KIND]), WorkflowParameterSelector(kind=[LIST_OF_VALUES_KIND])] = Field( # type: ignore description="Perspective polygons (for each batch at least one must be consisting of 4 vertices)", ) transformed_rect_width: Union[int, WorkflowParameterSelector(kind=[INTEGER_KIND])] = Field( # type: ignore @@ -127,6 +127,11 @@ def pick_largest_perspective_polygons( raise ValueError("Unexpected type of input") if not perspective_polygons_batch: raise ValueError("Unexpected empty batch") + if len(perspective_polygons_batch) == 4 and all( + isinstance(p, list) and len(p) == 2 for p in perspective_polygons_batch + ): + perspective_polygons_batch = [perspective_polygons_batch] + largest_perspective_polygons: List[np.ndarray] = [] for polygons in perspective_polygons_batch: if polygons is None: @@ -334,7 +339,7 @@ async def run( self, images: Batch[WorkflowImageData], predictions: Batch[sv.Detections], - perspective_polygons: Batch[List[np.ndarray]], + perspective_polygons: List[List[int]], transformed_rect_width: int, transformed_rect_height: int, extend_perspective_polygon_by_detections_anchor: Optional[str], From be04eecd00c15eb8d534125b5755b5168da8a94a Mon Sep 17 00:00:00 2001 From: Grzegorz Klimaszewski <166530809+grzegorz-roboflow@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:04:22 +0200 Subject: [PATCH 2/5] Fail if perspective polygons do not match predictions batch size --- .../transformations/perspective_correction.py | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/inference/core/workflows/core_steps/transformations/perspective_correction.py b/inference/core/workflows/core_steps/transformations/perspective_correction.py index 948f563ac..127236d1e 100644 --- a/inference/core/workflows/core_steps/transformations/perspective_correction.py +++ b/inference/core/workflows/core_steps/transformations/perspective_correction.py @@ -339,20 +339,31 @@ async def run( self, images: Batch[WorkflowImageData], predictions: Batch[sv.Detections], - perspective_polygons: List[List[int]], + perspective_polygons: Union[ + List[np.ndarray], + List[List[np.ndarray]], + List[List[List[int]]], + List[List[List[List[int]]]], + ], transformed_rect_width: int, transformed_rect_height: int, extend_perspective_polygon_by_detections_anchor: Optional[str], warp_image: Optional[bool], ) -> BlockResult: if not self.perspective_transformers: - try: - largest_perspective_polygons = pick_largest_perspective_polygons( - perspective_polygons + largest_perspective_polygons = pick_largest_perspective_polygons( + perspective_polygons + ) + + if len(largest_perspective_polygons) == 1 and len(predictions) > 1: + largest_perspective_polygons = largest_perspective_polygons * len( + predictions + ) + + if len(largest_perspective_polygons) != len(predictions): + raise ValueError( + f"Predictions batch size ({len(predictions)}) does not match number of perspective polygons ({largest_perspective_polygons})" ) - except ValueError as exc: - logger.error(exc) - largest_perspective_polygons = [None for _ in perspective_polygons] for polygon, detections in zip(largest_perspective_polygons, predictions): if polygon is None: From 96ed5e09d2d428811298aee9c89d8d927b86b799 Mon Sep 17 00:00:00 2001 From: Grzegorz Klimaszewski <166530809+grzegorz-roboflow@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:04:30 +0200 Subject: [PATCH 3/5] No need to grab detection index since detection is always 1-element sv.Detections object --- .../core_steps/transformations/perspective_correction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inference/core/workflows/core_steps/transformations/perspective_correction.py b/inference/core/workflows/core_steps/transformations/perspective_correction.py index 127236d1e..f2c22948b 100644 --- a/inference/core/workflows/core_steps/transformations/perspective_correction.py +++ b/inference/core/workflows/core_steps/transformations/perspective_correction.py @@ -296,7 +296,7 @@ def correct_detections( ] ) else: - xmin, ymin, xmax, ymax = np.around(detection[i].xyxy[0]).tolist() + xmin, ymin, xmax, ymax = np.around(detection.xyxy[0]).tolist() polygon = np.array( [[[xmin, ymin], [xmax, ymin], [xmax, ymax], [xmin, ymax]]], dtype=np.float32, From 01119e83dfd4a2129636af52d509925c0655c2d1 Mon Sep 17 00:00:00 2001 From: Grzegorz Klimaszewski <166530809+grzegorz-roboflow@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:49:41 +0200 Subject: [PATCH 4/5] Make predictions optional in perspective_converter block --- .../transformations/perspective_correction.py | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/inference/core/workflows/core_steps/transformations/perspective_correction.py b/inference/core/workflows/core_steps/transformations/perspective_correction.py index f2c22948b..52b77cf92 100644 --- a/inference/core/workflows/core_steps/transformations/perspective_correction.py +++ b/inference/core/workflows/core_steps/transformations/perspective_correction.py @@ -1,10 +1,10 @@ import math -from typing import List, Literal, Optional, Type, Union import cv2 as cv import numpy as np import supervision as sv from pydantic import AliasChoices, ConfigDict, Field +from typing_extensions import List, Literal, Optional, Type, Union from inference.core.logger import logger from inference.core.workflows.constants import KEYPOINTS_XY_KEY_IN_SV_DETECTIONS @@ -57,13 +57,16 @@ class PerspectiveCorrectionManifest(WorkflowBlockManifest): } ) type: Literal[f"{TYPE}"] - predictions: StepOutputSelector( - kind=[ - BATCH_OF_OBJECT_DETECTION_PREDICTION_KIND, - BATCH_OF_INSTANCE_SEGMENTATION_PREDICTION_KIND, - ] - ) = Field( # type: ignore + predictions: Optional[ + StepOutputSelector( + kind=[ + BATCH_OF_OBJECT_DETECTION_PREDICTION_KIND, + BATCH_OF_INSTANCE_SEGMENTATION_PREDICTION_KIND, + ] + ) + ] = Field( # type: ignore description="Predictions", + default=None, examples=["$steps.object_detection_model.predictions"], ) images: Union[WorkflowImageSelector, StepOutputImageSelector] = Field( @@ -228,9 +231,9 @@ def extend_perspective_polygon( def generate_transformation_matrix( src_polygon: np.ndarray, - detections: sv.Detections, transformed_rect_width: int, transformed_rect_height: int, + detections: Optional[sv.Detections] = None, detections_anchor: Optional[sv.Position] = None, ) -> np.ndarray: polygon_with_vertices_clockwise = sort_polygon_vertices_clockwise( @@ -239,7 +242,7 @@ def generate_transformation_matrix( src_polygon = roll_polygon_vertices_to_start_from_leftmost_bottom( polygon=polygon_with_vertices_clockwise ) - if detections_anchor: + if detections and detections_anchor: src_polygon = extend_perspective_polygon( polygon=src_polygon, detections=detections, @@ -338,7 +341,7 @@ def get_manifest(cls) -> Type[WorkflowBlockManifest]: async def run( self, images: Batch[WorkflowImageData], - predictions: Batch[sv.Detections], + predictions: Optional[Batch[sv.Detections]], perspective_polygons: Union[ List[np.ndarray], List[List[np.ndarray]], @@ -350,21 +353,30 @@ async def run( extend_perspective_polygon_by_detections_anchor: Optional[str], warp_image: Optional[bool], ) -> BlockResult: + if not predictions and not images: + raise ValueError( + "Either predictions or images are required to apply perspective correction." + ) + if warp_image and not images: + raise ValueError( + "images are required to warp image into requested perspective." + ) + if not predictions: + predictions = [None] * len(images) + if not self.perspective_transformers: largest_perspective_polygons = pick_largest_perspective_polygons( perspective_polygons ) - if len(largest_perspective_polygons) == 1 and len(predictions) > 1: - largest_perspective_polygons = largest_perspective_polygons * len( - predictions - ) + batch_size = len(predictions) if predictions else len(images) + if len(largest_perspective_polygons) == 1 and batch_size > 1: + largest_perspective_polygons = largest_perspective_polygons * batch_size - if len(largest_perspective_polygons) != len(predictions): + if len(largest_perspective_polygons) != batch_size: raise ValueError( - f"Predictions batch size ({len(predictions)}) does not match number of perspective polygons ({largest_perspective_polygons})" + f"Predictions batch size ({batch_size}) does not match number of perspective polygons ({largest_perspective_polygons})" ) - for polygon, detections in zip(largest_perspective_polygons, predictions): if polygon is None: self.perspective_transformers.append(None) @@ -392,7 +404,7 @@ async def run( dsize=(transformed_rect_width, transformed_rect_height), ) - if detections is None or perspective_transformer is None: + if detections is None: result.append( {OUTPUT_DETECTIONS_KEY: None, OUTPUT_IMAGE_KEY: result_image} ) From 4ef7f600e88905f2103a24034cf0da90203133d2 Mon Sep 17 00:00:00 2001 From: Grzegorz Klimaszewski <166530809+grzegorz-roboflow@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:18:24 +0200 Subject: [PATCH 5/5] set version at 0.15.3 --- inference/core/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inference/core/version.py b/inference/core/version.py index b5111dbc4..9470a5c7b 100644 --- a/inference/core/version.py +++ b/inference/core/version.py @@ -1,4 +1,4 @@ -__version__ = "0.15.2" +__version__ = "0.15.3" if __name__ == "__main__":