OME-ZARR conversion plugin
Write any array or image file to a pyramidal OME-ZARR store, add resolution
levels to an existing store, or store a label image inside an OME-ZARR under
the NGFF labels/ group. Uses only the core dependencies for arrays and
.zarr inputs; reading other file formats needs the optional bioio extra
(pip install "patchworks[bioio]").
Pyramids downsample X and Y only — Z (and channel/time) are kept at full
resolution, matching anisotropic microscopy stacks.
to_ome_zarr
patchworks.plugins.ome_zarr.to_ome_zarr(source: Union[da.Array, np.ndarray, str, Path], out_path: Union[str, Path], *, axes: Union[str, None] = None, pixel_size: Union[PixelSize, tuple, None] = None, scene: int = 0, sequence_pattern: Union[str, None] = None, n_levels: int = 5, downscale: int = 2, chunks: Union[tuple[int, ...], None] = None, shard: ShardSpec = False, reuse_pyramid: bool = False, progress: bool = True, overwrite: bool = False, ngff_version: Union[str, None] = 'auto', compression: Union[str, None] = None, downsample: str = 'mean') -> str
Write source as a pyramidal, calibrated OME-ZARR store.
source may be a dask/NumPy array, a .zarr store, an Imaris .ims
file, any image format readable by bioio (CZI, LIF, ND2, OME-TIFF, …), or
(with sequence_pattern set) a glob over a folder of single-plane TIFFs.
File inputs are read lazily; the pyramid is built level-by-level from disk
with bounded chunks, so the full volume never needs to fit in RAM. Only
x/y are downsampled; z (and channel/time) stay full-resolution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
(Array, ndarray, str or Path)
|
Array or path to convert. |
required |
out_path
|
str or Path
|
Destination |
required |
axes
|
str
|
One character per array dimension, e.g. |
None
|
pixel_size
|
(dict, tuple or None)
|
Physical voxel size in micrometers, as |
None
|
scene
|
int
|
Scene index for multi-scene bioio files. |
0
|
sequence_pattern
|
str
|
When given, source is treated as a glob pattern over a folder of
single-plane TIFFs (e.g. |
None
|
n_levels
|
int
|
Maximum number of pyramid levels including full resolution. |
5
|
downscale
|
int
|
Per-level X/Y downsampling factor (default 2). |
2
|
chunks
|
tuple of int
|
Chunk shape for the written levels. |
None
|
shard
|
bool or tuple of int
|
Pack many chunks into one shard file (zarr v3), cutting the file count
~100× on huge arrays. |
False
|
progress
|
bool
|
Show a per-level dask progress bar (default |
True
|
reuse_pyramid
|
bool
|
Imaris |
False
|
overwrite
|
bool
|
Overwrite an existing store at out_path. |
False
|
ngff_version
|
str or None
|
NGFF version (and therefore zarr format) to write: |
'auto'
|
downsample
|
('mean', 'mode', 'nearest')
|
How the pyramid's X/Y levels are made from the one above: |
"mean"
|
compression
|
str
|
Codec for the arrays written ( |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The path to the written store ( |
Examples:
>>> from patchworks.plugins.ome_zarr import to_ome_zarr
>>> to_ome_zarr("scan.ims", "scan.zarr", n_levels=4)
'scan.zarr'
>>> to_ome_zarr(
... "ZT18_Male4_Left/*.tif",
... "ZT18_Male4_Left.zarr",
... sequence_pattern=r"_T(?P<T>\d+)_Z(?P<Z>\d+)_C(?P<C>\d+)_V\d+",
... shard=True,
... )
'ZT18_Male4_Left.zarr'
Source code in src/patchworks/plugins/ome_zarr.py
2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 | |
add_pyramid
patchworks.plugins.ome_zarr.add_pyramid(group_path: Union[str, Path], *, base: str = '0', axes: Union[str, None] = None, pixel_size: Union[PixelSize, tuple, None] = None, n_levels: int = 5, downscale: int = 2, chunks: Union[tuple[int, ...], None] = None, shard: ShardSpec = False, progress: bool = True, ngff_version: Union[str, None] = 'auto', compression: Union[str, None] = None, downsample: Union[str, None] = None, translation: Union[PixelSize, None] = None) -> str
Add downsampled pyramid levels to an existing single-resolution zarr.
Reads the full-resolution array already at group_path/base, writes the
missing levels next to it (lazily, from disk), and (re)writes the NGFF
multiscales metadata. Existing calibration is preserved; pass
pixel_size to set it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
group_path
|
str or Path
|
Zarr group containing the full-resolution array at base. |
required |
base
|
str
|
Component name of the existing full-resolution level (default
|
'0'
|
axes
|
str
|
One letter per axis, e.g. |
None
|
pixel_size
|
(dict, tuple or None)
|
Physical voxel size in micrometers. |
None
|
n_levels
|
int
|
Maximum number of levels including the existing full-resolution one (default 5). |
5
|
downscale
|
int
|
Per-level X/Y downsampling factor (default 2). |
2
|
chunks
|
tuple of int
|
Chunk shape for the written levels. |
None
|
shard
|
bool or tuple of int
|
Sharding request (see :func: |
False
|
progress
|
bool
|
Show a per-level dask progress bar (default |
True
|
ngff_version
|
str or None
|
NGFF version (and therefore zarr format) to write: |
'auto'
|
downsample
|
('mean', 'mode', 'nearest')
|
How each level is made from the one above (see :func: |
"mean"
|
translation
|
dict
|
Physical offset |
None
|
compression
|
str
|
Codec for the arrays written ( |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The path to the updated group. |
Examples:
Source code in src/patchworks/plugins/ome_zarr.py
2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 | |
write_labels
patchworks.plugins.ome_zarr.write_labels(image_store: Union[str, Path], labels: Union[da.Array, np.ndarray], *, name: str = 'labels', axes: Union[str, None] = None, pixel_size: Union[PixelSize, tuple, None] = None, n_levels: Union[int, None] = None, downscale: int = 2, chunks: Union[tuple[int, ...], None] = None, shard: ShardSpec = False, progress: bool = True, overwrite: bool = False, n_objects: Union[int, None] = None, ngff_version: Union[str, None] = 'auto', compression: Union[str, None] = None, level: int = 0, provenance: Union[dict, None] = None) -> str
Store labels inside image_store under the NGFF labels/ group.
The labels are written as their own multi-scale pyramid at
image_store/labels/<name>/ and registered in
image_store/labels/.zattrs, so the image and its segmentation live in a
single OME-ZARR store. Calibration is inherited from the parent image
unless pixel_size is given.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image_store
|
str or Path
|
OME-ZARR store this label image belongs to. |
required |
labels
|
Array or ndarray
|
Integer label array (0 = background), same spatial shape as the image. |
required |
name
|
str
|
Label image name under |
'labels'
|
axes
|
str
|
One letter per axis. |
None
|
pixel_size
|
(dict, tuple or None)
|
Physical voxel size in micrometers. |
None
|
n_levels
|
int
|
Pyramid levels when the image has no pyramid to follow (default 5); otherwise exactly as many as the image, as the OME-Zarr spec requires of a label image. |
None
|
downscale
|
int
|
Per-level X/Y downsampling factor (default 2). |
2
|
chunks
|
tuple of int
|
Chunk shape for the written levels. |
None
|
shard
|
bool or tuple of int
|
Sharding request (see :func: |
False
|
progress
|
bool
|
Show a per-level dask progress bar (default |
True
|
overwrite
|
bool
|
Replace an existing label image of the same name (default
|
False
|
n_objects
|
int or None
|
Exact non-background object count, if known — forwarded to
:func: |
None
|
level
|
int
|
The image pyramid level labels were segmented at (default 0);
their calibration and offset are taken from that level. See
:func: |
0
|
ngff_version
|
str or None
|
NGFF version (and therefore zarr format) to write: |
'auto'
|
compression
|
str
|
Codec for the arrays written ( |
None
|
provenance
|
dict
|
How the labels were made (see :func: |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Path to the written label group ( |
Examples:
>>> from patchworks import merge_tile_labels
>>> merged, n = merge_tile_labels(
... "stage.zarr",
... input_component="staged",
... write_to="merged.zarr",
... sequential_labels=True,
... return_count=True,
... )
>>> write_labels(
... "scan.zarr", merged, name="cells", n_objects=n
... )
'scan.zarr/labels/cells'
Source code in src/patchworks/plugins/ome_zarr.py
3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 | |
register_labels
patchworks.plugins.ome_zarr.register_labels(image_store: Union[str, Path], name: str = 'labels', *, axes: Union[str, None] = None, pixel_size: Union[PixelSize, tuple, None] = None, n_levels: Union[int, None] = None, downscale: int = 2, chunks: Union[tuple[int, ...], None] = None, shard: ShardSpec = False, progress: bool = True, n_objects: Union[int, None] = None, ngff_version: Union[str, None] = 'auto', compression: Union[str, None] = None, level: int = 0, provenance: Union[dict, None] = None) -> str
Pyramidalise and register an existing labels/<name>/0 base level.
Assumes the full-resolution label array already exists at
image_store/labels/<name>/0. Adds the downsampled levels, tags the
group with NGFF image-label metadata, lists name in
labels/.zattrs, and inherits the parent image's pixel calibration
(unless pixel_size is given).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image_store
|
str or Path
|
OME-ZARR store path containing the image this label belongs to. |
required |
name
|
str
|
Label image name under |
'labels'
|
axes
|
str
|
One letter per axis. |
None
|
pixel_size
|
(dict, tuple or None)
|
Physical voxel size in micrometers. |
None
|
n_levels
|
int
|
Pyramid levels including full resolution, when the image has no pyramid to follow (default 5). With one, the labels get exactly as many levels as the image -- the OME-Zarr spec requires it of a label image -- whatever is asked here. |
None
|
downscale
|
int
|
Per-level X/Y downsampling factor (default 2). |
2
|
chunks
|
tuple of int
|
Chunk shape for the written levels. |
None
|
shard
|
bool or tuple of int
|
Sharding request (see :func: |
False
|
progress
|
bool
|
Show a per-level dask progress bar (default |
True
|
level
|
int
|
The image pyramid level the labels were segmented at (default 0). Their voxel size and offset are that level's, not level 0's: taking level 0's for labels made at level 1 draws them at half size, drifting further off the image the further from the origin. |
0
|
n_objects
|
int or None
|
Exact non-background object count, if known (e.g. from
:func: |
None
|
ngff_version
|
str or None
|
NGFF version (and therefore zarr format) to write: |
'auto'
|
compression
|
str
|
Codec for the arrays written ( |
None
|
provenance
|
dict
|
How the labels were made (see :func: |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Path to the label group ( |
Examples:
Source code in src/patchworks/plugins/ome_zarr.py
2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 | |
reshard_level
patchworks.plugins.ome_zarr.reshard_level(group_path: Union[str, Path], component: str = '0', shard: ShardSpec = True, progress: bool = True) -> str
Rewrite an existing array in place, sharded, via one dask pass.
For an array that had to be written unsharded because many writers
filled it a chunk at a time -- a label group's level 0, written tile by
tile by concurrent segment jobs, or by the merge's own worker pool -- a
shard cannot be produced while those writers are running: they would
read-modify-write the same shard file and silently lose each other's
chunks. Doing it afterwards, single-threaded through
:func:_to_zarr_level, is safe because one writer owns every shard.
The cost is one extra full read+write of the level, which is why this is
opt-in rather than implied by shard.
Array attributes are carried across. That matters for a label level 0: the merge records how far it got there, and losing that would let a re-run merge already-merged ids and collide unrelated objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
group_path
|
str or Path
|
Path of the zarr group holding the array. |
required |
component
|
str
|
Array name within the group (default |
'0'
|
shard
|
bool or tuple of int
|
Sharding request; see :func: |
True
|
progress
|
bool
|
Show a dask progress bar for the rewrite. |
True
|
Returns:
| Type | Description |
|---|---|
str
|
The path of the array that was rewritten. |
Source code in src/patchworks/plugins/ome_zarr.py
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 | |
read_pixel_size
patchworks.plugins.ome_zarr.read_pixel_size(store: Union[str, Path], level: int = 0) -> PixelSize
Physical voxel size recorded in an OME-ZARR's metadata for level.
The calibration the conversion carried over from the source file, as
{"z": .., "y": .., "x": ..} in micrometers. Axes left at scale 1.0
(uncalibrated) are omitted, so an empty dict means the store carries no
usable calibration.
Use this instead of retyping voxel sizes into a config: a deconvolution told the wrong voxel size produces a plausible-looking but wrong result.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
store
|
str or Path
|
Path of the OME-ZARR group. |
required |
level
|
int
|
Pyramid level (default 0, full resolution). Anything segmented or measured at a coarser level must use that level's voxel size. |
0
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
>>> read_pixel_size("scan.zarr")
{'z': 0.2, 'y': 0.1, 'x': 0.1}
>>> read_pixel_size("scan.zarr", level=1)
{'z': 0.2, 'y': 0.2, 'x': 0.2}
Source code in src/patchworks/plugins/ome_zarr.py
NGFF metadata layout
NGFF 0.4 is defined over zarr v2 and puts its keys at the top level; 0.5 is
the zarr-v3 revision and nests them under ome. patchworks writes whichever
matches the store, and reads both.
patchworks.plugins.ome_zarr.ngff_version() -> str
NGFF version currently being written.
Inside a write call this reflects that call's ngff_version=;
outside one it is the version an "auto" write would produce.
Source code in src/patchworks/plugins/ome_zarr.py
Every writer takes an ngff_version= keyword: "auto" (default) follows the
installed zarr (0.5, since patchworks requires zarr-python 3) and "0.4" pins the older, zarr-v2
layout. Writing into an existing store always follows that store's format,
so a label pyramid added later can never disagree with the image it sits in.
NGFF 0.6 is released but not written: RFC-5 replaces axes with
coordinateSystems and requires input/output on every coordinate
transformation, and no reader supports it yet.
patchworks.plugins.ome_zarr.read_ngff_attr(attrs, key: str, default=None)
Read an NGFF key from either layout.
Accepts both the 0.4 top-level placement and the 0.5 ome nesting, so
stores written by any patchworks version (or another tool) still load.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
attrs
|
Mapping
|
A zarr group's attributes. |
required |
key
|
str
|
NGFF key, e.g. |
required |
default
|
Any
|
Returned when the key is absent from both layouts. |
None
|
Source code in src/patchworks/plugins/ome_zarr.py
patchworks.plugins.ome_zarr.write_ngff_attrs(group, **entries) -> None
Write NGFF keys in the layout matching the store's zarr version.
On zarr v3 the keys are merged into the ome attribute (0.5); on v2
they go to the top level (0.4). Merging matters because several keys are
written at different times onto the same group -- multiscales by the
pyramid, then image-label when the labels are registered.