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.
- itasc/__init__.py +16 -0
- itasc/cellpose/__init__.py +31 -0
- itasc/cellpose/cellpose_runner.py +334 -0
- itasc/cellpose/divergence_maps.py +380 -0
- itasc/cellpose/flow_following.py +283 -0
- itasc/cellpose/joint.py +98 -0
- itasc/cellpose/napari.yaml +10 -0
- itasc/cellpose/native_masks.py +313 -0
- itasc/cellpose/retrack.py +232 -0
- itasc/cellpose/shape.py +51 -0
- itasc/cellpose/track_laptrack.py +279 -0
- itasc/contact_analysis/__init__.py +61 -0
- itasc/contact_analysis/_provenance.py +29 -0
- itasc/contact_analysis/catalog.py +393 -0
- itasc/contact_analysis/config.py +155 -0
- itasc/contact_analysis/contacts/__init__.py +24 -0
- itasc/contact_analysis/contacts/batch.py +158 -0
- itasc/contact_analysis/contacts/build.py +837 -0
- itasc/contact_analysis/contacts/contact_labels.py +136 -0
- itasc/contact_analysis/contacts/neighborhood.py +146 -0
- itasc/contact_analysis/contacts/reader.py +58 -0
- itasc/contact_analysis/contacts/signed_contact_length.py +143 -0
- itasc/contact_analysis/curation.py +185 -0
- itasc/contact_analysis/dynamics/__init__.py +43 -0
- itasc/contact_analysis/dynamics/collective.py +206 -0
- itasc/contact_analysis/dynamics/kinematics.py +278 -0
- itasc/contact_analysis/dynamics/msd.py +165 -0
- itasc/contact_analysis/dynamics/store.py +251 -0
- itasc/contact_analysis/dynamics/trajectories.py +100 -0
- itasc/contact_analysis/frame_interval.py +85 -0
- itasc/contact_analysis/napari.yaml +10 -0
- itasc/contact_analysis/pipeline.py +313 -0
- itasc/contact_analysis/pixel_size.py +166 -0
- itasc/contact_analysis/quantifier.py +204 -0
- itasc/contact_analysis/quantifiers/__init__.py +7 -0
- itasc/contact_analysis/quantifiers/_contacts_derived.py +42 -0
- itasc/contact_analysis/quantifiers/cell_density.py +65 -0
- itasc/contact_analysis/quantifiers/cell_dynamics.py +81 -0
- itasc/contact_analysis/quantifiers/cell_shape.py +32 -0
- itasc/contact_analysis/quantifiers/contacts.py +72 -0
- itasc/contact_analysis/quantifiers/neighbor_count.py +26 -0
- itasc/contact_analysis/quantifiers/nucleus_dynamics.py +77 -0
- itasc/contact_analysis/quantifiers/nucleus_shape.py +35 -0
- itasc/contact_analysis/quantifiers/shape_relational.py +34 -0
- itasc/contact_analysis/quantifiers/signed_contact_length.py +44 -0
- itasc/contact_analysis/records.py +129 -0
- itasc/contact_analysis/shape/__init__.py +19 -0
- itasc/contact_analysis/shape/core.py +141 -0
- itasc/contact_analysis/shape/relational.py +134 -0
- itasc/contact_analysis/shape_tables.py +387 -0
- itasc/core/__init__.py +0 -0
- itasc/core/cancellation.py +15 -0
- itasc/core/commit.py +61 -0
- itasc/core/imageops.py +34 -0
- itasc/core/label_store.py +53 -0
- itasc/core/lineage.py +113 -0
- itasc/core/logging.py +51 -0
- itasc/core/paths.py +34 -0
- itasc/core/tiff.py +13 -0
- itasc/correction/__init__.py +25 -0
- itasc/correction/labels.py +983 -0
- itasc/napari/__init__.py +7 -0
- itasc/napari/_experiments_panel.py +1091 -0
- itasc/napari/_flow_layout.py +98 -0
- itasc/napari/_icons.py +126 -0
- itasc/napari/_napari_compat.py +118 -0
- itasc/napari/_paths.py +231 -0
- itasc/napari/_preview_cache.py +60 -0
- itasc/napari/_spotlight.py +46 -0
- itasc/napari/_stage_loader.py +146 -0
- itasc/napari/_stage_status.py +110 -0
- itasc/napari/_standalone_paths.py +89 -0
- itasc/napari/_state.py +146 -0
- itasc/napari/_status_rail.py +128 -0
- itasc/napari/_track_render.py +114 -0
- itasc/napari/_widget_helpers.py +236 -0
- itasc/napari/aggregate_widget.py +339 -0
- itasc/napari/candidate_gallery_controller.py +285 -0
- itasc/napari/cell_workflow_widget.py +1405 -0
- itasc/napari/cellpose_segment_track_widget.py +1253 -0
- itasc/napari/cellpose_widget.py +1034 -0
- itasc/napari/contact_analysis/__init__.py +6 -0
- itasc/napari/contact_analysis/curation_controller.py +94 -0
- itasc/napari/contact_analysis/plugins/__init__.py +102 -0
- itasc/napari/contact_analysis/plugins/catalog_summary.py +56 -0
- itasc/napari/contact_analysis/plugins/curation.py +247 -0
- itasc/napari/contact_analysis/plugins/visualize_contacts.py +64 -0
- itasc/napari/contact_analysis_widget.py +951 -0
- itasc/napari/contact_visualization.py +895 -0
- itasc/napari/correction/__init__.py +8 -0
- itasc/napari/correction/_correction_anchor.py +54 -0
- itasc/napari/correction/_correction_candidate_panel.py +259 -0
- itasc/napari/correction/_correction_candidates.py +169 -0
- itasc/napari/correction/_correction_centroids.py +132 -0
- itasc/napari/correction/_correction_commit.py +71 -0
- itasc/napari/correction/_correction_events.py +39 -0
- itasc/napari/correction/_correction_keymap.py +80 -0
- itasc/napari/correction/_correction_layer_lifecycle.py +137 -0
- itasc/napari/correction/_correction_layer_loader.py +86 -0
- itasc/napari/correction/_correction_navigation.py +97 -0
- itasc/napari/correction/_correction_paint.py +46 -0
- itasc/napari/correction/_correction_playback.py +68 -0
- itasc/napari/correction/_correction_protection.py +44 -0
- itasc/napari/correction/_correction_takeover.py +64 -0
- itasc/napari/correction/_correction_track_accordion.py +566 -0
- itasc/napari/correction/_correction_track_path.py +388 -0
- itasc/napari/correction/_correction_ui.py +367 -0
- itasc/napari/correction/_correction_ui_nucleus.py +393 -0
- itasc/napari/correction/_correction_utils.py +204 -0
- itasc/napari/correction/_correction_validation.py +83 -0
- itasc/napari/correction/cell_correction_widget.py +1190 -0
- itasc/napari/correction/correction_widget.py +1363 -0
- itasc/napari/correction/nucleus_correction_widget.py +2185 -0
- itasc/napari/divergence_maps_widget.py +795 -0
- itasc/napari/lineage_canvas_controller.py +351 -0
- itasc/napari/main_widget.py +1184 -0
- itasc/napari/nucleus_atom_extraction_widget.py +648 -0
- itasc/napari/nucleus_db_browser_widget.py +1442 -0
- itasc/napari/nucleus_pipeline_widget.py +579 -0
- itasc/napari/nucleus_segmentation_inputs_widget.py +21 -0
- itasc/napari/nucleus_tracking_inputs_widget.py +248 -0
- itasc/napari/nucleus_workflow_widget.py +963 -0
- itasc/napari/track_path_controller.py +387 -0
- itasc/napari/ui_gate.py +266 -0
- itasc/napari/ui_style.py +503 -0
- itasc/napari/validated_overlay_controller.py +254 -0
- itasc/napari/widgets.py +616 -0
- itasc/napari.yaml +45 -0
- itasc/segmentation/__init__.py +60 -0
- itasc/segmentation/cell_divergence_segmentation.py +433 -0
- itasc/segmentation/cell_label_icm.py +637 -0
- itasc/segmentation/contour_filtering.py +128 -0
- itasc/segmentation/lineage.py +18 -0
- itasc/segmentation/nucleus_segmentation.py +35 -0
- itasc/tracking_ultrack/__init__.py +12 -0
- itasc/tracking_ultrack/_node_geometry.py +191 -0
- itasc/tracking_ultrack/atoms.py +470 -0
- itasc/tracking_ultrack/config.py +76 -0
- itasc/tracking_ultrack/corrections.py +1213 -0
- itasc/tracking_ultrack/db_build.py +492 -0
- itasc/tracking_ultrack/db_query.py +952 -0
- itasc/tracking_ultrack/export.py +133 -0
- itasc/tracking_ultrack/extend.py +243 -0
- itasc/tracking_ultrack/ingest.py +83 -0
- itasc/tracking_ultrack/linking.py +286 -0
- itasc/tracking_ultrack/multi_threshold.py +46 -0
- itasc/tracking_ultrack/napari.yaml +10 -0
- itasc/tracking_ultrack/reseed.py +456 -0
- itasc/tracking_ultrack/retracker.py +153 -0
- itasc/tracking_ultrack/scoring.py +74 -0
- itasc/tracking_ultrack/seed_prior.py +175 -0
- itasc/tracking_ultrack/solve.py +52 -0
- itasc/tracking_ultrack/swap_candidate.py +171 -0
- itasc/tracking_ultrack/track_quality.py +142 -0
- itasc/tracking_ultrack/validation_nodes.py +315 -0
- itasc/tracking_ultrack/validation_state.py +430 -0
- itasc-0.2.0.dev0.data/data/itasc/napari.yaml +45 -0
- itasc-0.2.0.dev0.dist-info/METADATA +186 -0
- itasc-0.2.0.dev0.dist-info/RECORD +162 -0
- itasc-0.2.0.dev0.dist-info/WHEEL +4 -0
- itasc-0.2.0.dev0.dist-info/entry_points.txt +2 -0
- 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
|