Skip to content
2 changes: 1 addition & 1 deletion .isort.cfg
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[settings]
known_third_party = dask,numpy,ome_zarr,omero,omero_rois,omero_zarr,pytest,setuptools,skimage,zarr
known_third_party = dask,numpy,ome_zarr,omero,omero_rois,omero_sys_ParametersI,omero_zarr,pytest,setuptools,skimage,zarr
156 changes: 137 additions & 19 deletions src/omero_zarr/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from zarr.hierarchy import open_group
from zarr.storage import FSStore

from .extinfo import external_info_str, get_extinfo, get_images, set_external_info
from .masks import (
MASK_DTYPE_SIZE,
MaskSaver,
Expand All @@ -51,6 +52,7 @@
- masks

"""

EXPORT_HELP = """Export an image in zarr format.

Using bioformats2raw
Expand Down Expand Up @@ -108,6 +110,12 @@

POLYGONS_HELP = """Export ROI Polygons on the Image or Plate in zarr format"""

EXTINFO_HELP = """Get the ExternalInfo path of an ome.zarr image.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get or set...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get, because that's the default action.


Use --set to set the external info path
or --reset in order to remove the ExternalInfo from the image.
"""


def gateway_required(func: Callable) -> Callable:
"""
Expand Down Expand Up @@ -164,7 +172,7 @@ def _configure(self, parser: Parser) -> None:
"--style",
choices=("split", "labeled"),
default="labeled",
help=("Choice of storage for ROIs [breaks ome-zarr]"),
help="Choice of storage for ROIs [breaks ome-zarr]",
)
polygons.add_argument(
"--label-path",
Expand All @@ -177,8 +185,8 @@ def _configure(self, parser: Parser) -> None:
polygons.add_argument(
"--source-image",
help=(
"Path to the multiscales group containing the source image/plate. "
"By default, use the output directory"
"Path to the multiscales group containing the source "
"image/plate. By default, use the output directory"
),
default=None,
)
Expand All @@ -191,7 +199,9 @@ def _configure(self, parser: Parser) -> None:
)
polygons.add_argument(
"--label-name",
help=("Name of the array that will be stored. Ignored for --style=split"),
help=(
"Name of the array that will be stored. Ignored for " "--style=split"
),
default="0",
)

Expand All @@ -204,8 +214,8 @@ def _configure(self, parser: Parser) -> None:
masks.add_argument(
"--source-image",
help=(
"Path to the multiscales group containing the source image/plate. "
"By default, use the output directory"
"Path to the multiscales group containing the source "
"image/plate. By default, use the output directory"
),
default=None,
)
Expand All @@ -219,14 +229,16 @@ def _configure(self, parser: Parser) -> None:
)
masks.add_argument(
"--label-name",
help=("Name of the array that will be stored. Ignored for --style=split"),
help=(
"Name of the array that will be stored. Ignored for " "--style=split"
),
default="0",
)
masks.add_argument(
"--style",
choices=("split", "labeled"),
default="labeled",
help=("Choice of storage for ROIs [breaks ome-zarr]"),
help="Choice of storage for ROIs [breaks ome-zarr]",
)
masks.add_argument(
"--label-bits",
Expand All @@ -249,13 +261,18 @@ def _configure(self, parser: Parser) -> None:
export.add_argument(
"--bf",
action="store_true",
help="Use bioformats2raw on the server to export images. Requires"
" bioformats2raw 0.3.0 or higher and access to the managed repo.",
help=(
"Use bioformats2raw on the server to export images. Requires "
"bioformats2raw 0.3.0 or higher and access to the managed "
"repo."
),
)
export.add_argument(
"--bfpath",
help="Use bioformats2raw on a local copy of a file. Requires"
" bioformats2raw 0.4.0 or higher.",
help=(
"Use bioformats2raw on a local copy of a file. Requires "
"bioformats2raw 0.4.0 or higher."
),
)
export.add_argument(
"--tile_width",
Expand All @@ -270,33 +287,82 @@ def _configure(self, parser: Parser) -> None:
export.add_argument(
"--resolutions",
default=None,
help="Number of pyramid resolutions to generate"
" (only for use with bioformats2raw)",
help=(
"Number of pyramid resolutions to generate "
"(only for use with bioformats2raw)"
),
)
export.add_argument(
"--max_workers",
default=None,
help="Maximum number of workers (only for use with bioformats2raw)",
help=("Maximum number of workers (only for " "use with bioformats2raw)"),
)
export.add_argument(
"object",
type=ProxyStringType("Image"),
help="The Image to export.",
)

extinfo = parser.add(sub, self.extinfo, EXTINFO_HELP)
extinfo.add_argument(
"object",
type=ProxyStringType(),
help="Object in Class:ID format",
)
extinfo.add_argument(
"--set",
action="store_true",
help="Set the ExternalInfo path",
)
extinfo.add_argument(
"--zarrPath",
default=None,
help=(
"Use a specific path (default: Determine from "
"clientPath) (only used in combination with --set)"
),
)
extinfo.add_argument(
"--entityType",
default="com.glencoesoftware.ngff:multiscales",
help=(
"Use a specific entityType (default: "
"com.glencoesoftware.ngff:multiscales) "
"(only used in combination with --set)"
),
)
extinfo.add_argument(
"--entityId",
default="3",
help=(
"Use a specific entityId (default: 3) "
"(only used in combination with --set)"
),
)
extinfo.add_argument(
"--reset",
action="store_true",
help="Removes the ExternalInfo",
)

for subcommand in (polygons, masks, export):
subcommand.add_argument(
"--output", type=str, default="", help="The output directory"
"--output",
type=str,
default="",
help="The output directory",
)
for subcommand in (polygons, masks):
subcommand.add_argument(
"--overlaps",
type=str,
default=MaskSaver.OVERLAPS[0],
choices=MaskSaver.OVERLAPS,
help="To allow overlapping shapes, use 'dtype_max':"
" All overlapping regions will be set to the"
" max value for the dtype",
help=(
"To allow overlapping shapes, use 'dtype_max': "
"All overlapping regions will be set to the "
"max value for the dtype"
),
)

@gateway_required
Expand Down Expand Up @@ -335,6 +401,58 @@ def export(self, args: argparse.Namespace) -> None:
plate = self._lookup(self.gateway, "Plate", args.object.id)
plate_to_zarr(plate, args)

@gateway_required
def extinfo(self, args: argparse.Namespace) -> None:
for img, well, idx in get_images(self.gateway, args.object):
img = img._obj
group_id = img.getDetails().getGroup().id.val
extinfo = get_extinfo(self.gateway, img)
if args.set:
try:
img = set_external_info(
self.gateway,
img,
well,
idx,
args.zarrPath,
args.entityType,
int(args.entityId),
)
img = self.gateway.getUpdateService().saveAndReturnObject(img)
self.ctx.out(
f"Set ExternalInfo for image ({img.id._val}) "
f"{img.name._val}:\n"
f"{external_info_str(img.details.externalInfo)}"
)
except Exception as e:
self.ctx.err(
f"Failed to set external info for image "
f"({img.id._val}) {img.name._val}: {e}"
)
elif args.reset:
if extinfo:
img.details.externalInfo = None
img = self.gateway.getUpdateService().saveAndReturnObject(img)
self.ctx.out(
f"Removed ExternalInfo from image "
f"({img.id._val}) {img.name._val}"
)
else:
self.ctx.out(
f"Image ({img.id._val}) {img.name._val} " "has no ExternalInfo"
)
else:
if extinfo:
self.ctx.out(
f"ExternalInfo for image ({img.id._val}) "
f"{img.name._val}:\n"
f"{external_info_str(extinfo)}"
)
else:
self.ctx.out(
f"Image ({img.id._val}) {img.name._val} has no " "ExternalInfo"
)

def _lookup(
self, gateway: BlitzGateway, otype: str, oid: int
) -> BlitzObjectWrapper:
Expand Down
Loading