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,286 @@
1
+ """Linking step: wire NodeDB into LinkDB.
2
+
3
+ Default mode uses Ultrack's built-in linker.
4
+ Shape mode uses the custom shape-scoring linker.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import math
9
+ from pathlib import Path
10
+ from collections.abc import Generator
11
+
12
+ import numpy as np
13
+
14
+ from itasc.tracking_ultrack import scoring
15
+ from itasc.tracking_ultrack.config import TrackingConfig
16
+ from itasc.tracking_ultrack.ingest import _build_ultrack_config
17
+
18
+
19
+ def run_linking(
20
+ working_dir: str | Path,
21
+ cfg: TrackingConfig,
22
+ *,
23
+ overwrite: bool = True,
24
+ ) -> Generator[tuple[int, int, str], None, None]:
25
+ """Run the linking step, yielding (step, total, label) progress tuples."""
26
+ if cfg.linking_mode == "shape":
27
+ yield from _run_shape_linking(working_dir, cfg, overwrite=overwrite)
28
+ return
29
+ if cfg.linking_mode != "default":
30
+ raise ValueError(f"Unknown linking_mode={cfg.linking_mode!r}")
31
+
32
+ total = 3
33
+ wd = Path(working_dir)
34
+ ultrack_cfg = _build_ultrack_config(cfg, wd)
35
+
36
+ from ultrack.core.linking.processing import link
37
+ from ultrack.core.linking.utils import clear_linking_data
38
+
39
+ if overwrite:
40
+ yield (0, total, "Clearing existing links…")
41
+ clear_linking_data(ultrack_cfg.data_config.database_path)
42
+ else:
43
+ yield (0, total, "Skipping link clear (overwrite=False)…")
44
+
45
+ yield (1, total, "Running Ultrack linker…")
46
+ link(ultrack_cfg, overwrite=False)
47
+
48
+ yield (total, total, "Linking done.")
49
+
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # Shape-scoring linking
53
+ # ---------------------------------------------------------------------------
54
+
55
+ def _node_coords_centroid(node, ndim: int | None = None) -> tuple[np.ndarray, np.ndarray] | None:
56
+ """Return (absolute-pixel coords, centroid) for node, or None on failure.
57
+
58
+ ndim defaults to the mask's own dimensionality.
59
+ """
60
+ mask = getattr(node, "mask", None)
61
+ if mask is None:
62
+ return None
63
+ mask = np.asarray(mask, dtype=bool)
64
+ if mask.ndim == 0 or not mask.any():
65
+ return None
66
+ if ndim is None:
67
+ ndim = int(mask.ndim)
68
+
69
+ # Resolve origin
70
+ origin = None
71
+ for attr in ("origin", "offset", "bbox_start", "bbox_min", "start"):
72
+ value = getattr(node, attr, None)
73
+ if value is None:
74
+ continue
75
+ arr = np.asarray(value, dtype=np.float32).reshape(-1)
76
+ if arr.size >= ndim:
77
+ origin = arr[-ndim:]
78
+ break
79
+ if origin is None:
80
+ bbox = getattr(node, "bbox", None)
81
+ if bbox is None:
82
+ return None
83
+ if isinstance(bbox, tuple) and bbox and all(hasattr(item, "start") for item in bbox):
84
+ starts = [0.0 if item.start is None else float(item.start) for item in bbox]
85
+ arr = np.asarray(starts, dtype=np.float32).reshape(-1)
86
+ if arr.size >= ndim:
87
+ origin = arr[-ndim:]
88
+ if origin is None:
89
+ arr = np.asarray(bbox, dtype=np.float32).reshape(-1)
90
+ # bbox is [min_0, ..., min_k, max_0, ..., max_k]; take first half for mins
91
+ if arr.size >= ndim * 2 and arr.size % 2 == 0:
92
+ half = arr.size // 2
93
+ origin = arr[:half][-ndim:]
94
+ elif arr.size >= ndim:
95
+ origin = arr[-ndim:]
96
+ if origin is None:
97
+ return None
98
+
99
+ coords = np.argwhere(mask).astype(np.float32) + origin
100
+ centroid = np.asarray(node.centroid, dtype=np.float32).reshape(-1)[-ndim:]
101
+ return coords, centroid
102
+
103
+
104
+ def _shape_pair_score(
105
+ src_coords: np.ndarray,
106
+ src_centroid: np.ndarray,
107
+ src_area: float,
108
+ tgt_coords: np.ndarray,
109
+ tgt_centroid: np.ndarray,
110
+ tgt_area: float,
111
+ distance: float,
112
+ cfg: TrackingConfig,
113
+ ) -> float | None:
114
+ """Gate and score one source→target pair under shape mode.
115
+
116
+ Returns None when the pair fails the area-ratio or IoU threshold,
117
+ otherwise returns the similarity score.
118
+ """
119
+ if src_area <= 0 or tgt_area <= 0:
120
+ return None
121
+ area_ratio = min(src_area, tgt_area) / max(src_area, tgt_area)
122
+ if area_ratio < cfg.min_area_ratio:
123
+ return None
124
+ iou = scoring.centroid_corrected_iou_from_coords(
125
+ src_coords, src_centroid, tgt_coords, tgt_centroid
126
+ )
127
+ if iou < cfg.min_link_iou:
128
+ return None
129
+ return scoring.similarity_score(
130
+ area_ratio=area_ratio,
131
+ centroid_corrected_iou=iou,
132
+ distance=distance,
133
+ area_weight=cfg.area_weight,
134
+ iou_weight=cfg.iou_weight,
135
+ distance_weight=cfg.distance_weight,
136
+ )
137
+
138
+
139
+ def compute_edge_weight(
140
+ source_node,
141
+ target_node,
142
+ distance: float,
143
+ cfg: TrackingConfig,
144
+ ) -> float | None:
145
+ """Per-pair edge weight matching the active linker mode.
146
+
147
+ Returns None when shape mode filters the pair out. Default mode never
148
+ filters and always returns a float.
149
+ """
150
+ if cfg.linking_mode == "shape":
151
+ result = _node_coords_centroid(source_node)
152
+ if result is None:
153
+ return None
154
+ src_coords, src_centroid = result
155
+
156
+ result = _node_coords_centroid(target_node)
157
+ if result is None:
158
+ return None
159
+ tgt_coords, tgt_centroid = result
160
+
161
+ src_area = float(getattr(source_node, "area", len(src_coords)))
162
+ tgt_area = float(getattr(target_node, "area", len(tgt_coords)))
163
+ return _shape_pair_score(
164
+ src_coords, src_centroid, src_area,
165
+ tgt_coords, tgt_centroid, tgt_area,
166
+ distance, cfg,
167
+ )
168
+
169
+ if cfg.linking_mode != "default":
170
+ raise ValueError(f"Unknown linking_mode={cfg.linking_mode!r}")
171
+ iou = float(source_node.IoU(target_node))
172
+ return iou - cfg.distance_weight * float(distance)
173
+
174
+
175
+ def _run_shape_linking(
176
+ working_dir: str | Path,
177
+ cfg: TrackingConfig,
178
+ *,
179
+ overwrite: bool = True,
180
+ ) -> Generator[tuple[int, int, str], None, None]:
181
+ total = 4
182
+ wd = Path(working_dir)
183
+ ultrack_cfg = _build_ultrack_config(cfg, wd)
184
+
185
+ from ultrack.core.database import NodeDB, maximum_time_from_database
186
+ from ultrack.core.linking.processing import add_links
187
+ from ultrack.core.linking.utils import clear_linking_data
188
+ from scipy.spatial import KDTree
189
+ import sqlalchemy as sqla
190
+ from sqlalchemy.orm import Session
191
+
192
+ if overwrite:
193
+ yield (0, total, "Clearing existing links…")
194
+ clear_linking_data(ultrack_cfg.data_config.database_path)
195
+ else:
196
+ yield (0, total, "Skipping link clear (overwrite=False)…")
197
+
198
+ engine = sqla.create_engine(ultrack_cfg.data_config.database_path)
199
+ max_t = int(maximum_time_from_database(ultrack_cfg.data_config))
200
+ if max_t <= 0:
201
+ yield (total, total, "No frames; skipping shape linking.")
202
+ return
203
+
204
+ yield (1, total, "Computing shape-weighted links…")
205
+ total_links = 0
206
+
207
+ with Session(engine) as session:
208
+ for time in range(max_t):
209
+ source_nodes = [n for (n,) in session.query(NodeDB.pickle).where(NodeDB.t == time)]
210
+ target_rows = list(
211
+ session.query(NodeDB.pickle, NodeDB.z_shift, NodeDB.y_shift, NodeDB.x_shift)
212
+ .where(NodeDB.t == time + 1)
213
+ )
214
+ if not source_nodes or not target_rows:
215
+ continue
216
+ target_nodes = [r[0] for r in target_rows]
217
+
218
+ source_pos = np.array([n.centroid for n in source_nodes], dtype=np.float32)
219
+ target_pos = np.array([n.centroid for n in target_nodes], dtype=np.float32)
220
+ for i, row in enumerate(target_rows):
221
+ shift = np.asarray(row[1:], dtype=np.float32)
222
+ target_pos[i] += shift[-target_pos.shape[1]:]
223
+
224
+ tree = KDTree(source_pos)
225
+ k = min(len(source_nodes), max(1, 2 * cfg.max_neighbors))
226
+ dists, neigh_idx = tree.query(target_pos, k=k, distance_upper_bound=cfg.max_distance)
227
+ if dists.ndim == 1:
228
+ dists, neigh_idx = dists[:, None], neigh_idx[:, None]
229
+
230
+ # Cache (coords, centroid, area) per source node id
231
+ src_cache: dict[int, tuple[np.ndarray, np.ndarray, float] | None] = {}
232
+
233
+ src_ids, tgt_ids, weights = [], [], []
234
+ for ti, (dist_row, ni_row) in enumerate(zip(dists, neigh_idx)):
235
+ target = target_nodes[ti]
236
+ candidates = []
237
+ for dist, si in zip(dist_row, ni_row):
238
+ if si >= len(source_nodes) or not np.isfinite(dist):
239
+ continue
240
+ source = source_nodes[si]
241
+ sid = int(source.id)
242
+ if sid not in src_cache:
243
+ result = _node_coords_centroid(source)
244
+ if result is None:
245
+ src_cache[sid] = None
246
+ else:
247
+ src_coords, src_centroid = result
248
+ src_area = float(getattr(source, "area", len(src_coords)))
249
+ src_cache[sid] = (src_coords, src_centroid, src_area)
250
+ cached = src_cache[sid]
251
+ if cached is None:
252
+ continue
253
+ src_coords, src_centroid, src_area = cached
254
+
255
+ tgt_result = _node_coords_centroid(target)
256
+ if tgt_result is None:
257
+ continue
258
+ tgt_coords, tgt_centroid = tgt_result
259
+ tgt_area = float(getattr(target, "area", len(tgt_coords)))
260
+
261
+ w = _shape_pair_score(
262
+ src_coords, src_centroid, src_area,
263
+ tgt_coords, tgt_centroid, tgt_area,
264
+ float(dist), cfg,
265
+ )
266
+ if w is None:
267
+ continue
268
+ candidates.append((w, sid, int(target.id)))
269
+
270
+ candidates.sort(reverse=True)
271
+ for w, sid, tid in candidates[:cfg.max_neighbors]:
272
+ src_ids.append(sid)
273
+ tgt_ids.append(tid)
274
+ weights.append(w)
275
+
276
+ if src_ids:
277
+ add_links(ultrack_cfg, src_ids, tgt_ids, weights)
278
+ total_links += len(src_ids)
279
+
280
+ yield (
281
+ 1 + int(math.floor((time + 1) / max(max_t, 1) * 2)),
282
+ total,
283
+ f"Linked t={time + 1}/{max_t} ({len(src_ids)} edges)",
284
+ )
285
+
286
+ yield (total, total, f"Shape linking done ({total_links} total edges).")
@@ -0,0 +1,46 @@
1
+ """Source-node provenance queries for Ultrack databases.
2
+
3
+ Reads the optional ``itasc_ultrack_source_nodes`` table that records
4
+ which merged node ids originated from which source segmentation. Used by
5
+ :mod:`itasc.tracking_ultrack.db_query` to scope hierarchy-cut-state
6
+ queries to a single source; returns empty results when the table is absent.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from pathlib import Path
11
+
12
+ import sqlalchemy as sqla
13
+
14
+ SOURCE_NODE_TABLE = "itasc_ultrack_source_nodes"
15
+
16
+
17
+ def _source_table_exists(conn) -> bool:
18
+ return (
19
+ conn.execute(
20
+ sqla.text(
21
+ "SELECT 1 FROM sqlite_master "
22
+ "WHERE type='table' AND name=:table_name"
23
+ ),
24
+ {"table_name": SOURCE_NODE_TABLE},
25
+ ).first()
26
+ is not None
27
+ )
28
+
29
+
30
+ def query_source_node_ids(db_path: str | Path, source_index: int) -> tuple[int, ...]:
31
+ """Return merged node ids that originated from ``source_index``."""
32
+ engine = sqla.create_engine(f"sqlite:///{Path(db_path)}")
33
+ try:
34
+ with engine.connect() as conn:
35
+ if not _source_table_exists(conn):
36
+ return ()
37
+ rows = conn.execute(
38
+ sqla.text(
39
+ f"SELECT node_id FROM {SOURCE_NODE_TABLE} "
40
+ "WHERE source_index=:source_index ORDER BY node_id"
41
+ ),
42
+ {"source_index": int(source_index)},
43
+ ).all()
44
+ return tuple(int(row[0]) for row in rows)
45
+ finally:
46
+ engine.dispose()
@@ -0,0 +1,10 @@
1
+ name: itasc-tracking
2
+ display_name: ITASC Nucleus Tracking
3
+ contributions:
4
+ commands:
5
+ - id: itasc-tracking.widget
6
+ title: "Ultrack Segment + Track"
7
+ python_name: itasc.napari.nucleus_workflow_widget:make_nucleus_tracking_widget
8
+ widgets:
9
+ - command: itasc-tracking.widget
10
+ display_name: "Ultrack Segment + Track"