patchworks 2.6.12__tar.gz → 2.7.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-2.6.12 → patchworks-2.7.0}/PKG-INFO +2 -1
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/api/plugins/ome_zarr.md +12 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/guide/snakemake.md +92 -6
- {patchworks-2.6.12 → patchworks-2.7.0}/pyproject.toml +10 -1
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_chunks.py +18 -4
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_io.py +15 -4
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/plugins/ome_zarr.py +440 -162
- patchworks-2.7.0/tests/ngff_schemas/0.4/image.schema +233 -0
- patchworks-2.7.0/tests/ngff_schemas/0.4/label.schema +77 -0
- patchworks-2.7.0/tests/ngff_schemas/0.4/ome.schema +17 -0
- patchworks-2.7.0/tests/ngff_schemas/0.5/_version.schema +10 -0
- patchworks-2.7.0/tests/ngff_schemas/0.5/image.schema +268 -0
- patchworks-2.7.0/tests/ngff_schemas/0.5/label.schema +91 -0
- patchworks-2.7.0/tests/ngff_schemas/0.5/ome.schema +33 -0
- patchworks-2.7.0/tests/ngff_schemas/README.md +16 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_allocation.py +28 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_ome_zarr.py +328 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_pw.py +60 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_run_multi.py +75 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/config/common.yaml +6 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/config/config.yaml +14 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/rules/common.smk +9 -2
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/scripts/_pw.py +38 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/scripts/build_occupancy.py +5 -1
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/scripts/convert.py +8 -2
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/scripts/merge.py +28 -1
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/scripts/run_multi.py +1 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/.github/workflows/docs.yml +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/.github/workflows/lint.yml +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/.github/workflows/release.yml +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/.gitignore +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/.markdownlint-cli2.yaml +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/LICENSE +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/README.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/cliff.toml +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/api/chunks.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/api/cluster.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/api/io.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/api/merge_tile_labels.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/api/plugins/cellpose.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/api/plugins/dog.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/api/plugins/napari.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/api/postprocess.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/api/relabel.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/api/tile_process.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/api/volume_filter.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/assets/logo.png +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/examples/cellpose_2d.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/examples/cellpose_2d.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/examples/cellpose_3d.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/examples/cellpose_3d.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/examples/custom.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/examples/custom_method.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/examples/dog.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/examples/dog.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/examples/standalone_merge.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/examples/stardist.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/examples/stardist_2d.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/getting_started.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/guide/custom_segmentation.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/guide/gpu_distributed.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/guide/label_relations.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/guide/measurements.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/guide/merging.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/guide/ome_zarr_napari.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/guide/performance.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/guide/pitfalls.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/guide/skip_empty.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/guide/tiling.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/docs/index.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/mkdocs.yml +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/__init__.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_cluster.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_core.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_distributed.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_gpu.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_merge.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_notify.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_occupancy.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_postprocess.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_progress.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_relabel.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_relations.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/_volume_filter.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/plugins/__init__.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/plugins/cellpose.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/plugins/dog.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/src/patchworks/plugins/napari.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_cellpose.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_core.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_distributed.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_dog.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_gpu.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_napari.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_notify.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_occupancy.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_postprocess.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_progress.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_relations.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/tests/test_volume_filter.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/README.md +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/Snakefile +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/config/config_cilia.yaml +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/config/config_cyto.yaml +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/config/config_nuclei.yaml +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/config/multi.yaml +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/pixi.toml +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/profile/slurm/config.yaml +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/rules/convert.smk +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/rules/merge.smk +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/rules/segment.smk +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/scripts/fetch_model.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/scripts/prepare_tiles.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/scripts/relate.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/scripts/segment_tile.py +0 -0
- {patchworks-2.6.12 → patchworks-2.7.0}/workflow/scripts/view.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: patchworks
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.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
|
|
@@ -57,6 +57,7 @@ Requires-Dist: cellpose<4,>=3.0; extra == 'cellpose3'
|
|
|
57
57
|
Provides-Extra: cellpose4
|
|
58
58
|
Requires-Dist: cellpose>=4; extra == 'cellpose4'
|
|
59
59
|
Provides-Extra: dev
|
|
60
|
+
Requires-Dist: jsonschema>=4.18; extra == 'dev'
|
|
60
61
|
Requires-Dist: psutil; extra == 'dev'
|
|
61
62
|
Requires-Dist: pytest; extra == 'dev'
|
|
62
63
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
@@ -25,6 +25,10 @@ resolution, matching anisotropic microscopy stacks.
|
|
|
25
25
|
|
|
26
26
|
::: patchworks.plugins.ome_zarr.register_labels
|
|
27
27
|
|
|
28
|
+
## reshard_level
|
|
29
|
+
|
|
30
|
+
::: patchworks.plugins.ome_zarr.reshard_level
|
|
31
|
+
|
|
28
32
|
## read_pixel_size
|
|
29
33
|
|
|
30
34
|
::: patchworks.plugins.ome_zarr.read_pixel_size
|
|
@@ -37,6 +41,14 @@ matches the store, and reads both.
|
|
|
37
41
|
|
|
38
42
|
::: patchworks.plugins.ome_zarr.ngff_version
|
|
39
43
|
|
|
44
|
+
Every writer takes an `ngff_version=` keyword: `"auto"` (default) follows the
|
|
45
|
+
installed zarr — 0.5 on v3, 0.4 on v2 — and `"0.4"` pins the older, zarr-v2
|
|
46
|
+
layout. Writing into an existing store always follows *that store's* format,
|
|
47
|
+
so a label pyramid added later can never disagree with the image it sits in.
|
|
48
|
+
NGFF 0.6 is released but not written: RFC-5 replaces `axes` with
|
|
49
|
+
`coordinateSystems` and requires `input`/`output` on every coordinate
|
|
50
|
+
transformation, and no reader supports it yet.
|
|
51
|
+
|
|
40
52
|
::: patchworks.plugins.ome_zarr.read_ngff_attr
|
|
41
53
|
|
|
42
54
|
::: patchworks.plugins.ome_zarr.write_ngff_attrs
|
|
@@ -50,7 +50,9 @@ work_dir: "/scratch/results" # everything is written here
|
|
|
50
50
|
# conversion (input → pyramidal OME-ZARR)
|
|
51
51
|
reuse_pyramid: true # .ims: copy its own pyramid (fast)
|
|
52
52
|
convert_chunks: null # null → bounded auto chunks; or [c,z,y,x]
|
|
53
|
-
shard: false # true → pack chunks into shards (fewer files)
|
|
53
|
+
shard: false # true → pack chunks into shards (fewer files);
|
|
54
|
+
# covers the image and the label pyramids
|
|
55
|
+
ngff_version: "auto" # OME-ZARR version: "auto" (0.5), or "0.4"
|
|
54
56
|
|
|
55
57
|
# tiling
|
|
56
58
|
channel: 0 # channel to segment, 0-based (null = keep all)
|
|
@@ -83,6 +85,8 @@ cellpose:
|
|
|
83
85
|
pyramid_levels: 5
|
|
84
86
|
pyramid_downscale: 2
|
|
85
87
|
sequential_labels: true # renumber labels to a contiguous 1..N
|
|
88
|
+
shard_labels: false # true → also reshard label level 0 after the
|
|
89
|
+
# merge (one extra pass; see tip below)
|
|
86
90
|
```
|
|
87
91
|
|
|
88
92
|
!!! tip "Growing labels after segmentation"
|
|
@@ -98,6 +102,77 @@ sequential_labels: true # renumber labels to a contiguous 1..N
|
|
|
98
102
|
labels afterwards](custom_segmentation.md#growing-labels-afterwards-dilation)
|
|
99
103
|
for how it works and the equivalent direct-API call.
|
|
100
104
|
|
|
105
|
+
!!! tip "What `shard: true` covers, and `shard_labels` for label level 0"
|
|
106
|
+
Without sharding, one chunk is one file, and a fine-chunked level 0 can
|
|
107
|
+
run to ~950k of them — painful on a shared filesystem at write time and
|
|
108
|
+
on every read after. `shard: true` packs chunks into far fewer files
|
|
109
|
+
without changing the chunking or the memory profile.
|
|
110
|
+
|
|
111
|
+
`shard: true` applies to **the converted image (all levels)** and to
|
|
112
|
+
**the label pyramid levels (1..N)**. It cannot apply to **label level
|
|
113
|
+
0** *while that level is being written*: a shard has to be written whole
|
|
114
|
+
by a single writer, while level 0 is filled one chunk at a time by
|
|
115
|
+
concurrent `segment` jobs (or by the merge's own worker pool). Two
|
|
116
|
+
writers doing a read-modify-write on the same shard file silently lose
|
|
117
|
+
each other's chunks.
|
|
118
|
+
|
|
119
|
+
Once the merge has finished, though, nothing is writing it anymore, so a
|
|
120
|
+
single-threaded pass *can* rewrite it sharded. That is `shard_labels`:
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
shard: true
|
|
124
|
+
shard_labels: true # or an explicit shape, e.g. [16, 512, 512]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`true` reuses whatever spec `shard` carries; a list overrides it.
|
|
128
|
+
`shard: false` does not veto it — an unsharded image with sharded labels
|
|
129
|
+
is a valid combination. It is opt-in because it costs one extra full
|
|
130
|
+
read+write of level 0, and because it is the level with the most chunks
|
|
131
|
+
it is also the one worth paying for. The array's attributes (including
|
|
132
|
+
the merge's own completion marker) are carried across, so a later rerun
|
|
133
|
+
still sees the merge as done.
|
|
134
|
+
|
|
135
|
+
Check whether the file count is actually a problem for your data first —
|
|
136
|
+
a `(126, 34000, 28500)` image at the `(16, 1024, 1024)` label chunk cap
|
|
137
|
+
is 7,616 files per label group, well under the 200,000 at which the
|
|
138
|
+
conversion starts warning. On scicore that is fine; on a filesystem with
|
|
139
|
+
a tighter inode or per-directory budget it may not be.
|
|
140
|
+
|
|
141
|
+
!!! tip "Which OME-ZARR version gets written (`ngff_version`)"
|
|
142
|
+
OME-ZARR is versioned, and the version decides the **zarr format** as
|
|
143
|
+
well as the metadata layout — the two are not separate choices. NGFF 0.4
|
|
144
|
+
is specified over zarr v2 and puts `multiscales`, `labels` and
|
|
145
|
+
`image-label` at the top level of the store's attributes; 0.5 is the
|
|
146
|
+
zarr-v3 revision and nests them under an `ome` key.
|
|
147
|
+
|
|
148
|
+
`ngff_version: "auto"` (the default) follows the installed zarr, which on
|
|
149
|
+
any current environment means **0.5**. Pin `"0.4"` only when a downstream
|
|
150
|
+
tool still cannot read 0.5:
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
ngff_version: "0.4"
|
|
154
|
+
shard: false # required: zarr v2 has no sharding codec
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
That is a real trade-off, not a formality — 0.4 means a zarr-v2 store, and
|
|
158
|
+
sharding is a zarr-v3 feature, so `shard`/`shard_labels` stop doing
|
|
159
|
+
anything. The workflow refuses the combination rather than silently
|
|
160
|
+
ignoring it. The store's own root file changes too (`.zgroup` instead of
|
|
161
|
+
`zarr.json`), which the rules account for.
|
|
162
|
+
|
|
163
|
+
Writing into an **existing** store always follows that store's format,
|
|
164
|
+
whatever this key says: the label pyramid is written by `merge`, long
|
|
165
|
+
after `convert` made the image, and a store with v2 arrays and v3
|
|
166
|
+
metadata is one no reader can open.
|
|
167
|
+
|
|
168
|
+
**What about 0.6?** It was released on 14 September 2026 and patchworks
|
|
169
|
+
does not write it. It is not a version bump but a different metadata
|
|
170
|
+
document: RFC-5 replaces a multiscale's `axes` with `coordinateSystems`
|
|
171
|
+
and requires an `input`/`output` pair on every coordinate transformation.
|
|
172
|
+
Nothing reads it yet either — ome-zarr-py, and so napari, still default
|
|
173
|
+
to 0.5. Setting `ngff_version: "0.6"` is rejected with that explanation
|
|
174
|
+
rather than writing a store you could not open.
|
|
175
|
+
|
|
101
176
|
!!! tip "Dropping objects by size with `min_volume`/`max_volume`"
|
|
102
177
|
`min_volume: N` drops any object smaller than `N` µm³; `max_volume: N`
|
|
103
178
|
drops any object larger than `N` µm³ (e.g. several objects merged into
|
|
@@ -362,6 +437,7 @@ input: "/data/scan.ims"
|
|
|
362
437
|
work_dir: "/scratch/results"
|
|
363
438
|
tile_shape: [16, 1024, 1024]
|
|
364
439
|
shard: false # true → far fewer files, same chunks
|
|
440
|
+
ngff_version: "auto" # convert reads it, so it belongs here
|
|
365
441
|
tiles_per_job: 4
|
|
366
442
|
```
|
|
367
443
|
|
|
@@ -447,11 +523,21 @@ snakemake --workflow-profile profile/slurm --configfile config/common.yaml confi
|
|
|
447
523
|
```
|
|
448
524
|
|
|
449
525
|
!!! warning "Conversion settings belong in the shared file"
|
|
450
|
-
`convert` runs **once**, from the first config only. A `shard`, `input
|
|
451
|
-
`
|
|
452
|
-
nothing logs that it was dropped.
|
|
453
|
-
|
|
454
|
-
the configs by hand, keep them in
|
|
526
|
+
`convert` runs **once**, from the first config only. A `shard`, `input`,
|
|
527
|
+
`convert_chunks`, `sequence_pattern` or `reuse_pyramid` set on the second
|
|
528
|
+
config is therefore never read, and nothing logs that it was dropped.
|
|
529
|
+
`run_multi` refuses to start when those keys disagree across configs and
|
|
530
|
+
tells you which one — but if you drive the configs by hand, keep them in
|
|
531
|
+
`common.yaml`.
|
|
532
|
+
|
|
533
|
+
`ngff_version` is in that list: it decides the store's zarr format, so a
|
|
534
|
+
second config disagreeing about it would describe a store that is not the
|
|
535
|
+
one on disk.
|
|
536
|
+
|
|
537
|
+
`merge` runs once **per config**, so the keys it reads —
|
|
538
|
+
`pyramid_levels`, `pyramid_downscale`, `sequential_labels`,
|
|
539
|
+
`min_volume`/`max_volume` and `shard_labels` — may legitimately differ
|
|
540
|
+
between them, and are only in `common.yaml` for convenience.
|
|
455
541
|
|
|
456
542
|
Splitting the configs is optional: a self-contained config still works,
|
|
457
543
|
and `common:` can simply be left out of `multi.yaml`.
|
|
@@ -96,7 +96,16 @@ napari = [
|
|
|
96
96
|
# openpyxl -> scripts/run_multi.py writes label_relations() output as an
|
|
97
97
|
# Excel workbook (per-object + per-container sheets), not a plain CSV.
|
|
98
98
|
workflow = ["snakemake>=8", "snakemake-executor-plugin-slurm", "openpyxl"]
|
|
99
|
-
|
|
99
|
+
# jsonschema validates what we write against the vendored official
|
|
100
|
+
# OME-NGFF schemas (tests/ngff_schemas/); without it that one test skips.
|
|
101
|
+
dev = [
|
|
102
|
+
"pytest",
|
|
103
|
+
"pytest-cov",
|
|
104
|
+
"scikit-image",
|
|
105
|
+
"psutil",
|
|
106
|
+
"tqdm",
|
|
107
|
+
"jsonschema>=4.18",
|
|
108
|
+
]
|
|
100
109
|
docs = ["mkdocs-material>=9.0", "mkdocstrings[python]>=0.24"]
|
|
101
110
|
all = [
|
|
102
111
|
"patchworks[io,gpu,bioio,imaris,napari]",
|
|
@@ -449,6 +449,18 @@ def auto_tile_shape_cellpose(
|
|
|
449
449
|
(1, 2048, 2048)
|
|
450
450
|
"""
|
|
451
451
|
n_workers = n_workers or cpu_allocation()
|
|
452
|
+
# Cellpose resizes the tile before the net runs -- by `rescale`
|
|
453
|
+
# (= 30 / diameter) on every axis, and by `anisotropy` on z as well -- so
|
|
454
|
+
# the array it actually holds is bigger than the one it was handed, and a
|
|
455
|
+
# budget computed from the unresized tile under-counts by that factor. A
|
|
456
|
+
# diameter half the model's 30 px means a 2x upsample per axis: 8x the
|
|
457
|
+
# voxels, enough to turn a comfortable tile into an OOM.
|
|
458
|
+
#
|
|
459
|
+
# Both only ever *shrink* the tile. A predicted downsample (diameter > 30)
|
|
460
|
+
# would license a bigger one, but these are a safety margin against a
|
|
461
|
+
# rough memory model, not a measurement to spend headroom on.
|
|
462
|
+
rescale = max(1.0, 30.0 / diameter) if diameter else 1.0
|
|
463
|
+
z_resize = max(1.0, anisotropy or 1.0)
|
|
452
464
|
# A tile holds n_channels planes per voxel (e.g. Cellpose's
|
|
453
465
|
# cyto+nuclei pair), so the per-voxel cost -- and every budget
|
|
454
466
|
# derived from it below -- scales with them.
|
|
@@ -475,7 +487,8 @@ def auto_tile_shape_cellpose(
|
|
|
475
487
|
min_tile = int(4 * diameter) if diameter is not None else 1
|
|
476
488
|
|
|
477
489
|
if n_spatial == 2 or not do_3D:
|
|
478
|
-
|
|
490
|
+
# Two axes resized, so the cost per configured pixel is rescale**2.
|
|
491
|
+
max_pixels_2d = max(1, int(max_raw_bytes // (itemsize * rescale**2)))
|
|
479
492
|
tile_side = max(min_tile, int(max_pixels_2d**0.5))
|
|
480
493
|
if n_spatial == 2:
|
|
481
494
|
y, x = shape[-2], shape[-1]
|
|
@@ -485,9 +498,10 @@ def auto_tile_shape_cellpose(
|
|
|
485
498
|
chunk_spatial = [1, min(y, tile_side), min(x, tile_side)]
|
|
486
499
|
else:
|
|
487
500
|
z, y, x = shape[-3], shape[-2], shape[-1]
|
|
488
|
-
#
|
|
489
|
-
#
|
|
490
|
-
|
|
501
|
+
# All three axes are resized: z by anisotropy * rescale, y/x by
|
|
502
|
+
# rescale each -- so one configured voxel costs
|
|
503
|
+
# anisotropy * rescale**3 of them.
|
|
504
|
+
effective_z = z * z_resize * rescale**3
|
|
491
505
|
max_pixels_per_slice = max(
|
|
492
506
|
1, int((max_raw_bytes // 3) // (effective_z * itemsize))
|
|
493
507
|
)
|
|
@@ -18,27 +18,38 @@ _ZARR_V3 = int(zarr.__version__.split(".")[0]) >= 3
|
|
|
18
18
|
_ZARR_V3 = int(zarr.__version__.split(".")[0]) >= 3
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def zarr_compressor_kwargs() -> dict:
|
|
21
|
+
def zarr_compressor_kwargs(zarr_format: int = 3) -> dict:
|
|
22
22
|
"""Keyword arguments pinning the compression codec for a new array.
|
|
23
23
|
|
|
24
24
|
zstd is already zarr v3's default, but relying on a library default means
|
|
25
25
|
the stores patchworks writes change silently if that default ever moves.
|
|
26
26
|
Labels in particular are highly compressible, so this is worth stating.
|
|
27
27
|
|
|
28
|
+
The codec *object* depends on the format of the array being written, not
|
|
29
|
+
on the installed zarr: zarr-python 3 can write a zarr-v2 array (which is
|
|
30
|
+
what NGFF 0.4 needs), and a v2 array rejects ``zarr.codecs.ZstdCodec`` --
|
|
31
|
+
it wants the numcodecs one.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
zarr_format : int, optional
|
|
36
|
+
Format of the array about to be created, 2 or 3 (default 3).
|
|
37
|
+
|
|
28
38
|
Returns
|
|
29
39
|
-------
|
|
30
40
|
dict
|
|
31
|
-
``compressors=``/``compressor=`` as
|
|
41
|
+
``compressors=``/``compressor=`` as that combination expects, or
|
|
32
42
|
empty if the codec cannot be built (then the default applies).
|
|
33
43
|
"""
|
|
34
44
|
try:
|
|
35
|
-
if _ZARR_V3:
|
|
45
|
+
if _ZARR_V3 and zarr_format != 2:
|
|
36
46
|
from zarr.codecs import ZstdCodec
|
|
37
47
|
|
|
38
48
|
return {"compressors": (ZstdCodec(level=1),)}
|
|
39
49
|
import numcodecs
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
codec = numcodecs.Zstd(level=1)
|
|
52
|
+
return {"compressors": (codec,)} if _ZARR_V3 else {"compressor": codec}
|
|
42
53
|
except Exception: # pragma: no cover - depends on the installed zarr
|
|
43
54
|
logger.debug("could not pin a compressor; using zarr's default")
|
|
44
55
|
return {}
|