motile-tracker 3.1.1.dev0__tar.gz → 3.1.2.dev0__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.
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/PKG-INFO +3 -1
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/getting_started.rst +10 -1
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/motile.rst +0 -13
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/pyproject.toml +2 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/application_menus/menu_widget.py +1 -0
- motile_tracker-3.1.2.dev0/src/motile_tracker/data_views/views/layers/contour_labels.py +121 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/layers/track_labels.py +33 -8
- motile_tracker-3.1.2.dev0/src/motile_tracker/data_views/views_coordinator/collection_widget.py +434 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views_coordinator/tracks_list.py +54 -8
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views_coordinator/tracks_viewer.py +25 -4
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/menus/csv_widget.py +3 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/menus/import_external_tracks_dialog.py +2 -1
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/run_editor.py +26 -1
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/run_viewer.py +1 -73
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker.egg-info/PKG-INFO +3 -1
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker.egg-info/SOURCES.txt +2 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker.egg-info/requires.txt +2 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/.github/workflows/deploy.yml +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/.github/workflows/docs.yml +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/.github/workflows/test.yml +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/.gitignore +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/.pre-commit-config.yaml +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/LICENSE +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/README.md +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/conda_config.yml +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/Makefile +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/conf.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/editing.rst +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/images/add_edge.png +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/images/add_node.png +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/images/break_edge.png +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/images/delete_node.png +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/images/results_demo_720p.mp4 +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/index.rst +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/key_bindings.rst +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/tree_view.rst +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/scripts/hela_example_tracks.csv +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/scripts/load_external_points.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/scripts/run_hela.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/scripts/test_edge_selection.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/scripts/view_external_tracks.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/setup.cfg +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/application_menus/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/application_menus/editing_menu.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/application_menus/main_app.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/action_history.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/actions.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/node_type.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/solution_tracks.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/tracks.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/tracks_controller.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/layers/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/layers/track_graph.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/layers/track_points.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/layers/tracks_layer_group.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/tree_view/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/tree_view/flip_axes_widget.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/tree_view/navigation_widget.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/tree_view/tree_view_feature_widget.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/tree_view/tree_view_mode_widget.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/tree_view/tree_widget.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/tree_view/tree_widget_utils.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/view_3d/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/view_3d/clipping_plane_sliders.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/view_3d/layer_dropdown.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/view_3d/multiple_view_widget.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/view_3d/view3D.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views_coordinator/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views_coordinator/node_selection_list.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/example_data.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/load_tracks.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/menus/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/menus/measurement_widget.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/menus/metadata_menu.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/menus/segmentation_widget.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/backend/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/backend/motile_run.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/backend/solve.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/backend/solver_params.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/__init__.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/motile_widget.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/param_values.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/params_editor.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/params_viewer.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/napari.yaml +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker.egg-info/dependency_links.txt +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker.egg-info/entry_points.txt +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker.egg-info/top_level.txt +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/conftest.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/data_model/test_action_history.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/data_model/test_actions.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/data_model/test_tracks.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/data_model/test_tracks_controller.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/import_export/test_export_solution_to_csv.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/import_export/test_import_external_tracks.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/motile/backend/test_motile_run.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/motile/backend/test_solver.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/motile/menus/test_run_editor.py +0 -0
- {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/motile_plugin/utils/test_tree_widget_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: motile-tracker
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.2.dev0
|
|
4
4
|
Summary: Application for interactive tracking with global optimization
|
|
5
5
|
Author-email: Caroline Malin-Mayor <malinmayorc@janelia.hhmi.org>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -31,6 +31,8 @@ Requires-Dist: motile>=0.3
|
|
|
31
31
|
Requires-Dist: motile_toolbox==0.4.0
|
|
32
32
|
Requires-Dist: pydantic
|
|
33
33
|
Requires-Dist: tifffile[all]
|
|
34
|
+
Requires-Dist: tqdm
|
|
35
|
+
Requires-Dist: dask[array]>=2021.10.0
|
|
34
36
|
Requires-Dist: fonticon-fontawesome6
|
|
35
37
|
Requires-Dist: pyqtgraph
|
|
36
38
|
Requires-Dist: lxml_html_clean
|
|
@@ -74,7 +74,16 @@ is incorporating the detection and linking corrections into the optimization tas
|
|
|
74
74
|
Each ``Tracking Run`` will be stored in the ``Results List`` widget.
|
|
75
75
|
These are the runs that are stored in memory - if you run tracking multiple
|
|
76
76
|
times with different inputs or parameters, you can click back and forth
|
|
77
|
-
between the results here. Here you can also save any runs that you want to store for later
|
|
77
|
+
between the results here. Here you can also save any runs that you want to store for later,
|
|
78
|
+
or export the tracks to a csv file. If your input was a Labels layer, the
|
|
79
|
+
``node_id`` will be determined by segmentation label id. If your original segmentation
|
|
80
|
+
repeated labels across time, the application will relabel them all to be unique, and
|
|
81
|
+
the new label id will be used as the node id.
|
|
82
|
+
If your input was a Points layer, the ``node_id`` is simply the index of the
|
|
83
|
+
node in the list of points.
|
|
84
|
+
Note: This does not save the output segmentation. If you want to save
|
|
85
|
+
the relabeled segmentation, you can do so through napari by selecting the
|
|
86
|
+
layer and then selecting ``File``-> ``Save selected layers``
|
|
78
87
|
Deleting runs you do not want to keep viewing is a good idea, since these are stored in memory.
|
|
79
88
|
Runs that were saved in previous sessions do not appear here until you load them from disk with the ``Load Tracks`` button.
|
|
80
89
|
The tracking results can also be visualized as a lineage tree.
|
|
@@ -47,19 +47,6 @@ The ``Run Viewer`` contains the following information:
|
|
|
47
47
|
- The ``Graph of solver gap``, which is mostly for debugging purposes.
|
|
48
48
|
The solver gap is an optimization value that should decrease at each iteration.
|
|
49
49
|
- The run settings, including ``Hyperparameters``, ``Costs``, and ``Attribute weights``.
|
|
50
|
-
- The ``Save run`` button. This button will take you to a file dialog to save the
|
|
51
|
-
whole run, so that if you close napari and re-open it, you can load the run
|
|
52
|
-
and see the results.
|
|
53
|
-
- The ``Export tracks to CSV`` button, which will take you to a file dialog for saving
|
|
54
|
-
a csv file containing the tracks. If your input was a Labels layer, the
|
|
55
|
-
``node_id`` will be determined by segmentation label id. If your original segmentation
|
|
56
|
-
repeated labels across time, the application will relabel them all to be unique, and
|
|
57
|
-
the new label id will be used as the node id.
|
|
58
|
-
If your input was a Points layer, the ``node_id`` is simply the index of the
|
|
59
|
-
node in the list of points.
|
|
60
|
-
- Note: This does not save the output segmentation. If you want to save
|
|
61
|
-
the relabeled segmentation, you can do so through napari by selecting the
|
|
62
|
-
layer and then selecting ``File``-> ``Save selected layers``
|
|
63
50
|
- The ``Back to editing`` button, which will return you to the ``Run Editor`` in its
|
|
64
51
|
previous state.
|
|
65
52
|
- The ``Edit this run`` button. This button will take you back to the ``Run Editor``,
|
|
@@ -25,6 +25,7 @@ class MenuWidget(QScrollArea):
|
|
|
25
25
|
tabwidget.addTab(motile_widget, "Track with Motile")
|
|
26
26
|
tabwidget.addTab(editing_widget, "Edit Tracks")
|
|
27
27
|
tabwidget.addTab(tracks_viewer.tracks_list, "Results List")
|
|
28
|
+
tabwidget.addTab(tracks_viewer.collection_widget, "Collections")
|
|
28
29
|
|
|
29
30
|
layout = QVBoxLayout()
|
|
30
31
|
layout.addWidget(tabwidget)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import napari
|
|
7
|
+
import numpy as np
|
|
8
|
+
from napari.layers.labels._labels_utils import (
|
|
9
|
+
expand_slice,
|
|
10
|
+
)
|
|
11
|
+
from napari.utils import DirectLabelColormap
|
|
12
|
+
from scipy import ndimage as ndi
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_contours(
|
|
16
|
+
labels: np.ndarray,
|
|
17
|
+
thickness: int,
|
|
18
|
+
background_label: int,
|
|
19
|
+
group_labels: list[int] | None = None,
|
|
20
|
+
):
|
|
21
|
+
"""Computes the contours of a 2D label image.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
labels : array of integers
|
|
26
|
+
An input labels image.
|
|
27
|
+
thickness : int
|
|
28
|
+
It controls the thickness of the inner boundaries. The outside thickness is always 1.
|
|
29
|
+
The final thickness of the contours will be `thickness + 1`.
|
|
30
|
+
background_label : int
|
|
31
|
+
That label is used to fill everything outside the boundaries.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
A new label image in which only the boundaries of the input image are kept.
|
|
36
|
+
"""
|
|
37
|
+
struct_elem = ndi.generate_binary_structure(labels.ndim, 1)
|
|
38
|
+
|
|
39
|
+
thick_struct_elem = ndi.iterate_structure(struct_elem, thickness).astype(bool)
|
|
40
|
+
|
|
41
|
+
dilated_labels = ndi.grey_dilation(labels, footprint=struct_elem)
|
|
42
|
+
eroded_labels = ndi.grey_erosion(labels, footprint=thick_struct_elem)
|
|
43
|
+
not_boundaries = dilated_labels == eroded_labels
|
|
44
|
+
|
|
45
|
+
contours = labels.copy()
|
|
46
|
+
contours[not_boundaries] = background_label
|
|
47
|
+
|
|
48
|
+
# instead of filling with background label, fill the group label with their normal color
|
|
49
|
+
if group_labels is not None and len(group_labels) > 0:
|
|
50
|
+
group_mask = functools.reduce(
|
|
51
|
+
np.logical_or, (labels == val for val in group_labels)
|
|
52
|
+
)
|
|
53
|
+
combined_mask = not_boundaries & group_mask
|
|
54
|
+
contours = np.where(combined_mask, labels, contours)
|
|
55
|
+
|
|
56
|
+
return contours
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ContourLabels(napari.layers.Labels):
|
|
60
|
+
"""Extended labels layer that allows to show contours and filled labels simultaneously"""
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def _type_string(self) -> str:
|
|
64
|
+
return "labels" # to make sure that the layer is treated as labels layer for saving
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
data: np.array,
|
|
69
|
+
name: str,
|
|
70
|
+
opacity: float,
|
|
71
|
+
scale: tuple,
|
|
72
|
+
colormap: DirectLabelColormap,
|
|
73
|
+
):
|
|
74
|
+
super().__init__(
|
|
75
|
+
data=data,
|
|
76
|
+
name=name,
|
|
77
|
+
opacity=opacity,
|
|
78
|
+
scale=scale,
|
|
79
|
+
colormap=colormap,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
self.group_labels = None
|
|
83
|
+
|
|
84
|
+
def _calculate_contour(
|
|
85
|
+
self, labels: np.ndarray, data_slice: tuple[slice, ...]
|
|
86
|
+
) -> Optional[np.ndarray]:
|
|
87
|
+
"""Calculate the contour of a given label array within the specified data slice.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
labels : np.ndarray
|
|
92
|
+
The label array.
|
|
93
|
+
data_slice : Tuple[slice, ...]
|
|
94
|
+
The slice of the label array on which to calculate the contour.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
Optional[np.ndarray]
|
|
99
|
+
The calculated contour as a boolean mask array.
|
|
100
|
+
Returns None if the contour parameter is less than 1,
|
|
101
|
+
or if the label array has more than 2 dimensions.
|
|
102
|
+
"""
|
|
103
|
+
if self.contour < 1:
|
|
104
|
+
return None
|
|
105
|
+
if labels.ndim > 2:
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
expanded_slice = expand_slice(data_slice, labels.shape, 1)
|
|
109
|
+
sliced_labels = get_contours(
|
|
110
|
+
labels[expanded_slice],
|
|
111
|
+
self.contour,
|
|
112
|
+
self.colormap.background_value,
|
|
113
|
+
self.group_labels,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Remove the latest one-pixel border from the result
|
|
117
|
+
delta_slice = tuple(
|
|
118
|
+
slice(s1.start - s2.start, s1.stop - s2.start)
|
|
119
|
+
for s1, s2 in zip(data_slice, expanded_slice, strict=False)
|
|
120
|
+
)
|
|
121
|
+
return sliced_labels[delta_slice]
|
|
@@ -18,6 +18,8 @@ if TYPE_CHECKING:
|
|
|
18
18
|
|
|
19
19
|
from motile_toolbox.candidate_graph.graph_attributes import NodeAttr
|
|
20
20
|
|
|
21
|
+
from motile_tracker.data_views.views.layers.contour_labels import ContourLabels
|
|
22
|
+
|
|
21
23
|
|
|
22
24
|
def new_label(layer: TrackLabels):
|
|
23
25
|
"""A function to override the default napari labels new_label function.
|
|
@@ -67,7 +69,7 @@ def _new_label(layer: TrackLabels, new_track_id=True):
|
|
|
67
69
|
)
|
|
68
70
|
|
|
69
71
|
|
|
70
|
-
class TrackLabels(
|
|
72
|
+
class TrackLabels(ContourLabels):
|
|
71
73
|
"""Extended labels layer that holds the track information and emits
|
|
72
74
|
and responds to dynamics visualization signals"""
|
|
73
75
|
|
|
@@ -97,6 +99,10 @@ class TrackLabels(napari.layers.Labels):
|
|
|
97
99
|
)
|
|
98
100
|
|
|
99
101
|
self.viewer = viewer
|
|
102
|
+
self.viewer.dims.events.ndisplay.connect(
|
|
103
|
+
lambda: self.update_label_colormap(visible=None)
|
|
104
|
+
)
|
|
105
|
+
self.group_labels = None
|
|
100
106
|
|
|
101
107
|
# Key bindings (should be specified both on the viewer (in tracks_viewer)
|
|
102
108
|
# and on the layer to overwrite napari defaults)
|
|
@@ -322,8 +328,22 @@ class TrackLabels(napari.layers.Labels):
|
|
|
322
328
|
with self.events.selected_label.blocker():
|
|
323
329
|
highlighted = self.tracks_viewer.selected_nodes
|
|
324
330
|
|
|
331
|
+
if visible is None:
|
|
332
|
+
visible = self.group_labels if self.group_labels is not None else "all"
|
|
333
|
+
|
|
334
|
+
# update the opacity of the cyclic label colormap values according to whether nodes are visible/invisible/highlighted
|
|
335
|
+
self.colormap.color_dict = {
|
|
336
|
+
key: np.array(
|
|
337
|
+
[*value[:-1], 0.6 if key is not None and key != 0 else value[-1]],
|
|
338
|
+
dtype=np.float32,
|
|
339
|
+
)
|
|
340
|
+
for key, value in self.colormap.color_dict.items()
|
|
341
|
+
}
|
|
342
|
+
|
|
325
343
|
# update the opacity of the cyclic label colormap values according to whether nodes are visible/invisible/highlighted
|
|
326
344
|
if visible == "all":
|
|
345
|
+
self.contour = 0
|
|
346
|
+
self.group_labels = None
|
|
327
347
|
self.colormap.color_dict = {
|
|
328
348
|
key: np.array(
|
|
329
349
|
[
|
|
@@ -336,13 +356,17 @@ class TrackLabels(napari.layers.Labels):
|
|
|
336
356
|
}
|
|
337
357
|
|
|
338
358
|
else:
|
|
339
|
-
self.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
359
|
+
if self.viewer.dims.ndisplay == 2:
|
|
360
|
+
self.contour = 1
|
|
361
|
+
self.group_labels = visible
|
|
362
|
+
else:
|
|
363
|
+
self.colormap.color_dict = {
|
|
364
|
+
key: np.array([*value[:-1], 0], dtype=np.float32)
|
|
365
|
+
for key, value in self.colormap.color_dict.items()
|
|
366
|
+
}
|
|
367
|
+
for node in visible:
|
|
368
|
+
# find the index in the colormap
|
|
369
|
+
self.colormap.color_dict[node][-1] = 0.6
|
|
346
370
|
|
|
347
371
|
for node in highlighted:
|
|
348
372
|
self.colormap.color_dict[node][-1] = 1 # full opacity
|
|
@@ -350,6 +374,7 @@ class TrackLabels(napari.layers.Labels):
|
|
|
350
374
|
self.colormap = DirectLabelColormap(
|
|
351
375
|
color_dict=self.colormap.color_dict
|
|
352
376
|
) # create a new colormap from the updated colors (otherwise it does not refresh)
|
|
377
|
+
self.refresh()
|
|
353
378
|
|
|
354
379
|
def new_colormap(self):
|
|
355
380
|
"""Override existing function to generate new colormap on tracks_viewer and
|