patchworks 2.6.0__tar.gz → 2.6.1__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.0 → patchworks-2.6.1}/PKG-INFO +1 -1
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/guide/snakemake.md +4 -3
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_chunks.py +22 -2
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_core.py +29 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/config/config_cyto.yaml +5 -3
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/scripts/prepare_tiles.py +9 -1
- {patchworks-2.6.0 → patchworks-2.6.1}/.github/workflows/docs.yml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/.github/workflows/lint.yml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/.github/workflows/release.yml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/.gitignore +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/.markdownlint-cli2.yaml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/LICENSE +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/README.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/cliff.toml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/api/chunks.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/api/cluster.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/api/io.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/api/merge_tile_labels.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/api/plugins/cellpose.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/api/plugins/dog.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/api/plugins/napari.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/api/plugins/ome_zarr.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/api/postprocess.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/api/relabel.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/api/tile_process.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/assets/logo.png +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/examples/cellpose_2d.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/examples/cellpose_2d.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/examples/cellpose_3d.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/examples/cellpose_3d.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/examples/custom.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/examples/custom_method.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/examples/dog.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/examples/dog.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/examples/standalone_merge.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/examples/stardist.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/examples/stardist_2d.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/getting_started.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/guide/custom_segmentation.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/guide/gpu_distributed.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/guide/label_relations.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/guide/measurements.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/guide/merging.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/guide/ome_zarr_napari.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/guide/performance.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/guide/pitfalls.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/guide/skip_empty.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/guide/tiling.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/docs/index.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/mkdocs.yml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/pyproject.toml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/__init__.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_cluster.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_core.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_distributed.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_gpu.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_io.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_merge.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_notify.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_occupancy.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_postprocess.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_progress.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_relabel.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/_relations.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/plugins/__init__.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/plugins/cellpose.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/plugins/dog.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/plugins/napari.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/src/patchworks/plugins/ome_zarr.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_allocation.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_distributed.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_dog.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_gpu.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_napari.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_notify.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_occupancy.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_ome_zarr.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_postprocess.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_progress.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_relations.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/tests/test_run_multi.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/README.md +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/Snakefile +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/config/common.yaml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/config/config.yaml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/config/config_cilia.yaml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/config/config_nuclei.yaml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/config/multi.yaml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/pixi.toml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/profile/slurm/config.yaml +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/rules/common.smk +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/rules/convert.smk +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/rules/merge.smk +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/rules/segment.smk +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/scripts/_pw.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/scripts/build_occupancy.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/scripts/convert.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/scripts/fetch_model.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/scripts/merge.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/scripts/run_multi.py +0 -0
- {patchworks-2.6.0 → patchworks-2.6.1}/workflow/scripts/segment_tile.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: patchworks
|
|
3
|
-
Version: 2.6.
|
|
3
|
+
Version: 2.6.1
|
|
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
|
|
@@ -321,9 +321,10 @@ occupancy map and the staged labels are byte-for-byte what a single-channel
|
|
|
321
321
|
run produces, and `merge` and `label_relations` need no changes. Two things
|
|
322
322
|
follow from that:
|
|
323
323
|
|
|
324
|
-
- A tile holds twice the bytes
|
|
325
|
-
|
|
326
|
-
|
|
324
|
+
- A tile holds twice the bytes. `tile_shape: "auto"` accounts for this — it is
|
|
325
|
+
told the tile carries two channels and shrinks each spatial side by ~1/√2,
|
|
326
|
+
so the tile still fits the same VRAM budget. A **hand-set** `tile_shape`
|
|
327
|
+
sized to fill a GPU has no such protection and needs halving yourself.
|
|
327
328
|
- The translation is version-specific. Cellpose 3 gets `channels: [1, 2]`
|
|
328
329
|
(1-based into the channel axis, `0` = grayscale); Cellpose 4 (cpsam) dropped
|
|
329
330
|
`channels` entirely and simply reads both. Either is overridable by setting
|
|
@@ -264,6 +264,7 @@ def auto_tile_shape(
|
|
|
264
264
|
gpu_memory: int | None = None,
|
|
265
265
|
available_memory: int | None = None,
|
|
266
266
|
n_workers: int | None = None,
|
|
267
|
+
n_channels: int = 1,
|
|
267
268
|
verbose: bool = False,
|
|
268
269
|
) -> tuple[int, ...]:
|
|
269
270
|
"""Balanced tile shape for general-purpose 3-D processing.
|
|
@@ -287,6 +288,10 @@ def auto_tile_shape(
|
|
|
287
288
|
Available host RAM in bytes; auto-queried when None.
|
|
288
289
|
n_workers:
|
|
289
290
|
Number of parallel workers (divides the RAM budget).
|
|
291
|
+
n_channels:
|
|
292
|
+
Channels each tile carries (default 1). Above 1 the per-voxel cost
|
|
293
|
+
scales with it, so the tile shrinks accordingly -- e.g. the workflow's
|
|
294
|
+
``nuclei_channel`` hands Cellpose a cyto+nuclei pair.
|
|
290
295
|
verbose:
|
|
291
296
|
Log the chosen shape and estimated tile size.
|
|
292
297
|
|
|
@@ -302,7 +307,12 @@ def auto_tile_shape(
|
|
|
302
307
|
(128, 512, 512)
|
|
303
308
|
"""
|
|
304
309
|
n_workers = n_workers or cpu_allocation()
|
|
305
|
-
|
|
310
|
+
# A tile holds n_channels planes per voxel (e.g. Cellpose's
|
|
311
|
+
# cyto+nuclei pair), so the per-voxel cost -- and every budget
|
|
312
|
+
# derived from it below -- scales with them.
|
|
313
|
+
if n_channels < 1:
|
|
314
|
+
raise ValueError(f"n_channels must be >= 1; got {n_channels!r}")
|
|
315
|
+
itemsize = np.dtype(dtype).itemsize * n_channels
|
|
306
316
|
n_spatial = min(3, len(shape))
|
|
307
317
|
|
|
308
318
|
if use_gpu:
|
|
@@ -359,6 +369,7 @@ def auto_tile_shape_cellpose(
|
|
|
359
369
|
n_workers: int | None = None,
|
|
360
370
|
model_memory_bytes: int = 2 * 1024**3,
|
|
361
371
|
cellpose_memory_factor: int = 20,
|
|
372
|
+
n_channels: int = 1,
|
|
362
373
|
verbose: bool = False,
|
|
363
374
|
) -> tuple[int, ...]:
|
|
364
375
|
"""Cellpose-optimised tile shape.
|
|
@@ -391,6 +402,10 @@ def auto_tile_shape_cellpose(
|
|
|
391
402
|
Memory consumed by the Cellpose model weights (default 2 GiB).
|
|
392
403
|
cellpose_memory_factor:
|
|
393
404
|
Cellpose allocates roughly this multiple of raw input bytes (default 20×).
|
|
405
|
+
n_channels:
|
|
406
|
+
Channels each tile carries (default 1). Above 1 the per-voxel cost
|
|
407
|
+
scales with it, so the tile shrinks accordingly -- e.g. the workflow's
|
|
408
|
+
``nuclei_channel`` hands Cellpose a cyto+nuclei pair.
|
|
394
409
|
verbose:
|
|
395
410
|
Log the chosen shape and memory estimates.
|
|
396
411
|
|
|
@@ -406,7 +421,12 @@ def auto_tile_shape_cellpose(
|
|
|
406
421
|
(1, 2048, 2048)
|
|
407
422
|
"""
|
|
408
423
|
n_workers = n_workers or cpu_allocation()
|
|
409
|
-
|
|
424
|
+
# A tile holds n_channels planes per voxel (e.g. Cellpose's
|
|
425
|
+
# cyto+nuclei pair), so the per-voxel cost -- and every budget
|
|
426
|
+
# derived from it below -- scales with them.
|
|
427
|
+
if n_channels < 1:
|
|
428
|
+
raise ValueError(f"n_channels must be >= 1; got {n_channels!r}")
|
|
429
|
+
itemsize = np.dtype(dtype).itemsize * n_channels
|
|
410
430
|
|
|
411
431
|
if use_gpu:
|
|
412
432
|
total_mem = gpu_memory if gpu_memory is not None else _get_gpu_memory()
|
|
@@ -389,3 +389,32 @@ def test_channel_selection_respects_the_stores_axes(tmp_path):
|
|
|
389
389
|
store4 = str(tmp_path / "czyx.zarr")
|
|
390
390
|
to_ome_zarr(vol4, store4, axes="czyx", n_levels=1, progress=False)
|
|
391
391
|
assert load_ome_zarr(store4, channel=1, level=0).shape == (8, 64, 64)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def test_auto_tile_shape_charges_for_extra_channels():
|
|
395
|
+
"""A 2-channel tile must fit the same byte budget, not twice it.
|
|
396
|
+
|
|
397
|
+
`nuclei_channel` doubles what a tile holds while the tile geometry stays
|
|
398
|
+
single-channel, so a sizer blind to it hands the GPU a tile needing twice
|
|
399
|
+
the VRAM it budgeted for.
|
|
400
|
+
"""
|
|
401
|
+
import pytest
|
|
402
|
+
|
|
403
|
+
from patchworks import auto_tile_shape, auto_tile_shape_cellpose
|
|
404
|
+
|
|
405
|
+
shape, dtype = (128, 2048, 2048), "uint16"
|
|
406
|
+
|
|
407
|
+
one = auto_tile_shape(shape, dtype)
|
|
408
|
+
two = auto_tile_shape(shape, dtype, n_channels=2)
|
|
409
|
+
# Same bytes overall: 2 channels of roughly half the area each.
|
|
410
|
+
assert np.prod(two) * 2 <= np.prod(one)
|
|
411
|
+
assert np.prod(two) * 2 >= np.prod(one) * 0.9
|
|
412
|
+
|
|
413
|
+
kw = dict(diameter=30, do_3D=True, use_gpu=True, gpu_memory=24 * 1024**3)
|
|
414
|
+
cp_one = auto_tile_shape_cellpose(shape, dtype, **kw)
|
|
415
|
+
cp_two = auto_tile_shape_cellpose(shape, dtype, n_channels=2, **kw)
|
|
416
|
+
assert np.prod(cp_two) * 2 <= np.prod(cp_one)
|
|
417
|
+
assert cp_two[0] == cp_one[0] # do_3D still pins z to the full extent
|
|
418
|
+
|
|
419
|
+
with pytest.raises(ValueError, match="n_channels"):
|
|
420
|
+
auto_tile_shape(shape, dtype, n_channels=0)
|
|
@@ -14,9 +14,11 @@ channel: 0 # cytoplasm/membrane channel
|
|
|
14
14
|
#
|
|
15
15
|
# Only the segment step reads it: the tile geometry, the occupancy map and the
|
|
16
16
|
# staged labels all stay exactly as they are for a single channel -- the pair
|
|
17
|
-
# is stacked on a leading axis that is carried into each tile, not tiled.
|
|
18
|
-
#
|
|
19
|
-
#
|
|
17
|
+
# is stacked on a leading axis that is carried into each tile, not tiled.
|
|
18
|
+
#
|
|
19
|
+
# It does double the bytes a tile holds. `tile_shape: "auto"` is told about
|
|
20
|
+
# that and shrinks each spatial side by ~1/sqrt(2) to fit the same VRAM
|
|
21
|
+
# budget; the hand-set tile_shape in common.yaml is not, so halve it yourself.
|
|
20
22
|
#
|
|
21
23
|
# Cellpose 3 turns this into channels: [1, 2]; Cellpose 4 (cpsam) dropped
|
|
22
24
|
# `channels` and just reads both. Override either by setting `channels:` or
|
|
@@ -44,6 +44,10 @@ if ts == "auto":
|
|
|
44
44
|
# "GPU memory query failed" fallback). None => the built-in 8 GiB default.
|
|
45
45
|
gpu_gb = cfg.get("gpu_memory_gb")
|
|
46
46
|
gpu_bytes = int(gpu_gb * 1024**3) if gpu_gb else None
|
|
47
|
+
# `image` is single-channel here (the geometry is spatial), but segment
|
|
48
|
+
# reads nuclei_channel alongside it, so a tile costs twice the bytes.
|
|
49
|
+
# Without this the sizer would hand the GPU a tile it cannot hold.
|
|
50
|
+
n_channels = 2 if cfg.get("nuclei_channel") is not None else 1
|
|
47
51
|
if method == "cellpose":
|
|
48
52
|
cp = cfg["cellpose"]
|
|
49
53
|
sizer = partial(
|
|
@@ -52,6 +56,7 @@ if ts == "auto":
|
|
|
52
56
|
use_gpu=cp.get("gpu", True),
|
|
53
57
|
diameter=cp.get("diameter"),
|
|
54
58
|
gpu_memory=gpu_bytes,
|
|
59
|
+
n_channels=n_channels,
|
|
55
60
|
)
|
|
56
61
|
else:
|
|
57
62
|
# cfg["cellpose"] used to be read unconditionally here, so a DoG or
|
|
@@ -59,7 +64,10 @@ if ts == "auto":
|
|
|
59
64
|
# 'cellpose'. The Cellpose estimator's memory model wouldn't apply to
|
|
60
65
|
# them anyway.
|
|
61
66
|
sizer = partial(
|
|
62
|
-
auto_tile_shape,
|
|
67
|
+
auto_tile_shape,
|
|
68
|
+
use_gpu=gpu_bytes is not None,
|
|
69
|
+
gpu_memory=gpu_bytes,
|
|
70
|
+
n_channels=n_channels,
|
|
63
71
|
)
|
|
64
72
|
tile_shape = tuple(sizer(image.shape, image.dtype))
|
|
65
73
|
else:
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|