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.
Files changed (89) hide show
  1. {patchworks-1.4.2 → patchworks-1.4.4}/PKG-INFO +1 -1
  2. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/plugins/cellpose.py +81 -2
  3. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/plugins/ome_zarr.py +10 -6
  4. {patchworks-1.4.2 → patchworks-1.4.4}/.github/workflows/docs.yml +0 -0
  5. {patchworks-1.4.2 → patchworks-1.4.4}/.github/workflows/lint.yml +0 -0
  6. {patchworks-1.4.2 → patchworks-1.4.4}/.github/workflows/release.yml +0 -0
  7. {patchworks-1.4.2 → patchworks-1.4.4}/.gitignore +0 -0
  8. {patchworks-1.4.2 → patchworks-1.4.4}/.markdownlint-cli2.yaml +0 -0
  9. {patchworks-1.4.2 → patchworks-1.4.4}/LICENSE +0 -0
  10. {patchworks-1.4.2 → patchworks-1.4.4}/README.md +0 -0
  11. {patchworks-1.4.2 → patchworks-1.4.4}/cliff.toml +0 -0
  12. {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/chunks.md +0 -0
  13. {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/cluster.md +0 -0
  14. {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/io.md +0 -0
  15. {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/merge_tile_labels.md +0 -0
  16. {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/plugins/cellpose.md +0 -0
  17. {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/plugins/dog.md +0 -0
  18. {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/plugins/napari.md +0 -0
  19. {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/plugins/ome_zarr.md +0 -0
  20. {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/postprocess.md +0 -0
  21. {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/relabel.md +0 -0
  22. {patchworks-1.4.2 → patchworks-1.4.4}/docs/api/tile_process.md +0 -0
  23. {patchworks-1.4.2 → patchworks-1.4.4}/docs/assets/logo.png +0 -0
  24. {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/cellpose_2d.md +0 -0
  25. {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/cellpose_2d.py +0 -0
  26. {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/cellpose_3d.md +0 -0
  27. {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/cellpose_3d.py +0 -0
  28. {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/custom.md +0 -0
  29. {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/custom_method.py +0 -0
  30. {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/dog.md +0 -0
  31. {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/dog.py +0 -0
  32. {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/standalone_merge.md +0 -0
  33. {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/stardist.md +0 -0
  34. {patchworks-1.4.2 → patchworks-1.4.4}/docs/examples/stardist_2d.py +0 -0
  35. {patchworks-1.4.2 → patchworks-1.4.4}/docs/getting_started.md +0 -0
  36. {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/custom_segmentation.md +0 -0
  37. {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/gpu_distributed.md +0 -0
  38. {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/label_relations.md +0 -0
  39. {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/measurements.md +0 -0
  40. {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/merging.md +0 -0
  41. {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/ome_zarr_napari.md +0 -0
  42. {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/performance.md +0 -0
  43. {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/pitfalls.md +0 -0
  44. {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/skip_empty.md +0 -0
  45. {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/snakemake.md +0 -0
  46. {patchworks-1.4.2 → patchworks-1.4.4}/docs/guide/tiling.md +0 -0
  47. {patchworks-1.4.2 → patchworks-1.4.4}/docs/index.md +0 -0
  48. {patchworks-1.4.2 → patchworks-1.4.4}/mkdocs.yml +0 -0
  49. {patchworks-1.4.2 → patchworks-1.4.4}/pyproject.toml +0 -0
  50. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/__init__.py +0 -0
  51. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_chunks.py +0 -0
  52. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_cluster.py +0 -0
  53. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_core.py +0 -0
  54. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_distributed.py +0 -0
  55. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_io.py +0 -0
  56. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_merge.py +0 -0
  57. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_postprocess.py +0 -0
  58. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_relabel.py +0 -0
  59. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/_relations.py +0 -0
  60. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/plugins/__init__.py +0 -0
  61. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/plugins/dog.py +0 -0
  62. {patchworks-1.4.2 → patchworks-1.4.4}/src/patchworks/plugins/napari.py +0 -0
  63. {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_core.py +0 -0
  64. {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_distributed.py +0 -0
  65. {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_dog.py +0 -0
  66. {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_napari.py +0 -0
  67. {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_ome_zarr.py +0 -0
  68. {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_postprocess.py +0 -0
  69. {patchworks-1.4.2 → patchworks-1.4.4}/tests/test_relations.py +0 -0
  70. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/README.md +0 -0
  71. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/Snakefile +0 -0
  72. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/config/config.yaml +0 -0
  73. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/config/config_cilia.yaml +0 -0
  74. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/config/config_cyto.yaml +0 -0
  75. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/config/config_nuclei.yaml +0 -0
  76. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/config/multi.yaml +0 -0
  77. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/pixi.toml +0 -0
  78. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/profile/slurm/config.yaml +0 -0
  79. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/rules/common.smk +0 -0
  80. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/rules/convert.smk +0 -0
  81. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/rules/merge.smk +0 -0
  82. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/rules/segment.smk +0 -0
  83. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/_pw.py +0 -0
  84. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/convert.py +0 -0
  85. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/fetch_model.py +0 -0
  86. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/merge.py +0 -0
  87. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/prepare_tiles.py +0 -0
  88. {patchworks-1.4.2 → patchworks-1.4.4}/workflow/scripts/run_multi.py +0 -0
  89. {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.2
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
- return model.eval(block, **kwargs)[0].astype("int32")
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.eval(img, **kwargs)[0].astype("int32")
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 leading singleton dims (e.g. a page/sample axis) even for
731
- # a plain single-page TIFFdrop those so one file maps to exactly (y, x).
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
- lead = len(chunkshape) - 2
734
- if lead < 0 or any(s != 1 for s in chunkshape[:lead]):
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
- if lead:
742
- arr = arr[(slice(None),) * len(ts.axes) + (0,) * lead]
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