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.
Files changed (104) hide show
  1. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/PKG-INFO +3 -1
  2. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/getting_started.rst +10 -1
  3. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/motile.rst +0 -13
  4. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/pyproject.toml +2 -0
  5. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/application_menus/menu_widget.py +1 -0
  6. motile_tracker-3.1.2.dev0/src/motile_tracker/data_views/views/layers/contour_labels.py +121 -0
  7. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/layers/track_labels.py +33 -8
  8. motile_tracker-3.1.2.dev0/src/motile_tracker/data_views/views_coordinator/collection_widget.py +434 -0
  9. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views_coordinator/tracks_list.py +54 -8
  10. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views_coordinator/tracks_viewer.py +25 -4
  11. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/menus/csv_widget.py +3 -0
  12. {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
  13. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/run_editor.py +26 -1
  14. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/run_viewer.py +1 -73
  15. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker.egg-info/PKG-INFO +3 -1
  16. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker.egg-info/SOURCES.txt +2 -0
  17. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker.egg-info/requires.txt +2 -0
  18. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/.github/workflows/deploy.yml +0 -0
  19. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/.github/workflows/docs.yml +0 -0
  20. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/.github/workflows/test.yml +0 -0
  21. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/.gitignore +0 -0
  22. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/.pre-commit-config.yaml +0 -0
  23. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/LICENSE +0 -0
  24. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/README.md +0 -0
  25. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/conda_config.yml +0 -0
  26. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/Makefile +0 -0
  27. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/conf.py +0 -0
  28. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/editing.rst +0 -0
  29. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/images/add_edge.png +0 -0
  30. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/images/add_node.png +0 -0
  31. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/images/break_edge.png +0 -0
  32. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/images/delete_node.png +0 -0
  33. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/images/results_demo_720p.mp4 +0 -0
  34. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/index.rst +0 -0
  35. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/key_bindings.rst +0 -0
  36. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/docs/source/tree_view.rst +0 -0
  37. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/scripts/hela_example_tracks.csv +0 -0
  38. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/scripts/load_external_points.py +0 -0
  39. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/scripts/run_hela.py +0 -0
  40. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/scripts/test_edge_selection.py +0 -0
  41. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/scripts/view_external_tracks.py +0 -0
  42. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/setup.cfg +0 -0
  43. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/__init__.py +0 -0
  44. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/application_menus/__init__.py +0 -0
  45. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/application_menus/editing_menu.py +0 -0
  46. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/application_menus/main_app.py +0 -0
  47. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/__init__.py +0 -0
  48. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/action_history.py +0 -0
  49. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/actions.py +0 -0
  50. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/node_type.py +0 -0
  51. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/solution_tracks.py +0 -0
  52. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/tracks.py +0 -0
  53. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_model/tracks_controller.py +0 -0
  54. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/__init__.py +0 -0
  55. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/__init__.py +0 -0
  56. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/layers/__init__.py +0 -0
  57. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/layers/track_graph.py +0 -0
  58. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/layers/track_points.py +0 -0
  59. {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
  60. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/tree_view/__init__.py +0 -0
  61. {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
  62. {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
  63. {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
  64. {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
  65. {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
  66. {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
  67. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/view_3d/__init__.py +0 -0
  68. {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
  69. {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
  70. {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
  71. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views/view_3d/view3D.py +0 -0
  72. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/data_views/views_coordinator/__init__.py +0 -0
  73. {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
  74. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/example_data.py +0 -0
  75. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/__init__.py +0 -0
  76. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/load_tracks.py +0 -0
  77. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/menus/__init__.py +0 -0
  78. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/menus/measurement_widget.py +0 -0
  79. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/menus/metadata_menu.py +0 -0
  80. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/import_export/menus/segmentation_widget.py +0 -0
  81. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/backend/__init__.py +0 -0
  82. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/backend/motile_run.py +0 -0
  83. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/backend/solve.py +0 -0
  84. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/backend/solver_params.py +0 -0
  85. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/__init__.py +0 -0
  86. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/motile_widget.py +0 -0
  87. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/param_values.py +0 -0
  88. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/params_editor.py +0 -0
  89. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/motile/menus/params_viewer.py +0 -0
  90. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker/napari.yaml +0 -0
  91. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker.egg-info/dependency_links.txt +0 -0
  92. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker.egg-info/entry_points.txt +0 -0
  93. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/src/motile_tracker.egg-info/top_level.txt +0 -0
  94. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/conftest.py +0 -0
  95. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/data_model/test_action_history.py +0 -0
  96. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/data_model/test_actions.py +0 -0
  97. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/data_model/test_tracks.py +0 -0
  98. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/data_model/test_tracks_controller.py +0 -0
  99. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/import_export/test_export_solution_to_csv.py +0 -0
  100. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/import_export/test_import_external_tracks.py +0 -0
  101. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/motile/backend/test_motile_run.py +0 -0
  102. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/motile/backend/test_solver.py +0 -0
  103. {motile_tracker-3.1.1.dev0 → motile_tracker-3.1.2.dev0}/tests/motile/menus/test_run_editor.py +0 -0
  104. {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.1.dev0
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``,
@@ -38,6 +38,8 @@ dependencies =[
38
38
  "motile_toolbox == 0.4.0",
39
39
  "pydantic",
40
40
  "tifffile[all]",
41
+ "tqdm",
42
+ "dask[array]>=2021.10.0",
41
43
  "fonticon-fontawesome6",
42
44
  "pyqtgraph",
43
45
  "lxml_html_clean", # only to deal with napari dependencies being broken
@@ -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(napari.layers.Labels):
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.colormap.color_dict = {
340
- key: np.array([*value[:-1], 0], dtype=np.float32)
341
- for key, value in self.colormap.color_dict.items()
342
- }
343
- for node in visible:
344
- # find the index in the colormap
345
- self.colormap.color_dict[node][-1] = 0.6
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