itasc 0.2.0.dev0__py3-none-any.whl

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 (162) hide show
  1. itasc/__init__.py +16 -0
  2. itasc/cellpose/__init__.py +31 -0
  3. itasc/cellpose/cellpose_runner.py +334 -0
  4. itasc/cellpose/divergence_maps.py +380 -0
  5. itasc/cellpose/flow_following.py +283 -0
  6. itasc/cellpose/joint.py +98 -0
  7. itasc/cellpose/napari.yaml +10 -0
  8. itasc/cellpose/native_masks.py +313 -0
  9. itasc/cellpose/retrack.py +232 -0
  10. itasc/cellpose/shape.py +51 -0
  11. itasc/cellpose/track_laptrack.py +279 -0
  12. itasc/contact_analysis/__init__.py +61 -0
  13. itasc/contact_analysis/_provenance.py +29 -0
  14. itasc/contact_analysis/catalog.py +393 -0
  15. itasc/contact_analysis/config.py +155 -0
  16. itasc/contact_analysis/contacts/__init__.py +24 -0
  17. itasc/contact_analysis/contacts/batch.py +158 -0
  18. itasc/contact_analysis/contacts/build.py +837 -0
  19. itasc/contact_analysis/contacts/contact_labels.py +136 -0
  20. itasc/contact_analysis/contacts/neighborhood.py +146 -0
  21. itasc/contact_analysis/contacts/reader.py +58 -0
  22. itasc/contact_analysis/contacts/signed_contact_length.py +143 -0
  23. itasc/contact_analysis/curation.py +185 -0
  24. itasc/contact_analysis/dynamics/__init__.py +43 -0
  25. itasc/contact_analysis/dynamics/collective.py +206 -0
  26. itasc/contact_analysis/dynamics/kinematics.py +278 -0
  27. itasc/contact_analysis/dynamics/msd.py +165 -0
  28. itasc/contact_analysis/dynamics/store.py +251 -0
  29. itasc/contact_analysis/dynamics/trajectories.py +100 -0
  30. itasc/contact_analysis/frame_interval.py +85 -0
  31. itasc/contact_analysis/napari.yaml +10 -0
  32. itasc/contact_analysis/pipeline.py +313 -0
  33. itasc/contact_analysis/pixel_size.py +166 -0
  34. itasc/contact_analysis/quantifier.py +204 -0
  35. itasc/contact_analysis/quantifiers/__init__.py +7 -0
  36. itasc/contact_analysis/quantifiers/_contacts_derived.py +42 -0
  37. itasc/contact_analysis/quantifiers/cell_density.py +65 -0
  38. itasc/contact_analysis/quantifiers/cell_dynamics.py +81 -0
  39. itasc/contact_analysis/quantifiers/cell_shape.py +32 -0
  40. itasc/contact_analysis/quantifiers/contacts.py +72 -0
  41. itasc/contact_analysis/quantifiers/neighbor_count.py +26 -0
  42. itasc/contact_analysis/quantifiers/nucleus_dynamics.py +77 -0
  43. itasc/contact_analysis/quantifiers/nucleus_shape.py +35 -0
  44. itasc/contact_analysis/quantifiers/shape_relational.py +34 -0
  45. itasc/contact_analysis/quantifiers/signed_contact_length.py +44 -0
  46. itasc/contact_analysis/records.py +129 -0
  47. itasc/contact_analysis/shape/__init__.py +19 -0
  48. itasc/contact_analysis/shape/core.py +141 -0
  49. itasc/contact_analysis/shape/relational.py +134 -0
  50. itasc/contact_analysis/shape_tables.py +387 -0
  51. itasc/core/__init__.py +0 -0
  52. itasc/core/cancellation.py +15 -0
  53. itasc/core/commit.py +61 -0
  54. itasc/core/imageops.py +34 -0
  55. itasc/core/label_store.py +53 -0
  56. itasc/core/lineage.py +113 -0
  57. itasc/core/logging.py +51 -0
  58. itasc/core/paths.py +34 -0
  59. itasc/core/tiff.py +13 -0
  60. itasc/correction/__init__.py +25 -0
  61. itasc/correction/labels.py +983 -0
  62. itasc/napari/__init__.py +7 -0
  63. itasc/napari/_experiments_panel.py +1091 -0
  64. itasc/napari/_flow_layout.py +98 -0
  65. itasc/napari/_icons.py +126 -0
  66. itasc/napari/_napari_compat.py +118 -0
  67. itasc/napari/_paths.py +231 -0
  68. itasc/napari/_preview_cache.py +60 -0
  69. itasc/napari/_spotlight.py +46 -0
  70. itasc/napari/_stage_loader.py +146 -0
  71. itasc/napari/_stage_status.py +110 -0
  72. itasc/napari/_standalone_paths.py +89 -0
  73. itasc/napari/_state.py +146 -0
  74. itasc/napari/_status_rail.py +128 -0
  75. itasc/napari/_track_render.py +114 -0
  76. itasc/napari/_widget_helpers.py +236 -0
  77. itasc/napari/aggregate_widget.py +339 -0
  78. itasc/napari/candidate_gallery_controller.py +285 -0
  79. itasc/napari/cell_workflow_widget.py +1405 -0
  80. itasc/napari/cellpose_segment_track_widget.py +1253 -0
  81. itasc/napari/cellpose_widget.py +1034 -0
  82. itasc/napari/contact_analysis/__init__.py +6 -0
  83. itasc/napari/contact_analysis/curation_controller.py +94 -0
  84. itasc/napari/contact_analysis/plugins/__init__.py +102 -0
  85. itasc/napari/contact_analysis/plugins/catalog_summary.py +56 -0
  86. itasc/napari/contact_analysis/plugins/curation.py +247 -0
  87. itasc/napari/contact_analysis/plugins/visualize_contacts.py +64 -0
  88. itasc/napari/contact_analysis_widget.py +951 -0
  89. itasc/napari/contact_visualization.py +895 -0
  90. itasc/napari/correction/__init__.py +8 -0
  91. itasc/napari/correction/_correction_anchor.py +54 -0
  92. itasc/napari/correction/_correction_candidate_panel.py +259 -0
  93. itasc/napari/correction/_correction_candidates.py +169 -0
  94. itasc/napari/correction/_correction_centroids.py +132 -0
  95. itasc/napari/correction/_correction_commit.py +71 -0
  96. itasc/napari/correction/_correction_events.py +39 -0
  97. itasc/napari/correction/_correction_keymap.py +80 -0
  98. itasc/napari/correction/_correction_layer_lifecycle.py +137 -0
  99. itasc/napari/correction/_correction_layer_loader.py +86 -0
  100. itasc/napari/correction/_correction_navigation.py +97 -0
  101. itasc/napari/correction/_correction_paint.py +46 -0
  102. itasc/napari/correction/_correction_playback.py +68 -0
  103. itasc/napari/correction/_correction_protection.py +44 -0
  104. itasc/napari/correction/_correction_takeover.py +64 -0
  105. itasc/napari/correction/_correction_track_accordion.py +566 -0
  106. itasc/napari/correction/_correction_track_path.py +388 -0
  107. itasc/napari/correction/_correction_ui.py +367 -0
  108. itasc/napari/correction/_correction_ui_nucleus.py +393 -0
  109. itasc/napari/correction/_correction_utils.py +204 -0
  110. itasc/napari/correction/_correction_validation.py +83 -0
  111. itasc/napari/correction/cell_correction_widget.py +1190 -0
  112. itasc/napari/correction/correction_widget.py +1363 -0
  113. itasc/napari/correction/nucleus_correction_widget.py +2185 -0
  114. itasc/napari/divergence_maps_widget.py +795 -0
  115. itasc/napari/lineage_canvas_controller.py +351 -0
  116. itasc/napari/main_widget.py +1184 -0
  117. itasc/napari/nucleus_atom_extraction_widget.py +648 -0
  118. itasc/napari/nucleus_db_browser_widget.py +1442 -0
  119. itasc/napari/nucleus_pipeline_widget.py +579 -0
  120. itasc/napari/nucleus_segmentation_inputs_widget.py +21 -0
  121. itasc/napari/nucleus_tracking_inputs_widget.py +248 -0
  122. itasc/napari/nucleus_workflow_widget.py +963 -0
  123. itasc/napari/track_path_controller.py +387 -0
  124. itasc/napari/ui_gate.py +266 -0
  125. itasc/napari/ui_style.py +503 -0
  126. itasc/napari/validated_overlay_controller.py +254 -0
  127. itasc/napari/widgets.py +616 -0
  128. itasc/napari.yaml +45 -0
  129. itasc/segmentation/__init__.py +60 -0
  130. itasc/segmentation/cell_divergence_segmentation.py +433 -0
  131. itasc/segmentation/cell_label_icm.py +637 -0
  132. itasc/segmentation/contour_filtering.py +128 -0
  133. itasc/segmentation/lineage.py +18 -0
  134. itasc/segmentation/nucleus_segmentation.py +35 -0
  135. itasc/tracking_ultrack/__init__.py +12 -0
  136. itasc/tracking_ultrack/_node_geometry.py +191 -0
  137. itasc/tracking_ultrack/atoms.py +470 -0
  138. itasc/tracking_ultrack/config.py +76 -0
  139. itasc/tracking_ultrack/corrections.py +1213 -0
  140. itasc/tracking_ultrack/db_build.py +492 -0
  141. itasc/tracking_ultrack/db_query.py +952 -0
  142. itasc/tracking_ultrack/export.py +133 -0
  143. itasc/tracking_ultrack/extend.py +243 -0
  144. itasc/tracking_ultrack/ingest.py +83 -0
  145. itasc/tracking_ultrack/linking.py +286 -0
  146. itasc/tracking_ultrack/multi_threshold.py +46 -0
  147. itasc/tracking_ultrack/napari.yaml +10 -0
  148. itasc/tracking_ultrack/reseed.py +456 -0
  149. itasc/tracking_ultrack/retracker.py +153 -0
  150. itasc/tracking_ultrack/scoring.py +74 -0
  151. itasc/tracking_ultrack/seed_prior.py +175 -0
  152. itasc/tracking_ultrack/solve.py +52 -0
  153. itasc/tracking_ultrack/swap_candidate.py +171 -0
  154. itasc/tracking_ultrack/track_quality.py +142 -0
  155. itasc/tracking_ultrack/validation_nodes.py +315 -0
  156. itasc/tracking_ultrack/validation_state.py +430 -0
  157. itasc-0.2.0.dev0.data/data/itasc/napari.yaml +45 -0
  158. itasc-0.2.0.dev0.dist-info/METADATA +186 -0
  159. itasc-0.2.0.dev0.dist-info/RECORD +162 -0
  160. itasc-0.2.0.dev0.dist-info/WHEEL +4 -0
  161. itasc-0.2.0.dev0.dist-info/entry_points.txt +2 -0
  162. itasc-0.2.0.dev0.dist-info/licenses/LICENSE +661 -0
@@ -0,0 +1,158 @@
1
+ """Name-based batch discovery and headless contact-analysis builds.
2
+
3
+ The standalone contact piece is visualizer-first; the ``.h5`` it shows is a pure
4
+ derived artifact of the label inputs. This module lets users pre-compute that
5
+ artifact for many positions at once: name the three files (cell labels, optional
6
+ nucleus labels, output ``.h5``), point at one top-level folder, and every folder
7
+ that contains a cell-labels file becomes one job. A nucleus file is associated
8
+ only when it sits in that same folder; otherwise the job runs cell-only. Outputs
9
+ that already exist are skipped unless ``overwrite`` is set.
10
+
11
+ Position-agnostic and napari-free: shipped by the ``itasc-aggregate`` wheel.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from collections.abc import Callable, Iterable
16
+ from dataclasses import dataclass
17
+ from pathlib import Path
18
+
19
+ from .build import ensure_contacts
20
+
21
+ __all__ = [
22
+ "ContactBatchJob",
23
+ "ContactBatchResult",
24
+ "discover_contact_batch_jobs",
25
+ "run_contact_batch",
26
+ ]
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class ContactBatchJob:
31
+ """One discovered contact-analysis build: a cell-labels file and where its
32
+ output goes, with an optional co-located nucleus-labels file."""
33
+
34
+ group_dir: Path
35
+ cell_labels: Path
36
+ output: Path
37
+ nucleus_labels: Path | None = None
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class ContactBatchResult:
42
+ """Outcome of a single :class:`ContactBatchJob`.
43
+
44
+ ``status`` is ``"built"`` (computed), ``"skipped"`` (output already present),
45
+ or ``"failed"`` (``error`` holds the message).
46
+ """
47
+
48
+ job: ContactBatchJob
49
+ status: str
50
+ error: str | None = None
51
+
52
+
53
+ def _top_level_dir(path: Path, root: Path) -> Path:
54
+ """The position folder a discovered file belongs to: the first directory
55
+ component under ``root`` (or ``root`` itself for files directly in it)."""
56
+ rel = path.relative_to(root)
57
+ return root / rel.parts[0] if len(rel.parts) > 1 else root
58
+
59
+
60
+ def discover_contact_batch_jobs(
61
+ root: str | Path,
62
+ *,
63
+ cell_name: str,
64
+ h5_name: str,
65
+ nucleus_name: str | None = None,
66
+ ) -> list[ContactBatchJob]:
67
+ """Find contact-analysis jobs under ``root`` by file name.
68
+
69
+ Files are discovered recursively, then grouped by **position folder** — the
70
+ first directory under ``root`` on the path to each match (``root`` itself for
71
+ files lying directly in it). The named files of one position may live in
72
+ different subfolders (e.g. ``pos01/3_cell/`` and ``pos01/2_nucleus/``); they
73
+ are still grouped together.
74
+
75
+ For a position with exactly one cell-labels file, that file is one job whose
76
+ output is ``<position>/<h5_name>``; the nucleus is associated only when
77
+ exactly one ``nucleus_name`` file exists anywhere in that position (zero or
78
+ several → cell-only, "can't associate → don't assign"). A position holding
79
+ several cell-labels files is ambiguous to group, so each falls back to a
80
+ cell-only job written beside its own cell file. Jobs are sorted by cell path.
81
+ """
82
+ root = Path(root)
83
+ cells = [c for c in root.rglob(cell_name) if c.is_file()]
84
+
85
+ groups: dict[Path, list[Path]] = {}
86
+ for cell in cells:
87
+ groups.setdefault(_top_level_dir(cell, root), []).append(cell)
88
+
89
+ jobs: list[ContactBatchJob] = []
90
+ for position, position_cells in groups.items():
91
+ if len(position_cells) == 1:
92
+ cell = position_cells[0]
93
+ nucleus: Path | None = None
94
+ if nucleus_name:
95
+ matches = [
96
+ n for n in position.rglob(nucleus_name) if n.is_file()
97
+ ]
98
+ if len(matches) == 1:
99
+ nucleus = matches[0]
100
+ jobs.append(
101
+ ContactBatchJob(
102
+ group_dir=position,
103
+ cell_labels=cell,
104
+ output=position / h5_name,
105
+ nucleus_labels=nucleus,
106
+ )
107
+ )
108
+ else:
109
+ # Several cell files share a position folder: too ambiguous to pair a
110
+ # single nucleus or a single output, so keep them independent.
111
+ for cell in position_cells:
112
+ jobs.append(
113
+ ContactBatchJob(
114
+ group_dir=cell.parent,
115
+ cell_labels=cell,
116
+ output=cell.parent / h5_name,
117
+ nucleus_labels=None,
118
+ )
119
+ )
120
+ return sorted(jobs, key=lambda job: job.cell_labels)
121
+
122
+
123
+ def run_contact_batch(
124
+ jobs: Iterable[ContactBatchJob],
125
+ *,
126
+ overwrite: bool = False,
127
+ edge_extraction_params: dict | None = None,
128
+ progress_cb: Callable[[int, int, str], None] | None = None,
129
+ cancel: Callable[[], bool] | None = None,
130
+ ) -> list[ContactBatchResult]:
131
+ """Build the contact-analysis HDF5 for each job, collecting per-job outcomes.
132
+
133
+ A single job's failure never aborts the run — it is recorded as a ``failed``
134
+ result and the batch continues. ``cancel`` is polled before each job;
135
+ ``progress_cb(done, total, label)`` is reported after each.
136
+ """
137
+ jobs = list(jobs)
138
+ total = len(jobs)
139
+ results: list[ContactBatchResult] = []
140
+ for index, job in enumerate(jobs):
141
+ if cancel is not None and cancel():
142
+ break
143
+ try:
144
+ _, built = ensure_contacts(
145
+ cell_labels_path=job.cell_labels,
146
+ output_path=job.output,
147
+ nucleus_labels_path=job.nucleus_labels,
148
+ overwrite=overwrite,
149
+ edge_extraction_params=edge_extraction_params,
150
+ )
151
+ results.append(
152
+ ContactBatchResult(job, "built" if built else "skipped")
153
+ )
154
+ except Exception as exc:
155
+ results.append(ContactBatchResult(job, "failed", str(exc)))
156
+ if progress_cb is not None:
157
+ progress_cb(index + 1, total, job.group_dir.name)
158
+ return results