patchworks 1.4.2__tar.gz → 1.4.4__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-1.4.2 → patchworks-1.4.4}/PKG-INFO +1 -1
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/plugins/cellpose.py +81 -2
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/plugins/ome_zarr.py +10 -6
- {patchworks-1.4.2 → patchworks-1.4.4}/.github/workflows/docs.yml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/.github/workflows/lint.yml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/.github/workflows/release.yml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/.gitignore +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/.markdownlint-cli2.yaml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/LICENSE +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/README.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/cliff.toml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/chunks.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/cluster.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/io.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/merge_tile_labels.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/plugins/cellpose.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/plugins/dog.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/plugins/napari.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/plugins/ome_zarr.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/postprocess.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/relabel.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/tile_process.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/assets/logo.png +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/cellpose_2d.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/cellpose_2d.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/cellpose_3d.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/cellpose_3d.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/custom.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/custom_method.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/dog.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/dog.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/standalone_merge.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/stardist.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/stardist_2d.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/getting_started.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/custom_segmentation.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/gpu_distributed.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/label_relations.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/measurements.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/merging.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/ome_zarr_napari.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/performance.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/pitfalls.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/skip_empty.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/snakemake.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/tiling.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/docs/index.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/mkdocs.yml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/pyproject.toml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/__init__.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_chunks.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_cluster.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_core.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_distributed.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_io.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_merge.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_postprocess.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_relabel.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_relations.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/plugins/__init__.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/plugins/dog.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/plugins/napari.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_core.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_distributed.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_dog.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_napari.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_ome_zarr.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_postprocess.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_relations.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/README.md +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/Snakefile +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/config/config.yaml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/config/config_cilia.yaml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/config/config_cyto.yaml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/config/config_nuclei.yaml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/config/multi.yaml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/pixi.toml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/profile/slurm/config.yaml +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/rules/common.smk +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/rules/convert.smk +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/rules/merge.smk +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/rules/segment.smk +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/_pw.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/convert.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/fetch_model.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/merge.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/prepare_tiles.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/run_multi.py +0 -0
- {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/segment_tile.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: patchworks
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.4
|
|
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
|
|
@@ -196,6 +196,82 @@ def _get_model(cellpose_dict: dict[str, Any]) -> Any:
|
|
|
196
196
|
return _model_cache[key]
|
|
197
197
|
|
|
198
198
|
|
|
199
|
+
def _is_cuda_oom(exc: Exception) -> bool:
|
|
200
|
+
"""Return whether *exc* looks like a CUDA out-of-memory error.
|
|
201
|
+
|
|
202
|
+
Parameters
|
|
203
|
+
----------
|
|
204
|
+
exc : Exception
|
|
205
|
+
Exception raised by ``model.eval``.
|
|
206
|
+
|
|
207
|
+
Returns
|
|
208
|
+
-------
|
|
209
|
+
bool
|
|
210
|
+
True if the exception is a CUDA OOM.
|
|
211
|
+
"""
|
|
212
|
+
return "out of memory" in str(exc).lower()
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
_OOM_RETRIES = 4
|
|
216
|
+
_OOM_BACKOFF_SECONDS = 30
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _eval_with_oom_fallback(
|
|
220
|
+
model: Any, img: np.ndarray, kwargs: dict[str, Any], cellpose_dict: dict[str, Any]
|
|
221
|
+
) -> np.ndarray:
|
|
222
|
+
"""Run ``model.eval``, surviving transient GPU contention.
|
|
223
|
+
|
|
224
|
+
Cluster GPUs are often shared: another job's memory footprint can grow
|
|
225
|
+
mid-run and push an otherwise-fine tile size over the edge. Staying on
|
|
226
|
+
GPU matters — this tile can take well over an hour, so falling back to
|
|
227
|
+
CPU would be far worse than waiting. Instead this clears the allocator
|
|
228
|
+
cache (fixes fragmentation) and retries with a backoff, giving the
|
|
229
|
+
cotenant job time to free memory, before finally giving up.
|
|
230
|
+
|
|
231
|
+
Parameters
|
|
232
|
+
----------
|
|
233
|
+
model : Any
|
|
234
|
+
Cellpose model instance.
|
|
235
|
+
img : np.ndarray
|
|
236
|
+
Image to segment.
|
|
237
|
+
kwargs : dict
|
|
238
|
+
Keyword arguments for ``model.eval``.
|
|
239
|
+
cellpose_dict : dict
|
|
240
|
+
Configuration from :func:`_make_config`.
|
|
241
|
+
|
|
242
|
+
Returns
|
|
243
|
+
-------
|
|
244
|
+
np.ndarray
|
|
245
|
+
Label array from ``model.eval``.
|
|
246
|
+
"""
|
|
247
|
+
import time
|
|
248
|
+
|
|
249
|
+
for attempt in range(_OOM_RETRIES + 1):
|
|
250
|
+
try:
|
|
251
|
+
return model.eval(img, **kwargs)[0]
|
|
252
|
+
except RuntimeError as exc:
|
|
253
|
+
if not cellpose_dict.get("gpu", False) or not _is_cuda_oom(exc):
|
|
254
|
+
raise
|
|
255
|
+
import torch
|
|
256
|
+
|
|
257
|
+
torch.cuda.empty_cache()
|
|
258
|
+
if attempt == _OOM_RETRIES:
|
|
259
|
+
logger.error(
|
|
260
|
+
"CUDA OOM persisted after %d retries; giving up on tile.",
|
|
261
|
+
_OOM_RETRIES,
|
|
262
|
+
)
|
|
263
|
+
raise
|
|
264
|
+
wait = _OOM_BACKOFF_SECONDS * (attempt + 1)
|
|
265
|
+
logger.warning(
|
|
266
|
+
"CUDA OOM on tile (likely GPU contention); cleared cache, "
|
|
267
|
+
"retrying in %ds (attempt %d/%d).",
|
|
268
|
+
wait,
|
|
269
|
+
attempt + 1,
|
|
270
|
+
_OOM_RETRIES,
|
|
271
|
+
)
|
|
272
|
+
time.sleep(wait)
|
|
273
|
+
|
|
274
|
+
|
|
199
275
|
def _run(block: np.ndarray, cellpose_dict: dict[str, Any]) -> np.ndarray:
|
|
200
276
|
"""Segment one tile with a cached Cellpose model.
|
|
201
277
|
|
|
@@ -231,12 +307,15 @@ def _run(block: np.ndarray, cellpose_dict: dict[str, Any]) -> np.ndarray:
|
|
|
231
307
|
|
|
232
308
|
if do_3D:
|
|
233
309
|
kwargs["z_axis"] = 0
|
|
234
|
-
|
|
310
|
+
masks = _eval_with_oom_fallback(model, block, kwargs, cellpose_dict)
|
|
311
|
+
return masks.astype("int32")
|
|
235
312
|
else:
|
|
236
313
|
# Squeeze singleton z so Cellpose gets a clean 2-D image
|
|
237
314
|
squeeze = block.ndim == 3 and block.shape[0] == 1
|
|
238
315
|
img = block[0] if squeeze else block
|
|
239
|
-
masks = model
|
|
316
|
+
masks = _eval_with_oom_fallback(model, img, kwargs, cellpose_dict).astype(
|
|
317
|
+
"int32"
|
|
318
|
+
)
|
|
240
319
|
return masks[np.newaxis] if squeeze else masks
|
|
241
320
|
|
|
242
321
|
|
|
@@ -727,19 +727,23 @@ def _open_tiff_sequence(
|
|
|
727
727
|
ts = tifffile.TiffSequence(pattern, pattern=sequence_pattern)
|
|
728
728
|
|
|
729
729
|
# Each file should be a single 2D (y, x) plane, but tifffile.imread can
|
|
730
|
-
# report extra
|
|
731
|
-
#
|
|
730
|
+
# report extra singleton dims — leading, trailing, or both (e.g. a stray
|
|
731
|
+
# page or samples-per-pixel axis) — even for a plain single-page TIFF.
|
|
732
|
+
# Drop whichever ones are singleton so one file maps to exactly (y, x).
|
|
732
733
|
chunkshape = tifffile.imread(ts[0]).shape
|
|
733
|
-
|
|
734
|
-
if
|
|
734
|
+
keep_chunk = [i for i, s in enumerate(chunkshape) if s != 1]
|
|
735
|
+
if len(keep_chunk) != 2:
|
|
735
736
|
raise ValueError(
|
|
736
737
|
f"expected single 2D (y, x) planes per file, got shape "
|
|
737
738
|
f"{chunkshape} for {ts[0]!r}"
|
|
738
739
|
)
|
|
739
740
|
|
|
740
741
|
arr = da.from_zarr(zarr.open(store=ts.aszarr()))
|
|
741
|
-
|
|
742
|
-
|
|
742
|
+
n_seq = len(ts.axes)
|
|
743
|
+
index = tuple(
|
|
744
|
+
slice(None) if i in keep_chunk else 0 for i in range(len(chunkshape))
|
|
745
|
+
)
|
|
746
|
+
arr = arr[(slice(None),) * n_seq + index]
|
|
743
747
|
full_axes = ts.axes.lower() + "yx"
|
|
744
748
|
|
|
745
749
|
# Canonicalise to patchworks' tczyx axis order regardless of the order
|
|
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
|