patchworks 0.7.0__tar.gz → 0.8.0__tar.gz
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.
- {patchworks-0.7.0 → patchworks-0.8.0}/PKG-INFO +1 -1
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/guide/ome_zarr_napari.md +10 -6
- {patchworks-0.7.0 → patchworks-0.8.0}/src/patchworks/plugins/ome_zarr.py +67 -4
- {patchworks-0.7.0 → patchworks-0.8.0}/tests/test_ome_zarr.py +11 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/.github/workflows/docs.yml +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/.github/workflows/lint.yml +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/.github/workflows/release.yml +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/.gitignore +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/README.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/cliff.toml +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/api/chunks.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/api/cluster.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/api/io.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/api/merge_tile_labels.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/api/plugins/cellpose.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/api/plugins/napari.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/api/plugins/ome_zarr.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/api/relabel.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/api/tile_process.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/examples/cellpose_2d.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/examples/cellpose_2d.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/examples/cellpose_3d.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/examples/cellpose_3d.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/examples/custom.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/examples/custom_method.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/examples/standalone_merge.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/examples/stardist.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/examples/stardist_2d.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/getting_started.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/guide/gpu_distributed.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/guide/merging.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/guide/performance.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/guide/pitfalls.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/guide/skip_empty.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/guide/tiling.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/docs/index.md +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/mkdocs.yml +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/pyproject.toml +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/src/patchworks/__init__.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/src/patchworks/_chunks.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/src/patchworks/_cluster.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/src/patchworks/_core.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/src/patchworks/_io.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/src/patchworks/_merge.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/src/patchworks/_relabel.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/src/patchworks/plugins/__init__.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/src/patchworks/plugins/cellpose.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/src/patchworks/plugins/napari.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/tests/test_core.py +0 -0
- {patchworks-0.7.0 → patchworks-0.8.0}/tests/test_napari.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: patchworks
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Tiled processing of arbitrarily large images with globally consistent labels
|
|
5
5
|
Project-URL: Homepage, https://github.com/imcf/patchworks
|
|
6
6
|
Project-URL: Issues, https://github.com/imcf/patchworks/issues
|
|
@@ -50,12 +50,16 @@ to_ome_zarr("scan.czi", "scan.zarr", n_levels=5) # via bioio
|
|
|
50
50
|
to_ome_zarr("scan.ims", "scan.zarr") # Imaris, native HDF5
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
!!! note "Imaris pyramids
|
|
54
|
-
`.ims` files carry their own resolution pyramid
|
|
55
|
-
only the **full-resolution** level and **builds a fresh NGFF pyramid**
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
!!! note "Imaris pyramids: rebuild (default) or reuse"
|
|
54
|
+
`.ims` files carry their own resolution pyramid. By default `to_ome_zarr`
|
|
55
|
+
reads only the **full-resolution** level and **builds a fresh NGFF pyramid**
|
|
56
|
+
(XY-only, nearest-neighbour, calibrated) for consistency. Pass
|
|
57
|
+
`reuse_pyramid=True` to instead **copy the Imaris levels** as-is — faster,
|
|
58
|
+
no recompute, keeping each level's native scale:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
to_ome_zarr("scan.ims", "scan.zarr", reuse_pyramid=True)
|
|
62
|
+
```
|
|
59
63
|
|
|
60
64
|
### Pixel calibration
|
|
61
65
|
|
|
@@ -255,8 +255,8 @@ def _open_bioio(path: str, scene: int) -> tuple[da.Array, str, PixelSize]:
|
|
|
255
255
|
return arr, axes, pixel_size
|
|
256
256
|
|
|
257
257
|
|
|
258
|
-
def _open_imaris(path: str) -> tuple[da.Array, str, PixelSize]:
|
|
259
|
-
"""Open an Imaris ``.ims``
|
|
258
|
+
def _open_imaris(path: str, level: int = 0) -> tuple[da.Array, str, PixelSize]:
|
|
259
|
+
"""Open an Imaris ``.ims`` *level* lazily → ``(array, axes, pixel_size)``."""
|
|
260
260
|
try:
|
|
261
261
|
from imaris_ims_file_reader.ims import ims
|
|
262
262
|
except ImportError as exc:
|
|
@@ -265,8 +265,8 @@ def _open_imaris(path: str) -> tuple[da.Array, str, PixelSize]:
|
|
|
265
265
|
"Install it with:\n pip install 'patchworks[imaris]'"
|
|
266
266
|
) from exc
|
|
267
267
|
|
|
268
|
-
#
|
|
269
|
-
reader = ims(path, ResolutionLevelLock=
|
|
268
|
+
# The object is array-like and h5py-backed (lazy).
|
|
269
|
+
reader = ims(path, ResolutionLevelLock=level)
|
|
270
270
|
order = _DEFAULT_ORDER[len(_DEFAULT_ORDER) - reader.ndim :]
|
|
271
271
|
arr = da.from_array(reader, chunks=_default_chunks(reader.shape, order))
|
|
272
272
|
|
|
@@ -293,6 +293,46 @@ def _open_imaris(path: str) -> tuple[da.Array, str, PixelSize]:
|
|
|
293
293
|
return arr, axes, pixel_size
|
|
294
294
|
|
|
295
295
|
|
|
296
|
+
def _write_imaris_pyramid(
|
|
297
|
+
path: str,
|
|
298
|
+
out: str,
|
|
299
|
+
*,
|
|
300
|
+
chunks: Union[tuple[int, ...], None],
|
|
301
|
+
overwrite: bool,
|
|
302
|
+
) -> str:
|
|
303
|
+
"""Copy an Imaris file's own resolution levels into an OME-ZARR.
|
|
304
|
+
|
|
305
|
+
Each Imaris ``ResolutionLevel`` is written as a pyramid level with its own
|
|
306
|
+
physical scale, so no downsampling is recomputed. Lazy (h5py-backed) reads
|
|
307
|
+
stream straight to disk.
|
|
308
|
+
"""
|
|
309
|
+
from imaris_ims_file_reader.ims import ims
|
|
310
|
+
|
|
311
|
+
base = ims(path, ResolutionLevelLock=0)
|
|
312
|
+
n_levels = int(getattr(base, "ResolutionLevels", 1) or 1)
|
|
313
|
+
|
|
314
|
+
zarr.open_group(out, mode="w" if overwrite else "w-")
|
|
315
|
+
datasets: list[dict] = []
|
|
316
|
+
axes = ""
|
|
317
|
+
calibrated = False
|
|
318
|
+
for level in range(n_levels):
|
|
319
|
+
arr, axes, ps = _open_imaris(path, level=level)
|
|
320
|
+
scale = _base_scale(axes, ps)
|
|
321
|
+
calibrated = calibrated or bool(ps)
|
|
322
|
+
da.to_zarr(
|
|
323
|
+
arr.rechunk(chunks or _default_chunks(arr.shape, axes)),
|
|
324
|
+
out,
|
|
325
|
+
component=str(level),
|
|
326
|
+
overwrite=True,
|
|
327
|
+
)
|
|
328
|
+
datasets.append(_dataset(str(level), scale))
|
|
329
|
+
logger.info("imaris level %d copied: shape=%s", level, arr.shape)
|
|
330
|
+
_write_multiscales(
|
|
331
|
+
out, axes, datasets, Path(out).stem, calibrated=calibrated
|
|
332
|
+
)
|
|
333
|
+
return out
|
|
334
|
+
|
|
335
|
+
|
|
296
336
|
def _to_dask(
|
|
297
337
|
source: Union[da.Array, np.ndarray, str, Path],
|
|
298
338
|
axes: Union[str, None],
|
|
@@ -327,6 +367,7 @@ def to_ome_zarr(
|
|
|
327
367
|
n_levels: int = 5,
|
|
328
368
|
downscale: int = 2,
|
|
329
369
|
chunks: Union[tuple[int, ...], None] = None,
|
|
370
|
+
reuse_pyramid: bool = False,
|
|
330
371
|
overwrite: bool = False,
|
|
331
372
|
) -> str:
|
|
332
373
|
"""Write *source* as a pyramidal, calibrated OME-ZARR store.
|
|
@@ -359,6 +400,12 @@ def to_ome_zarr(
|
|
|
359
400
|
Per-level X/Y downsampling factor (default 2).
|
|
360
401
|
chunks : tuple of int, optional
|
|
361
402
|
Chunk shape for the written levels. ``None`` → a bounded default.
|
|
403
|
+
reuse_pyramid : bool, optional
|
|
404
|
+
*Imaris ``.ims`` only.* Copy the file's **own** resolution levels
|
|
405
|
+
instead of rebuilding the pyramid (faster, no recompute), keeping each
|
|
406
|
+
level's native scale. Ignored for other inputs; falls back to a
|
|
407
|
+
rebuild if the Imaris levels can't be read. Default ``False`` (rebuild,
|
|
408
|
+
for a consistent XY-only, nearest-neighbour NGFF pyramid).
|
|
362
409
|
overwrite : bool, optional
|
|
363
410
|
Overwrite an existing store at *out_path*.
|
|
364
411
|
|
|
@@ -378,6 +425,22 @@ def to_ome_zarr(
|
|
|
378
425
|
if n_levels < 1:
|
|
379
426
|
raise ValueError("n_levels must be >= 1")
|
|
380
427
|
|
|
428
|
+
# Reuse an Imaris file's own resolution pyramid instead of rebuilding it.
|
|
429
|
+
if (
|
|
430
|
+
reuse_pyramid
|
|
431
|
+
and isinstance(source, (str, Path))
|
|
432
|
+
and str(source).lower().endswith(".ims")
|
|
433
|
+
):
|
|
434
|
+
try:
|
|
435
|
+
return _write_imaris_pyramid(
|
|
436
|
+
str(source), str(out_path), chunks=chunks, overwrite=overwrite
|
|
437
|
+
)
|
|
438
|
+
except Exception as exc:
|
|
439
|
+
logger.warning(
|
|
440
|
+
"reuse_pyramid failed (%s); rebuilding the pyramid instead.",
|
|
441
|
+
exc,
|
|
442
|
+
)
|
|
443
|
+
|
|
381
444
|
arr, axes, detected = _to_dask(source, axes, scene)
|
|
382
445
|
if len(axes) != arr.ndim:
|
|
383
446
|
raise ValueError(
|
|
@@ -128,3 +128,14 @@ def test_write_labels_into_store(tmp_path):
|
|
|
128
128
|
assert load_ome_zarr(group, channel=None, level=1).shape == (8, 4, 4)
|
|
129
129
|
lg = zarr.open_group(group, mode="r")
|
|
130
130
|
assert lg.attrs["image-label"]["version"]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_reuse_pyramid_ignored_for_arrays(tmp_path):
|
|
134
|
+
"""reuse_pyramid only affects .ims inputs; arrays still rebuild."""
|
|
135
|
+
out = to_ome_zarr(
|
|
136
|
+
np.zeros((8, 8, 8), "uint16"),
|
|
137
|
+
tmp_path / "arr.zarr",
|
|
138
|
+
n_levels=2,
|
|
139
|
+
reuse_pyramid=True,
|
|
140
|
+
)
|
|
141
|
+
assert load_ome_zarr(out, channel=None, level=1).shape == (8, 4, 4)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|