From 2d0c1200a5fa76294a73f72cc800b1d39fdb07a6 Mon Sep 17 00:00:00 2001 From: Yu-An Chen Date: Fri, 27 Sep 2024 16:16:15 -0700 Subject: [PATCH] Add affine transformations of ROI shapes --- ezomero/_gets.py | 28 +++++++++++++++++++++------- ezomero/_posts.py | 17 ++++++++++++++++- ezomero/rois.py | 15 ++++++++++++++- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/ezomero/_gets.py b/ezomero/_gets.py index 45574e4..1a4f9e3 100644 --- a/ezomero/_gets.py +++ b/ezomero/_gets.py @@ -14,6 +14,7 @@ from omero.model import enums as omero_enums from .rois import Point, Line, Rectangle from .rois import Ellipse, Polygon, Polyline, Label, ezShape +from .rois import AffineTransform import importlib.util if (importlib.util.find_spec('pandas')): @@ -1761,33 +1762,46 @@ def _omero_shape_to_shape(omero_shape: Shape mk_end = omero_shape.markerEnd except AttributeError: mk_end = None + try: + transform = omero_shape.transform + except AttributeError: + transform = None + if transform is not None: + transform = AffineTransform( + transform.a00.val, + transform.a10.val, + transform.a01.val, + transform.a11.val, + transform.a02.val, + transform.a12.val, + ) shape: Union[Point, Line, Rectangle, Ellipse, Polygon, Polyline, Label] if shape_type == "Point": x = omero_shape.x y = omero_shape.y shape = Point(x, y, z_val, c_val, t_val, text, fill_color, - stroke_color, stroke_width) + stroke_color, stroke_width, transform=transform) elif shape_type == "Line": x1 = omero_shape.x1 x2 = omero_shape.x2 y1 = omero_shape.y1 y2 = omero_shape.y2 shape = Line(x1, y1, x2, y2, z_val, c_val, t_val, mk_start, mk_end, - text, fill_color, stroke_color, stroke_width) + text, fill_color, stroke_color, stroke_width, transform=transform) elif shape_type == "Rectangle": x = omero_shape.x y = omero_shape.y width = omero_shape.width height = omero_shape.height shape = Rectangle(x, y, width, height, z_val, c_val, t_val, text, - fill_color, stroke_color, stroke_width) + fill_color, stroke_color, stroke_width, transform=transform) elif shape_type == "Ellipse": x = omero_shape.x y = omero_shape.y radiusX = omero_shape.radiusX radiusY = omero_shape.radiusY shape = Ellipse(x, y, radiusX, radiusY, z_val, c_val, t_val, text, - fill_color, stroke_color, stroke_width) + fill_color, stroke_color, stroke_width, transform=transform) elif shape_type == "Polygon": omero_points = omero_shape.points.split() points = [] @@ -1795,7 +1809,7 @@ def _omero_shape_to_shape(omero_shape: Shape coords = point.split(',') points.append((float(coords[0]), float(coords[1]))) shape = Polygon(points, z_val, c_val, t_val, text, fill_color, - stroke_color, stroke_width) + stroke_color, stroke_width, transform=transform) elif shape_type == "Polyline": omero_points = omero_shape.points.split() points = [] @@ -1803,13 +1817,13 @@ def _omero_shape_to_shape(omero_shape: Shape coords = point.split(',') points.append((float(coords[0]), float(coords[1]))) shape = Polyline(points, z_val, c_val, t_val, text, fill_color, - stroke_color, stroke_width) + stroke_color, stroke_width, transform=transform) elif shape_type == "Label": x = omero_shape.x y = omero_shape.y fsize = omero_shape.getFontSize().getValue() shape = Label(x, y, text, fsize, z_val, c_val, t_val, fill_color, - stroke_color, stroke_width) + stroke_color, stroke_width, transform=transform) else: err = 'The shape passed for the roi is not a valid shape type' raise TypeError(err) diff --git a/ezomero/_posts.py b/ezomero/_posts.py index 8e90f5e..6299473 100644 --- a/ezomero/_posts.py +++ b/ezomero/_posts.py @@ -8,7 +8,7 @@ from omero.gateway import BlitzGateway from omero.model import RoiI, PointI, LineI, RectangleI, EllipseI from omero.model import PolygonI, PolylineI, LabelI, LengthI, enums -from omero.model import DatasetI, ProjectI, ScreenI, Shape +from omero.model import DatasetI, ProjectI, ScreenI, Shape, AffineTransformI from omero.grid import BoolColumn, LongColumn from omero.grid import StringColumn, DoubleColumn, Column from omero.gateway import ProjectWrapper, DatasetWrapper @@ -752,6 +752,17 @@ def create_columns(table: Any, return cols +def _omero_affine(a00, a10, a01, a11, a02, a12): + affine = AffineTransformI() + affine.a00 = rdouble(a00) + affine.a10 = rdouble(a10) + affine.a01 = rdouble(a01) + affine.a11 = rdouble(a11) + affine.a02 = rdouble(a02) + affine.a12 = rdouble(a12) + return affine + + def _shape_to_omero_shape(shape: Union[Point, Line, Rectangle, Ellipse, Polygon, Polyline, Label]) -> Shape: """ Helper function to convert ezomero shapes into omero shapes""" @@ -822,6 +833,10 @@ def _shape_to_omero_shape(shape: Union[Point, Line, Rectangle, Ellipse, enums.UnitsLength.PIXEL)) else: omero_shape.setStrokeWidth(LengthI(1.0, enums.UnitsLength.PIXEL)) + if shape.transform is not None: + t_ = shape.transform + omero_shape.transform = _omero_affine(t_.a00, t_.a10, t_.a01, + t_.a11, t_.a02, t_.a12) return omero_shape diff --git a/ezomero/rois.py b/ezomero/rois.py index 839588f..c38cfc4 100644 --- a/ezomero/rois.py +++ b/ezomero/rois.py @@ -8,7 +8,19 @@ "Polygon", "Polyline", "Label", - "ezShape"] + "ezShape", + "AffineTransform"] + + +@dataclass(frozen=True) +class AffineTransform(): + """A dataclss for affine transformation matrix of shapes""" + a00: float = field(default=1.0) + a10: float = field(default=0.0) + a01: float = field(default=0.0) + a11: float = field(default=1.0) + a02: float = field(default=0.0) + a12: float = field(default=0.0) @dataclass(frozen=True) @@ -20,6 +32,7 @@ class ezShape: This dataclass is frozen and should not be modified after instantiation """ + transform: Union[AffineTransform, None] = field(default=None, kw_only=True) @dataclass(frozen=True)