patchworks 0.4.0__tar.gz → 0.5.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.
Files changed (50) hide show
  1. {patchworks-0.4.0 → patchworks-0.5.0}/.github/workflows/docs.yml +4 -0
  2. {patchworks-0.4.0 → patchworks-0.5.0}/.github/workflows/lint.yml +2 -0
  3. {patchworks-0.4.0 → patchworks-0.5.0}/PKG-INFO +8 -2
  4. {patchworks-0.4.0 → patchworks-0.5.0}/README.md +4 -1
  5. {patchworks-0.4.0 → patchworks-0.5.0}/docs/guide/ome_zarr_napari.md +21 -7
  6. {patchworks-0.4.0 → patchworks-0.5.0}/mkdocs.yml +3 -1
  7. {patchworks-0.4.0 → patchworks-0.5.0}/pyproject.toml +3 -1
  8. {patchworks-0.4.0 → patchworks-0.5.0}/src/patchworks/_io.py +9 -2
  9. patchworks-0.5.0/src/patchworks/plugins/ome_zarr.py +570 -0
  10. {patchworks-0.4.0 → patchworks-0.5.0}/tests/test_ome_zarr.py +35 -0
  11. patchworks-0.4.0/src/patchworks/plugins/ome_zarr.py +0 -459
  12. {patchworks-0.4.0 → patchworks-0.5.0}/.github/workflows/release.yml +0 -0
  13. {patchworks-0.4.0 → patchworks-0.5.0}/.gitignore +0 -0
  14. {patchworks-0.4.0 → patchworks-0.5.0}/cliff.toml +0 -0
  15. {patchworks-0.4.0 → patchworks-0.5.0}/docs/api/chunks.md +0 -0
  16. {patchworks-0.4.0 → patchworks-0.5.0}/docs/api/cluster.md +0 -0
  17. {patchworks-0.4.0 → patchworks-0.5.0}/docs/api/io.md +0 -0
  18. {patchworks-0.4.0 → patchworks-0.5.0}/docs/api/merge_tile_labels.md +0 -0
  19. {patchworks-0.4.0 → patchworks-0.5.0}/docs/api/plugins/cellpose.md +0 -0
  20. {patchworks-0.4.0 → patchworks-0.5.0}/docs/api/plugins/napari.md +0 -0
  21. {patchworks-0.4.0 → patchworks-0.5.0}/docs/api/plugins/ome_zarr.md +0 -0
  22. {patchworks-0.4.0 → patchworks-0.5.0}/docs/api/relabel.md +0 -0
  23. {patchworks-0.4.0 → patchworks-0.5.0}/docs/api/tile_process.md +0 -0
  24. {patchworks-0.4.0 → patchworks-0.5.0}/docs/examples/cellpose_2d.md +0 -0
  25. {patchworks-0.4.0 → patchworks-0.5.0}/docs/examples/cellpose_2d.py +0 -0
  26. {patchworks-0.4.0 → patchworks-0.5.0}/docs/examples/cellpose_3d.md +0 -0
  27. {patchworks-0.4.0 → patchworks-0.5.0}/docs/examples/cellpose_3d.py +0 -0
  28. {patchworks-0.4.0 → patchworks-0.5.0}/docs/examples/custom.md +0 -0
  29. {patchworks-0.4.0 → patchworks-0.5.0}/docs/examples/custom_method.py +0 -0
  30. {patchworks-0.4.0 → patchworks-0.5.0}/docs/examples/standalone_merge.md +0 -0
  31. {patchworks-0.4.0 → patchworks-0.5.0}/docs/examples/stardist.md +0 -0
  32. {patchworks-0.4.0 → patchworks-0.5.0}/docs/examples/stardist_2d.py +0 -0
  33. {patchworks-0.4.0 → patchworks-0.5.0}/docs/getting_started.md +0 -0
  34. {patchworks-0.4.0 → patchworks-0.5.0}/docs/guide/gpu_distributed.md +0 -0
  35. {patchworks-0.4.0 → patchworks-0.5.0}/docs/guide/merging.md +0 -0
  36. {patchworks-0.4.0 → patchworks-0.5.0}/docs/guide/pitfalls.md +0 -0
  37. {patchworks-0.4.0 → patchworks-0.5.0}/docs/guide/skip_empty.md +0 -0
  38. {patchworks-0.4.0 → patchworks-0.5.0}/docs/guide/tiling.md +0 -0
  39. {patchworks-0.4.0 → patchworks-0.5.0}/docs/index.md +0 -0
  40. {patchworks-0.4.0 → patchworks-0.5.0}/src/patchworks/__init__.py +0 -0
  41. {patchworks-0.4.0 → patchworks-0.5.0}/src/patchworks/_chunks.py +0 -0
  42. {patchworks-0.4.0 → patchworks-0.5.0}/src/patchworks/_cluster.py +0 -0
  43. {patchworks-0.4.0 → patchworks-0.5.0}/src/patchworks/_core.py +0 -0
  44. {patchworks-0.4.0 → patchworks-0.5.0}/src/patchworks/_merge.py +0 -0
  45. {patchworks-0.4.0 → patchworks-0.5.0}/src/patchworks/_relabel.py +0 -0
  46. {patchworks-0.4.0 → patchworks-0.5.0}/src/patchworks/plugins/__init__.py +0 -0
  47. {patchworks-0.4.0 → patchworks-0.5.0}/src/patchworks/plugins/cellpose.py +0 -0
  48. {patchworks-0.4.0 → patchworks-0.5.0}/src/patchworks/plugins/napari.py +0 -0
  49. {patchworks-0.4.0 → patchworks-0.5.0}/tests/test_core.py +0 -0
  50. {patchworks-0.4.0 → patchworks-0.5.0}/tests/test_napari.py +0 -0
@@ -4,6 +4,10 @@ on:
4
4
  push:
5
5
  branches:
6
6
  - main
7
+ # Rebuild after each release so the version shown in the header refreshes.
8
+ release:
9
+ types: [published]
10
+ workflow_dispatch:
7
11
 
8
12
  permissions:
9
13
  contents: write
@@ -15,9 +15,11 @@ jobs:
15
15
  - name: ruff check
16
16
  uses: astral-sh/ruff-action@v3
17
17
  with:
18
+ version: "0.15.18"
18
19
  args: "check"
19
20
 
20
21
  - name: ruff format --check
21
22
  uses: astral-sh/ruff-action@v3
22
23
  with:
24
+ version: "0.15.18"
23
25
  args: "format --check"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: patchworks
3
- Version: 0.4.0
3
+ Version: 0.5.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
@@ -29,6 +29,7 @@ Requires-Dist: bioio-lif; extra == 'all'
29
29
  Requires-Dist: bioio-nd2; extra == 'all'
30
30
  Requires-Dist: bioio-ome-tiff; extra == 'all'
31
31
  Requires-Dist: bioio-tifffile; extra == 'all'
32
+ Requires-Dist: imaris-ims-file-reader; extra == 'all'
32
33
  Requires-Dist: nvidia-ml-py; extra == 'all'
33
34
  Requires-Dist: psutil; extra == 'all'
34
35
  Requires-Dist: scikit-image; extra == 'all'
@@ -54,6 +55,8 @@ Requires-Dist: mkdocs-material>=9.0; extra == 'docs'
54
55
  Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
55
56
  Provides-Extra: gpu
56
57
  Requires-Dist: nvidia-ml-py; extra == 'gpu'
58
+ Provides-Extra: imaris
59
+ Requires-Dist: imaris-ims-file-reader; extra == 'imaris'
57
60
  Provides-Extra: io
58
61
  Requires-Dist: psutil; extra == 'io'
59
62
  Requires-Dist: tqdm; extra == 'io'
@@ -71,7 +74,7 @@ Description-Content-Type: text/markdown
71
74
  > Tiled processing of arbitrarily large images — any image, any function.
72
75
 
73
76
  ```
74
- ┌──────┬──────┬──────┐ fn(tile) → labels ┌──────┬──────┬──────┐
77
+ ┌──────┬──────┬──────┐ fn(tile) → labels ┌──────┬──────┬──────┐
75
78
  │ tile │ tile │ tile │ ─────────────────────► │ 1 │ 2 │ 3 │
76
79
  ├──────┼──────┼──────┤ ├──────┼──────┼──────┤
77
80
  │ tile │ tile │ tile │ │ 4 │ 5 │ 6 │ globally
@@ -98,6 +101,7 @@ Optional extras:
98
101
  pip install "patchworks[gpu]" # GPU VRAM querying (nvidia-ml-py)
99
102
  pip install "patchworks[cellpose]" # Cellpose plugin
100
103
  pip install "patchworks[bioio]" # convert any image format to OME-ZARR
104
+ pip install "patchworks[imaris]" # convert Imaris .ims files to OME-ZARR
101
105
  pip install "patchworks[napari]" # interactive napari viewer plugin
102
106
  pip install "patchworks[all]" # Everything (except napari GUI)
103
107
  ```
@@ -105,6 +109,8 @@ pip install "patchworks[all]" # Everything (except napari GUI)
105
109
  > `bioio` reads CZI/LIF/ND2/OME-TIFF/… The `[bioio]` extra bundles the common
106
110
  > native readers (`bioio-nd2`, `bioio-ome-tiff`, `bioio-czi`, `bioio-tifffile`,
107
111
  > `bioio-lif`) plus `bioio-bioformats`, the Bio-Formats catch-all reader (JVM).
112
+ > `[imaris]` adds native `.ims` support (HDF5, no JVM). Physical pixel
113
+ > calibration is read from the input and written into the OME-ZARR.
108
114
 
109
115
  ---
110
116
 
@@ -8,7 +8,7 @@
8
8
  > Tiled processing of arbitrarily large images — any image, any function.
9
9
 
10
10
  ```
11
- ┌──────┬──────┬──────┐ fn(tile) → labels ┌──────┬──────┬──────┐
11
+ ┌──────┬──────┬──────┐ fn(tile) → labels ┌──────┬──────┬──────┐
12
12
  │ tile │ tile │ tile │ ─────────────────────► │ 1 │ 2 │ 3 │
13
13
  ├──────┼──────┼──────┤ ├──────┼──────┼──────┤
14
14
  │ tile │ tile │ tile │ │ 4 │ 5 │ 6 │ globally
@@ -35,6 +35,7 @@ Optional extras:
35
35
  pip install "patchworks[gpu]" # GPU VRAM querying (nvidia-ml-py)
36
36
  pip install "patchworks[cellpose]" # Cellpose plugin
37
37
  pip install "patchworks[bioio]" # convert any image format to OME-ZARR
38
+ pip install "patchworks[imaris]" # convert Imaris .ims files to OME-ZARR
38
39
  pip install "patchworks[napari]" # interactive napari viewer plugin
39
40
  pip install "patchworks[all]" # Everything (except napari GUI)
40
41
  ```
@@ -42,6 +43,8 @@ pip install "patchworks[all]" # Everything (except napari GUI)
42
43
  > `bioio` reads CZI/LIF/ND2/OME-TIFF/… The `[bioio]` extra bundles the common
43
44
  > native readers (`bioio-nd2`, `bioio-ome-tiff`, `bioio-czi`, `bioio-tifffile`,
44
45
  > `bioio-lif`) plus `bioio-bioformats`, the Bio-Formats catch-all reader (JVM).
46
+ > `[imaris]` adds native `.ims` support (HDF5, no JVM). Physical pixel
47
+ > calibration is read from the input and written into the OME-ZARR.
45
48
 
46
49
  ---
47
50
 
@@ -38,19 +38,33 @@ existed.
38
38
 
39
39
  ## Convert any image to OME-ZARR
40
40
 
41
- `to_ome_zarr` accepts a dask/NumPy array, an existing `.zarr` store, or **any
42
- file format** readable by [bioio](https://github.com/bioio-devs/bioio) (CZI,
43
- LIF, ND2, OME-TIFF, …). File inputs are read **lazily** — pixels stream from
44
- disk and are written level by level through dask, so terabyte images convert in
45
- bounded RAM.
41
+ `to_ome_zarr` accepts a dask/NumPy array, an existing `.zarr` store, an
42
+ **Imaris `.ims`** file, or **any format** readable by
43
+ [bioio](https://github.com/bioio-devs/bioio) (CZI, LIF, ND2, OME-TIFF, …). File
44
+ inputs are read **lazily**.
46
45
 
47
46
  ```python
48
47
  from patchworks.plugins.ome_zarr import to_ome_zarr
49
48
 
50
- # From a proprietary microscope file (lazy, via bioio):
51
- to_ome_zarr("scan.czi", "scan.zarr", n_levels=5)
49
+ to_ome_zarr("scan.czi", "scan.zarr", n_levels=5) # via bioio
50
+ to_ome_zarr("scan.ims", "scan.zarr") # Imaris, native HDF5
52
51
  ```
53
52
 
53
+ ### Pixel calibration
54
+
55
+ The physical voxel size is read from the input — bioio's `physical_pixel_sizes`,
56
+ the Imaris resolution metadata, or an existing OME-ZARR's scale — and written
57
+ into the NGFF `coordinateTransformations` (in micrometers), so calibration is
58
+ preserved regardless of input. Override or supply it for bare arrays with
59
+ `pixel_size={"z": 2.0, "y": 0.32, "x": 0.32}`.
60
+
61
+ ### Won't OOM
62
+
63
+ Each pyramid level is built by reading the **previous level back from disk**
64
+ and streaming the downsampled result out through dask with bounded chunks. The
65
+ graph never chains level-on-level and no whole plane/volume is held in RAM, so
66
+ terabyte images convert in bounded memory.
67
+
54
68
  !!! note "Install the readers you need"
55
69
  `pip install "patchworks[bioio]"` pulls `bioio` plus the `bioio-bioformats`
56
70
  catch-all reader (needs a JVM). For speed, add native readers for your
@@ -22,9 +22,11 @@ theme:
22
22
  - content.code.annotate
23
23
  - content.code.copy
24
24
  - content.tabs.link
25
- - navigation.tabs
25
+ - navigation.expand
26
+ - navigation.sections
26
27
  - navigation.top
27
28
  - navigation.footer
29
+ - toc.integrate
28
30
  - search.highlight
29
31
  - search.share
30
32
 
@@ -54,11 +54,13 @@ bioio = [
54
54
  "bioio-tifffile",
55
55
  "bioio-lif",
56
56
  ]
57
+ # imaris reads .ims files natively (HDF5, no JVM) for OME-ZARR conversion.
58
+ imaris = ["imaris-ims-file-reader"]
57
59
  # napari enables the interactive viewer plugin (GUI-heavy, kept out of [all]).
58
60
  napari = ["napari[all]"]
59
61
  dev = ["pytest", "pytest-cov", "scikit-image", "psutil", "tqdm"]
60
62
  docs = ["mkdocs-material>=9.0", "mkdocstrings[python]>=0.24"]
61
- all = ["patchworks[io,gpu,bioio]", "psutil", "tqdm", "scikit-image"]
63
+ all = ["patchworks[io,gpu,bioio,imaris]", "psutil", "tqdm", "scikit-image"]
62
64
 
63
65
  [project.urls]
64
66
  Homepage = "https://github.com/imcf/patchworks"
@@ -47,8 +47,11 @@ def load_ome_zarr(
47
47
  (128, 2048, 2048)
48
48
  """
49
49
  root = zarr.open_group(str(store_path), mode="r")
50
+ # OME-ZARR 0.5 nests under "ome" key; older stores use "multiscales" directly
51
+ _attrs = dict(root.attrs)
52
+ _ms = _attrs.get("multiscales") or _attrs.get("ome", {}).get("multiscales")
50
53
  try:
51
- path = root.attrs["multiscales"][0]["datasets"][level]["path"]
54
+ path = _ms[0]["datasets"][level]["path"]
52
55
  except (KeyError, IndexError, TypeError) as exc:
53
56
  raise ValueError(
54
57
  f"Cannot read OME-ZARR multiscales metadata at level {level} "
@@ -157,8 +160,12 @@ def estimate_empty_tiles(
157
160
  z_src: Any = None
158
161
  if isinstance(image, (str, Path)):
159
162
  _root = zarr.open_group(str(image), mode="r")
163
+ _rattr = dict(_root.attrs)
164
+ _rms = _rattr.get("multiscales") or _rattr.get("ome", {}).get(
165
+ "multiscales"
166
+ )
160
167
  try:
161
- _zpath = _root.attrs["multiscales"][0]["datasets"][level]["path"]
168
+ _zpath = _rms[0]["datasets"][level]["path"]
162
169
  except (KeyError, IndexError, TypeError) as exc:
163
170
  raise ValueError(
164
171
  f"Cannot read OME-ZARR multiscales metadata at level {level} "