Skip to content

Commit fcd0f2d

Browse files
authored
Merge pull request #102 from sebi06/work_in_progress
Work in progress
2 parents 013a3a2 + 54252be commit fcd0f2d

File tree

16 files changed

+1182
-14743
lines changed

16 files changed

+1182
-14743
lines changed

demo/notebooks/omezarr_from_czi_5d.ipynb

Lines changed: 184 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
" # Install dependencies\n",
5858
" ! pip install --upgrade pip\n",
5959
" ! pip install czitools\n",
60+
" ! pip install ome-zarr\n",
6061
" ! pip install ngff-zarr[validate, dask-image]"
6162
]
6263
},
@@ -70,25 +71,134 @@
7071
"name": "stdout",
7172
"output_type": "stream",
7273
"text": [
73-
"ZARR Verion: 3.1.1 NGFF-ZARR Version: 0.16.1\n"
74+
"\u001b[32m2025-08-25 15:17:31,259 - czitools - INFO - Using ngff format version: 0.5\u001b[0m\n",
75+
"\u001b[32m2025-08-25 15:17:31,260 - czitools - INFO - ZARR Version: 3.1.1\u001b[0m\n",
76+
"\u001b[32m2025-08-25 15:17:31,260 - czitools - INFO - NGFF-ZARR Version: 0.16.1\u001b[0m\n",
77+
"\u001b[32m2025-08-25 15:17:31,262 - czitools - INFO - OME-ZARR Version: 0.12.2\u001b[0m\n"
7478
]
7579
}
7680
],
7781
"source": [
7882
"from czitools.read_tools import read_tools\n",
7983
"from czitools.metadata_tools import czi_metadata as czimd\n",
80-
"from pathlib import Path\n",
81-
"import shutil\n",
82-
"import requests\n",
83-
"import os\n",
8484
"import ngff_zarr as nz\n",
85+
"from pathlib import Path\n",
86+
"import dask.array as da\n",
8587
"import zarr\n",
86-
"print(f\"ZARR Verion: {zarr.__version__} NGFF-ZARR Version: {nz.__version__}\")"
88+
"import os\n",
89+
"import requests\n",
90+
"import ome_zarr.writer\n",
91+
"import ome_zarr.format\n",
92+
"from ome_zarr.io import parse_url\n",
93+
"from typing import Union\n",
94+
"import shutil\n",
95+
"import numpy as np\n",
96+
"from czitools.utils import logging_tools\n",
97+
"from importlib.metadata import version\n",
98+
"\n",
99+
"logger = logging_tools.set_logging()\n",
100+
"\n",
101+
"# show currently used version of NGFF specification\n",
102+
"ngff_version = ome_zarr.format.CurrentFormat().version\n",
103+
"logger.info(f\"Using ngff format version: {ngff_version}\")\n",
104+
"logger.info(f\"ZARR Version: {zarr.__version__}\")\n",
105+
"logger.info(f\"NGFF-ZARR Version: {nz.__version__}\")\n",
106+
"logger.info(f\"OME-ZARR Version: {version('ome-zarr')}\")"
87107
]
88108
},
89109
{
90110
"cell_type": "code",
91111
"execution_count": 4,
112+
"id": "1ba3c0b3",
113+
"metadata": {},
114+
"outputs": [],
115+
"source": [
116+
"def write_omezarr(\n",
117+
" array5d: Union[np.ndarray, da.Array],\n",
118+
" zarr_path: str,\n",
119+
" axes: str = \"tczyx\",\n",
120+
" overwrite: bool = False,\n",
121+
") -> str:\n",
122+
" \"\"\"\n",
123+
" Writes a 5D array to an OME-ZARR file.\n",
124+
" Parameters:\n",
125+
" -----------\n",
126+
" array5d : Union[np.ndarray, da.Array]\n",
127+
" The 5D array to be written. The dimensions should not exceed 5.\n",
128+
" zarr_path : str\n",
129+
" The path where the OME-ZARR file will be saved.\n",
130+
" axes : str, optional\n",
131+
" The order of axes in the array. Default is \"tczyx\".\n",
132+
" overwrite : bool, optional\n",
133+
" If True, the existing file at zarr_path will be overwritten. Default is False.\n",
134+
" Returns:\n",
135+
" --------\n",
136+
" str\n",
137+
" The path to the written OME-ZARR folder if successful, otherwise None.\n",
138+
" Notes:\n",
139+
" ------\n",
140+
" - The function ensures the axes are in lowercase and removes any invalid dimensions.\n",
141+
" - If the zarr_path already exists and overwrite is True, the existing directory will be removed.\n",
142+
" - The function logs the NGFF format version being used.\n",
143+
" - The function writes the image data to the specified zarr_path.\n",
144+
" - If the writing process is successful, the function returns the zarr_path; otherwise, it returns None.\n",
145+
" \"\"\"\n",
146+
"\n",
147+
" # check number of dimension of input array\n",
148+
" if len(array5d.shape) > 5:\n",
149+
" logger.warning(\"Input array as more than 5 dimensions.\")\n",
150+
" return None\n",
151+
"\n",
152+
" # make sure lower case is use for axes order\n",
153+
" axes = axes.lower()\n",
154+
"\n",
155+
" # check for invalid dimensions and clean up\n",
156+
" for character in [\"b\", \"h\", \"s\", \"i\", \"v\", \"a\"]:\n",
157+
" axes = axes.replace(character, \"\")\n",
158+
"\n",
159+
" # check if zarr_path already exits\n",
160+
" if Path(zarr_path).exists() and overwrite:\n",
161+
" shutil.rmtree(zarr_path, ignore_errors=False, onerror=None)\n",
162+
" elif Path(zarr_path).exists() and not overwrite:\n",
163+
" logger.warning(\n",
164+
" f\"File already exists at {zarr_path}. Set overwrite=True to remove.\"\n",
165+
" )\n",
166+
" return None\n",
167+
"\n",
168+
" # write the image data\n",
169+
" store = parse_url(zarr_path, mode=\"w\").store\n",
170+
" root = zarr.group(store=store, overwrite=overwrite)\n",
171+
"\n",
172+
" # TODO: Add Channel information etc. to the root along those lines\n",
173+
" \"\"\"\n",
174+
" # add omero metadata_tools: the napari ome-zarr plugin uses this to pass rendering\n",
175+
" # options to napari.\n",
176+
" root.attrs['omero'] = {\n",
177+
" 'channels': [{\n",
178+
" 'color': 'ffffff',\n",
179+
" 'label': 'LS-data',\n",
180+
" 'active': True,\n",
181+
" }]\n",
182+
" }\n",
183+
"\n",
184+
" \"\"\"\n",
185+
"\n",
186+
" # write the OME-ZARR file\n",
187+
" ome_zarr.writer.write_image(\n",
188+
" image=array5d,\n",
189+
" group=root,\n",
190+
" axes=axes,\n",
191+
" storage_options=dict(chunks=array5d.shape),\n",
192+
" )\n",
193+
"\n",
194+
" logger.info(f\"Finished writing OME-ZARR to: {zarr_path}\")\n",
195+
"\n",
196+
" return zarr_path"
197+
]
198+
},
199+
{
200+
"cell_type": "code",
201+
"execution_count": 5,
92202
"id": "5b96a0fa",
93203
"metadata": {},
94204
"outputs": [],
@@ -119,15 +229,15 @@
119229
},
120230
{
121231
"cell_type": "code",
122-
"execution_count": 5,
232+
"execution_count": 6,
123233
"id": "2ab02d0c",
124234
"metadata": {},
125235
"outputs": [
126236
{
127237
"name": "stdout",
128238
"output_type": "stream",
129239
"text": [
130-
"/datadisk1/Github/czitools/data/CellDivision_T3_Z5_CH2_X240_Y170.ome.zarr\n"
240+
"\u001b[32m2025-08-25 15:18:12,909 - czitools - INFO - /datadisk1/Github/czitools/data/CellDivision_T3_Z5_CH2_X240_Y170.ome.zarr\u001b[0m\n"
131241
]
132242
}
133243
],
@@ -141,7 +251,7 @@
141251
" filepath = os.path.join(defaultdir, \"CellDivision_T3_Z5_CH2_X240_Y170.czi\")\n",
142252
" zarr_path = defaultdir / Path(filepath[:-4] + \".ome.zarr\")\n",
143253
"\n",
144-
"print(zarr_path)\n",
254+
"logger.info(zarr_path)\n",
145255
"\n",
146256
"# check if path exists\n",
147257
"remove = True\n",
@@ -151,14 +261,14 @@
151261
},
152262
{
153263
"cell_type": "code",
154-
"execution_count": 6,
264+
"execution_count": 9,
155265
"id": "68d184ed",
156266
"metadata": {},
157267
"outputs": [
158268
{
159269
"data": {
160270
"application/vnd.jupyter.widget-view+json": {
161-
"model_id": "b0087b111b7248de9e456bb662f75d3e",
271+
"model_id": "ecceeaa3a1c5428f94e965bd04c4932a",
162272
"version_major": 2,
163273
"version_minor": 0
164274
},
@@ -173,27 +283,13 @@
173283
"name": "stdout",
174284
"output_type": "stream",
175285
"text": [
176-
"Number of Scenes: None\n"
286+
"\u001b[32m2025-08-25 15:19:51,505 - czitools - INFO - Number of Scenes: None\u001b[0m\n"
177287
]
178-
}
179-
],
180-
"source": [
181-
"# get the metadata at once as one big class\n",
182-
"mdata = czimd.CziMetadata(filepath)\n",
183-
"print(\"Number of Scenes: \", mdata.image.SizeS)\n",
184-
"scene_id = 0"
185-
]
186-
},
187-
{
188-
"cell_type": "code",
189-
"execution_count": 7,
190-
"id": "1e32b670",
191-
"metadata": {},
192-
"outputs": [
288+
},
193289
{
194290
"data": {
195291
"application/vnd.jupyter.widget-view+json": {
196-
"model_id": "b7a21676cc0c4d659417a073ec8b5822",
292+
"model_id": "b9304e7cdf8c44378de6765920202915",
197293
"version_major": 2,
198294
"version_minor": 0
199295
},
@@ -207,7 +303,7 @@
207303
{
208304
"data": {
209305
"application/vnd.jupyter.widget-view+json": {
210-
"model_id": "14a2b4e5d47e4f3c98521409cfb8f130",
306+
"model_id": "f462a65c4dbf464099307f28ac00ae1a",
211307
"version_major": 2,
212308
"version_minor": 0
213309
},
@@ -222,41 +318,85 @@
222318
"name": "stdout",
223319
"output_type": "stream",
224320
"text": [
225-
"Array Shape: (3, 2, 5, 170, 240)\n"
321+
"\u001b[32m2025-08-25 15:19:51,812 - czitools - INFO - Array Shape: (3, 2, 5, 170, 240)\u001b[0m\n"
226322
]
227323
}
228324
],
229325
"source": [
326+
"# get the metadata at once as one big class\n",
327+
"mdata = czimd.CziMetadata(filepath)\n",
328+
"logger.info(f\"Number of Scenes: {mdata.image.SizeS}\")\n",
329+
"scene_id = 0\n",
330+
"\n",
230331
"array, mdata = read_tools.read_6darray(filepath)\n",
231332
"\n",
232333
"array = array[scene_id, ...]\n",
233-
"print(f\"Array Shape: {array.shape}\")"
334+
"logger.info(f\"Array Shape: {array.shape}\")"
234335
]
235336
},
236337
{
237338
"cell_type": "code",
238-
"execution_count": 8,
239-
"id": "c670b17c",
339+
"execution_count": null,
340+
"id": "e192c410",
240341
"metadata": {},
241-
"outputs": [],
342+
"outputs": [
343+
{
344+
"name": "stdout",
345+
"output_type": "stream",
346+
"text": [
347+
"\u001b[32m2025-08-25 15:20:13,717 - czitools - INFO - Finished writing OME-ZARR to: /datadisk1/Github/czitools/data/CellDivision_T3_Z5_CH2_X240_Y170_1.ome.zarr\u001b[0m\n",
348+
"\u001b[32m2025-08-25 15:20:13,717 - czitools - INFO - Written OME-ZARR using ome-zarr.py: /datadisk1/Github/czitools/data/CellDivision_T3_Z5_CH2_X240_Y170_1.ome.zarr\u001b[0m\n"
349+
]
350+
}
351+
],
242352
"source": [
243-
"# create NGFF image from the array\n",
244-
"image = nz.to_ngff_image(array.data,\n",
245-
" dims=[\"t\", \"c\", \"z\", \"y\", \"x\"],\n",
246-
" scale={\"y\": mdata.scale.Y, \"x\": mdata.scale.X, \"z\": mdata.scale.Z},\n",
247-
" name=mdata.filename)\n",
248-
"\n"
353+
"# Approach 1: Use ome-zarr-py to write OME-ZARR\n",
354+
"zarr_path1 = Path(str(filepath)[:-4] + \"_1.ome.zarr\")\n",
355+
"\n",
356+
"# write OME-ZARR using utility function\n",
357+
"zarr_path1 = write_omezarr(\n",
358+
" array, zarr_path=str(zarr_path1), axes=\"tczyx\", overwrite=True\n",
359+
")\n",
360+
"\n",
361+
"logger.info(f\"Written OME-ZARR using ome-zarr.py: {zarr_path1}\")\n",
362+
"\n",
363+
"write_omezarr1 = True"
249364
]
250365
},
251366
{
252367
"cell_type": "code",
253-
"execution_count": 9,
254-
"id": "24a93ccf-5cd4-4435-867b-190093bba3cc",
368+
"execution_count": null,
369+
"id": "c670b17c",
255370
"metadata": {},
256-
"outputs": [],
371+
"outputs": [
372+
{
373+
"name": "stdout",
374+
"output_type": "stream",
375+
"text": [
376+
"\u001b[32m2025-08-25 15:20:19,409 - czitools - INFO - NGFF Image: NgffImage(data=dask.array<rechunk-merge, shape=(3, 2, 5, 170, 240), dtype=uint16, chunksize=(1, 2, 5, 128, 128), chunktype=numpy.ndarray>, dims=['t', 'c', 'z', 'y', 'x'], scale={'y': np.float64(0.091), 'x': np.float64(0.091), 'z': np.float64(0.32)}, translation={'z': 0.0, 'y': 0.0, 'x': 0.0}, name='CellDivision_T3_Z5_CH2_X240_Y170.czi', axes_units=None, axes_orientations=None, computed_callbacks=[])\u001b[0m\n",
377+
"\u001b[32m2025-08-25 15:20:19,409 - czitools - INFO - Written OME-ZARR using ngff-zarr: /datadisk1/Github/czitools/data/CellDivision_T3_Z5_CH2_X240_Y170_2.ome.zarr\u001b[0m\n"
378+
]
379+
}
380+
],
257381
"source": [
382+
"# Approach 2: Use ngff-zarr to create NGFF structure and write using ome-zarr-py\n",
383+
"zarr_path2 = Path(str(filepath)[:-4] + \"_2.ome.zarr\")\n",
384+
"\n",
385+
"# create NGFF image from the array\n",
386+
"image = nz.to_ngff_image(array.data,\n",
387+
" dims=[\"t\", \"c\", \"z\", \"y\", \"x\"],\n",
388+
" scale={\"y\": mdata.scale.Y, \"x\": mdata.scale.X, \"z\": mdata.scale.Z},\n",
389+
" name=mdata.filename)\n",
390+
"\n",
258391
"# create multi-scaled, chunked data structure from the image\n",
259-
"multiscales = nz.to_multiscales(image, [2, 4], method=nz.Methods.DASK_IMAGE_GAUSSIAN)"
392+
"multiscales = nz.to_multiscales(image, [2, 4], method=nz.Methods.DASK_IMAGE_GAUSSIAN)\n",
393+
"\n",
394+
"# write using ngff-zarr\n",
395+
"nz.to_ngff_zarr(zarr_path2, multiscales)\n",
396+
"logger.info(f\"NGFF Image: {image}\")\n",
397+
"logger.info(f\"Written OME-ZARR using ngff-zarr: {zarr_path2}\")\n",
398+
"\n",
399+
"write_omezarr2 = True"
260400
]
261401
}
262402
],

0 commit comments

Comments
 (0)