-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #65 from funkelab/53-add-lineage-tree-view
53 add lineage tree view
- Loading branch information
Showing
35 changed files
with
1,760 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,7 @@ | ||
from importlib.metadata import PackageNotFoundError, version | ||
|
||
from .widgets.motile_widget import MotileWidget | ||
|
||
try: | ||
__version__ = version("motile-toolbox") | ||
__version__ = version("motile-plugin") | ||
except PackageNotFoundError: | ||
# package is not installed | ||
__version__ = "uninstalled" | ||
|
||
__all__ = ("MotileWidget",) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .motile_run import MotileRun # noqa | ||
from .solver_params import SolverParams # noqa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .tracks import Tracks # noqa | ||
from .node_type import NodeType # noqa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from enum import Enum | ||
|
||
|
||
class NodeType(Enum): | ||
"""Types of nodes in the track graph. Currently used for standardizing | ||
visualization. All nodes are exactly one type. | ||
""" | ||
|
||
SPLIT = "SPLIT" | ||
END = "END" | ||
CONTINUE = "CONTINUE" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
from __future__ import annotations | ||
|
||
from typing import TYPE_CHECKING | ||
|
||
from motile_toolbox.candidate_graph import NodeAttr | ||
from pydantic import BaseModel | ||
|
||
if TYPE_CHECKING: | ||
from typing import Any | ||
|
||
import networkx as nx | ||
import numpy as np | ||
|
||
|
||
class Tracks(BaseModel): | ||
"""A set of tracks consisting of a graph and an optional segmentation. | ||
The graph nodes represent detections and must have a time attribute and | ||
position attribute. Edges in the graph represent links across time. | ||
Attributes: | ||
graph (nx.DiGraph): A graph with nodes representing detections and | ||
and edges representing links across time. Assumed to be "valid" | ||
tracks (e.g., this is not supposed to be a candidate graph), | ||
but the structure is not verified. | ||
segmentation (Optional(np.ndarray)): An optional segmentation that | ||
accompanies the tracking graph. If a segmentation is provided, | ||
it is assumed that the graph has an attribute (default | ||
"seg_id") holding the segmentation id. Defaults to None. | ||
time_attr (str): The attribute in the graph that specifies the time | ||
frame each node is in. | ||
pos_attr (str | tuple[str] | list[str]): The attribute in the graph | ||
that specifies the position of each node. Can be a single attribute | ||
that holds a list, or a list of attribute keys. | ||
""" | ||
|
||
graph: nx.DiGraph | ||
segmentation: np.ndarray | None = None | ||
time_attr: str = NodeAttr.TIME.value | ||
pos_attr: str | tuple[str] | list[str] = NodeAttr.POS.value | ||
scale: list[float] | None = None | ||
# pydantic does not check numpy arrays | ||
model_config = {"arbitrary_types_allowed": True} | ||
|
||
def get_location(self, node: Any, incl_time: bool = False): | ||
"""Get the location of a node in the graph. Optionally include the | ||
time frame as the first dimension. Raises an error if the node | ||
is not in the graph. | ||
Args: | ||
node (Any): The node id in the graph to get the location of. | ||
incl_time (bool, optional): If true, include the time as the | ||
first element of the location array. Defaults to False. | ||
Returns: | ||
list[float]: A list holding the location. If the position | ||
is stored in a single key, the location could be any number | ||
of dimensions. | ||
""" | ||
data = self.graph.nodes[node] | ||
if isinstance(self.pos_attr, (tuple, list)): | ||
pos = [data[dim] for dim in self.pos_attr] | ||
else: | ||
pos = data[self.pos_attr] | ||
|
||
if incl_time: | ||
pos = [data[self.time_attr], *pos] | ||
|
||
return pos | ||
|
||
def get_time(self, node: Any) -> int: | ||
"""Get the time frame of a given node. Raises an error if the node | ||
is not in the graph. | ||
Args: | ||
node (Any): The node id to get the time frame for | ||
Returns: | ||
int: The time frame that the node is in | ||
""" | ||
return self.graph.nodes[node][self.time_attr] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import copy | ||
|
||
import napari | ||
import networkx as nx | ||
import numpy as np | ||
from motile_toolbox.visualization import to_napari_tracks_layer | ||
from napari.utils import CyclicLabelColormap | ||
|
||
from motile_plugin.core import Tracks | ||
|
||
|
||
class TrackGraph(napari.layers.Tracks): | ||
"""Extended tracks layer that holds the track information and emits and responds | ||
to dynamics visualization signals""" | ||
|
||
def __init__( | ||
self, | ||
viewer: napari.Viewer, | ||
tracks: Tracks, | ||
name: str, | ||
colormap: CyclicLabelColormap, | ||
scale: tuple, | ||
): | ||
if tracks is None or tracks.graph is None: | ||
graph = nx.DiGraph() | ||
else: | ||
graph = tracks.graph | ||
|
||
track_data, track_props, track_edges = to_napari_tracks_layer( | ||
graph, frame_key=tracks.time_attr, location_key=tracks.pos_attr | ||
) | ||
|
||
super().__init__( | ||
data=track_data, | ||
graph=track_edges, | ||
properties=track_props, | ||
name=name, | ||
tail_length=3, | ||
color_by="track_id", | ||
scale=scale, | ||
) | ||
|
||
self.viewer = viewer | ||
self.colormaps_dict["track_id"] = colormap | ||
|
||
self.tracks_layer_graph = copy.deepcopy( | ||
self.graph | ||
) # for restoring graph later | ||
|
||
def update_track_visibility(self, visible: list[int] | str) -> None: | ||
"""Optionally show only the tracks of a current lineage""" | ||
|
||
if visible == "all": | ||
self.track_colors[:, 3] = 1 | ||
self.graph = self.tracks_layer_graph | ||
else: | ||
track_id_mask = np.isin( | ||
self.properties["track_id"], | ||
visible, | ||
) | ||
self.graph = { | ||
key: self.tracks_layer_graph[key] | ||
for key in visible | ||
if key in self.tracks_layer_graph | ||
} | ||
|
||
self.track_colors[:, 3] = 0 | ||
self.track_colors[track_id_mask, 3] = 1 | ||
if len(self.graph.items()) == 0: | ||
self.display_graph = False # empty dicts to not trigger update (bug?) so disable the graph entirely as a workaround | ||
else: | ||
self.display_graph = True |
Oops, something went wrong.