napari_track_edit.data_views.views.layers.track_labels ====================================================== .. py:module:: napari_track_edit.data_views.views.layers.track_labels Classes ------- .. autoapisummary:: napari_track_edit.data_views.views.layers.track_labels.TrackLabels Functions --------- .. autoapisummary:: napari_track_edit.data_views.views.layers.track_labels.updates_from_masked_atoms napari_track_edit.data_views.views.layers.track_labels.updates_from_index_atoms napari_track_edit.data_views.views.layers.track_labels.new_label napari_track_edit.data_views.views.layers.track_labels._new_label Module Contents --------------- .. py:function:: updates_from_masked_atoms(atoms) -> list[tuple[tracksdata.nodes.Mask, int, int]] Turn napari >= 0.8 paint atoms into one segmentation update per label. Each atom is a ``_MaskedPaintAtom``: a bounding box, a mask of the pixels that changed inside it, their values before the change, and the value painted. Note: A stroke emits one atom per brush position, normally napari records a pixel only the first time it is painted: it drops pixels that already hold the value being painted. That check reads the layer's data, which we never write back (see ``ContourLabels._paint_region_with_mask``), so every brush position reports its whole box afresh, and a slow drag repeats the same box dozens of times. Atoms that share a box are therefore mergedfirst, so that a pixel is turned into mask pixels only once. :param atoms: the atoms of one paint event, all in the mask form. :returns: one (mask, time, old value) per label painted over, per brush position and time point. Each mask carries the bounding box of the brush position it came from; funtracks tightens them around their own pixels and combines the ones of a label. :rtype: list[tuple[Mask, int, int]] .. py:function:: updates_from_index_atoms(atoms) -> list[tuple[tuple[numpy.ndarray, ...], int]] Turn napari ≤ 0.7 paint atoms into one segmentation update per label. Each atom is a ``data_setitem`` 3-tuple: a multi-index of the elements that changed, their values before the change, and the value after it. There is no bounding box to work from, so the coordinates are handed to funtracks, which builds the masks. :param atoms: the atoms of one paint event, all in the multi-index form. :returns: one (multi-index, old value) per label painted over, per time point. A coordinate may appear twice in an index, which funtracks is free to ignore: painting a pixel into a mask is idempotent. :rtype: list[tuple[tuple[np.ndarray, ...], int]] .. py:function:: new_label(layer: TrackLabels) A function to override the default napari labels new_label function. Must be registered (see end of this file) .. py:function:: _new_label(layer: TrackLabels, new_track_id=True) A function to get a new label for a given TrackLabels layer. This helper is abstracted out because we want to do the same thing both with and without making a new track id for the layer. :param layer: A TrackLabels layer from which get a new label for drawing a new segmentation. Updates the selected_label attribute. :type layer: TrackLabels :param new_track_id: If you should also generate a new track id and set it to the selected_track attribute. Defaults to True. :type new_track_id: bool, optional .. py:class:: TrackLabels(viewer: napari.Viewer, data: numpy.array, name: str, opacity: float, scale: tuple, tracks_viewer: napari_track_edit.data_views.views_coordinator.tracks_viewer.TracksViewer) Bases: :py:obj:`napari_track_edit.data_views.views.layers.contour_labels.ContourLabels` Extended labels layer that holds the track information and emits and responds to dynamics visualization signals .. py:property:: _type_string :type: str .. py:attribute:: tracks_viewer .. py:attribute:: track_colormap .. py:attribute:: viewer .. py:attribute:: highlight_opacity :value: 1 .. py:attribute:: foreground_opacity :value: 0.6 .. py:attribute:: background_opacity :value: 0.3 .. py:attribute:: highlight_contour :value: False .. py:attribute:: foreground_contour :value: False .. py:method:: click(_, event) .. py:method:: new_label() -> None Select a valid new label to paint a new track with. Called by TracksViewer.request_new_track, which owns the "start a new track" action for all views. The label is new by construction, guard can be skipped. .. py:method:: process_click(event: napari.utils.events.Event, value: int | None = None, side_button: int | None = None, layer: napari_track_edit.data_views.views.layers.contour_labels.ContourLabels | None = None) Process the click event to update the selected nodes. :param event: The click event. :type event: Event :param value: The label value (node) at the clicked position. :type value: int :param side_button: the integer for the mouse side buttons (4: back, 5: forward) :type side_button: int | None :param layer: The (ortho view) layer from which the click originated. If provided, it is used to check label visibility in that layer's colormap. :type layer: ContourLabels | None .. py:method:: _check_mode() Check if the mode is valid and call the ensure_valid_label function .. py:method:: redo() Overwrite the redo functionality of the labels layer and invoke redo action on the tracks_viewer.tracks first .. py:method:: undo() Overwrite undo function and invoke undo action on the tracks_viewer.tracks first .. py:method:: _parse_paint_event(event_val) :staticmethod: Turn a paint event into the segmentation updates funtracks expects. napari reports the atoms of an event in one of two forms, never mixed within an event: the mask form of napari >= 0.8, and the multi-index form that ``data_setitem`` still uses. :param event_val: the paint "atoms" the labels layer recorded for this event. :type event_val: list[tuple] :returns: the updates for every label painted over, in whichever of the two forms ``UserUpdateSegmentation`` was handed. Empty when the event changed nothing. :rtype: list[tuple] .. py:method:: _revert_paint(_, source_layer: napari.layers.Labels | None = None) Revert a paint event after it fails validation (no actions have been created). This keeps the view synced with the backend data. been created). If a source_layer is provided, the paint event will be reverted on this layer (this is necessary for orthoviews). This keeps the view synced with the backend data. .. py:method:: _on_paint(event) Listen to the paint event and check which track_ids have changed .. py:method:: _refresh() Refresh the data in the labels layer .. py:method:: update_label_colormap(visible: list[int] | str) -> None Updates the opacity for the highlighted, foreground, and background labels, and adds labels to the filled_labels if necessary. .. py:method:: new_colormap() Override existing function to generate new colormap on tracks_viewer and emit refresh signal to update colors in all layers/widgets .. py:method:: update_selected_label() Update the selected label in the labels layer .. py:method:: _ensure_valid_label(event: napari.utils.events.Event | None = None) Make sure a valid label is selected, because it is not allowed to paint with a label that already exists at a different timepoint. Scenarios: 1. If a node with the selected label value (node id) exists at a different time point, check if there is any node with the same track_id at the current time point. a. If there is a node with the same track id, select that one, so that it can be used to update an existing node. b. If there is no node with the same track id, create a new node id and paint with the track_id of the selected label. This can be used to add a new node with the same track id at a time point where it does not (yet) exist (anymore). 2. If there is no existing node with this value in the graph, it is assumed that you want to add a node with the current track id. Retrieve the track_id from self.current_track_id and use it to find if there are any nodes of this track id at current time point. 3. If no node with this label exists yet, it is valid and can be used to start a new track id. Therefore, create a new node id and map a new color. Add it to the dictionary. 4. If a node with the label exists at the current time point, it is valid and can be used to update the existing node in a paint event. No action is needed.