patchworks 3.1.0__tar.gz → 3.3.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-3.1.0 → patchworks-3.3.0}/.github/workflows/test.yml +2 -4
- {patchworks-3.1.0 → patchworks-3.3.0}/PKG-INFO +13 -2
- {patchworks-3.1.0 → patchworks-3.3.0}/README.md +8 -0
- patchworks-3.3.0/docs/api/plugins/careamics.md +11 -0
- patchworks-3.3.0/docs/api/plugins/plantseg.md +11 -0
- patchworks-3.3.0/docs/api/plugins/watershed.md +11 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/examples/dog.md +18 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/cli.md +20 -1
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/custom_segmentation.md +8 -0
- patchworks-3.3.0/docs/guide/membrane_cells.md +283 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/review.md +7 -3
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/snakemake.md +67 -18
- {patchworks-3.1.0 → patchworks-3.3.0}/mkdocs.yml +4 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/pyproject.toml +9 -1
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_distributed.py +40 -2
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_merge.py +253 -18
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_occupancy.py +1 -1
- patchworks-3.3.0/src/patchworks/_relations.py +329 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_review.py +43 -7
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_tables.py +187 -36
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_volume_filter.py +44 -8
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/cli.py +91 -1
- patchworks-3.3.0/src/patchworks/plugins/careamics.py +381 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/plugins/dog.py +39 -1
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/plugins/napari.py +22 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/plugins/ome_zarr.py +176 -8
- patchworks-3.3.0/src/patchworks/plugins/plantseg.py +750 -0
- patchworks-3.3.0/src/patchworks/plugins/watershed.py +403 -0
- patchworks-3.3.0/tests/test_careamics.py +170 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_distributed.py +163 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_dog.py +17 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_napari.py +27 -0
- patchworks-3.3.0/tests/test_ngff_conformance.py +177 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_ome_zarr.py +4 -3
- patchworks-3.3.0/tests/test_plantseg.py +369 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_position.py +90 -7
- patchworks-3.3.0/tests/test_pw.py +662 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_relations.py +59 -1
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_run_multi.py +775 -21
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_volume_filter.py +35 -0
- patchworks-3.3.0/tests/test_watershed.py +124 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/config/config.yaml +28 -2
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/config/config_cilia.yaml +5 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/config/config_cyto.yaml +12 -0
- patchworks-3.3.0/workflow/config/config_cyto_plantseg.yaml +51 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/config/multi.yaml +5 -2
- patchworks-3.3.0/workflow/config/multi_plantseg.yaml +33 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/pixi.toml +91 -8
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/rules/common.smk +3 -1
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/scripts/_pw.py +385 -9
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/scripts/export_iso.py +35 -3
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/scripts/merge.py +87 -28
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/scripts/prepare_tiles.py +17 -2
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/scripts/relate.py +124 -65
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/scripts/run_multi.py +547 -62
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/scripts/segment_tile.py +17 -2
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/scripts/view.py +13 -1
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/viewer/pixi.toml +12 -4
- patchworks-3.1.0/src/patchworks/_relations.py +0 -194
- patchworks-3.1.0/tests/test_pw.py +0 -278
- {patchworks-3.1.0 → patchworks-3.3.0}/.github/min-versions.txt +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/.github/workflows/docs.yml +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/.github/workflows/lint.yml +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/.github/workflows/release.yml +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/.gitignore +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/.markdownlint-cli2.yaml +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/.pre-commit-config.yaml +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/LICENSE +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/benchmarks/bench.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/benchmarks/compare.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/cliff.toml +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/chunks.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/cluster.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/io.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/merge_tile_labels.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/plugins/cellpose.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/plugins/dog.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/plugins/napari.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/plugins/ome_zarr.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/postprocess.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/provenance.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/relabel.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/review.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/seams.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/tile_process.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/api/volume_filter.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/assets/logo.png +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/assets/review_panel.png +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/assets/review_position.png +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/examples/cellpose_2d.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/examples/cellpose_2d.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/examples/cellpose_3d.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/examples/cellpose_3d.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/examples/custom.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/examples/custom_method.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/examples/dog.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/examples/standalone_merge.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/examples/stardist.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/examples/stardist_2d.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/getting_started.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/gpu_distributed.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/label_relations.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/launcher.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/measurements.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/merging.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/ome_zarr_napari.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/performance.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/pitfalls.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/skip_empty.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/guide/tiling.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/docs/index.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/__init__.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_autotune.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_chunks.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_cluster.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_core.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_gpu.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_io.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_notify.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_postprocess.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_progress.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_provenance.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_relabel.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/_seams.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/plugins/__init__.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/plugins/cellpose.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/plugins/review.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/src/patchworks/py.typed +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/ngff_schemas/0.4/image.schema +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/ngff_schemas/0.4/label.schema +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/ngff_schemas/0.4/ome.schema +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/ngff_schemas/0.5/_version.schema +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/ngff_schemas/0.5/image.schema +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/ngff_schemas/0.5/label.schema +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/ngff_schemas/0.5/ome.schema +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/ngff_schemas/README.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_allocation.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_autotune.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_cellpose.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_cli.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_core.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_gpu.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_launcher.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_notify.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_occupancy.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_postprocess.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_progress.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_remote.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_review.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_review_napari.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_seams.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/tests/test_tables.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/README.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/Snakefile +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/config/common.yaml +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/config/config_nuclei.yaml +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/launcher/README.md +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/launcher/app.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/launcher/clusters.yaml +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/launcher/launcher_core.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/launcher/requirements.txt +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/profile/slurm/config.yaml +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/rules/convert.smk +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/rules/merge.smk +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/rules/segment.smk +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/scripts/build_occupancy.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/scripts/convert.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/scripts/fetch_model.py +0 -0
- {patchworks-3.1.0 → patchworks-3.3.0}/workflow/scripts/reshard_store.py +0 -0
|
@@ -32,11 +32,9 @@ jobs:
|
|
|
32
32
|
run: pytest -q --doctest-modules src
|
|
33
33
|
|
|
34
34
|
other-platforms:
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
# above once it is green.
|
|
35
|
+
# Green on macOS and Windows since 2026-10-07: a failure there now fails
|
|
36
|
+
# the build like any other.
|
|
38
37
|
runs-on: ${{ matrix.os }}
|
|
39
|
-
continue-on-error: true
|
|
40
38
|
strategy:
|
|
41
39
|
fail-fast: false
|
|
42
40
|
matrix:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: patchworks
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.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
|
|
@@ -52,6 +52,8 @@ Requires-Dist: bioio-lif; extra == 'bioio'
|
|
|
52
52
|
Requires-Dist: bioio-nd2; extra == 'bioio'
|
|
53
53
|
Requires-Dist: bioio-ome-tiff; extra == 'bioio'
|
|
54
54
|
Requires-Dist: bioio-tifffile; extra == 'bioio'
|
|
55
|
+
Provides-Extra: careamics
|
|
56
|
+
Requires-Dist: careamics>=0.3; extra == 'careamics'
|
|
55
57
|
Provides-Extra: cellpose
|
|
56
58
|
Requires-Dist: cellpose>=3.0; extra == 'cellpose'
|
|
57
59
|
Provides-Extra: cellpose3
|
|
@@ -99,7 +101,8 @@ Requires-Dist: pandas>=2.0; extra == 'review'
|
|
|
99
101
|
Provides-Extra: workflow
|
|
100
102
|
Requires-Dist: openpyxl; extra == 'workflow'
|
|
101
103
|
Requires-Dist: pandas>=2.0; extra == 'workflow'
|
|
102
|
-
Requires-Dist:
|
|
104
|
+
Requires-Dist: scikit-image; extra == 'workflow'
|
|
105
|
+
Requires-Dist: snakemake-executor-plugin-slurm>=2.2; extra == 'workflow'
|
|
103
106
|
Requires-Dist: snakemake>=8; extra == 'workflow'
|
|
104
107
|
Description-Content-Type: text/markdown
|
|
105
108
|
|
|
@@ -154,6 +157,7 @@ pip install "patchworks[cellpose]" # Cellpose plugin (>=3.0, v3 or v4)
|
|
|
154
157
|
pip install "patchworks[cellpose3]" # Cellpose plugin, pinned to v3.x
|
|
155
158
|
pip install "patchworks[cellpose4]" # Cellpose plugin, pinned to v4+
|
|
156
159
|
pip install "patchworks[dog]" # deconvolution + DoG plugin (pycudadecon)
|
|
160
|
+
pip install "patchworks[careamics]" # Noise2Void denoising before segmenting
|
|
157
161
|
pip install "patchworks[bioio]" # convert any image format to OME-ZARR
|
|
158
162
|
pip install "patchworks[imaris]" # convert Imaris .ims files to OME-ZARR
|
|
159
163
|
pip install "patchworks[napari]" # interactive napari viewer plugin
|
|
@@ -457,6 +461,13 @@ Optional:
|
|
|
457
461
|
- `cellpose` — Cellpose plugin, v3 or v4 (`patchworks[cellpose]`);
|
|
458
462
|
pin with `[cellpose3]` or `[cellpose4]`
|
|
459
463
|
- `pycudadecon` — deconvolution step of the `dog` plugin (`patchworks[dog]`)
|
|
464
|
+
- `careamics` — denoise tiles before segmenting them (Noise2Void),
|
|
465
|
+
`patchworks[careamics]`
|
|
466
|
+
- `plant-seg` — PlantSeg plugin (boundary U-Net + GASP/multicut), from
|
|
467
|
+
conda-forge only (`conda install -c conda-forge plant-seg`, or the
|
|
468
|
+
workflow's `pixi install -e plantseg`). The nuclei-seeded watershed plugin
|
|
469
|
+
needs only scikit-image. See the
|
|
470
|
+
[membrane cells guide](https://imcf.one/patchworks/guide/membrane_cells/).
|
|
460
471
|
- `bioio` + readers — convert CZI/LIF/ND2/OME-TIFF/… to OME-ZARR
|
|
461
472
|
(`patchworks[bioio]`)
|
|
462
473
|
- `imaris-ims-file-reader` — convert Imaris `.ims` (`patchworks[imaris]`)
|
|
@@ -49,6 +49,7 @@ pip install "patchworks[cellpose]" # Cellpose plugin (>=3.0, v3 or v4)
|
|
|
49
49
|
pip install "patchworks[cellpose3]" # Cellpose plugin, pinned to v3.x
|
|
50
50
|
pip install "patchworks[cellpose4]" # Cellpose plugin, pinned to v4+
|
|
51
51
|
pip install "patchworks[dog]" # deconvolution + DoG plugin (pycudadecon)
|
|
52
|
+
pip install "patchworks[careamics]" # Noise2Void denoising before segmenting
|
|
52
53
|
pip install "patchworks[bioio]" # convert any image format to OME-ZARR
|
|
53
54
|
pip install "patchworks[imaris]" # convert Imaris .ims files to OME-ZARR
|
|
54
55
|
pip install "patchworks[napari]" # interactive napari viewer plugin
|
|
@@ -352,6 +353,13 @@ Optional:
|
|
|
352
353
|
- `cellpose` — Cellpose plugin, v3 or v4 (`patchworks[cellpose]`);
|
|
353
354
|
pin with `[cellpose3]` or `[cellpose4]`
|
|
354
355
|
- `pycudadecon` — deconvolution step of the `dog` plugin (`patchworks[dog]`)
|
|
356
|
+
- `careamics` — denoise tiles before segmenting them (Noise2Void),
|
|
357
|
+
`patchworks[careamics]`
|
|
358
|
+
- `plant-seg` — PlantSeg plugin (boundary U-Net + GASP/multicut), from
|
|
359
|
+
conda-forge only (`conda install -c conda-forge plant-seg`, or the
|
|
360
|
+
workflow's `pixi install -e plantseg`). The nuclei-seeded watershed plugin
|
|
361
|
+
needs only scikit-image. See the
|
|
362
|
+
[membrane cells guide](https://imcf.one/patchworks/guide/membrane_cells/).
|
|
355
363
|
- `bioio` + readers — convert CZI/LIF/ND2/OME-TIFF/… to OME-ZARR
|
|
356
364
|
(`patchworks[bioio]`)
|
|
357
365
|
- `imaris-ims-file-reader` — convert Imaris `.ims` (`patchworks[imaris]`)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# CAREamics denoising plugin
|
|
2
|
+
|
|
3
|
+
See [Denoising first](../../guide/membrane_cells.md#denoising-first-careamics).
|
|
4
|
+
|
|
5
|
+
::: patchworks.plugins.careamics.denoise_fn
|
|
6
|
+
|
|
7
|
+
::: patchworks.plugins.careamics.denoise
|
|
8
|
+
|
|
9
|
+
::: patchworks.plugins.careamics.train_n2v
|
|
10
|
+
|
|
11
|
+
::: patchworks.plugins.careamics.training_crops
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# PlantSeg plugin
|
|
2
|
+
|
|
3
|
+
See [Cells from a membrane stain](../../guide/membrane_cells.md#plantseg).
|
|
4
|
+
|
|
5
|
+
::: patchworks.plugins.plantseg.plantseg_fn
|
|
6
|
+
|
|
7
|
+
::: patchworks.plugins.plantseg.fetch_model
|
|
8
|
+
|
|
9
|
+
::: patchworks.plugins.plantseg.available_models
|
|
10
|
+
|
|
11
|
+
::: patchworks.plugins.plantseg.rescale_factors
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Nuclei-seeded watershed plugin
|
|
2
|
+
|
|
3
|
+
See [Cells from a membrane stain](../../guide/membrane_cells.md).
|
|
4
|
+
|
|
5
|
+
::: patchworks.plugins.watershed.watershed_fn
|
|
6
|
+
|
|
7
|
+
::: patchworks.plugins.watershed.nuclei_seeds
|
|
8
|
+
|
|
9
|
+
::: patchworks.plugins.watershed.foreground_mask
|
|
10
|
+
|
|
11
|
+
::: patchworks.plugins.watershed.seeded_watershed
|
|
@@ -54,6 +54,24 @@ directly to the DoG image — start near the DoG's typical peak value on a
|
|
|
54
54
|
known-positive region and adjust from there; there's no auto (Otsu-style)
|
|
55
55
|
option, since the DoG image isn't bimodal the way a raw intensity image is.
|
|
56
56
|
|
|
57
|
+
## Thin, oblique objects: `connectivity`
|
|
58
|
+
|
|
59
|
+
The thresholded voxels are joined into objects across shared **faces** by
|
|
60
|
+
default. A cilium lying obliquely is a staircase of voxels touching only
|
|
61
|
+
along edges or at corners, and comes out as a row of fragments.
|
|
62
|
+
`connectivity=2` also joins voxels sharing an edge, `connectivity=3` (3-D)
|
|
63
|
+
also a corner:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
fn = dog_label_fn(low_sigma=1.0, high_sigma=3.0, threshold=0.02, connectivity=3)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The merge has to join tiles the same way, or objects crossing a tile
|
|
70
|
+
boundary diagonally are split there: the workflow does this by itself; with
|
|
71
|
+
the API, pass the same value to `merge_tile_labels(..., connectivity=3)`.
|
|
72
|
+
The merged result is then exactly that of labelling the whole image at once,
|
|
73
|
+
whatever the tile size.
|
|
74
|
+
|
|
57
75
|
## GPU
|
|
58
76
|
|
|
59
77
|
```python
|
|
@@ -25,8 +25,18 @@ patchworks tables scan.zarr --relate nuclei:cells
|
|
|
25
25
|
|
|
26
26
|
# Look at the likely mistakes one by one and fix them (napari)
|
|
27
27
|
patchworks review scan.zarr --expect cells:nuclei=1
|
|
28
|
+
|
|
29
|
+
# Bring a store written by an older patchworks up to the OME-Zarr spec
|
|
30
|
+
patchworks fix-metadata scan.zarr
|
|
28
31
|
```
|
|
29
32
|
|
|
33
|
+
`patchworks fix-metadata` changes metadata only, in place: it gives every
|
|
34
|
+
OME-Zarr 0.5 array its `dimension_names` (required by 0.5; versions before
|
|
35
|
+
this one wrote none, which strict readers such as `ome-zarr-models` refuse),
|
|
36
|
+
and drops surplus coarse levels from a label image with more pyramid levels
|
|
37
|
+
than its image (the spec requires the same number). Running it twice
|
|
38
|
+
changes nothing the second time.
|
|
39
|
+
|
|
30
40
|
`patchworks review` without a window: `--summary` (counts and error
|
|
31
41
|
estimate), `--export DIR --format csv|xlsx|parquet` (corrected tables),
|
|
32
42
|
`--workbooks DIR` (relation workbooks), `--write-labels NAME` (a label image
|
|
@@ -46,7 +56,16 @@ gives a child touching no parent the nearest one. See
|
|
|
46
56
|
| `custom` | any importable function | `--fn module:function`, `--fn-kwargs '{"k": 1}'` |
|
|
47
57
|
|
|
48
58
|
Cellpose's anisotropy and the DoG plugin's voxel size are read from the
|
|
49
|
-
store's own calibration, at the level being segmented
|
|
59
|
+
store's own calibration, at the level being segmented -- as is `voxel_size`
|
|
60
|
+
for a custom function that takes one, such as the PlantSeg and watershed
|
|
61
|
+
plugins: `--method custom --fn patchworks.plugins.plantseg:segment
|
|
62
|
+
--fn-kwargs '{"segmentation": "gasp"}' --stitch iou` (`iou`: these fill
|
|
63
|
+
space, so neighbouring cells touch at every seam).
|
|
64
|
+
|
|
65
|
+
`--denoise MODEL` denoises every tile with a CAREamics model before any
|
|
66
|
+
method segments it; `patchworks denoise-train STORE --channel 0 --out
|
|
67
|
+
n2v.ckpt` trains one (Noise2Void, no ground truth). See
|
|
68
|
+
[Cells from a membrane stain](membrane_cells.md).
|
|
50
69
|
|
|
51
70
|
Tiling and stitching take the same options as
|
|
52
71
|
[`tile_process`](../api/tile_process.md): `--tile-shape` (`z,y,x`, `auto` or
|
|
@@ -29,6 +29,14 @@ That is the whole interface. Anything that turns an image tile into a label
|
|
|
29
29
|
image works: classic image processing, StarDist, a trained model, an external
|
|
30
30
|
binary you shell out to, …
|
|
31
31
|
|
|
32
|
+
Ready-made ones ship with patchworks: `patchworks.plugins.dog` (spots,
|
|
33
|
+
cilia), and for cells from a membrane stain `patchworks.plugins.watershed`
|
|
34
|
+
(nuclei-seeded watershed) and `patchworks.plugins.plantseg` (PlantSeg) --
|
|
35
|
+
see [Cells from a membrane stain](membrane_cells.md). With
|
|
36
|
+
`nuclei_channel` set, the tile is `(2, z, y, x)`: `[channel,
|
|
37
|
+
nuclei_channel]` on the first axis. Any method can also run on
|
|
38
|
+
[denoised tiles](membrane_cells.md#denoising-first-careamics).
|
|
39
|
+
|
|
32
40
|
## Minimal example (no GPU, no deps beyond scikit-image)
|
|
33
41
|
|
|
34
42
|
```python
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# Cells from a membrane stain
|
|
2
|
+
|
|
3
|
+
Epithelia, organoids and tissues are often imaged with a **membrane** (or
|
|
4
|
+
cortex) marker plus a **nuclear** dye. Cellpose struggles there in a
|
|
5
|
+
recognisable way: Cellpose 4 (`cpsam`) reads every channel it is given
|
|
6
|
+
without a "cytoplasm" or "nucleus" role, so with a bright nuclear channel
|
|
7
|
+
next to a faint membrane it segments the **nuclei**; and on the membrane
|
|
8
|
+
alone, a wall that is faint in places merges two cells. Patchworks ships
|
|
9
|
+
three plugins for this case, plus an optional denoising step in front of any
|
|
10
|
+
method.
|
|
11
|
+
|
|
12
|
+
| | Needs | Cells come from | Best when |
|
|
13
|
+
| --- | --- | --- | --- |
|
|
14
|
+
| [Nuclei-seeded watershed](#nuclei-seeded-watershed) | scikit-image | the membrane, flooded from the nuclei | every cell has one nucleus; start here |
|
|
15
|
+
| [PlantSeg](#plantseg) | `plant-seg` (conda-forge) | a boundary U-Net, then GASP / multicut | the membrane is uneven or noisy |
|
|
16
|
+
| [PlantSeg + nuclei](#plantseg) | same | the U-Net's boundaries and the nuclei | both of the above |
|
|
17
|
+
| Cellpose, membrane only | cellpose | Cellpose | `nuclei_channel: null`, `do_3D: true` |
|
|
18
|
+
| [Denoising first](#denoising-first-careamics) | `careamics` | any of these, on denoised tiles | noisy acquisitions |
|
|
19
|
+
|
|
20
|
+
All of them are ordinary [custom functions](custom_segmentation.md): one
|
|
21
|
+
config block, the same tiling, merge, tables and review as any other run.
|
|
22
|
+
|
|
23
|
+
## Nuclei-seeded watershed
|
|
24
|
+
|
|
25
|
+
The nuclei are the easy part of the image: bright, compact, separated. They
|
|
26
|
+
say how many cells there are and where; flooding the membrane image from
|
|
27
|
+
them grows each nucleus out to its cell's walls. A cell can then not be
|
|
28
|
+
split (one seed each) or merged with its neighbour (two seeds never join),
|
|
29
|
+
even across a gap in the wall.
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
channel: 0 # membrane
|
|
33
|
+
nuclei_channel: 1 # stacked onto each tile as [membrane, nuclei]
|
|
34
|
+
method: "custom"
|
|
35
|
+
label_name: "cyto_labels"
|
|
36
|
+
stitch: "iou" # required: see below
|
|
37
|
+
custom:
|
|
38
|
+
module: "patchworks.plugins.watershed"
|
|
39
|
+
kwargs:
|
|
40
|
+
nuclei_min_size: 200 # voxels: specks smaller than a nucleus
|
|
41
|
+
foreground: "otsu" # stop at the tissue edge
|
|
42
|
+
# max_radius_um: 15 # or: no further than this from the nucleus
|
|
43
|
+
# nuclei_threshold: 800 # intensity, if Otsu misses dim nuclei
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`stitch: "iou"` is required with this plugin and with PlantSeg, and
|
|
47
|
+
`prepare` refuses the config without it. They give every voxel to some
|
|
48
|
+
cell, so neighbouring cells touch at every tile seam, and the default
|
|
49
|
+
`"touch"` stitching would join each such pair: on a test grid of 12 cells
|
|
50
|
+
cut by six tiles, 5 came out. `"iou"` joins two pieces only where both tiles
|
|
51
|
+
agree on their overlap, and gives the 12. (From the command line, pass
|
|
52
|
+
`--stitch iou` yourself: nothing checks it there.)
|
|
53
|
+
|
|
54
|
+
Two nuclei touching each other give one seed, so one cell for two: raise
|
|
55
|
+
`nuclei_threshold` if that happens -- or grow the cells from nuclei you have
|
|
56
|
+
already segmented, with `seed_labels` instead of `nuclei_channel` (see
|
|
57
|
+
[the example below](#how-seed_labels-works)). Each tile's halo (`overlap`) must hold a
|
|
58
|
+
whole cell, as for any method.
|
|
59
|
+
|
|
60
|
+
## PlantSeg
|
|
61
|
+
|
|
62
|
+
[PlantSeg](https://github.com/kreshuklab/plant-seg) predicts cell
|
|
63
|
+
boundaries with a 3-D U-Net trained on membrane stains, then partitions the
|
|
64
|
+
boundary map into cells (supervoxels by a distance-transform watershed,
|
|
65
|
+
merged by GASP, mutex watershed or multicut). A wall the U-Net sees at all
|
|
66
|
+
gets closed by the partitioning, where Cellpose would merge across it.
|
|
67
|
+
|
|
68
|
+
PlantSeg is on conda-forge only, so it has its own pixi environment:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
export CONDA_OVERRIDE_CUDA=12.0 # login node without a GPU: see below
|
|
72
|
+
pixi install -e plantseg
|
|
73
|
+
pixi run -e plantseg plantseg-fetch generic_confocal_3D_unet # once, with internet
|
|
74
|
+
pixi run -e plantseg multi-slurm
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
method: "custom"
|
|
79
|
+
stitch: "iou" # required, as for the watershed
|
|
80
|
+
custom:
|
|
81
|
+
module: "patchworks.plugins.plantseg"
|
|
82
|
+
kwargs:
|
|
83
|
+
model: "generic_confocal_3D_unet" # or generic_light_sheet_3D_unet, ...
|
|
84
|
+
segmentation: "gasp" # gasp | mutex_ws | multicut | dt_watershed
|
|
85
|
+
beta: 0.6 # lower merges more, higher splits more
|
|
86
|
+
foreground: "otsu" # a boundary U-Net puts cells everywhere
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
With `nuclei_channel` set, two more `segmentation` modes use the nuclei:
|
|
90
|
+
|
|
91
|
+
- `"nuclei_watershed"` -- the U-Net's boundary map flooded from the nuclei:
|
|
92
|
+
the watershed above, on a much cleaner boundary image.
|
|
93
|
+
- `"lifted_multicut"` -- PlantSeg's lifted multicut: supervoxels in one
|
|
94
|
+
nucleus pulled together, in different nuclei pushed apart.
|
|
95
|
+
|
|
96
|
+
Each tile is resampled to the voxel size the model was trained at (from the
|
|
97
|
+
image's own calibration, `rescale: true`), which matters more than any other
|
|
98
|
+
setting for a pretrained U-Net; the prediction is resampled back.
|
|
99
|
+
|
|
100
|
+
## Example: Cellpose nuclei + PlantSeg cells in one run
|
|
101
|
+
|
|
102
|
+
The workflow ships this pairing ready to edit: Cellpose segments the nuclei,
|
|
103
|
+
then PlantSeg grows the cells from **exactly those nuclei** -- one cell per
|
|
104
|
+
nucleus Cellpose found -- and the two are related. Three files, next to
|
|
105
|
+
`config/multi.yaml`:
|
|
106
|
+
|
|
107
|
+
```yaml
|
|
108
|
+
# config/multi_plantseg.yaml
|
|
109
|
+
common: config/common.yaml # input, work_dir, tile_shape, level: shared
|
|
110
|
+
|
|
111
|
+
segmentations:
|
|
112
|
+
- config/config_nuclei.yaml # Cellpose "nuclei" model on channel 1
|
|
113
|
+
- config/config_cyto_plantseg.yaml # PlantSeg on channel 0, seeded by nuclei_labels
|
|
114
|
+
|
|
115
|
+
relations:
|
|
116
|
+
- a: nuclei_labels
|
|
117
|
+
b: cyto_labels
|
|
118
|
+
output: nuclei_to_cyto.xlsx
|
|
119
|
+
|
|
120
|
+
review: # a sanity check: one nucleus per cell
|
|
121
|
+
expect:
|
|
122
|
+
cyto_labels:
|
|
123
|
+
nuclei_labels: 1
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
# config/config_nuclei.yaml (unchanged)
|
|
128
|
+
channel: 1
|
|
129
|
+
overlap: [4, 30, 30]
|
|
130
|
+
method: "cellpose"
|
|
131
|
+
label_name: "nuclei_labels"
|
|
132
|
+
cellpose:
|
|
133
|
+
model: "nuclei"
|
|
134
|
+
diameter: 15
|
|
135
|
+
do_3D: true
|
|
136
|
+
gpu: true
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```yaml
|
|
140
|
+
# config/config_cyto_plantseg.yaml
|
|
141
|
+
channel: 0 # membrane
|
|
142
|
+
seed_labels: "nuclei_labels" # each tile becomes [membrane, nuclei labels]
|
|
143
|
+
overlap: [4, 40, 40] # the halo must hold a whole cell
|
|
144
|
+
stitch: "iou" # cells touch at every seam: join on agreement only
|
|
145
|
+
method: "custom"
|
|
146
|
+
label_name: "cyto_labels"
|
|
147
|
+
custom:
|
|
148
|
+
module: "patchworks.plugins.plantseg"
|
|
149
|
+
function: "segment"
|
|
150
|
+
kwargs:
|
|
151
|
+
model: "generic_confocal_3D_unet" # generic_light_sheet_3D_unet for light-sheet
|
|
152
|
+
segmentation: "nuclei_watershed" # U-Net boundaries flooded from the nuclei
|
|
153
|
+
foreground: "otsu" # keep the tissue only
|
|
154
|
+
# max_radius_um: 15
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Set `input` and `work_dir` in `config/common.yaml`, then, from `workflow/`:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
pixi install -e plantseg
|
|
161
|
+
pixi run -e plantseg plantseg-fetch generic_confocal_3D_unet # once, with internet
|
|
162
|
+
pixi run -e plantseg multi-plantseg-dry # check the plan
|
|
163
|
+
pixi run -e plantseg multi-plantseg-slurm # submit
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Your own multi config with a PlantSeg segmentation in it runs the same
|
|
167
|
+
way, from the same environment:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
pixi run -e plantseg multi-slurm --config /path/to/my_multi.yaml
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
!!! note "`Virtual package '__cuda' does not match` on a login node"
|
|
174
|
+
|
|
175
|
+
The `plantseg` environment holds a CUDA build of PyTorch, and pixi
|
|
176
|
+
refuses to install or run an environment needing CUDA on a machine with
|
|
177
|
+
no GPU driver -- a login node. Only the GPU jobs need CUDA, and they
|
|
178
|
+
start the environment's Python directly, without pixi. Tell pixi on the
|
|
179
|
+
login node that a driver is there:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
export CONDA_OVERRIDE_CUDA=12.0 # any 12.x; put it in ~/.bashrc
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
It must be set for every `pixi run -e plantseg ...` there, not only for
|
|
186
|
+
the install. Check the GPU nodes' driver supports CUDA 12 (`nvidia-smi`
|
|
187
|
+
in a GPU job prints "CUDA Version: 12.x" or later).
|
|
188
|
+
|
|
189
|
+
The `-e plantseg` matters: every job runs in the environment the command
|
|
190
|
+
was started from, and the default one has no PlantSeg. `run_multi` checks
|
|
191
|
+
this before converting anything and says which environment to use (as it
|
|
192
|
+
does for `denoise:`, which needs `-e careamics`).
|
|
193
|
+
|
|
194
|
+
The `plantseg` environment is the default one plus PlantSeg and cupy, so
|
|
195
|
+
the Cellpose run comes from it too, as does a cilia config with the DoG
|
|
196
|
+
plugin's `use_gpu: true` (cupy) and deconvolution. Afterwards, `nuclei_to_cyto.xlsx` gives
|
|
197
|
+
each nucleus its cell, and `pixi run -e viewer review <work_dir>/image.zarr`
|
|
198
|
+
lists any cell not holding exactly one nucleus.
|
|
199
|
+
|
|
200
|
+
### How `seed_labels` works
|
|
201
|
+
|
|
202
|
+
- **Order.** `run_multi` starts the cells' config only once the config
|
|
203
|
+
producing `nuclei_labels` has finished; everything else listed (cilia,
|
|
204
|
+
say) runs alongside. If the nuclei fail, the cells are skipped, not run
|
|
205
|
+
without seeds. Two configs seeding each other are refused up front. A
|
|
206
|
+
dry run (`-n`) waits for nothing.
|
|
207
|
+
- **Tiles.** The nuclei label image is stacked onto the membrane as each
|
|
208
|
+
tile's second channel, halo included, so neighbouring tiles see the same
|
|
209
|
+
nuclei and a cell crossing a seam is grown from the same nucleus on both
|
|
210
|
+
sides. Both runs must use the same `level`, which `run_multi` already
|
|
211
|
+
enforces; the plugin is told `seeds: "labels"` automatically.
|
|
212
|
+
- **Seeds as given.** Two touching nuclei that Cellpose split stay two
|
|
213
|
+
cells; a dim nucleus Cellpose found still gets its cell. Cellpose's
|
|
214
|
+
mistakes carry over the same way: a nucleus split in two makes two
|
|
215
|
+
cells. Correcting the nuclei first (`patchworks review`, then
|
|
216
|
+
`--write-labels nuclei_labels` and `seed_labels: nuclei_labels_reviewed`)
|
|
217
|
+
gives the cells the corrected nuclei.
|
|
218
|
+
- **On its own**, outside `run_multi`, the cells' config needs
|
|
219
|
+
`labels/nuclei_labels` already in `image.zarr`: `prepare` checks, and
|
|
220
|
+
stops with a message rather than segmenting without seeds.
|
|
221
|
+
- **Re-segmenting the nuclei** does not re-run the cells by itself: delete
|
|
222
|
+
`<work_dir>/cyto_labels` and `image.zarr/labels/cyto_labels` to grow them
|
|
223
|
+
again from the new nuclei.
|
|
224
|
+
|
|
225
|
+
`nuclei_channel: 1` instead of `seed_labels` makes the plugin find the
|
|
226
|
+
nuclei itself, in the nuclear stain (Otsu per tile, `nuclei_*` options),
|
|
227
|
+
independently of Cellpose -- both segmentations then run at the same time.
|
|
228
|
+
|
|
229
|
+
Without PlantSeg, the same works with the plain
|
|
230
|
+
[nuclei-seeded watershed](#nuclei-seeded-watershed): set `module:
|
|
231
|
+
"patchworks.plugins.watershed"` with only the `foreground` and
|
|
232
|
+
`max_radius_um` keys, and use the default environment (`pixi run
|
|
233
|
+
multi-slurm` with this pair listed in `config/multi.yaml`).
|
|
234
|
+
|
|
235
|
+
## Cellpose: membrane only, in 3-D
|
|
236
|
+
|
|
237
|
+
If you stay with Cellpose on such images:
|
|
238
|
+
|
|
239
|
+
- Give it **only the membrane** (`nuclei_channel: null`): `cpsam` then has
|
|
240
|
+
nothing brighter to lock onto.
|
|
241
|
+
- `do_3D: true` combines the three orthogonal views, so a wall faint in one
|
|
242
|
+
plane is found in the others. `stitch_threshold` instead joins
|
|
243
|
+
independent 2-D masks across z and fixes nothing in the masks
|
|
244
|
+
themselves. A GPU (`gpu: true`) only changes the speed.
|
|
245
|
+
|
|
246
|
+
## Denoising first (CAREamics)
|
|
247
|
+
|
|
248
|
+
Noise breaks segmentations: a wall lost in the noise merges two cells.
|
|
249
|
+
[Noise2Void](https://careamics.github.io) learns to remove the noise from
|
|
250
|
+
the image itself (no clean ground truth, no annotation), so a model trained
|
|
251
|
+
once on a few crops of the store denoises every tile before any method sees
|
|
252
|
+
it.
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
pixi install -e careamics
|
|
256
|
+
# on a GPU node: the brightest crops of the channel, ~30 epochs
|
|
257
|
+
pixi run -e careamics denoise-train <work_dir>/image.zarr --channel 0 --out n2v_membrane.ckpt
|
|
258
|
+
pixi run -e careamics denoise-train <work_dir>/image.zarr --channel 1 --out n2v_nuclei.ckpt
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
then, in the segmentation config, with any `method`:
|
|
262
|
+
|
|
263
|
+
```yaml
|
|
264
|
+
denoise:
|
|
265
|
+
model: "/path/to/n2v_membrane.ckpt" # .ckpt, or a BioImage.IO .zip
|
|
266
|
+
nuclei_model: "/path/to/n2v_nuclei.ckpt" # optional, for nuclei_channel
|
|
267
|
+
# tile_size: [16, 256, 256] # CAREamics' tiling inside a tile (VRAM)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
and run from the `careamics` environment (`plantseg-careamics` for both).
|
|
271
|
+
The model paths are checked during `prepare`; the denoised image is not
|
|
272
|
+
stored, only used for segmenting. From the command line, `patchworks segment
|
|
273
|
+
... --denoise n2v_membrane.ckpt` does the same.
|
|
274
|
+
|
|
275
|
+
Look at a denoised tile before a full run:
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
from patchworks import load_ome_zarr
|
|
279
|
+
from patchworks.plugins.careamics import denoise
|
|
280
|
+
|
|
281
|
+
tile = load_ome_zarr("image.zarr", channel=0)[20:44, 1000:1512, 1000:1512]
|
|
282
|
+
clean = denoise(tile.compute(), model="n2v_membrane.ckpt")
|
|
283
|
+
```
|
|
@@ -223,9 +223,13 @@ image.zarr/labels/cilia_labels/
|
|
|
223
223
|
So it travels with the labels, including in a zip bundle, and it is
|
|
224
224
|
replaced whenever the labels are. A table computed from older labels is
|
|
225
225
|
recognised and ignored, never shown against the wrong segmentation.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
226
|
+
The table costs no extra read of the labels: each segment job measures
|
|
227
|
+
its tiles' objects as it writes them, and the merge adds those sums up per
|
|
228
|
+
merged object (an object cut by tile boundaries gets exactly the values it
|
|
229
|
+
would have measured whole). Intensity columns (`table_channels: [0, 2]`)
|
|
230
|
+
need the image, so with them the merged labels are measured once instead.
|
|
231
|
+
Turn tables off with `object_table: false` (see the
|
|
232
|
+
[workflow config](snakemake.md)).
|
|
229
233
|
|
|
230
234
|
For a store that has none, for example a run made before tables existed,
|
|
231
235
|
or labels from elsewhere:
|
|
@@ -363,8 +363,12 @@ shard_labels: false # true → also reshard label level 0 after the
|
|
|
363
363
|
one blob). Set either, both, or neither (`null`, the default, disables
|
|
364
364
|
each). Both run once on the **fully merged** image — not per tile, where
|
|
365
365
|
an object crossing a tile boundary would look smaller or larger than it
|
|
366
|
-
really is.
|
|
367
|
-
|
|
366
|
+
really is. The sizes come from what the segment jobs measured per tile,
|
|
367
|
+
added up per merged object, and the filter is applied in the merge's
|
|
368
|
+
own relabelling pass, so it costs no pass of its own over the image
|
|
369
|
+
(runs whose tiles were segmented by an older version filter the merged
|
|
370
|
+
labels in a separate pass instead). Every pyramid level reflects the
|
|
371
|
+
filtered result. It needs `image.zarr` to
|
|
368
372
|
carry a pixel size (the same calibration deconvolution's voxel sizes and
|
|
369
373
|
Cellpose's `anisotropy` are derived from — see the tip below); an
|
|
370
374
|
uncalibrated store raises rather than silently skipping the filter. See
|
|
@@ -650,6 +654,14 @@ running the workflow **twice with two configs against the same `work_dir`**
|
|
|
650
654
|
never collides: each run gets its own private subdirectory, and both reuse
|
|
651
655
|
the *same* already-converted `image.zarr` (conversion never re-runs).
|
|
652
656
|
|
|
657
|
+
`labels.done` is what makes Snakemake call a segmentation finished, while
|
|
658
|
+
the labels themselves are in `image.zarr/labels/<label_name>`. Deleting only
|
|
659
|
+
the latter leaves the run "done" with nothing re-making it: `run_multi`
|
|
660
|
+
refuses that up front and names the `work_dir/<label_name>` folder to remove
|
|
661
|
+
for a fresh segmentation. It also refuses a relation naming a label image
|
|
662
|
+
that no listed config makes and the store does not hold, and skips (with a
|
|
663
|
+
message) a relation whose labels are still missing once everything has run.
|
|
664
|
+
|
|
653
665
|
Most of what those configs contain is identical — the input, the `work_dir`,
|
|
654
666
|
the tiling, everything `convert` reads. Put it in **one** shared file and let
|
|
655
667
|
each config carry only what actually differs. Snakemake merges several
|
|
@@ -696,6 +708,14 @@ cellpose:
|
|
|
696
708
|
usually improves cytoplasm segmentation. Both indices are 0-based, like
|
|
697
709
|
`channel`.
|
|
698
710
|
|
|
711
|
+
!!! tip "Cellpose 4 and a bright nuclear channel"
|
|
712
|
+
|
|
713
|
+
`cpsam` gives the two channels no roles, so next to a faint membrane it
|
|
714
|
+
may segment the nuclei instead of the cells. Give it the membrane alone
|
|
715
|
+
(`nuclei_channel: null`), or use the nuclei the other way round: as
|
|
716
|
+
seeds, with the nuclei-seeded watershed or PlantSeg plugins. See
|
|
717
|
+
[Cells from a membrane stain](membrane_cells.md).
|
|
718
|
+
|
|
699
719
|
Only the `segment` step reads it. The pair is stacked on a leading axis that
|
|
700
720
|
is *carried* into each tile rather than tiled, so the tile geometry, the
|
|
701
721
|
occupancy map and the staged labels are byte-for-byte what a single-channel
|
|
@@ -802,9 +822,10 @@ results/image.zarr/labels/cyto_labels/
|
|
|
802
822
|
rule, so it doesn't get a `log:` directive for free. Standalone (or
|
|
803
823
|
under plain `multi`), it writes to `<work_dir>/logs/relate.log`
|
|
804
824
|
(override with `relate.py --log`), the same tee-to-file-and-stdout
|
|
805
|
-
behaviour as the other steps. Under `multi-slurm`, where each
|
|
806
|
-
|
|
807
|
-
instead — `<work_dir>/logs/relate
|
|
825
|
+
behaviour as the other steps. Under `multi-slurm`, where each parent's
|
|
826
|
+
relations are one concurrent job, `run_multi.py` points each job at its
|
|
827
|
+
own file instead — `<work_dir>/logs/relate/to_<parent>.log`, or
|
|
828
|
+
`<a>_to_<b>.log` for a parent with a single child — so concurrent jobs
|
|
808
829
|
don't interleave into one log; check there instead of scrolling back
|
|
809
830
|
through `srun`'s live output.
|
|
810
831
|
|
|
@@ -833,6 +854,11 @@ relations:
|
|
|
833
854
|
output: nuclei_to_cyto.xlsx # written into work_dir
|
|
834
855
|
```
|
|
835
856
|
|
|
857
|
+
A second example, `config/multi_plantseg.yaml`, pairs Cellpose nuclei with
|
|
858
|
+
PlantSeg cells seeded from the nuclei (`pixi run -e plantseg
|
|
859
|
+
multi-plantseg-slurm`); see [Cells from a membrane
|
|
860
|
+
stain](membrane_cells.md#example-cellpose-nuclei-plantseg-cells-in-one-run).
|
|
861
|
+
|
|
836
862
|
`common:` is optional — leave it out and each config must be self-contained,
|
|
837
863
|
as before. With it, changing the input path or turning on `shard` is a
|
|
838
864
|
one-line edit in one file instead of the same edit repeated per config.
|
|
@@ -843,6 +869,19 @@ pixi run multi # run locally
|
|
|
843
869
|
pixi run multi-slurm # submit every segmentation to SLURM
|
|
844
870
|
```
|
|
845
871
|
|
|
872
|
+
They read `config/multi.yaml` unless given another with `--config`:
|
|
873
|
+
|
|
874
|
+
```bash
|
|
875
|
+
pixi run multi-slurm --config /data/run42/multi.yaml
|
|
876
|
+
pixi run multi-dry --config my_multi.yaml # relative to where you run pixi
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
A relative `--config` is looked for in the directory you run `pixi` from,
|
|
880
|
+
then in `workflow/`. The `common:` and `segmentations:` paths inside it are
|
|
881
|
+
looked for next to the multi config first, then in `workflow/` -- so a run's
|
|
882
|
+
configs can live together in a folder of their own, beside the data, while
|
|
883
|
+
the shipped `config/multi.yaml` keeps working as it is.
|
|
884
|
+
|
|
846
885
|
Before anything is submitted, the script checks that every listed config
|
|
847
886
|
shares one `work_dir` (so `label_relations` has a single `image.zarr` to read
|
|
848
887
|
both label groups from), that `tile_shape` and `level` are identical (so the
|
|
@@ -880,7 +919,7 @@ abort the others; you get a per-config status and a non-zero exit.
|
|
|
880
919
|
# config/multi.yaml
|
|
881
920
|
relate:
|
|
882
921
|
qos: "1day"
|
|
883
|
-
time: 720 # minutes, per
|
|
922
|
+
time: 720 # minutes, per job (one per parent); under the QOS's MaxWall
|
|
884
923
|
```
|
|
885
924
|
|
|
886
925
|
A `--relate-*` flag overrides the block for that one key; anything the
|
|
@@ -889,18 +928,23 @@ abort the others; you get a per-config status and a non-zero exit.
|
|
|
889
928
|
default you meant to replace. Under plain `multi` (no `--profile`), relations
|
|
890
929
|
still run locally, in-process, one after another, as before.
|
|
891
930
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
931
|
+
The relations are grouped by parent: one job reads each parent label
|
|
932
|
+
image once for all its children (e.g. nuclei, cilia and the other cell
|
|
933
|
+
segmentation against `cyto_labels`), and only the chunks some child has
|
|
934
|
+
labels in. Each job logs its images' shape, chunking and object counts
|
|
935
|
+
before it starts, then a progress line roughly once a minute
|
|
936
|
+
(`label_relations: 412/3,600 (11%) after 7m, ~55m left`), so a long
|
|
937
|
+
relation is distinguishable from a hung one in `logs/relate/`.
|
|
938
|
+
|
|
939
|
+
Because each parent gets its own job, one running long does not starve
|
|
940
|
+
the others out of a shared time budget. `relate.py` skips a pair whose
|
|
941
|
+
workbook (the `.xlsx`, or the two `.csv` files a sheet too long for
|
|
942
|
+
Excel is written as) is already newer than both labels' merge marker,
|
|
943
|
+
and rewrites a missing workbook from the object tables when they
|
|
944
|
+
already hold the relation for the current labels, so **re-running the
|
|
945
|
+
exact same `multi-slurm` command only recomputes what's still missing
|
|
946
|
+
or stale** — delete a specific workbook to force just that one. The
|
|
947
|
+
bundle is likewise left alone when nothing in the store changed.
|
|
904
948
|
|
|
905
949
|
!!! tip "After a killed run"
|
|
906
950
|
Snakemake only releases its lock on a clean exit, so a run that was killed
|
|
@@ -1075,6 +1119,11 @@ pixi run go # run locally (8 cores)
|
|
|
1075
1119
|
pixi run slurm # submit to SLURM (edit profile/slurm/config.yaml first)
|
|
1076
1120
|
```
|
|
1077
1121
|
|
|
1122
|
+
Optional environments add methods: `-e plantseg` (PlantSeg, from
|
|
1123
|
+
conda-forge), `-e careamics` (the `denoise:` step and `pixi run denoise-train`),
|
|
1124
|
+
`-e plantseg-careamics` for both -- see
|
|
1125
|
+
[Cells from a membrane stain](membrane_cells.md).
|
|
1126
|
+
|
|
1078
1127
|
`pixi run …` activates the env, so the rule scripts execute in that env — do
|
|
1079
1128
|
**not** pass `--use-conda`. On a cluster, keep the `workflow/` directory on a
|
|
1080
1129
|
shared filesystem the compute nodes can read: the SLURM executor re-launches
|