diff --git a/src/omero_zarr/zarr_import.py b/src/omero_zarr/zarr_import.py index b02aadc..05bfe74 100644 --- a/src/omero_zarr/zarr_import.py +++ b/src/omero_zarr/zarr_import.py @@ -24,8 +24,8 @@ import omero import zarr from numpy import finfo, iinfo -from omero.gateway import BlitzGateway, ImageWrapper -from omero.model import ExternalInfoI +from omero.gateway import BlitzGateway, ImageWrapper, PixelsWrapper +from omero.model import ExternalInfoI, LengthI from omero.model.enums import ( PixelsTypecomplex, PixelsTypedouble, @@ -36,6 +36,7 @@ PixelsTypeuint8, PixelsTypeuint16, PixelsTypeuint32, + UnitsLength, ) from omero.rtypes import rbool, rdouble, rint, rlong, rstring from zarr.core import Array @@ -69,6 +70,8 @@ "complex64": PixelsTypecomplex, } +UL = sorted(UnitsLength._enumerators.values()) + def get_omexml_bytes(store: zarr.storage.Store) -> Optional[bytes]: # Zarr v3 get() is async. Need to sync to get the bytes @@ -134,8 +137,48 @@ def parse_image_metadata( else: sizes[axis["name"]] = size + pixel_size = {} + transforms = multiscale_attrs["datasets"][0]["coordinateTransformations"] + for transform in transforms: + if transform["type"] == "scale": + scale = transform["scale"] + pixel_size = { + axis["name"]: (pixel_size, axis.get("unit", "")) + for axis, pixel_size in zip(axes, scale) + if axis["name"] in "xyz" + } + break + pixels_type = array_data.dtype.name - return sizes, pixels_type + return sizes, pixels_type, pixel_size + + +def get_unit_length(value: str) -> Optional[UnitsLength]: + for unit in UL: + if unit.name.lower() == value: + return unit + return None + + +def create_length(value_unit: Array) -> omero.model.LengthI: + if len(value_unit) > 1 and value_unit[1]: + try: + unit = get_unit_length(value_unit[1]) + if unit is None: + return LengthI(value_unit[0], UnitsLength.PIXEL) + return LengthI(value_unit[0], unit) + except TypeError: + pass + return LengthI(value_unit[0], UnitsLength.PIXEL) + + +def set_pixel_size(pixels: PixelsWrapper, pixel_size: dict) -> None: + if "x" in pixel_size: + pixels.setPhysicalSizeX(create_length(pixel_size["x"])) + if "y" in pixel_size: + pixels.setPhysicalSizeY(create_length(pixel_size["y"])) + if "z" in pixel_size: + pixels.setPhysicalSizeZ(create_length(pixel_size["z"])) def create_image( @@ -153,7 +196,9 @@ def create_image( """ query_service = conn.getQueryService() pixels_service = conn.getPixelsService() - sizes, pixels_type = parse_image_metadata(store, image_attrs, image_path) + sizes, pixels_type, pixel_size = parse_image_metadata( + store, image_attrs, image_path + ) size_t = sizes.get("t", 1) size_z = sizes.get("z", 1) size_x = sizes.get("x", 1) @@ -184,6 +229,9 @@ def create_image( ) img_obj = image._obj + + set_pixel_size(image.getPrimaryPixels(), pixel_size) + set_external_info(img_obj, kwargs, image_path) return img_obj, rnd_def @@ -544,7 +592,7 @@ def import_zarr( image_path = str(series) image_attrs = load_attrs(store, image_path) # pixels_type is only used if we have *incomplete* `omero` metadata - sizes, pixels_type = parse_image_metadata( + sizes, pixels_type, pixel_size = parse_image_metadata( store, image_attrs, image_path ) rnd_def = set_rendering_settings( @@ -552,6 +600,8 @@ def import_zarr( ) if rnd_def is not None: conn.getUpdateService().saveAndReturnObject(rnd_def) + + set_pixel_size(image.getPrimaryPixels(), pixel_size) set_external_info(image._obj, kwargs, image_path=image_path) # default name is METADATA.ome.xml [series], based on clientPath? new_name = image.name.replace("METADATA.ome.xml", zarr_name) diff --git a/test/integration/clitest/test_import.py b/test/integration/clitest/test_import.py index b58dc28..625b2f0 100644 --- a/test/integration/clitest/test_import.py +++ b/test/integration/clitest/test_import.py @@ -31,6 +31,7 @@ "6001240.zarr": { "url": "https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0062A/6001240.zarr", "dataset_name": "Test Import 6001240", + "pixel_sizes_x": [0.36], }, "13457227.zarr": { "url": "https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0101A/13457227.zarr",