From 5966edd05a11576358b8290c56a83eb0052f7565 Mon Sep 17 00:00:00 2001 From: William Moore Date: Thu, 7 Jul 2022 14:36:51 +0100 Subject: [PATCH 1/3] Support export of Fileset:ID --- src/omero_zarr/cli.py | 6 ++++- src/omero_zarr/raw_pixels.py | 43 +++++++++++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/omero_zarr/cli.py b/src/omero_zarr/cli.py index a94500f..1302902 100644 --- a/src/omero_zarr/cli.py +++ b/src/omero_zarr/cli.py @@ -7,7 +7,7 @@ from omero.cli import CLI, BaseControl, Parser, ProxyStringType from omero.gateway import BlitzGateway, BlitzObjectWrapper -from omero.model import ImageI, PlateI +from omero.model import FilesetI, ImageI, PlateI from zarr.hierarchy import open_group from zarr.storage import FSStore @@ -20,6 +20,7 @@ from .raw_pixels import ( add_omero_metadata, add_toplevel_metadata, + fileset_to_zarr, image_to_zarr, plate_to_zarr, ) @@ -323,6 +324,9 @@ def export(self, args: argparse.Namespace) -> None: elif isinstance(args.object, PlateI): plate = self._lookup(self.gateway, "Plate", args.object.id) plate_to_zarr(plate, args) + elif isinstance(args.object, FilesetI): + fileset = self._lookup(self.gateway, "Fileset", args.object.id) + fileset_to_zarr(fileset, args) def _lookup( self, gateway: BlitzGateway, otype: str, oid: int diff --git a/src/omero_zarr/raw_pixels.py b/src/omero_zarr/raw_pixels.py index b1bcc7b..0bb9589 100644 --- a/src/omero_zarr/raw_pixels.py +++ b/src/omero_zarr/raw_pixels.py @@ -21,11 +21,48 @@ from .util import marshal_axes, marshal_transformations, open_store, print_status -def image_to_zarr(image: omero.gateway.ImageWrapper, args: argparse.Namespace) -> None: +def fileset_to_zarr( + fileset: omero.gateway.FilesetWrapper, args: argparse.Namespace +) -> None: + """Exports a FileSet according to bioformats2raw.layout""" + images = list(fileset.copyImages()) + images_to_zarr(images, args) + + +def images_to_zarr( + images: List[omero.gateway.ImageWrapper], + args: argparse.Namespace, + target_dir: str = None, +) -> None: + """Exports images in bioformats2raw.layout, each under path '0', '1' etc.""" + if target_dir is None: + target_dir = args.output if args.output else "zarr_export" + dir_index = 1 + dir_name = target_dir + while os.path.exists(dir_name): + dir_name = f"{target_dir}({dir_index})" + dir_index += 1 + # TODO: generate OME/METADATA.xml + print("dir_name", dir_name) + for index, image in enumerate(images): + print("index, image", index, image) + target = os.path.join(dir_name, str(index)) + image_to_zarr(image, args, target) + + +def image_to_zarr( + image: omero.gateway.ImageWrapper, args: argparse.Namespace, target: str = None +) -> None: + """ + Export an image to zarr + @param target: Optional path/to/image.zarr. Default is args.output/ID.zarr + """ target_dir = args.output cache_dir = target_dir if args.cache_numpy else None - - name = os.path.join(target_dir, "%s.zarr" % image.id) + if target is None: + name = os.path.join(target_dir, "%s.zarr" % image.id) + else: + name = target print(f"Exporting to {name} ({VERSION})") store = open_store(name) root = open_group(store) From 949221886424561f4ea17f80676ce0f0c86acf18 Mon Sep 17 00:00:00 2001 From: William Moore Date: Thu, 7 Jul 2022 15:31:38 +0100 Subject: [PATCH 2/3] Export .zattrs and OME/METADATA.ome.xml --- src/omero_zarr/raw_pixels.py | 31 +++++++++++++++--- src/omero_zarr/util.py | 62 ++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/omero_zarr/raw_pixels.py b/src/omero_zarr/raw_pixels.py index 0bb9589..4d98e6d 100644 --- a/src/omero_zarr/raw_pixels.py +++ b/src/omero_zarr/raw_pixels.py @@ -18,7 +18,13 @@ from . import __version__ from . import ngff_version as VERSION -from .util import marshal_axes, marshal_transformations, open_store, print_status +from .util import ( + get_minimum_image_ome_xml, + marshal_axes, + marshal_transformations, + open_store, + print_status, +) def fileset_to_zarr( @@ -42,10 +48,27 @@ def images_to_zarr( while os.path.exists(dir_name): dir_name = f"{target_dir}({dir_index})" dir_index += 1 - # TODO: generate OME/METADATA.xml - print("dir_name", dir_name) + os.mkdir(dir_name) + + # write bioformats2raw.layout + zattrs = os.path.join(dir_name, ".zattrs") + with open(zattrs, "w") as zfile: + zfile.write( + """{ + "bioformats2raw.layout" : 3 +}""" + ) + # write OME/METADATA.ome.xml + ome_dir = os.path.join(dir_name, "OME") + xml_path = os.path.join(ome_dir, "METADATA.ome.xml") + os.mkdir(ome_dir) + images = list(images) # in case it's a generator + ome_xml = get_minimum_image_ome_xml(images) + with open(xml_path, "w") as xml_file: + xml_file.write(ome_xml) + + # write images for index, image in enumerate(images): - print("index, image", index, image) target = os.path.join(dir_name, str(index)) image_to_zarr(image, args, target) diff --git a/src/omero_zarr/util.py b/src/omero_zarr/util.py index d4d00cb..e66faf1 100644 --- a/src/omero_zarr/util.py +++ b/src/omero_zarr/util.py @@ -2,6 +2,16 @@ from typing import Dict, List from omero.gateway import ImageWrapper +from omero.model.enums import ( + PixelsTypedouble, + PixelsTypefloat, + PixelsTypeint8, + PixelsTypeint16, + PixelsTypeint32, + PixelsTypeuint8, + PixelsTypeuint16, + PixelsTypeuint32, +) from zarr.storage import FSStore @@ -112,3 +122,55 @@ def marshal_transformations( zooms["y"] = zooms["y"] * multiscales_zoom return transformations + + +def get_minimum_image_ome_xml(images: List[ImageWrapper]) -> str: + """Generates minimal OME.xml for""" + + omero_pix_types = { + PixelsTypeint8: "int8", + PixelsTypeuint8: "uint8", + PixelsTypeint16: "int16", + PixelsTypeuint16: "uint16", + PixelsTypeint32: "int32", + PixelsTypeuint32: "uint32", + PixelsTypefloat: "float32", + PixelsTypedouble: "double", + } + + images_xml = [] + for image in images: + image_id = image.id + name = image.name + pixels_id = image.getPixelsId() + pixels_type = image.getPrimaryPixels().getPixelsType().getValue() + p_type = omero_pix_types[pixels_type] + size_x = image.getSizeX() + size_y = image.getSizeY() + size_z = image.getSizeZ() + size_c = image.getSizeC() + size_t = image.getSizeT() + images_xml.append( + f""" + + + + """ + ) + + img_xml = "\n".join(images_xml) + return f""" + + {img_xml} +""" From 553b35f498764274f8dc882fb54c7e0a30fb6737 Mon Sep 17 00:00:00 2001 From: William Moore Date: Tue, 2 Aug 2022 10:46:53 +0100 Subject: [PATCH 3/3] Use ome-types for OME.xml generation --- .isort.cfg | 2 +- setup.py | 2 +- src/omero_zarr/util.py | 72 ++++++++++++------------------------------ 3 files changed, 22 insertions(+), 54 deletions(-) diff --git a/.isort.cfg b/.isort.cfg index 0e8f21a..10ca857 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -1,2 +1,2 @@ [settings] -known_third_party = numpy,ome_zarr,omero,omero_zarr,setuptools,skimage,zarr +known_third_party = numpy,ome_types,ome_zarr,omero,omero_zarr,setuptools,skimage,zarr diff --git a/setup.py b/setup.py index 47f6352..47888e5 100755 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ def get_long_description() -> str: author="The Open Microscopy Team", author_email="", python_requires=">=3", - install_requires=["omero-py>=5.6.0", "ome-zarr>=0.5.0"], + install_requires=["omero-py>=5.6.0", "ome-zarr>=0.5.0", "ome-types"], long_description=long_description, keywords=["OMERO.CLI", "plugin"], url="https://github.com/ome/omero-cli-zarr/", diff --git a/src/omero_zarr/util.py b/src/omero_zarr/util.py index e66faf1..7492c62 100644 --- a/src/omero_zarr/util.py +++ b/src/omero_zarr/util.py @@ -1,17 +1,10 @@ import time from typing import Dict, List +from ome_types import to_xml +from ome_types.model import OME, Image, Pixels +from ome_types.model.simple_types import ImageID, PixelsID, PixelType from omero.gateway import ImageWrapper -from omero.model.enums import ( - PixelsTypedouble, - PixelsTypefloat, - PixelsTypeint8, - PixelsTypeint16, - PixelsTypeint32, - PixelsTypeuint8, - PixelsTypeuint16, - PixelsTypeuint32, -) from zarr.storage import FSStore @@ -127,50 +120,25 @@ def marshal_transformations( def get_minimum_image_ome_xml(images: List[ImageWrapper]) -> str: """Generates minimal OME.xml for""" - omero_pix_types = { - PixelsTypeint8: "int8", - PixelsTypeuint8: "uint8", - PixelsTypeint16: "int16", - PixelsTypeuint16: "uint16", - PixelsTypeint32: "int32", - PixelsTypeuint32: "uint32", - PixelsTypefloat: "float32", - PixelsTypedouble: "double", - } - - images_xml = [] + ome = OME() + for image in images: - image_id = image.id - name = image.name pixels_id = image.getPixelsId() + pix = image.getPrimaryPixels() pixels_type = image.getPrimaryPixels().getPixelsType().getValue() - p_type = omero_pix_types[pixels_type] - size_x = image.getSizeX() - size_y = image.getSizeY() - size_z = image.getSizeZ() - size_c = image.getSizeC() - size_t = image.getSizeT() - images_xml.append( - f""" - - - - """ + ptype = PixelType(pixels_type) + pixels = Pixels( + id=PixelsID("Pixels:%s" % pixels_id), + dimension_order=pix.getDimensionOrder().getValue(), + size_c=image.getSizeC(), + size_t=image.getSizeT(), + size_z=image.getSizeZ(), + size_x=image.getSizeX(), + size_y=image.getSizeY(), + type=ptype, + metadata_only=True, ) + img = Image(id=ImageID("Image:%s" % image.id), pixels=pixels, name=image.name) + ome.images.append(img) - img_xml = "\n".join(images_xml) - return f""" - - {img_xml} -""" + return to_xml(ome)