ngio 0.5.0a1__py3-none-any.whl → 0.5.0a2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ngio/common/_pyramid.py +12 -2
- ngio/hcs/_plate.py +66 -82
- ngio/images/_create.py +14 -10
- ngio/images/_create_synt_container.py +3 -3
- ngio/images/_image.py +2 -2
- ngio/images/_label.py +1 -1
- ngio/images/_ome_zarr_container.py +6 -6
- ngio/tables/_tables_container.py +23 -16
- ngio/tables/v1/_roi_table.py +3 -3
- ngio/utils/__init__.py +2 -0
- ngio/utils/_cache.py +48 -0
- ngio/utils/_zarr_utils.py +126 -193
- {ngio-0.5.0a1.dist-info → ngio-0.5.0a2.dist-info}/METADATA +3 -3
- {ngio-0.5.0a1.dist-info → ngio-0.5.0a2.dist-info}/RECORD +16 -15
- {ngio-0.5.0a1.dist-info → ngio-0.5.0a2.dist-info}/WHEEL +0 -0
- {ngio-0.5.0a1.dist-info → ngio-0.5.0a2.dist-info}/licenses/LICENSE +0 -0
ngio/common/_pyramid.py
CHANGED
|
@@ -41,9 +41,19 @@ def _on_disk_dask_zoom(
|
|
|
41
41
|
source_array = da.from_zarr(source)
|
|
42
42
|
target_array = dask_zoom(source_array, target_shape=target.shape, order=order)
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
# This is a potential fix for Dask 2025.11
|
|
45
|
+
# import dask.config
|
|
46
|
+
# chunk_size_bytes = np.prod(target.chunks) * target_array.dtype.itemsize
|
|
47
|
+
# current_chunk_size = dask.config.get("array.chunk-size")
|
|
48
|
+
# Increase the chunk size to avoid dask potentially creating
|
|
49
|
+
# corrupted chunks when writing chunks that are not multiple of the
|
|
50
|
+
# target chunk size
|
|
51
|
+
# dask.config.set({"array.chunk-size": f"{chunk_size_bytes}B"})
|
|
52
|
+
target_array = target_array.rechunk(target.chunks)
|
|
53
|
+
target_array = target_array.compute_chunk_sizes()
|
|
46
54
|
target_array.to_zarr(target)
|
|
55
|
+
# Restore previous chunk size
|
|
56
|
+
# dask.config.set({"array.chunk-size": current_chunk_size})
|
|
47
57
|
|
|
48
58
|
|
|
49
59
|
def _on_disk_coarsen(
|
ngio/hcs/_plate.py
CHANGED
|
@@ -40,6 +40,7 @@ from ngio.tables import (
|
|
|
40
40
|
)
|
|
41
41
|
from ngio.utils import (
|
|
42
42
|
AccessModeLiteral,
|
|
43
|
+
NgioCache,
|
|
43
44
|
NgioValueError,
|
|
44
45
|
StoreOrGroup,
|
|
45
46
|
ZarrGroupHandler,
|
|
@@ -239,6 +240,12 @@ class OmeZarrPlate:
|
|
|
239
240
|
self._group_handler = group_handler
|
|
240
241
|
self._meta_handler = find_plate_meta_handler(group_handler)
|
|
241
242
|
self._tables_container = table_container
|
|
243
|
+
self._wells_cache: NgioCache[OmeZarrWell] = NgioCache(
|
|
244
|
+
use_cache=self._group_handler.use_cache
|
|
245
|
+
)
|
|
246
|
+
self._images_cache: NgioCache[OmeZarrContainer] = NgioCache(
|
|
247
|
+
use_cache=self._group_handler.use_cache
|
|
248
|
+
)
|
|
242
249
|
|
|
243
250
|
def __repr__(self) -> str:
|
|
244
251
|
"""Return a string representation of the plate."""
|
|
@@ -356,6 +363,24 @@ class OmeZarrPlate:
|
|
|
356
363
|
well = self.get_well(row=row, column=column)
|
|
357
364
|
return well.get_image_acquisition_id(image_path=image_path)
|
|
358
365
|
|
|
366
|
+
def _get_well(self, well_path: str) -> OmeZarrWell:
|
|
367
|
+
"""Get a well from the plate by its path.
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
well_path (str): The path of the well.
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
OmeZarrWell: The well.
|
|
374
|
+
|
|
375
|
+
"""
|
|
376
|
+
cached_well = self._wells_cache.get(well_path)
|
|
377
|
+
if cached_well is not None:
|
|
378
|
+
return cached_well
|
|
379
|
+
|
|
380
|
+
group_handler = self._group_handler.derive_handler(well_path)
|
|
381
|
+
self._wells_cache.set(well_path, OmeZarrWell(group_handler))
|
|
382
|
+
return OmeZarrWell(group_handler)
|
|
383
|
+
|
|
359
384
|
def get_well(self, row: str, column: int | str) -> OmeZarrWell:
|
|
360
385
|
"""Get a well from the plate.
|
|
361
386
|
|
|
@@ -367,8 +392,7 @@ class OmeZarrPlate:
|
|
|
367
392
|
OmeZarrWell: The well.
|
|
368
393
|
"""
|
|
369
394
|
well_path = self._well_path(row=row, column=column)
|
|
370
|
-
|
|
371
|
-
return OmeZarrWell(group_handler)
|
|
395
|
+
return self._get_well(well_path=well_path)
|
|
372
396
|
|
|
373
397
|
async def get_wells_async(self) -> dict[str, OmeZarrWell]:
|
|
374
398
|
"""Get all wells in the plate asynchronously.
|
|
@@ -380,26 +404,17 @@ class OmeZarrPlate:
|
|
|
380
404
|
dict[str, OmeZarrWell]: A dictionary of wells, where the key is the well
|
|
381
405
|
path and the value is the well object.
|
|
382
406
|
"""
|
|
383
|
-
wells = self._group_handler.get_from_cache("wells")
|
|
384
|
-
if wells is not None:
|
|
385
|
-
assert isinstance(wells, dict)
|
|
386
|
-
return wells
|
|
387
|
-
|
|
388
|
-
def process_well(well_path):
|
|
389
|
-
group_handler = self._group_handler.derive_handler(well_path)
|
|
390
|
-
well = OmeZarrWell(group_handler)
|
|
391
|
-
return well_path, well
|
|
392
|
-
|
|
393
407
|
wells, tasks = {}, []
|
|
394
408
|
for well_path in self.wells_paths():
|
|
395
|
-
task = asyncio.to_thread(
|
|
409
|
+
task = asyncio.to_thread(
|
|
410
|
+
lambda well_path: (well_path, self._get_well(well_path)), well_path
|
|
411
|
+
)
|
|
396
412
|
tasks.append(task)
|
|
397
413
|
|
|
398
414
|
results = await asyncio.gather(*tasks)
|
|
399
415
|
for well_path, well in results:
|
|
400
416
|
wells[well_path] = well
|
|
401
417
|
|
|
402
|
-
self._group_handler.add_to_cache("wells", wells)
|
|
403
418
|
return wells
|
|
404
419
|
|
|
405
420
|
def get_wells(self) -> dict[str, OmeZarrWell]:
|
|
@@ -409,24 +424,25 @@ class OmeZarrPlate:
|
|
|
409
424
|
dict[str, OmeZarrWell]: A dictionary of wells, where the key is the well
|
|
410
425
|
path and the value is the well object.
|
|
411
426
|
"""
|
|
412
|
-
wells = self._group_handler.get_from_cache("wells")
|
|
413
|
-
if wells is not None:
|
|
414
|
-
assert isinstance(wells, dict)
|
|
415
|
-
return wells
|
|
416
|
-
|
|
417
|
-
def process_well(well_path):
|
|
418
|
-
group_handler = self._group_handler.derive_handler(well_path)
|
|
419
|
-
well = OmeZarrWell(group_handler)
|
|
420
|
-
return well_path, well
|
|
421
|
-
|
|
422
427
|
wells = {}
|
|
423
428
|
for well_path in self.wells_paths():
|
|
424
|
-
|
|
425
|
-
wells[well_path] = well
|
|
426
|
-
|
|
427
|
-
self._group_handler.add_to_cache("wells", wells)
|
|
429
|
+
wells[well_path] = self._get_well(well_path)
|
|
428
430
|
return wells
|
|
429
431
|
|
|
432
|
+
def _get_image(self, image_path: str) -> OmeZarrContainer:
|
|
433
|
+
"""Get an image from the plate by its path.
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
image_path (str): The path of the image.
|
|
437
|
+
"""
|
|
438
|
+
cached_image = self._images_cache.get(image_path)
|
|
439
|
+
if cached_image is not None:
|
|
440
|
+
return cached_image
|
|
441
|
+
img_group_handler = self._group_handler.derive_handler(image_path)
|
|
442
|
+
image = OmeZarrContainer(img_group_handler)
|
|
443
|
+
self._images_cache.set(image_path, image)
|
|
444
|
+
return image
|
|
445
|
+
|
|
430
446
|
async def get_images_async(
|
|
431
447
|
self, acquisition: int | None = None
|
|
432
448
|
) -> dict[str, OmeZarrContainer]:
|
|
@@ -442,30 +458,19 @@ class OmeZarrPlate:
|
|
|
442
458
|
dict[str, OmeZarrContainer]: A dictionary of images, where the key is the
|
|
443
459
|
image path and the value is the image object.
|
|
444
460
|
"""
|
|
445
|
-
images = self._group_handler.get_from_cache("images")
|
|
446
|
-
if images is not None:
|
|
447
|
-
assert isinstance(images, dict)
|
|
448
|
-
return images
|
|
449
|
-
|
|
450
461
|
paths = await self.images_paths_async(acquisition=acquisition)
|
|
451
462
|
|
|
452
|
-
def process_image(image_path):
|
|
453
|
-
"""Process a single image and return the image path and image object."""
|
|
454
|
-
img_group_handler = self._group_handler.derive_handler(image_path)
|
|
455
|
-
image = OmeZarrContainer(img_group_handler)
|
|
456
|
-
return image_path, image
|
|
457
|
-
|
|
458
463
|
images, tasks = {}, []
|
|
459
464
|
for image_path in paths:
|
|
460
|
-
task = asyncio.to_thread(
|
|
465
|
+
task = asyncio.to_thread(
|
|
466
|
+
lambda image_path: (image_path, self._get_image(image_path)), image_path
|
|
467
|
+
)
|
|
461
468
|
tasks.append(task)
|
|
462
469
|
|
|
463
470
|
results = await asyncio.gather(*tasks)
|
|
464
471
|
|
|
465
472
|
for image_path, image in results:
|
|
466
473
|
images[image_path] = image
|
|
467
|
-
|
|
468
|
-
self._group_handler.add_to_cache("images", images)
|
|
469
474
|
return images
|
|
470
475
|
|
|
471
476
|
def get_images(self, acquisition: int | None = None) -> dict[str, OmeZarrContainer]:
|
|
@@ -474,24 +479,11 @@ class OmeZarrPlate:
|
|
|
474
479
|
Args:
|
|
475
480
|
acquisition: The acquisition id to filter the images.
|
|
476
481
|
"""
|
|
477
|
-
images = self._group_handler.get_from_cache("images")
|
|
478
|
-
if images is not None:
|
|
479
|
-
assert isinstance(images, dict)
|
|
480
|
-
return images
|
|
481
482
|
paths = self.images_paths(acquisition=acquisition)
|
|
482
|
-
|
|
483
|
-
def process_image(image_path):
|
|
484
|
-
"""Process a single image and return the image path and image object."""
|
|
485
|
-
img_group_handler = self._group_handler.derive_handler(image_path)
|
|
486
|
-
image = OmeZarrContainer(img_group_handler)
|
|
487
|
-
return image_path, image
|
|
488
|
-
|
|
489
483
|
images = {}
|
|
490
484
|
for image_path in paths:
|
|
491
|
-
|
|
492
|
-
images[image_path] = image
|
|
485
|
+
images[image_path] = self._get_image(image_path)
|
|
493
486
|
|
|
494
|
-
self._group_handler.add_to_cache("images", images)
|
|
495
487
|
return images
|
|
496
488
|
|
|
497
489
|
def get_image(
|
|
@@ -508,8 +500,7 @@ class OmeZarrPlate:
|
|
|
508
500
|
OmeZarrContainer: The image.
|
|
509
501
|
"""
|
|
510
502
|
image_path = self._image_path(row=row, column=column, path=image_path)
|
|
511
|
-
|
|
512
|
-
return OmeZarrContainer(group_handler)
|
|
503
|
+
return self._get_image(image_path)
|
|
513
504
|
|
|
514
505
|
def get_image_store(
|
|
515
506
|
self, row: str, column: int | str, image_path: str
|
|
@@ -785,7 +776,6 @@ class OmeZarrPlate:
|
|
|
785
776
|
keep_acquisitions: bool = False,
|
|
786
777
|
cache: bool = False,
|
|
787
778
|
overwrite: bool = False,
|
|
788
|
-
parallel_safe: bool = True,
|
|
789
779
|
) -> "OmeZarrPlate":
|
|
790
780
|
"""Derive a new OME-Zarr plate from an existing one.
|
|
791
781
|
|
|
@@ -796,7 +786,6 @@ class OmeZarrPlate:
|
|
|
796
786
|
keep_acquisitions (bool): Whether to keep the acquisitions in the new plate.
|
|
797
787
|
cache (bool): Whether to use a cache for the zarr group metadata.
|
|
798
788
|
overwrite (bool): Whether to overwrite the existing plate.
|
|
799
|
-
parallel_safe (bool): Whether the group handler is parallel safe.
|
|
800
789
|
"""
|
|
801
790
|
return derive_ome_zarr_plate(
|
|
802
791
|
ome_zarr_plate=self,
|
|
@@ -806,7 +795,6 @@ class OmeZarrPlate:
|
|
|
806
795
|
keep_acquisitions=keep_acquisitions,
|
|
807
796
|
cache=cache,
|
|
808
797
|
overwrite=overwrite,
|
|
809
|
-
parallel_safe=parallel_safe,
|
|
810
798
|
)
|
|
811
799
|
|
|
812
800
|
def _get_tables_container(self) -> TablesContainer | None:
|
|
@@ -1143,7 +1131,6 @@ def open_ome_zarr_plate(
|
|
|
1143
1131
|
store: StoreOrGroup,
|
|
1144
1132
|
cache: bool = False,
|
|
1145
1133
|
mode: AccessModeLiteral = "r+",
|
|
1146
|
-
parallel_safe: bool = True,
|
|
1147
1134
|
) -> OmeZarrPlate:
|
|
1148
1135
|
"""Open an OME-Zarr plate.
|
|
1149
1136
|
|
|
@@ -1152,11 +1139,8 @@ def open_ome_zarr_plate(
|
|
|
1152
1139
|
cache (bool): Whether to use a cache for the zarr group metadata.
|
|
1153
1140
|
mode (AccessModeLiteral): The
|
|
1154
1141
|
access mode for the image. Defaults to "r+".
|
|
1155
|
-
parallel_safe (bool): Whether the group handler is parallel safe.
|
|
1156
1142
|
"""
|
|
1157
|
-
group_handler = ZarrGroupHandler(
|
|
1158
|
-
store=store, cache=cache, mode=mode, parallel_safe=parallel_safe
|
|
1159
|
-
)
|
|
1143
|
+
group_handler = ZarrGroupHandler(store=store, cache=cache, mode=mode)
|
|
1160
1144
|
return OmeZarrPlate(group_handler)
|
|
1161
1145
|
|
|
1162
1146
|
|
|
@@ -1168,9 +1152,7 @@ def _create_empty_plate_from_meta(
|
|
|
1168
1152
|
) -> ZarrGroupHandler:
|
|
1169
1153
|
"""Create an empty OME-Zarr plate from metadata."""
|
|
1170
1154
|
mode = "w" if overwrite else "w-"
|
|
1171
|
-
group_handler = ZarrGroupHandler(
|
|
1172
|
-
store=store, cache=True, mode=mode, parallel_safe=False
|
|
1173
|
-
)
|
|
1155
|
+
group_handler = ZarrGroupHandler(store=store, cache=True, mode=mode)
|
|
1174
1156
|
meta_handler = get_plate_meta_handler(group_handler, version=version)
|
|
1175
1157
|
meta_handler.write_meta(meta)
|
|
1176
1158
|
return group_handler
|
|
@@ -1183,9 +1165,18 @@ def create_empty_plate(
|
|
|
1183
1165
|
version: NgffVersions = "0.4",
|
|
1184
1166
|
cache: bool = False,
|
|
1185
1167
|
overwrite: bool = False,
|
|
1186
|
-
parallel_safe: bool = True,
|
|
1187
1168
|
) -> OmeZarrPlate:
|
|
1188
|
-
"""Initialize and create an empty OME-Zarr plate.
|
|
1169
|
+
"""Initialize and create an empty OME-Zarr plate.
|
|
1170
|
+
|
|
1171
|
+
Args:
|
|
1172
|
+
store (StoreOrGroup): The Zarr store or group that stores the plate.
|
|
1173
|
+
name (str): The name of the plate.
|
|
1174
|
+
images (list[ImageInWellPath] | None): A list of images to add to the plate.
|
|
1175
|
+
If None, no images are added. Defaults to None.
|
|
1176
|
+
version (NgffVersion): The version of the new plate.
|
|
1177
|
+
cache (bool): Whether to use a cache for the zarr group metadata.
|
|
1178
|
+
overwrite (bool): Whether to overwrite the existing plate.
|
|
1179
|
+
"""
|
|
1189
1180
|
plate_meta = NgioPlateMeta.default_init(
|
|
1190
1181
|
name=name,
|
|
1191
1182
|
version=version,
|
|
@@ -1211,7 +1202,6 @@ def create_empty_plate(
|
|
|
1211
1202
|
store=store,
|
|
1212
1203
|
cache=cache,
|
|
1213
1204
|
mode="r+",
|
|
1214
|
-
parallel_safe=parallel_safe,
|
|
1215
1205
|
)
|
|
1216
1206
|
|
|
1217
1207
|
|
|
@@ -1223,7 +1213,6 @@ def derive_ome_zarr_plate(
|
|
|
1223
1213
|
keep_acquisitions: bool = False,
|
|
1224
1214
|
cache: bool = False,
|
|
1225
1215
|
overwrite: bool = False,
|
|
1226
|
-
parallel_safe: bool = True,
|
|
1227
1216
|
) -> OmeZarrPlate:
|
|
1228
1217
|
"""Derive a new OME-Zarr plate from an existing one.
|
|
1229
1218
|
|
|
@@ -1235,7 +1224,6 @@ def derive_ome_zarr_plate(
|
|
|
1235
1224
|
keep_acquisitions (bool): Whether to keep the acquisitions in the new plate.
|
|
1236
1225
|
cache (bool): Whether to use a cache for the zarr group metadata.
|
|
1237
1226
|
overwrite (bool): Whether to overwrite the existing plate.
|
|
1238
|
-
parallel_safe (bool): Whether the group handler is parallel safe.
|
|
1239
1227
|
"""
|
|
1240
1228
|
if plate_name is None:
|
|
1241
1229
|
plate_name = ome_zarr_plate.meta.plate.name
|
|
@@ -1255,7 +1243,6 @@ def derive_ome_zarr_plate(
|
|
|
1255
1243
|
store=store,
|
|
1256
1244
|
cache=cache,
|
|
1257
1245
|
mode="r+",
|
|
1258
|
-
parallel_safe=parallel_safe,
|
|
1259
1246
|
)
|
|
1260
1247
|
|
|
1261
1248
|
|
|
@@ -1263,7 +1250,6 @@ def open_ome_zarr_well(
|
|
|
1263
1250
|
store: StoreOrGroup,
|
|
1264
1251
|
cache: bool = False,
|
|
1265
1252
|
mode: AccessModeLiteral = "r+",
|
|
1266
|
-
parallel_safe: bool = True,
|
|
1267
1253
|
) -> OmeZarrWell:
|
|
1268
1254
|
"""Open an OME-Zarr well.
|
|
1269
1255
|
|
|
@@ -1271,10 +1257,11 @@ def open_ome_zarr_well(
|
|
|
1271
1257
|
store (StoreOrGroup): The Zarr store or group that stores the plate.
|
|
1272
1258
|
cache (bool): Whether to use a cache for the zarr group metadata.
|
|
1273
1259
|
mode (AccessModeLiteral): The access mode for the image. Defaults to "r+".
|
|
1274
|
-
parallel_safe (bool): Whether the group handler is parallel safe.
|
|
1275
1260
|
"""
|
|
1276
1261
|
group_handler = ZarrGroupHandler(
|
|
1277
|
-
store=store,
|
|
1262
|
+
store=store,
|
|
1263
|
+
cache=cache,
|
|
1264
|
+
mode=mode,
|
|
1278
1265
|
)
|
|
1279
1266
|
return OmeZarrWell(group_handler)
|
|
1280
1267
|
|
|
@@ -1284,7 +1271,6 @@ def create_empty_well(
|
|
|
1284
1271
|
version: NgffVersions = "0.4",
|
|
1285
1272
|
cache: bool = False,
|
|
1286
1273
|
overwrite: bool = False,
|
|
1287
|
-
parallel_safe: bool = True,
|
|
1288
1274
|
) -> OmeZarrWell:
|
|
1289
1275
|
"""Create an empty OME-Zarr well.
|
|
1290
1276
|
|
|
@@ -1293,10 +1279,9 @@ def create_empty_well(
|
|
|
1293
1279
|
version (NgffVersion): The version of the new well.
|
|
1294
1280
|
cache (bool): Whether to use a cache for the zarr group metadata.
|
|
1295
1281
|
overwrite (bool): Whether to overwrite the existing well.
|
|
1296
|
-
parallel_safe (bool): Whether the group handler is parallel safe.
|
|
1297
1282
|
"""
|
|
1298
1283
|
group_handler = ZarrGroupHandler(
|
|
1299
|
-
store=store, cache=True, mode="w" if overwrite else "w-"
|
|
1284
|
+
store=store, cache=True, mode="w" if overwrite else "w-"
|
|
1300
1285
|
)
|
|
1301
1286
|
meta_handler = get_well_meta_handler(group_handler, version=version)
|
|
1302
1287
|
meta = NgioWellMeta.default_init()
|
|
@@ -1306,5 +1291,4 @@ def create_empty_well(
|
|
|
1306
1291
|
store=store,
|
|
1307
1292
|
cache=cache,
|
|
1308
1293
|
mode="r+",
|
|
1309
|
-
parallel_safe=parallel_safe,
|
|
1310
1294
|
)
|
ngio/images/_create.py
CHANGED
|
@@ -40,7 +40,7 @@ def _init_generic_meta(
|
|
|
40
40
|
space_unit: SpaceUnits | str | None = DefaultSpaceUnit,
|
|
41
41
|
time_unit: TimeUnits | str | None = DefaultTimeUnit,
|
|
42
42
|
name: str | None = None,
|
|
43
|
-
|
|
43
|
+
ngff_version: NgffVersions = DefaultNgffVersion,
|
|
44
44
|
) -> tuple[_image_or_label_meta, list[float]]:
|
|
45
45
|
"""Initialize the metadata for an image or label."""
|
|
46
46
|
scaling_factors = []
|
|
@@ -75,7 +75,7 @@ def _init_generic_meta(
|
|
|
75
75
|
axes_names=axes_names,
|
|
76
76
|
pixel_size=pixel_sizes,
|
|
77
77
|
scaling_factors=scaling_factors,
|
|
78
|
-
version=
|
|
78
|
+
version=ngff_version,
|
|
79
79
|
)
|
|
80
80
|
return meta, scaling_factors
|
|
81
81
|
|
|
@@ -98,7 +98,7 @@ def create_empty_label_container(
|
|
|
98
98
|
dimension_separator: Literal[".", "/"] = "/",
|
|
99
99
|
compressors: CompressorLike = "auto",
|
|
100
100
|
overwrite: bool = False,
|
|
101
|
-
|
|
101
|
+
ngff_version: NgffVersions = DefaultNgffVersion,
|
|
102
102
|
) -> ZarrGroupHandler:
|
|
103
103
|
"""Create an empty label with the given shape and metadata.
|
|
104
104
|
|
|
@@ -130,7 +130,7 @@ def create_empty_label_container(
|
|
|
130
130
|
dtype (str, optional): The data type of the image. Defaults to "uint16".
|
|
131
131
|
overwrite (bool, optional): Whether to overwrite an existing image.
|
|
132
132
|
Defaults to True.
|
|
133
|
-
|
|
133
|
+
ngff_version (str, optional): The version of the OME-Zarr specification.
|
|
134
134
|
Defaults to DefaultVersion.
|
|
135
135
|
|
|
136
136
|
"""
|
|
@@ -155,14 +155,16 @@ def create_empty_label_container(
|
|
|
155
155
|
time_unit=time_unit,
|
|
156
156
|
axes_names=axes_names,
|
|
157
157
|
name=name,
|
|
158
|
-
|
|
158
|
+
ngff_version=ngff_version,
|
|
159
159
|
)
|
|
160
160
|
|
|
161
161
|
mode = "w" if overwrite else "w-"
|
|
162
162
|
group_handler = ZarrGroupHandler(
|
|
163
163
|
store=store, mode=mode, cache=False, zarr_format=meta.zarr_format
|
|
164
164
|
)
|
|
165
|
-
image_handler = get_label_meta_handler(
|
|
165
|
+
image_handler = get_label_meta_handler(
|
|
166
|
+
version=ngff_version, group_handler=group_handler
|
|
167
|
+
)
|
|
166
168
|
image_handler.write_meta(meta)
|
|
167
169
|
|
|
168
170
|
init_empty_pyramid(
|
|
@@ -199,7 +201,7 @@ def create_empty_image_container(
|
|
|
199
201
|
dimension_separator: Literal[".", "/"] = "/",
|
|
200
202
|
compressors: CompressorLike = "auto",
|
|
201
203
|
overwrite: bool = False,
|
|
202
|
-
|
|
204
|
+
ngff_version: NgffVersions = DefaultNgffVersion,
|
|
203
205
|
) -> ZarrGroupHandler:
|
|
204
206
|
"""Create an empty OME-Zarr image with the given shape and metadata.
|
|
205
207
|
|
|
@@ -231,7 +233,7 @@ def create_empty_image_container(
|
|
|
231
233
|
compressors (CompressorLike): The compressors to use. Defaults to "auto".
|
|
232
234
|
overwrite (bool, optional): Whether to overwrite an existing image.
|
|
233
235
|
Defaults to True.
|
|
234
|
-
|
|
236
|
+
ngff_version (str, optional): The version of the OME-Zarr specification.
|
|
235
237
|
Defaults to DefaultVersion.
|
|
236
238
|
|
|
237
239
|
"""
|
|
@@ -256,13 +258,15 @@ def create_empty_image_container(
|
|
|
256
258
|
time_unit=time_unit,
|
|
257
259
|
axes_names=axes_names,
|
|
258
260
|
name=name,
|
|
259
|
-
|
|
261
|
+
ngff_version=ngff_version,
|
|
260
262
|
)
|
|
261
263
|
mode = "w" if overwrite else "w-"
|
|
262
264
|
group_handler = ZarrGroupHandler(
|
|
263
265
|
store=store, mode=mode, cache=False, zarr_format=meta.zarr_format
|
|
264
266
|
)
|
|
265
|
-
image_handler = get_image_meta_handler(
|
|
267
|
+
image_handler = get_image_meta_handler(
|
|
268
|
+
version=ngff_version, group_handler=group_handler
|
|
269
|
+
)
|
|
266
270
|
image_handler.write_meta(meta)
|
|
267
271
|
|
|
268
272
|
init_empty_pyramid(
|
|
@@ -40,7 +40,7 @@ def create_synthetic_ome_zarr(
|
|
|
40
40
|
dimension_separator: Literal[".", "/"] = "/",
|
|
41
41
|
compressors: CompressorLike = "auto",
|
|
42
42
|
overwrite: bool = False,
|
|
43
|
-
|
|
43
|
+
ngff_version: NgffVersions = DefaultNgffVersion,
|
|
44
44
|
) -> OmeZarrContainer:
|
|
45
45
|
"""Create an empty OME-Zarr image with the given shape and metadata.
|
|
46
46
|
|
|
@@ -72,7 +72,7 @@ def create_synthetic_ome_zarr(
|
|
|
72
72
|
compressors (CompressorLike): The compressors to use. Defaults to "auto".
|
|
73
73
|
overwrite (bool, optional): Whether to overwrite an existing image.
|
|
74
74
|
Defaults to True.
|
|
75
|
-
|
|
75
|
+
ngff_version (NgffVersion, optional): The version of the OME-Zarr specification.
|
|
76
76
|
Defaults to DefaultNgffVersion.
|
|
77
77
|
"""
|
|
78
78
|
if isinstance(reference_sample, str):
|
|
@@ -105,7 +105,7 @@ def create_synthetic_ome_zarr(
|
|
|
105
105
|
overwrite=overwrite,
|
|
106
106
|
dimension_separator=dimension_separator,
|
|
107
107
|
compressors=compressors,
|
|
108
|
-
|
|
108
|
+
ngff_version=ngff_version,
|
|
109
109
|
)
|
|
110
110
|
|
|
111
111
|
image = ome_zarr.get_image()
|
ngio/images/_image.py
CHANGED
|
@@ -823,7 +823,7 @@ def derive_image_container(
|
|
|
823
823
|
dimension_separator=dimension_separator,
|
|
824
824
|
compressors=compressors,
|
|
825
825
|
overwrite=overwrite,
|
|
826
|
-
|
|
826
|
+
ngff_version=ngff_version,
|
|
827
827
|
)
|
|
828
828
|
image_container = ImagesContainer(handler)
|
|
829
829
|
|
|
@@ -885,7 +885,7 @@ def _parse_str_or_model(
|
|
|
885
885
|
)
|
|
886
886
|
elif channel_selection.mode == "wavelength_id":
|
|
887
887
|
return image.get_channel_idx(
|
|
888
|
-
|
|
888
|
+
wavelength_id=str(channel_selection.identifier)
|
|
889
889
|
)
|
|
890
890
|
elif channel_selection.mode == "index":
|
|
891
891
|
return int(channel_selection.identifier)
|
ngio/images/_label.py
CHANGED
|
@@ -845,7 +845,7 @@ def create_empty_ome_zarr(
|
|
|
845
845
|
channel_colors: Sequence[str] | None = None,
|
|
846
846
|
channel_active: Sequence[bool] | None = None,
|
|
847
847
|
overwrite: bool = False,
|
|
848
|
-
|
|
848
|
+
ngff_version: NgffVersions = DefaultNgffVersion,
|
|
849
849
|
) -> OmeZarrContainer:
|
|
850
850
|
"""Create an empty OME-Zarr image with the given shape and metadata.
|
|
851
851
|
|
|
@@ -885,7 +885,7 @@ def create_empty_ome_zarr(
|
|
|
885
885
|
active. Defaults to None.
|
|
886
886
|
overwrite (bool, optional): Whether to overwrite an existing image.
|
|
887
887
|
Defaults to True.
|
|
888
|
-
|
|
888
|
+
ngff_version (NgffVersion, optional): The version of the OME-Zarr specification.
|
|
889
889
|
Defaults to DefaultNgffVersion.
|
|
890
890
|
"""
|
|
891
891
|
handler = create_empty_image_container(
|
|
@@ -906,7 +906,7 @@ def create_empty_ome_zarr(
|
|
|
906
906
|
dimension_separator=dimension_separator,
|
|
907
907
|
compressors=compressors,
|
|
908
908
|
overwrite=overwrite,
|
|
909
|
-
|
|
909
|
+
ngff_version=ngff_version,
|
|
910
910
|
)
|
|
911
911
|
|
|
912
912
|
ome_zarr = OmeZarrContainer(group_handler=handler)
|
|
@@ -942,7 +942,7 @@ def create_ome_zarr_from_array(
|
|
|
942
942
|
dimension_separator: Literal[".", "/"] = "/",
|
|
943
943
|
compressors: CompressorLike = "auto",
|
|
944
944
|
overwrite: bool = False,
|
|
945
|
-
|
|
945
|
+
ngff_version: NgffVersions = DefaultNgffVersion,
|
|
946
946
|
) -> OmeZarrContainer:
|
|
947
947
|
"""Create an OME-Zarr image from a numpy array.
|
|
948
948
|
|
|
@@ -983,7 +983,7 @@ def create_ome_zarr_from_array(
|
|
|
983
983
|
compressors (CompressorLike): The compressors to use. Defaults to "auto".
|
|
984
984
|
overwrite (bool, optional): Whether to overwrite an existing image.
|
|
985
985
|
Defaults to True.
|
|
986
|
-
|
|
986
|
+
ngff_version (str, optional): The version of the OME-Zarr specification.
|
|
987
987
|
Defaults to DefaultNgffVersion.
|
|
988
988
|
"""
|
|
989
989
|
handler = create_empty_image_container(
|
|
@@ -1004,7 +1004,7 @@ def create_ome_zarr_from_array(
|
|
|
1004
1004
|
overwrite=overwrite,
|
|
1005
1005
|
dimension_separator=dimension_separator,
|
|
1006
1006
|
compressors=compressors,
|
|
1007
|
-
|
|
1007
|
+
ngff_version=ngff_version,
|
|
1008
1008
|
)
|
|
1009
1009
|
|
|
1010
1010
|
ome_zarr = OmeZarrContainer(group_handler=handler)
|
ngio/tables/_tables_container.py
CHANGED
|
@@ -359,13 +359,10 @@ ImplementedTables().add_implementation(ConditionTableV1)
|
|
|
359
359
|
def open_tables_container(
|
|
360
360
|
store: StoreOrGroup,
|
|
361
361
|
cache: bool = False,
|
|
362
|
-
mode: AccessModeLiteral = "
|
|
363
|
-
parallel_safe: bool = False,
|
|
362
|
+
mode: AccessModeLiteral = "r+",
|
|
364
363
|
) -> TablesContainer:
|
|
365
364
|
"""Open a table handler from a Zarr store."""
|
|
366
|
-
handler = ZarrGroupHandler(
|
|
367
|
-
store=store, cache=cache, mode=mode, parallel_safe=parallel_safe
|
|
368
|
-
)
|
|
365
|
+
handler = ZarrGroupHandler(store=store, cache=cache, mode=mode)
|
|
369
366
|
return TablesContainer(handler)
|
|
370
367
|
|
|
371
368
|
|
|
@@ -373,12 +370,13 @@ def open_table(
|
|
|
373
370
|
store: StoreOrGroup,
|
|
374
371
|
backend: TableBackend | None = None,
|
|
375
372
|
cache: bool = False,
|
|
376
|
-
mode: AccessModeLiteral = "
|
|
377
|
-
parallel_safe: bool = False,
|
|
373
|
+
mode: AccessModeLiteral = "r+",
|
|
378
374
|
) -> Table:
|
|
379
375
|
"""Open a table from a Zarr store."""
|
|
380
376
|
handler = ZarrGroupHandler(
|
|
381
|
-
store=store,
|
|
377
|
+
store=store,
|
|
378
|
+
cache=cache,
|
|
379
|
+
mode=mode,
|
|
382
380
|
)
|
|
383
381
|
meta = _get_meta(handler)
|
|
384
382
|
return ImplementedTables().get_table(
|
|
@@ -391,12 +389,13 @@ def open_table_as(
|
|
|
391
389
|
table_cls: type[TableType],
|
|
392
390
|
backend: TableBackend | None = None,
|
|
393
391
|
cache: bool = False,
|
|
394
|
-
mode: AccessModeLiteral = "
|
|
395
|
-
parallel_safe: bool = False,
|
|
392
|
+
mode: AccessModeLiteral = "r+",
|
|
396
393
|
) -> TableType:
|
|
397
394
|
"""Open a table from a Zarr store as a specific type."""
|
|
398
395
|
handler = ZarrGroupHandler(
|
|
399
|
-
store=store,
|
|
396
|
+
store=store,
|
|
397
|
+
cache=cache,
|
|
398
|
+
mode=mode,
|
|
400
399
|
)
|
|
401
400
|
return table_cls.from_handler(
|
|
402
401
|
handler=handler,
|
|
@@ -410,12 +409,20 @@ def write_table(
|
|
|
410
409
|
backend: TableBackend = DefaultTableBackend,
|
|
411
410
|
cache: bool = False,
|
|
412
411
|
mode: AccessModeLiteral = "a",
|
|
413
|
-
parallel_safe: bool = False,
|
|
414
412
|
) -> None:
|
|
415
|
-
"""Write a table to a Zarr store.
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
413
|
+
"""Write a table to a Zarr store.
|
|
414
|
+
|
|
415
|
+
A table will be created at the given store location.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
store (StoreOrGroup): The Zarr store or group to write the table to.
|
|
419
|
+
table (Table): The table to write.
|
|
420
|
+
backend (TableBackend): The backend to use for writing the table.
|
|
421
|
+
cache (bool): Whether to use caching for the Zarr group handler.
|
|
422
|
+
mode (AccessModeLiteral): The access mode to use for the Zarr group handler.
|
|
423
|
+
|
|
424
|
+
"""
|
|
425
|
+
handler = ZarrGroupHandler(store=store, cache=cache, mode=mode)
|
|
419
426
|
table.set_backend(
|
|
420
427
|
handler=handler,
|
|
421
428
|
backend=backend,
|
ngio/tables/v1/_roi_table.py
CHANGED
|
@@ -86,10 +86,10 @@ def _dataframe_to_rois(
|
|
|
86
86
|
) -> dict[str, Roi]:
|
|
87
87
|
"""Convert a DataFrame to a WorldCooROI object."""
|
|
88
88
|
# Validate the columns of the DataFrame
|
|
89
|
-
|
|
90
|
-
if len(
|
|
89
|
+
_missing_columns = set(required_columns).difference(set(dataframe.columns))
|
|
90
|
+
if len(_missing_columns) != 0:
|
|
91
91
|
raise NgioTableValidationError(
|
|
92
|
-
f"Could not find required columns: {
|
|
92
|
+
f"Could not find required columns: {_missing_columns} in the table."
|
|
93
93
|
)
|
|
94
94
|
|
|
95
95
|
extra_columns = set(dataframe.columns).difference(
|
ngio/utils/__init__.py
CHANGED
|
@@ -18,6 +18,7 @@ from ngio.utils._fractal_fsspec_store import fractal_fsspec_store
|
|
|
18
18
|
from ngio.utils._logger import ngio_logger, ngio_warn, set_logger_level
|
|
19
19
|
from ngio.utils._zarr_utils import (
|
|
20
20
|
AccessModeLiteral,
|
|
21
|
+
NgioCache,
|
|
21
22
|
StoreOrGroup,
|
|
22
23
|
ZarrGroupHandler,
|
|
23
24
|
open_group_wrapper,
|
|
@@ -28,6 +29,7 @@ set_logger_level(os.getenv("NGIO_LOGGER_LEVEL", "WARNING"))
|
|
|
28
29
|
__all__ = [
|
|
29
30
|
# Zarr
|
|
30
31
|
"AccessModeLiteral",
|
|
32
|
+
"NgioCache",
|
|
31
33
|
# Errors
|
|
32
34
|
"NgioFileExistsError",
|
|
33
35
|
"NgioFileNotFoundError",
|
ngio/utils/_cache.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from typing import Generic, TypeVar
|
|
2
|
+
|
|
3
|
+
T = TypeVar("T")
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class NgioCache(Generic[T]):
|
|
7
|
+
"""A simple cache for NGIO objects."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, use_cache: bool = True):
|
|
10
|
+
self._cache: dict[str, T] = {}
|
|
11
|
+
self._use_cache = use_cache
|
|
12
|
+
|
|
13
|
+
def _cache_sanity_check(self) -> None:
|
|
14
|
+
if len(self._cache) > 0:
|
|
15
|
+
raise RuntimeError(
|
|
16
|
+
"Cache is disabled, but cache contains items. "
|
|
17
|
+
"This indicates a logic error."
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def use_cache(self) -> bool:
|
|
22
|
+
return self._use_cache
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def cache(self) -> dict[str, T]:
|
|
26
|
+
return self._cache
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def is_empty(self) -> bool:
|
|
30
|
+
return len(self._cache) == 0
|
|
31
|
+
|
|
32
|
+
def get(self, key: str, default: T | None = None) -> T | None:
|
|
33
|
+
if not self._use_cache:
|
|
34
|
+
self._cache_sanity_check()
|
|
35
|
+
return default
|
|
36
|
+
return self._cache.get(key, default)
|
|
37
|
+
|
|
38
|
+
def set(self, key: str, value: T, overwrite: bool = True) -> None:
|
|
39
|
+
if not self._use_cache:
|
|
40
|
+
self._cache_sanity_check()
|
|
41
|
+
return
|
|
42
|
+
self._cache[key] = value
|
|
43
|
+
|
|
44
|
+
def clear(self) -> None:
|
|
45
|
+
if not self._use_cache:
|
|
46
|
+
self._cache_sanity_check()
|
|
47
|
+
return
|
|
48
|
+
self._cache.clear()
|
ngio/utils/_zarr_utils.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Common utilities for working with Zarr groups in consistent ways."""
|
|
2
2
|
|
|
3
|
+
import warnings
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import Literal
|
|
5
6
|
|
|
@@ -7,12 +8,16 @@ import fsspec
|
|
|
7
8
|
import zarr
|
|
8
9
|
from filelock import BaseFileLock, FileLock
|
|
9
10
|
from zarr.abc.store import Store
|
|
10
|
-
from zarr.core.array import CompressorLike
|
|
11
11
|
from zarr.errors import ContainsGroupError
|
|
12
12
|
from zarr.storage import FsspecStore, LocalStore, MemoryStore
|
|
13
13
|
|
|
14
|
-
from ngio.utils import
|
|
15
|
-
from ngio.utils._errors import
|
|
14
|
+
from ngio.utils._cache import NgioCache
|
|
15
|
+
from ngio.utils._errors import (
|
|
16
|
+
NgioError,
|
|
17
|
+
NgioFileExistsError,
|
|
18
|
+
NgioFileNotFoundError,
|
|
19
|
+
NgioValueError,
|
|
20
|
+
)
|
|
16
21
|
|
|
17
22
|
AccessModeLiteral = Literal["r", "r+", "w", "w-", "a"]
|
|
18
23
|
# StoreLike is more restrictive than it could be
|
|
@@ -27,32 +32,33 @@ StoreOrGroup = GenericStore | zarr.Group
|
|
|
27
32
|
|
|
28
33
|
def _check_store(store) -> NgioSupportedStore:
|
|
29
34
|
"""Check the store and return a valid store."""
|
|
30
|
-
if isinstance(store, NgioSupportedStore):
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
if not isinstance(store, NgioSupportedStore):
|
|
36
|
+
warnings.warn(
|
|
37
|
+
f"Store type {type(store)} is not explicitly supported. "
|
|
38
|
+
f"Supported types are: {NgioSupportedStore}. "
|
|
39
|
+
"Proceeding, but this may lead to unexpected behavior.",
|
|
40
|
+
UserWarning,
|
|
41
|
+
stacklevel=2,
|
|
42
|
+
)
|
|
43
|
+
return store
|
|
37
44
|
|
|
38
45
|
|
|
39
|
-
def _check_group(
|
|
46
|
+
def _check_group(
|
|
47
|
+
group: zarr.Group, mode: AccessModeLiteral | None = None
|
|
48
|
+
) -> zarr.Group:
|
|
40
49
|
"""Check the group and return a valid group."""
|
|
41
|
-
if group.read_only and mode in [
|
|
42
|
-
raise NgioValueError(
|
|
43
|
-
"The group is read only. Cannot open in write mode ['w', 'w-']"
|
|
44
|
-
)
|
|
50
|
+
if group.read_only and mode not in [None, "r"]:
|
|
51
|
+
raise NgioValueError(f"The group is read only. Cannot open in mode {mode}.")
|
|
45
52
|
|
|
46
53
|
if mode == "r" and not group.read_only:
|
|
47
54
|
# let's make sure we don't accidentally write to the group
|
|
48
55
|
group = zarr.open_group(store=group.store, path=group.path, mode="r")
|
|
49
|
-
|
|
50
56
|
return group
|
|
51
57
|
|
|
52
58
|
|
|
53
59
|
def open_group_wrapper(
|
|
54
60
|
store: StoreOrGroup,
|
|
55
|
-
mode: AccessModeLiteral,
|
|
61
|
+
mode: AccessModeLiteral | None = None,
|
|
56
62
|
zarr_format: Literal[2, 3] | None = None,
|
|
57
63
|
) -> zarr.Group:
|
|
58
64
|
"""Wrapper around zarr.open_group with some additional checks.
|
|
@@ -72,6 +78,7 @@ def open_group_wrapper(
|
|
|
72
78
|
|
|
73
79
|
try:
|
|
74
80
|
_check_store(store)
|
|
81
|
+
mode = mode if mode is not None else "a"
|
|
75
82
|
group = zarr.open_group(store=store, mode=mode, zarr_format=zarr_format)
|
|
76
83
|
|
|
77
84
|
except FileExistsError as e:
|
|
@@ -98,68 +105,32 @@ class ZarrGroupHandler:
|
|
|
98
105
|
store: StoreOrGroup,
|
|
99
106
|
zarr_format: Literal[2, 3] | None = None,
|
|
100
107
|
cache: bool = False,
|
|
101
|
-
mode: AccessModeLiteral =
|
|
102
|
-
parallel_safe: bool = False,
|
|
103
|
-
parent: "ZarrGroupHandler | None" = None,
|
|
108
|
+
mode: AccessModeLiteral | None = None,
|
|
104
109
|
):
|
|
105
110
|
"""Initialize the handler.
|
|
106
111
|
|
|
107
112
|
Args:
|
|
108
113
|
store (StoreOrGroup): The Zarr store or group containing the image data.
|
|
109
114
|
meta_mode (str): The mode of the metadata handler.
|
|
110
|
-
zarr_format (int): The Zarr format version to use.
|
|
115
|
+
zarr_format (int | None): The Zarr format version to use.
|
|
111
116
|
cache (bool): Whether to cache the metadata.
|
|
112
|
-
mode (str): The mode of the store.
|
|
113
|
-
parallel_safe (bool): If True, the handler will create a lock file to make
|
|
114
|
-
that can be used to make the handler parallel safe.
|
|
115
|
-
Be aware that the lock needs to be used manually.
|
|
116
|
-
parent (ZarrGroupHandler | None): The parent handler.
|
|
117
|
+
mode (str | None): The mode of the store.
|
|
117
118
|
"""
|
|
118
|
-
if mode not in ["r", "r+", "w", "w-", "a"]:
|
|
119
|
+
if mode not in ["r", "r+", "w", "w-", "a", None]:
|
|
119
120
|
raise NgioValueError(f"Mode {mode} is not supported.")
|
|
120
121
|
|
|
121
|
-
if parallel_safe and cache:
|
|
122
|
-
raise NgioValueError(
|
|
123
|
-
"The cache and parallel_safe options are mutually exclusive."
|
|
124
|
-
"If you want to use the lock mechanism, you should not use the cache."
|
|
125
|
-
)
|
|
126
|
-
|
|
127
122
|
group = open_group_wrapper(store=store, mode=mode, zarr_format=zarr_format)
|
|
128
|
-
_store = group.store
|
|
129
|
-
|
|
130
|
-
# Make sure the cache is set in the attrs
|
|
131
|
-
# in the same way as the cache in the handler
|
|
132
|
-
|
|
133
|
-
## TODO
|
|
134
|
-
# Figure out how to handle the cache in the new zarr version
|
|
135
|
-
# group.attrs.cache = cache
|
|
136
|
-
|
|
137
|
-
if parallel_safe:
|
|
138
|
-
if not isinstance(_store, LocalStore):
|
|
139
|
-
raise NgioValueError(
|
|
140
|
-
"The store needs to be a LocalStore to use the lock mechanism. "
|
|
141
|
-
f"Instead, got {_store.__class__.__name__}."
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
store_path = _store.root / group.path
|
|
145
|
-
self._lock_path = store_path.with_suffix(".lock")
|
|
146
|
-
self._lock = FileLock(self._lock_path, timeout=10)
|
|
147
|
-
|
|
148
|
-
else:
|
|
149
|
-
self._lock_path = None
|
|
150
|
-
self._lock = None
|
|
151
|
-
|
|
152
123
|
self._group = group
|
|
153
|
-
self._mode = mode
|
|
154
124
|
self.use_cache = cache
|
|
155
|
-
|
|
156
|
-
self.
|
|
157
|
-
self.
|
|
125
|
+
|
|
126
|
+
self._group_cache: NgioCache[zarr.Group] = NgioCache(use_cache=cache)
|
|
127
|
+
self._array_cache: NgioCache[zarr.Array] = NgioCache(use_cache=cache)
|
|
128
|
+
self._lock: tuple[Path, BaseFileLock] | None = None
|
|
158
129
|
|
|
159
130
|
def __repr__(self) -> str:
|
|
160
131
|
"""Return a string representation of the handler."""
|
|
161
132
|
return (
|
|
162
|
-
f"ZarrGroupHandler(full_url={self.full_url},
|
|
133
|
+
f"ZarrGroupHandler(full_url={self.full_url}, read_only={self.read_only}, "
|
|
163
134
|
f"cache={self.use_cache}"
|
|
164
135
|
)
|
|
165
136
|
|
|
@@ -183,35 +154,55 @@ class ZarrGroupHandler:
|
|
|
183
154
|
return self._group.metadata.zarr_format
|
|
184
155
|
|
|
185
156
|
@property
|
|
186
|
-
def
|
|
187
|
-
"""Return the
|
|
188
|
-
return self.
|
|
157
|
+
def read_only(self) -> bool:
|
|
158
|
+
"""Return whether the group is read only."""
|
|
159
|
+
return self._group.read_only
|
|
160
|
+
|
|
161
|
+
def _create_lock(self) -> tuple[Path, BaseFileLock]:
|
|
162
|
+
"""Create the lock."""
|
|
163
|
+
if self._lock is not None:
|
|
164
|
+
return self._lock
|
|
165
|
+
|
|
166
|
+
if self.use_cache is True:
|
|
167
|
+
raise NgioValueError(
|
|
168
|
+
"Lock mechanism is not compatible with caching. "
|
|
169
|
+
"Please set cache=False to use the lock mechanism."
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
if not isinstance(self.store, LocalStore):
|
|
173
|
+
raise NgioValueError(
|
|
174
|
+
"The store needs to be a LocalStore to use the lock mechanism. "
|
|
175
|
+
f"Instead, got {self.store.__class__.__name__}."
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
store_path = Path(self.store.root) / self.group.path
|
|
179
|
+
_lock_path = store_path.with_suffix(".lock")
|
|
180
|
+
_lock = FileLock(_lock_path, timeout=10)
|
|
181
|
+
return _lock_path, _lock
|
|
189
182
|
|
|
190
183
|
@property
|
|
191
184
|
def lock(self) -> BaseFileLock:
|
|
192
185
|
"""Return the lock."""
|
|
193
186
|
if self._lock is None:
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
"Reopen the handler with parallel_safe=True."
|
|
197
|
-
)
|
|
198
|
-
return self._lock
|
|
187
|
+
self._lock = self._create_lock()
|
|
188
|
+
return self._lock[1]
|
|
199
189
|
|
|
200
190
|
@property
|
|
201
|
-
def
|
|
202
|
-
"""Return the
|
|
203
|
-
|
|
191
|
+
def lock_path(self) -> Path:
|
|
192
|
+
"""Return the lock path."""
|
|
193
|
+
if self._lock is None:
|
|
194
|
+
self._lock = self._create_lock()
|
|
195
|
+
return self._lock[0]
|
|
204
196
|
|
|
205
197
|
def remove_lock(self) -> None:
|
|
206
198
|
"""Return the lock."""
|
|
207
|
-
if self._lock is None
|
|
199
|
+
if self._lock is None:
|
|
208
200
|
return None
|
|
209
201
|
|
|
210
|
-
lock_path =
|
|
211
|
-
if lock_path.exists() and
|
|
202
|
+
lock_path, lock = self._lock
|
|
203
|
+
if lock_path.exists() and lock.lock_counter == 0:
|
|
212
204
|
lock_path.unlink()
|
|
213
205
|
self._lock = None
|
|
214
|
-
self._lock_path = None
|
|
215
206
|
return None
|
|
216
207
|
|
|
217
208
|
raise NgioValueError("The lock is still in use. Cannot remove it.")
|
|
@@ -222,10 +213,7 @@ class ZarrGroupHandler:
|
|
|
222
213
|
This is useful when the group has been modified
|
|
223
214
|
outside of the handler.
|
|
224
215
|
"""
|
|
225
|
-
if self.
|
|
226
|
-
mode = "r"
|
|
227
|
-
else:
|
|
228
|
-
mode = "r+"
|
|
216
|
+
mode = "r" if self.read_only else "r+"
|
|
229
217
|
return zarr.open_group(
|
|
230
218
|
store=self._group.store,
|
|
231
219
|
path=self._group.path,
|
|
@@ -233,72 +221,57 @@ class ZarrGroupHandler:
|
|
|
233
221
|
zarr_format=self._group.metadata.zarr_format,
|
|
234
222
|
)
|
|
235
223
|
|
|
224
|
+
def reopen_handler(self) -> "ZarrGroupHandler":
|
|
225
|
+
"""Reopen the handler.
|
|
226
|
+
|
|
227
|
+
This is useful when the group has been modified
|
|
228
|
+
outside of the handler.
|
|
229
|
+
"""
|
|
230
|
+
mode = "r" if self.read_only else "r+"
|
|
231
|
+
group = self.reopen_group()
|
|
232
|
+
return ZarrGroupHandler(
|
|
233
|
+
store=group,
|
|
234
|
+
zarr_format=group.metadata.zarr_format,
|
|
235
|
+
cache=self.use_cache,
|
|
236
|
+
mode=mode,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
def clean_cache(self) -> None:
|
|
240
|
+
"""Clear the cached metadata."""
|
|
241
|
+
group = self.reopen_group()
|
|
242
|
+
self.__init__(
|
|
243
|
+
store=group,
|
|
244
|
+
zarr_format=group.metadata.zarr_format,
|
|
245
|
+
cache=self.use_cache,
|
|
246
|
+
mode="r" if self.read_only else "r+",
|
|
247
|
+
)
|
|
248
|
+
|
|
236
249
|
@property
|
|
237
250
|
def group(self) -> zarr.Group:
|
|
238
251
|
"""Return the group."""
|
|
239
|
-
if self.
|
|
240
|
-
# If we are
|
|
252
|
+
if self.use_cache is False:
|
|
253
|
+
# If we are not using cache, we need to reopen the group
|
|
241
254
|
# to make sure that the attributes are up to date
|
|
242
255
|
return self.reopen_group()
|
|
243
256
|
return self._group
|
|
244
257
|
|
|
245
|
-
def add_to_cache(self, key: str, value: object) -> None:
|
|
246
|
-
"""Add an object to the cache."""
|
|
247
|
-
if not self.use_cache:
|
|
248
|
-
return None
|
|
249
|
-
self._cache[key] = value
|
|
250
|
-
|
|
251
|
-
def get_from_cache(self, key: str) -> object | None:
|
|
252
|
-
"""Get an object from the cache."""
|
|
253
|
-
if not self.use_cache:
|
|
254
|
-
return None
|
|
255
|
-
return self._cache.get(key, None)
|
|
256
|
-
|
|
257
|
-
def clean_cache(self) -> None:
|
|
258
|
-
"""Clear the cached metadata."""
|
|
259
|
-
self._cache = {}
|
|
260
|
-
|
|
261
258
|
def load_attrs(self) -> dict:
|
|
262
259
|
"""Load the attributes of the group."""
|
|
263
|
-
|
|
264
|
-
if attrs is not None and isinstance(attrs, dict):
|
|
265
|
-
return attrs
|
|
266
|
-
|
|
267
|
-
attrs = dict(self.group.attrs)
|
|
268
|
-
|
|
269
|
-
self.add_to_cache("attrs", attrs)
|
|
270
|
-
return attrs
|
|
271
|
-
|
|
272
|
-
def _write_attrs(self, attrs: dict, overwrite: bool = False) -> None:
|
|
273
|
-
"""Write the metadata to the store."""
|
|
274
|
-
if self.group.read_only:
|
|
275
|
-
raise NgioValueError("The group is read only. Cannot write metadata.")
|
|
276
|
-
|
|
277
|
-
# we need to invalidate the current attrs cache
|
|
278
|
-
self.add_to_cache("attrs", None)
|
|
279
|
-
if overwrite:
|
|
280
|
-
self.group.attrs.clear()
|
|
281
|
-
|
|
282
|
-
self.group.attrs.update(attrs)
|
|
260
|
+
return self.reopen_group().attrs.asdict()
|
|
283
261
|
|
|
284
262
|
def write_attrs(self, attrs: dict, overwrite: bool = False) -> None:
|
|
285
263
|
"""Write the metadata to the store."""
|
|
286
264
|
# Maybe we should use the lock here
|
|
287
|
-
self.
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return group_or_array
|
|
294
|
-
|
|
295
|
-
group_or_array = self.group.get(path, None)
|
|
296
|
-
self.add_to_cache(path, group_or_array)
|
|
297
|
-
return group_or_array
|
|
265
|
+
if self.read_only:
|
|
266
|
+
raise NgioValueError("The group is read only. Cannot write metadata.")
|
|
267
|
+
group = self.reopen_group()
|
|
268
|
+
if overwrite:
|
|
269
|
+
group.attrs.clear()
|
|
270
|
+
group.attrs.update(attrs)
|
|
298
271
|
|
|
299
272
|
def create_group(self, path: str, overwrite: bool = False) -> zarr.Group:
|
|
300
273
|
"""Create a group in the group."""
|
|
301
|
-
if self.
|
|
274
|
+
if self.group.read_only:
|
|
302
275
|
raise NgioValueError("Cannot create a group in read only mode.")
|
|
303
276
|
|
|
304
277
|
try:
|
|
@@ -308,7 +281,7 @@ class ZarrGroupHandler:
|
|
|
308
281
|
f"A Zarr group already exists at {path}, "
|
|
309
282
|
"consider setting overwrite=True."
|
|
310
283
|
) from e
|
|
311
|
-
self.
|
|
284
|
+
self._group_cache.set(path, group, overwrite=overwrite)
|
|
312
285
|
return group
|
|
313
286
|
|
|
314
287
|
def get_group(
|
|
@@ -334,18 +307,22 @@ class ZarrGroupHandler:
|
|
|
334
307
|
if overwrite:
|
|
335
308
|
return self.create_group(path, overwrite=overwrite)
|
|
336
309
|
|
|
337
|
-
group = self.
|
|
310
|
+
group = self._group_cache.get(path)
|
|
338
311
|
if isinstance(group, zarr.Group):
|
|
339
312
|
return group
|
|
340
313
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
314
|
+
group = self.group.get(path, default=None)
|
|
315
|
+
if isinstance(group, zarr.Group):
|
|
316
|
+
self._group_cache.set(path, group, overwrite=overwrite)
|
|
317
|
+
return group
|
|
318
|
+
|
|
319
|
+
if isinstance(group, zarr.Array):
|
|
320
|
+
raise NgioValueError(f"The object at {path} is not a group, but an array.")
|
|
345
321
|
|
|
346
322
|
if not create_mode:
|
|
347
323
|
raise NgioFileNotFoundError(f"No group found at {path}")
|
|
348
324
|
group = self.create_group(path)
|
|
325
|
+
self._group_cache.set(path, group, overwrite=overwrite)
|
|
349
326
|
return group
|
|
350
327
|
|
|
351
328
|
def safe_get_group(
|
|
@@ -369,56 +346,17 @@ class ZarrGroupHandler:
|
|
|
369
346
|
|
|
370
347
|
def get_array(self, path: str) -> zarr.Array:
|
|
371
348
|
"""Get an array from the group."""
|
|
372
|
-
array = self.
|
|
373
|
-
if array
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
path: str,
|
|
384
|
-
shape: tuple[int, ...],
|
|
385
|
-
dtype: str,
|
|
386
|
-
chunks: tuple[int, ...] | Literal["auto"] = "auto",
|
|
387
|
-
compressors: CompressorLike = "auto",
|
|
388
|
-
separator: Literal[".", "/"] = "/",
|
|
389
|
-
overwrite: bool = False,
|
|
390
|
-
) -> zarr.Array:
|
|
391
|
-
if self.mode == "r":
|
|
392
|
-
raise NgioValueError("Cannot create an array in read only mode.")
|
|
393
|
-
|
|
394
|
-
if self.zarr_format == 2:
|
|
395
|
-
chunks_encoding = {
|
|
396
|
-
"name": "v2",
|
|
397
|
-
"separator": separator,
|
|
398
|
-
}
|
|
399
|
-
else:
|
|
400
|
-
chunks_encoding = {
|
|
401
|
-
"name": "default",
|
|
402
|
-
"separator": separator,
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
try:
|
|
406
|
-
return self.group.create_array(
|
|
407
|
-
name=path,
|
|
408
|
-
shape=shape,
|
|
409
|
-
dtype=dtype,
|
|
410
|
-
chunks=chunks,
|
|
411
|
-
chunk_key_encoding=chunks_encoding,
|
|
412
|
-
overwrite=overwrite,
|
|
413
|
-
compressors=compressors,
|
|
414
|
-
)
|
|
415
|
-
except ContainsGroupError as e:
|
|
416
|
-
raise NgioFileExistsError(
|
|
417
|
-
f"A Zarr array already exists at {path}, "
|
|
418
|
-
"consider setting overwrite=True."
|
|
419
|
-
) from e
|
|
420
|
-
except Exception as e:
|
|
421
|
-
raise NgioValueError(f"Error creating array at {path}") from e
|
|
349
|
+
array = self._array_cache.get(path)
|
|
350
|
+
if isinstance(array, zarr.Array):
|
|
351
|
+
return array
|
|
352
|
+
array = self.group.get(path, default=None)
|
|
353
|
+
if isinstance(array, zarr.Array):
|
|
354
|
+
self._array_cache.set(path, array)
|
|
355
|
+
return array
|
|
356
|
+
|
|
357
|
+
if isinstance(array, zarr.Group):
|
|
358
|
+
raise NgioValueError(f"The object at {path} is not an array, but a group.")
|
|
359
|
+
raise NgioFileNotFoundError(f"No array found at {path}")
|
|
422
360
|
|
|
423
361
|
def derive_handler(
|
|
424
362
|
self,
|
|
@@ -433,12 +371,7 @@ class ZarrGroupHandler:
|
|
|
433
371
|
"""
|
|
434
372
|
group = self.get_group(path, create_mode=True, overwrite=overwrite)
|
|
435
373
|
return ZarrGroupHandler(
|
|
436
|
-
store=group,
|
|
437
|
-
zarr_format=self.zarr_format,
|
|
438
|
-
cache=self.use_cache,
|
|
439
|
-
mode=self.mode,
|
|
440
|
-
parallel_safe=self._parallel_safe,
|
|
441
|
-
parent=self,
|
|
374
|
+
store=group, zarr_format=self.zarr_format, cache=self.use_cache, mode="r+"
|
|
442
375
|
)
|
|
443
376
|
|
|
444
377
|
def safe_derive_handler(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ngio
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.0a2
|
|
4
4
|
Summary: Next Generation file format IO
|
|
5
5
|
Project-URL: homepage, https://github.com/BioVisionCenter/ngio
|
|
6
6
|
Project-URL: repository, https://github.com/BioVisionCenter/ngio
|
|
@@ -17,8 +17,8 @@ Classifier: Typing :: Typed
|
|
|
17
17
|
Requires-Python: <3.14,>=3.11
|
|
18
18
|
Requires-Dist: aiohttp
|
|
19
19
|
Requires-Dist: anndata
|
|
20
|
-
Requires-Dist: dask[array]
|
|
21
|
-
Requires-Dist: dask[distributed]
|
|
20
|
+
Requires-Dist: dask[array]<2025.11.0
|
|
21
|
+
Requires-Dist: dask[distributed]<2025.11.0
|
|
22
22
|
Requires-Dist: filelock
|
|
23
23
|
Requires-Dist: numpy
|
|
24
24
|
Requires-Dist: ome-zarr-models
|
|
@@ -2,7 +2,7 @@ ngio/__init__.py,sha256=rEgnXuU6TCejUUGsxt4eKmjMhxjYh0fYBxWF4o5YjbE,1435
|
|
|
2
2
|
ngio/common/__init__.py,sha256=aPSuUbdGryrxbnlWrsVNe3LZoBAWC4GijR1BNH1UwuU,612
|
|
3
3
|
ngio/common/_dimensions.py,sha256=w8PYgyWxA8hgJETjFbw5CXf7WrasCL5FbzgfL1in86M,11361
|
|
4
4
|
ngio/common/_masking_roi.py,sha256=ZZTXordEZoq_ADk0OzADvq-5dPOwUBSuNobzFR8fpTw,5697
|
|
5
|
-
ngio/common/_pyramid.py,sha256=
|
|
5
|
+
ngio/common/_pyramid.py,sha256=loQbtoGWs0g5Ke2s1tSBDsvos0235F-ElhFspsMsIr0,9238
|
|
6
6
|
ngio/common/_roi.py,sha256=9fKFTHoUiP0xmxvQiFkNmIuwWg3bFuRaAx-froCSqvA,11487
|
|
7
7
|
ngio/common/_synt_images_utils.py,sha256=B6uYOW1NyrM06YMR-csca3_YnAAkPRTbvnbLdy9tk9E,3188
|
|
8
8
|
ngio/common/_zoom.py,sha256=U01c-vqXjzZkrpd9Yvs24frVfTls_xPJeeaFCGmUwYI,6727
|
|
@@ -15,15 +15,15 @@ ngio/experimental/iterators/_mappers.py,sha256=VVVsjems57wJUnWeufUFcgqa23k7VPeFL
|
|
|
15
15
|
ngio/experimental/iterators/_rois_utils.py,sha256=Q-8lQ26neYn63h_RvfypYqvrq2UUN2O3xqVe57k_ufU,4363
|
|
16
16
|
ngio/experimental/iterators/_segmentation.py,sha256=xzotGvTn04HPeMeXZ_URnQqWco6d2lH6Ng6vkCUh9NM,9153
|
|
17
17
|
ngio/hcs/__init__.py,sha256=G8j9vD-liLeB_UeGtKYIgshWvJnUA6ks9GwjvWBLdHs,357
|
|
18
|
-
ngio/hcs/_plate.py,sha256=
|
|
18
|
+
ngio/hcs/_plate.py,sha256=9kvvI_qu946xbRqWeY9q1bp45duREyOx-3a-QeKkrGU,43376
|
|
19
19
|
ngio/images/__init__.py,sha256=9Whvt7GTiCgT_vXaEEqGnDaY1-UsRk3dhLTv091F_g4,1211
|
|
20
20
|
ngio/images/_abstract_image.py,sha256=hrB9xn4MFRxnxE1d7HKnM8SXVPUGhMD9u32yBHTsFiU,18517
|
|
21
|
-
ngio/images/_create.py,sha256=
|
|
22
|
-
ngio/images/_create_synt_container.py,sha256=
|
|
23
|
-
ngio/images/_image.py,sha256=
|
|
24
|
-
ngio/images/_label.py,sha256=
|
|
21
|
+
ngio/images/_create.py,sha256=IIdX0vER9J3c3aJYZRB7bDGMGv_kv0CbaJjGJM7yjyY,10536
|
|
22
|
+
ngio/images/_create_synt_container.py,sha256=HeRscNNWYHY3rJTO-oKDr5mMuQD6RHWThrv9Rc9yZdg,5539
|
|
23
|
+
ngio/images/_image.py,sha256=LP_XZrL0OLSasPTxaySldKSysyMZvznhFm-E3YLN5Tg,33488
|
|
24
|
+
ngio/images/_label.py,sha256=o7dFDfn-vAgBA80g-vR54M64av_tNebduQqtnG0AkIw,11926
|
|
25
25
|
ngio/images/_masked_image.py,sha256=YhbBzgPZMav6rX0WYue1BaxAzEIsfaQrxUIOK6ZWZcw,18848
|
|
26
|
-
ngio/images/_ome_zarr_container.py,sha256=
|
|
26
|
+
ngio/images/_ome_zarr_container.py,sha256=IlXZojr9HDnfGboLAWD0TlV5Bv7J5fvzKqYc8U43WdU,38777
|
|
27
27
|
ngio/images/_table_ops.py,sha256=jFv_AMqoB4JBpoWsMtZppZVW7dAOC_u-JpfNm8b33kY,15292
|
|
28
28
|
ngio/io_pipes/__init__.py,sha256=arW_7GWzZs82kPNKdm_6B1sIDFV0lWwp-ZaORr9Q1FQ,2412
|
|
29
29
|
ngio/io_pipes/_io_pipes.py,sha256=KuwMYofE11EKx0iplWXBQZ-eE1A9YpGDprWh98cUlAI,10710
|
|
@@ -58,7 +58,7 @@ ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png,sha
|
|
|
58
58
|
ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/raw.jpg,sha256=82lejQAIokj5w9g-qqhysDTWpHtNvJTkdURG_BjqIxQ,37743
|
|
59
59
|
ngio/tables/__init__.py,sha256=_BV3sclNMLITu_J8_3DkkUrCB6Kro0HzeWLDCD1ivKM,877
|
|
60
60
|
ngio/tables/_abstract_table.py,sha256=rwGa47TzbFmosucBWVfFq6JEXtgGvOdUVtU9DIelV88,8204
|
|
61
|
-
ngio/tables/_tables_container.py,sha256=
|
|
61
|
+
ngio/tables/_tables_container.py,sha256=BfwnCsJ3RGnQGRnRXO2XjdaYniL9sxO4HLiJYVgHkOs,12361
|
|
62
62
|
ngio/tables/backends/__init__.py,sha256=MwSRXNF1rWQBFOTDA_vT3oGoNZpviVgytsL5Txnu08I,1619
|
|
63
63
|
ngio/tables/backends/_abstract_backend.py,sha256=M1ogsBpWBiQMV65YweZhA845PAtkzG2BsZCPN_7Xp8U,7613
|
|
64
64
|
ngio/tables/backends/_anndata.py,sha256=KBzXVxaXstVx3ofcLYYFm8ECVFDaHxfUWcKdg5vQPqY,2992
|
|
@@ -73,16 +73,17 @@ ngio/tables/v1/__init__.py,sha256=Wr1_9RZFpaN8FYMTnxT9Yjkw4AS7y9FMWailmB_uj5g,61
|
|
|
73
73
|
ngio/tables/v1/_condition_table.py,sha256=T0Uq5BKkmMoEspt_Rx0U99Ow6S9GAMZDHqvUO5obCAM,1780
|
|
74
74
|
ngio/tables/v1/_feature_table.py,sha256=n9uMHwoBh-_dlOhUXCFbmAjXFVXncNCR3SjE2qzXI68,3821
|
|
75
75
|
ngio/tables/v1/_generic_table.py,sha256=1ktJHeuv7U1g5Z8PFUuTkCjOzcYMQd8xegKHKUedJB8,1240
|
|
76
|
-
ngio/tables/v1/_roi_table.py,sha256=
|
|
76
|
+
ngio/tables/v1/_roi_table.py,sha256=b7lwjsdCSUOCMT6Lx4hwAqGBKC25Q5bdzoK3DQWnAQM,17074
|
|
77
77
|
ngio/transforms/__init__.py,sha256=JA0-Ui7skbXkm9ofN-AEhU1FTLutkMkwTdVD-310frQ,113
|
|
78
78
|
ngio/transforms/_zoom.py,sha256=otyE-vxFnywUJ8U4mHjat-bNG_7_jv62ckTpqDMxyVQ,550
|
|
79
|
-
ngio/utils/__init__.py,sha256=
|
|
79
|
+
ngio/utils/__init__.py,sha256=w_v3BaphrlsVlZF3tZHlyp6dOfRdRHaauBmdwGsjaCE,1174
|
|
80
|
+
ngio/utils/_cache.py,sha256=Ey9fgc_BTdMyqg6c80C0CuGDhOafln8-3e_1MQ0MFzw,1283
|
|
80
81
|
ngio/utils/_datasets.py,sha256=6GtxfPkjutNaeg5BHuJDBP0GudvQXHLU6mmHp_o0bGA,5650
|
|
81
82
|
ngio/utils/_errors.py,sha256=pKQ12LUjQLYE1nUawemA5h7HsgznjaSvV1n2PQU33N0,759
|
|
82
83
|
ngio/utils/_fractal_fsspec_store.py,sha256=RdcCFOgHexRKX9zZvJV5RI-5OPc7VOPS6q_IeRxm24I,1548
|
|
83
84
|
ngio/utils/_logger.py,sha256=N5W0a_xwze4blS1MolidBkTMbjTbg8GPguJZNun3mAE,1392
|
|
84
|
-
ngio/utils/_zarr_utils.py,sha256=
|
|
85
|
-
ngio-0.5.
|
|
86
|
-
ngio-0.5.
|
|
87
|
-
ngio-0.5.
|
|
88
|
-
ngio-0.5.
|
|
85
|
+
ngio/utils/_zarr_utils.py,sha256=Ak3M07hxI9-5W1mG2jxZE8rUrSZnidJ-nEQenKuCqls,14114
|
|
86
|
+
ngio-0.5.0a2.dist-info/METADATA,sha256=Skm_1lG1u3unJ7Ij9mnIYm8RVyU0P-AFISHvl-HEz2M,6124
|
|
87
|
+
ngio-0.5.0a2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
88
|
+
ngio-0.5.0a2.dist-info/licenses/LICENSE,sha256=UgN_a1QCeNh9rZWfz-wORQFxE3elQzLWPQaoK6N6fxQ,1502
|
|
89
|
+
ngio-0.5.0a2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|