99from __future__ import annotations
1010
1111import importlib
12+ from typing import Literal
1213
1314import numpy as np
1415import sigima .params
1718from guidata .qthelpers import exec_dialog
1819from plotpy .widgets .resizedialog import ResizeDialog
1920from qtpy import QtWidgets as QW
20- from sigima .objects import ImageROI , ROI2DParam
21+ from sigima .objects import ImageObj , ImageROI , ROI2DParam
2122from sigima .objects .scalar import GeometryResult
2223from sigima .proc .decorator import ComputationMetadata
2324from sigima .proc .transformations import transformer
2425
26+ from datalab .adapters_metadata import GeometryAdapter
2527from datalab .config import APP_NAME , _
2628from datalab .gui .processor .base import BaseProcessor
2729from datalab .gui .profiledialog import ProfileExtractionDialog
2830from datalab .gui .roigrideditor import ImageGridROIEditor
2931from datalab .objectmodel import get_uuid
30- from datalab .utils .geometry_transforms import apply_geometry_transform
3132from datalab .utils .qthelpers import create_progress_bar , qt_try_except
3233from datalab .widgets import imagebackground
3334
3435
36+ def apply_geometry_transform (
37+ obj : ImageObj ,
38+ operation : Literal [
39+ "translate" , "scale" , "rotate90" , "rotate270" , "fliph" , "flipv" , "transpose"
40+ ],
41+ ** kwargs ,
42+ ) -> None :
43+ """Apply a geometric transformation to all geometry results in an object.
44+
45+ This uses the Sigima transformation system for proper geometric operations.
46+ For image objects, rotations are performed around the image center to match
47+ how the image data is transformed.
48+
49+ Args:
50+ obj: The object containing geometry results to transform
51+ operation: The transformation operation name
52+ **kwargs: Optional parameters for the transformation (e.g., angle for rotate)
53+ """
54+ assert operation in [
55+ "translate" ,
56+ "scale" ,
57+ "rotate90" ,
58+ "rotate270" ,
59+ "fliph" ,
60+ "flipv" ,
61+ "transpose" ,
62+ ], f"Unknown operation: { operation } "
63+ if operation == "translate" :
64+ if not kwargs or "dx" not in kwargs or "dy" not in kwargs :
65+ raise ValueError ("translate operation requires 'dx' and 'dy' parameters" )
66+ dx , dy = kwargs ["dx" ], kwargs ["dy" ]
67+ elif operation == "scale" :
68+ if not kwargs or "sx" not in kwargs or "sy" not in kwargs :
69+ raise ValueError ("scale operation requires 'sx' and 'sy' parameters" )
70+ sx , sy = kwargs ["sx" ], kwargs ["sy" ]
71+ for adapter in list (GeometryAdapter .iterate_from_obj (obj )):
72+ geometry = adapter .geometry
73+ assert geometry is not None , "Geometry should not be None"
74+ assert len (geometry .coords ) > 0 , "Geometry coordinates should not be empty"
75+ if operation == "translate" :
76+ tr_geometry = transformer .translate (geometry , dx , dy )
77+ elif operation == "scale" :
78+ tr_geometry = transformer .scale (geometry , sx , sy , (obj .xc , obj .yc ))
79+ elif operation == "rotate90" :
80+ tr_geometry = transformer .rotate (geometry , - np .pi / 2 , (obj .xc , obj .yc ))
81+ elif operation == "rotate270" :
82+ tr_geometry = transformer .rotate (geometry , np .pi / 2 , (obj .xc , obj .yc ))
83+ elif operation == "fliph" :
84+ tr_geometry = transformer .fliph (geometry , obj .xc )
85+ elif operation == "flipv" :
86+ tr_geometry = transformer .flipv (geometry , obj .yc )
87+ elif operation == "transpose" :
88+ tr_geometry = transformer .transpose (geometry )
89+ else :
90+ raise ValueError (f"Unknown operation: { operation } " )
91+
92+ # Remove the old geometry and add the transformed one
93+ adapter .remove_from (obj )
94+ tr_adapter = GeometryAdapter (tr_geometry )
95+ tr_adapter .add_to (obj )
96+
97+
3598class GeometricTransformWrapper :
3699 """Pickleable wrapper for geometric transformation functions.
37100
@@ -72,24 +135,7 @@ def __call__(self, src_obj, param=None):
72135 dst_obj = self .func (src_obj , param )
73136 else :
74137 dst_obj = self .func (src_obj )
75-
76- # Apply geometry transformation to geometry results (if any)
77- if self .operation == "rotate90" :
78- apply_geometry_transform (dst_obj , "rotate90" , None )
79- elif self .operation == "rotate270" :
80- apply_geometry_transform (dst_obj , "rotate270" , None )
81- elif self .operation == "fliph" :
82- apply_geometry_transform (dst_obj , "fliph" , None )
83- elif self .operation == "flipv" :
84- apply_geometry_transform (dst_obj , "flipv" , None )
85- elif self .operation == "transpose" :
86- # For diagonal flip/transpose
87- apply_geometry_transform (dst_obj , "transpose" , None )
88- else :
89- raise NotImplementedError (
90- f"Geometry operation '{ self .operation } ' is not implemented."
91- )
92-
138+ apply_geometry_transform (dst_obj , operation = self .operation )
93139 return dst_obj
94140
95141 def __getstate__ (self ):
@@ -786,7 +832,7 @@ def distribute_on_grid(self, param: sigima.params.GridParam | None = None) -> No
786832 return
787833 objs = self .panel .objview .get_sel_objects (include_groups = True )
788834 g_row , g_col , x0 , y0 , x0_0 , y0_0 = 0 , 0 , 0.0 , 0.0 , 0.0 , 0.0
789- delta_x0 , delta_y0 = 0.0 , 0.0
835+ dx0 , dy0 = 0.0 , 0.0
790836 with create_progress_bar (self .panel , title , max_ = len (objs )) as progress :
791837 for i_row , obj in enumerate (objs ):
792838 progress .setValue (i_row + 1 )
@@ -796,15 +842,11 @@ def distribute_on_grid(self, param: sigima.params.GridParam | None = None) -> No
796842 if i_row == 0 :
797843 x0_0 , y0_0 = x0 , y0 = obj .x0 , obj .y0
798844 else :
799- delta_x0 , delta_y0 = x0 - obj .x0 , y0 - obj .y0
800- obj .x0 += delta_x0
801- obj .y0 += delta_y0
802- apply_geometry_transform (
803- obj , "translate" , {"dx" : delta_x0 , "dy" : delta_y0 }
804- )
805- transformer .transform_roi (
806- obj , "translate" , dx = delta_x0 , dy = delta_y0
807- )
845+ dx0 , dy0 = x0 - obj .x0 , y0 - obj .y0
846+ obj .x0 += dx0
847+ obj .y0 += dy0
848+ apply_geometry_transform (obj , "translate" , dx = dx0 , dy = dy0 )
849+ transformer .transform_roi (obj , "translate" , dx = dx0 , dy = dy0 )
808850 if param .direction == "row" :
809851 # Distributing images over rows
810852 sign = np .sign (param .rows )
@@ -829,19 +871,17 @@ def distribute_on_grid(self, param: sigima.params.GridParam | None = None) -> No
829871 def reset_positions (self ) -> None :
830872 """Reset image positions"""
831873 x0_0 , y0_0 = 0.0 , 0.0
832- delta_x0 , delta_y0 = 0.0 , 0.0
874+ dx0 , dy0 = 0.0 , 0.0
833875 objs = self .panel .objview .get_sel_objects (include_groups = True )
834876 for i_row , obj in enumerate (objs ):
835877 if i_row == 0 :
836878 x0_0 , y0_0 = obj .x0 , obj .y0
837879 else :
838- delta_x0 , delta_y0 = x0_0 - obj .x0 , y0_0 - obj .y0
839- obj .x0 += delta_x0
840- obj .y0 += delta_y0
841- apply_geometry_transform (
842- obj , "translate" , {"dx" : delta_x0 , "dy" : delta_y0 }
843- )
844- transformer .transform_roi (obj , "translate" , dx = delta_x0 , dy = delta_y0 )
880+ dx0 , dy0 = x0_0 - obj .x0 , y0_0 - obj .y0
881+ obj .x0 += dx0
882+ obj .y0 += dy0
883+ apply_geometry_transform (obj , "translate" , dx = dx0 , dy = dy0 )
884+ transformer .transform_roi (obj , "translate" , dx = dx0 , dy = dy0 )
845885 self .panel .refresh_plot ("selected" , True , False )
846886
847887 # ------Image Processing
0 commit comments