diff --git a/docs/howtos/extending/magicgui.md b/docs/howtos/extending/magicgui.md index 9be04618f..2ee81808e 100644 --- a/docs/howtos/extending/magicgui.md +++ b/docs/howtos/extending/magicgui.md @@ -539,8 +539,8 @@ The following are all valid {attr}`napari.types.LayerDataTuple` examples: # an empty points layer (None, {}, 'points') -# points with properties -(np.random.rand(20, 2), {'properties': {'values': np.random.rand(20)}}, 'points') +# points with features +(np.random.rand(20, 2), {'features': {'values': np.random.rand(20)}}, 'points') ``` An example of using a {attr}`~napari.types.LayerDataTuple` return annotation in @@ -553,8 +553,8 @@ import napari.types @magicgui(call_button='Make Points') def make_points(n_points=40) -> napari.types.LayerDataTuple: data = 500 * np.random.rand(n_points, 2) - props = {'values': np.random.rand(n_points)} - return (data, {'properties': props}, 'points') + features = {'values': np.random.rand(n_points)} + return (data, {'features': features}, 'points') viewer = napari.Viewer() viewer.window.add_dock_widget(make_points) diff --git a/docs/howtos/layers/points.md b/docs/howtos/layers/points.md index 36b6d369a..366150640 100644 --- a/docs/howtos/layers/points.md +++ b/docs/howtos/layers/points.md @@ -15,8 +15,8 @@ kernelspec: ```{Admonition} DEPRECATED ATTRIBUTES :class: warning -As of napari 0.5.0, `edge_*` attributes are being renamed to -`border_*` attributes. We have yet to update the images and/or videos in +As of napari 0.5.0, `edge_*` attributes are being renamed to +`border_*` attributes. We have yet to update the images and/or videos in this tutorial. Please use `border` in place of `edge` for all `Points` attributes moving forward. The code in this tutorial uses the latest API. Only images and videos may be out of date. @@ -37,11 +37,11 @@ points independently. You can also adjust `opacity` and `symbol` representing all the points simultaneously. Each data point can have annotations associated with it using the -`Points.properties` dictionary. These properties can be used to set the face and +`Points.features` table. These features can be used to set the face and border colors of the points. For example, when displaying points of different classes/types, one could automatically set color the individual points by their -respective class/type. For more details on point properties, see -[](#setting-point-border-and-face-color-with-properties) or +respective class/type. For more details on point features, see +[](#setting-point-border-and-face-color-with-features) or [point annotation tutorial](../../tutorials/annotation/annotate_points). ## Creating and editing the `points` layer using the GUI @@ -116,7 +116,7 @@ layer: to move around the `points` layer as you create your selection. * **Pan/zoom** - ![image: Pan/zoom tool](../../images/pan-zoom-tool.png) + ![image: Pan/zoom tool](../../images/pan-zoom-tool.png) The default mode of the points layer supports panning and zooming, as in the image layer. This mode is represented by the magnifying glass in the layers @@ -194,7 +194,7 @@ layer: Note that when entering 3D rendering mode the GUI `Add point`, `Delete selected points`, and `Select points` tools are all disabled. Those options are supported only when viewing a layer using 2D rendering. - + * `ctrl-c` and `ctrl-v` (copying and pasting points) Copy and paste any selected points using `ctrl-c` and `ctrl-v`, respectively. @@ -214,12 +214,12 @@ the same. In these examples we'll mainly use `add_points` to overlay points onto on an existing image. Each data point can have annotations associated with it using the -`Points.properties` dictionary. These properties can be used to set the face and +`Points.features` table. These features can be used to set the face and border colors of the points. For example, when displaying points of different classes/types, one could automatically set the color of the individual points by -their respective class/type. For more details on point properties, see -[](#setting-point-border-and-face-color-with-properties) below or the -[Point annotation tutorial](../../tutorials/annotation/annotate_points). +their respective class/type. For more details on point features, see +[](#setting-point-border-and-face-color-with-features) below or the +[Point annotation tutorial](annotating-points). In this example, we will overlay some points on the image of an astronaut: @@ -267,14 +267,40 @@ the same as the ordering of the dimensions for image layers. This array is always accessible through the `layer.data` property and will grow or shrink as new points are either added or deleted. -### Using the points properties dictionary +(points-features-table)= + +### Using the points features table + +The `Points` layer can contain features that annotate each point. +`Points.features` stores the features in a table or data frame where each column +represents a feature and each row represents a point. +Therefore, the table has N rows for the N points in `Points.data`. +This table can be provided as a dictionary that maps from feature names to +the columns of feature values. +For example, the following dictionary can be used as the value for the `features` +parameter in {meth}`Viewer.add_points` + +```python +features = { + 'good_point': [True, True, False], + 'confidence': [0.99, 0.8, 0.2], +} +``` + +and corresponds to the following features table + +| point index | good_point | confidence | +| ----------- | ---------- | ---------- | +| 0 | True | 0.99 | +| 1 | True | 0.8 | +| 2 | False | 0.2 | + +where the point index is the index for a point in both `data` and its corresponding +row in the `features` table. -The `points` layer can contain properties that annotate each point. -`Points.properties` stores the properties in a dictionary where each key is the -name of the property and the values are NumPy arrays with a value for each point -(i.e., length N for N points in `Points.data`). As we will see below, we can use -the values in a property to set the display properties of the points (e.g., face -color or border color). To see the points properties in action, please see the +As we will see below, we can use feature values to determine the display properties +of the points (e.g., face color or border color). +To see the points features in action, please see the [Point annotation tutorial](annotating-points). @@ -338,17 +364,17 @@ properties are different from the `layer.current_border_color` and `layer.current_face_color` properties that will determine the color of the next point to be added or any currently selected points. -### Setting point border and face color with properties +### Setting point border and face color with features -Point border and face colors can be set as a function of a property in -`Points.properties`. There are two ways the values in `Points.properties` can be +Point border and face colors can be set as a function of a feature in +`Points.features`. There are two ways that these feature values can be mapped to colors: (1) color cycles and (2) colormaps. -Color cycles are sets of colors that are mapped to categorical properties. The -colors are repeated if the number of unique property values is greater than the -number of colors in the color cycle. +Color cycles are sets of colors that are mapped to categorical features. +The colors are repeated if the number of unique feature values is greater +than the number of colors in the color cycle. -Colormaps are a continuum of colors that are mapped to a continuous property +Colormaps are a continuum of colors that are mapped to a continuous feature value. The available colormaps are listed below (colormaps are from [vispy](https://vispy.org/api/vispy.color.colormap.html#vispy.color.colormap.Colormap)). For guidance on choosing colormaps, see the @@ -360,21 +386,21 @@ list(napari.utils.colormaps.AVAILABLE_COLORMAPS) ### Setting border or face color with a color cycle -Here we will set the border color of the markers with a color cycle on a property. +Here we will set the border color of the markers with a color cycle on a feature. To do the same for a face color, substitute `face_color` for `border_color` in the example snippet below. ```{code-cell} python viewer = napari.view_image(data.astronaut(), rgb=True) points = np.array([[100, 100], [200, 200], [300, 100]]) -point_properties = { - 'good_point': np.array([True, True, False]), - 'confidence': np.array([0.99, 0.8, 0.2]), +point_features = { + 'good_point': [True, True, False], + 'confidence': [0.99, 0.8, 0.2], } points_layer = viewer.add_points( points, - properties=point_properties, + features=point_features, border_color='good_point', border_color_cycle=['magenta', 'green'], border_width=0.5, @@ -392,33 +418,34 @@ nbscreenshot(viewer, alt_text="3 points overlaid on an astronaut image, where th viewer.close() ``` -In the example above, the `point_properties` were provided as a dictionary with -two properties: `good_point` and `confidence`. The values of each property are -stored in a NumPy ndarray with length 3 since there were 3 coordinates provided -in `points`. We set the border color as a function of the `good_point` property by -providing the keyword argument `border_color='good_point'` to the -`viewer.add_points()` method. The color cycle is set via the `border_color_cycle` -keyword argument, `border_color_cycle=['magenta', 'green']`. The color cycle can -be provided as a list of colors (as a list of strings or a (M x 4) array of M -RGBA colors). +In the example above, the `point_features` table was provided as a +dictionary with two keys or features: `good_point` and `confidence` +as described in [](points-features-table). +The values of each feature are stored in a list of length 3 since there were three +coordinates provided in `points`. We set the border color as a function of the +`good_point` feature by providing the keyword argument +`border_color='good_point'` to the `viewer.add_points()` method. +The color cycle is set via the `border_color_cycle` keyword argument, +`border_color_cycle=['magenta', 'green']`. The color cycle can be provided as a +list of colors (a list of strings or a (M x 4) array of M RGBA colors). ### Setting border or face color with a colormap In the example snippet below, we set the face color of the markers with a -colormap on a property. To do the same for a border color, substitute `face` for +colormap on a feature. To do the same for a border color, substitute `face` for `border`. ```{code-cell} python viewer = napari.view_image(data.astronaut(), rgb=True) points = np.array([[100, 100], [200, 200], [300, 100]]) -point_properties = { - 'good_point': np.array([True, True, False]), - 'confidence': np.array([0.99, 0.8, 0.2]), +point_features = { + 'good_point': [True, True, False], + 'confidence': [0.99, 0.8, 0.2], } points_layer = viewer.add_points( points, - properties=point_properties, + features=point_features, face_color='confidence', face_colormap='viridis', ) @@ -435,13 +462,15 @@ nbscreenshot(viewer, alt_text="3 points overlaid on an astronaut image, where th viewer.close() ``` -In the example above, the `point_properties` were provided as a dictionary with -two properties: `good_point` and `confidence`. The values of each property are -stored in a NumPy ndarray with length 3 since there were 3 coordinates provided -in `points`. We set the face color as a function of the `confidence` property by -providing the keyword argument `face_color='confidence'` to the -`viewer.add_points()` method. We set the colormap to viridis using the -`face_colormap` keyword argument as `face_colormap='viridis'`. +In the example above, the `point_features` table was provided as a +dictionary with two keys or features: `good_point` and `confidence` +as described in [](points-features-table). +The values of each feature are stored in a list of length 3 since there were three +coordinates provided in `points`. +We set the face color as a function of the `confidence` feature by providing the +keyword argument `face_color='confidence'` to the `viewer.add_points()` method. +We set the colormap to viridis using the `face_colormap` keyword argument +as `face_colormap='viridis'`. ### Changing the points symbol @@ -452,7 +481,7 @@ layer using the `symbol` keyword argument. ## Putting it all together Here you can see an example of adding, selecting, deleting points, and changing -their properties: +their features: ```{raw} html
diff --git a/docs/howtos/layers/tracks.md b/docs/howtos/layers/tracks.md index 0f46bf10c..10c900f60 100644 --- a/docs/howtos/layers/tracks.md +++ b/docs/howtos/layers/tracks.md @@ -31,8 +31,8 @@ code there to add a tracks layer first, then explore the GUI controls. The `tracks` layer allows you to display trajectories in `nD+t` while visualizing the recent history of the track via a fading tail. -Each track can have annotations associated with it using the `Tracks.properties` -dictionary. These properties can be used to set the colors of the tracks. +Each track can have annotations associated with it using the `Tracks.features` +table. These features can be used to set the colors of the tracks. For example, when displaying tracks of different classes/types, one could automatically set the color of the individual tracks by their respective @@ -40,9 +40,9 @@ class/type. ## A simple example -You can create a new viewer and add a set of tracks in one go using the -`napari.view_tracks` method, or if you already have an existing viewer, you can -add tracks to it using `viewer.add_tracks`. The API of both methods is the same. +You can create a new viewer and add a set of tracks in one go using {func}`napari.view_tracks`, +or if you already have an existing viewer, you can add tracks to it using {meth}`viewer.add_tracks`. +The API of both methods is the same. In this example, we will overlay some tracks on an image from the Hubble space telescope: @@ -118,68 +118,7 @@ napari.run() * Show ID - check this box to display a previously assigned `track_id` label for each track. Assigning values to `track_id` is explained in [Tracks data](#tracks-data) below. -* Graph - check this box to display a previously created graph as explained in - [](#arguments-of-view_tracks-and-add_tracks). - -## Arguments of `view_tracks` and `add_tracks` - -Both `view_tracks` and `add_tracks` have the following docstrings: - -```python -""" -Parameters ----------- -data : array (N, D+1) - Coordinates for N points in D+1 dimensions. ID,T,(Z),Y,X. The first - axis is the integer ID of the track. D is either 3 or 4 for planar - or volumetric timeseries respectively. -properties : dict {str: array (N,)}, DataFrame - Properties for each point. Each property should be an array of length N, - where N is the number of points. -graph : dict {int: list} - Graph representing associations between tracks. Dictionary defines the - mapping between a track ID and the parents of the track. This can be - one (the track has one parent, and the parent has >=1 child) in the - case of track splitting, or more than one (the track has multiple - parents, but only one child) in the case of track merging. - See examples/tracks_3d_with_graph.py -color_by: str - Track property (from property keys) by which to color vertices. -tail_width : float - Width of the track tails in pixels. -tail_length : float - Length of the track tails in units of time. -colormap : str - Default colormap to use to set vertex colors. Specialized colormaps, - relating to specified properties can be passed to the layer via - colormaps_dict. -colormaps_dict : dict {str: napari.utils.Colormap} - Optional dictionary mapping each property to a colormap for that - property. This allows each property to be assigned a specific colormap, - rather than having a global colormap for everything. -name : str - Name of the layer. -metadata : dict - Layer metadata. -scale : tuple of float - Scale factors for the layer. -translate : tuple of float - Translation values for the layer. -opacity : float - Opacity of the layer visual, between 0.0 and 1.0. -blending : str - One of a list of preset blending modes that determines how RGB and - alpha values of the layer visual get mixed. Allowed values are - {'opaque', 'translucent', and 'additive'}. -visible : bool - Whether the layer visual is currently being displayed. - -Returns -------- -layer : napari.layers.Tracks - The newly-created tracks layer. -""" -``` +* Graph - check this box to display a previously created graph. ## Tracks data @@ -250,13 +189,13 @@ graph = { For a full example of 3d+t tracks data with a parent graph, please see [](../../gallery/tracks_3d_with_graph). -## Using the `tracks` properties dictionary +## Using the `tracks` features table -The `tracks` layer can contain properties that annotate the vertices of each -track. `Tracks.properties` stores the properties in a dictionary where each key -is the name of the property and the values are NumPy arrays with a value for +The `Tracks` layer can contain features that annotate the vertices of each +track. `Tracks.features` stores the features in a table where each column +is the name of the feature and the values are rows with a value for each vertex in the track (i.e., length `N` for `N` vertices in `Tracks.data`). -As we will see below, we can use the values in a property to set the display +As we will see below, we can use the feature values to set the display properties of the tracks (e.g., the track color). ## 3D rendering @@ -335,14 +274,14 @@ length" slider in the `tracks` layer controls.
``` -## Setting the track color with properties +## Setting the track color with features -We can color the tracks by mapping colors to the track properties defined in -`Tracks.properties`. If we define properties and pass them via the `properties` +We can color the tracks by mapping colors to the track features defined in +`Tracks.features`. If we define features and pass them via the `features` keyword argument in the `viewer.add_tracks()` and `napari.view_tracks()` -methods, we can then select the property we would like to color the tracks by in +methods, we can then select the feature we would like to color the tracks by in the "color by" dropdown menu in the `tracks` layer controls. We can additionally -specify the colormap used to map the property value to color via the "colormap" +specify the colormap used to map the feature value to color via the "colormap" dropdown menu. ```python @@ -370,13 +309,13 @@ tracks_data = np.asarray([ [3, 4, 636, 1000] ]) track_confidence = np.array(5*[0.9] + 5*[0.3] + 5 * [0.1]) -properties = { +features = { 'time': tracks_data[:, 1], 'confidence': track_confidence } viewer = napari.view_image(hubble_image) -viewer.add_tracks(tracks_data, properties=properties) +viewer.add_tracks(tracks_data, features=features) napari.run() ``` @@ -387,7 +326,7 @@ napari.run() Selecting tracks by the time property and changing the color. diff --git a/docs/tutorials/annotation/annotate_points.md b/docs/tutorials/annotation/annotate_points.md index e88d7d218..0a2afbc25 100644 --- a/docs/tutorials/annotation/annotate_points.md +++ b/docs/tutorials/annotation/annotate_points.md @@ -4,8 +4,8 @@ ```{Admonition} DEPRECATED ATTRIBUTES :class: warning -As of napari 0.5.0, `edge_*` attributes are being renamed to -`border_*` attributes. We have yet to update the images and/or videos in +As of napari 0.5.0, `edge_*` attributes are being renamed to +`border_*` attributes. We have yet to update the images and/or videos in this tutorial. Please use `border` in place of `edge` for all `Points` attributes moving forward. The code in this tutorial uses the latest API. Only images and videos may be out of date. @@ -15,7 +15,7 @@ The code in this tutorial uses the latest API. Only images and videos may be out In this tutorial, we will use napari (requires version 0.3.2 or greater) to make a simple GUI application for annotating points in videos. This GUI could be useful for making annotations required to train algorithms for markless tracking of animals (e.g., [DeepLabCut](http://www.mackenziemathislab.org/deeplabcut)). -In this tutorial, we will cover creating and interacting with a Points layer with properties (i.e., labels for the points), connecting custom UI elements to events, and creating custom keybindings. +In this tutorial, we will cover creating and interacting with a Points layer with features (i.e., labels for the points), connecting custom UI elements to events, and creating custom keybindings. At the end of this tutorial, we will have created a GUI for annotating points in videos that we can simply call by: @@ -48,7 +48,7 @@ from typing import List from dask_image.imread import imread from magicgui.widgets import ComboBox, Container import napari -import numpy as np +import pandas as pd COLOR_CYCLE = [ '#1f77b4', @@ -86,17 +86,17 @@ def create_label_menu(points_layer, labels): def update_label_menu(event): """Update the label menu when the point selection changes""" - new_label = str(points_layer.current_properties['label'][0]) + new_label = str(points_layer.feature_defaults['label'][0]) if new_label != label_menu.value: label_menu.value = new_label - points_layer.events.current_properties.connect(update_label_menu) + points_layer.events.feature_defaults.connect(update_label_menu) - def label_changed(new_label): + def label_changed(selected_label): """Update the Points layer when the label menu selection changes""" - current_properties = points_layer.current_properties - current_properties['label'] = np.asarray([new_label]) - points_layer.current_properties = current_properties + feature_defaults = points_layer.feature_defaults + feature_defaults['label'] = selected_label + points_layer.feature_defaults = feature_defaults points_layer.refresh_colors() label_menu.changed.connect(label_changed) @@ -122,7 +122,7 @@ def point_annotator( viewer = napari.view_image(stack) points_layer = viewer.add_points( ndim=3, - property_choices={'label': labels}, + features=pd.DataFrame({'label': pd.Categorical([], categories=labels)}), border_color='label', border_color_cycle=COLOR_CYCLE, symbol='o', @@ -139,13 +139,13 @@ def point_annotator( @viewer.bind_key('.') def next_label(event=None): """Keybinding to advance to the next label with wraparound""" - current_properties = points_layer.current_properties - current_label = current_properties['label'][0] - ind = list(labels).index(current_label) + feature_defaults = points_layer.feature_defaults + default_label = feature_defaults['label'][0] + ind = list(labels).index(default_label) new_ind = (ind + 1) % len(labels) new_label = labels[new_ind] - current_properties['label'] = np.array([new_label]) - points_layer.current_properties = current_properties + feature_defaults['label'] = new_label + points_layer.feature_defaults = feature_defaults points_layer.refresh_colors() def next_on_click(layer, event): @@ -164,14 +164,13 @@ def point_annotator( @viewer.bind_key(',') def prev_label(event): """Keybinding to decrement to the previous label with wraparound""" - current_properties = points_layer.current_properties - current_label = current_properties['label'][0] - ind = list(labels).index(current_label) + feature_defaults = points_layer.feature_defaults + default_label = feature_defaults['label'][0] + ind = list(labels).index(default_label) n_labels = len(labels) new_ind = ((ind - 1) + n_labels) % n_labels - new_label = labels[new_ind] - current_properties['label'] = np.array([new_label]) - points_layer.current_properties = current_properties + feature_defaults['label'] = labels[new_ind] + points_layer.feature_defaults = feature_defaults points_layer.refresh_colors() napari.run() @@ -221,21 +220,21 @@ napari.run() We will annotate the features of interest using points in a napari Points layer. Each feature will be given a different label so that we can track them across frames. -To achieve this, we will store the label in the `Points.properties` property in the 'label' key. +To achieve this, we will store the label in the `Points.features` table in the 'label' column. We will instantiate the `Points` layer without any points. -However, we will initialize `Points.properties` with the property values we will be using to annotate the images. -To do so, we will define a properties dictionary with a key named `label` and values `labels`. -The key, 'label', is the name of the property we are storing which feature of interest each point corresponds with. +However, we will initialize `Points.features` with the feature values we will be using to annotate the images. +To do so, we will define a feature table with a categorical column named `label` with category values from `labels`. +The key, 'label', is the name of the feature we are storing. The values, 'labels', is the list of the names of the features we will be annotating (defined above in the "point_annotator()" section). We add the `Points` layer to the viewer using the `viewer.add_points()` method. -As discussed above, we will be storing which feature of interest each point corresponds to via the `label` property we defined in the `properties` dictionary. -To visualize the feature each point represents, we set the border color as a color cycle mapped to the `label` property (`border_color='label'`). +As discussed above, we will be storing which feature of interest each point corresponds to via the `label` feature we defined in the `features` table. +To visualize the feature each point represents, we set the border color as a color cycle mapped to the `label` feature (`border_color='label'`). ```python points_layer = viewer.add_points( ndim=3, - property_choices={'label': labels}, + features=pd.DataFrame({'label': pd.Categorical([], categories=labels)}), border_color='label', border_color_cycle=COLOR_CYCLE, symbol='o', @@ -312,18 +311,18 @@ label_widget = Container(widgets=[label_menu]) We then need to connect the dropdown menu (`label_menu`) to the points layer to ensure the menu selection is always synchronized to the `Points` layer model. First, we define a function to update the label dropdown menu GUI when the value of the selected point or next point to be added is changed. -On the points layer, the property values of the next point to be added are stored in the `current_properties` property. -The points layer has an event that gets emitted when the `current_properties` property is changed (`points_layer.events.current_properties`). -We connect the function we created to the event so that `update_label_menu()` is called whenever `Points.current_properties` is changed. +On the points layer, the feature values of the next point to be added are stored in the `feature_defaults` property. +The points layer has an event that gets emitted when the `feature_defaults` property is changed (`points_layer.events.feature_defaults`). +We connect the function we created to the event so that `update_label_menu()` is called whenever `Points.feature_defaults` is changed. ```python def update_label_menu(event): """Update the label menu when the point selection changes""" - new_label = str(points_layer.current_properties['label'][0]) + new_label = str(points_layer.feature_defaults['label'][0]) if new_label != label_menu.value: label_menu.value = new_label -points_layer.events.current_properties.connect(update_label_menu) +points_layer.events.feature_defaults.connect(update_label_menu) ``` Next, we define a function to update the points layer if the selection in the labels dropdown menu is changed. @@ -331,11 +330,11 @@ Similar to the points layer, the magicgui object has an event that gets emitted To ensure the points layer is updated whenever the GUI selection is changed, we connect `label_changed()` to the `label_menu.changed` event. ```python -def label_changed(new_label): +def label_changed(selected_label): """Update the Points layer when the label menu selection changes""" - current_properties = points_layer.current_properties - current_properties['label'] = np.asarray([new_label]) - points_layer.current_properties = current_properties + feature_defaults = points_layer.feature_defaults + feature_defaults['label'] = selected_label + points_layer.feature_defaults = feature_defaults points_layer.refresh_colors() label_menu.changed.connect(label_changed) @@ -361,21 +360,13 @@ In this case, we are binding `next_label()` to the `.` key. @viewer.bind_key('.') def next_label(event=None): """Keybinding to advance to the next label with wraparound""" - - # get the currently selected label - current_properties = points_layer.current_properties - current_label = current_properties['label'][0] - - # determine the index of that label in the labels list - ind = list(labels).index(current_label) - - # increment the label with wraparound + feature_defaults = points_layer.feature_defaults + default_label = feature_defaults['label'][0] + ind = list(labels).index(default_label) new_ind = (ind + 1) % len(labels) - - # get the new label and assign it new_label = labels[new_ind] - current_properties['label'] = np.array([new_label]) - points_layer.current_properties = current_properties + feature_defaults['label'] = new_label + points_layer.feature_defaults = feature_defaults points_layer.refresh_colors() ``` @@ -385,14 +376,13 @@ We can do the same with another function that instead decrements the label with @viewer.bind_key(',') def prev_label(event): """Keybinding to decrement to the previous label with wraparound""" - current_properties = points_layer.current_properties - current_label = current_properties['label'][0] - ind = list(labels).index(current_label) + feature_defaults = points_layer.feature_defaults + default_label = feature_defaults['label'][0] + ind = list(labels).index(default_label) n_labels = len(labels) new_ind = ((ind - 1) + n_labels) % n_labels - new_label = labels[new_ind] - current_properties['label'] = np.array([new_label]) - points_layer.current_properties = current_properties + feature_defaults['label'] = labels[new_ind] + points_layer.feature_defaults = feature_defaults points_layer.refresh_colors() ``` diff --git a/docs/tutorials/segmentation/annotate_segmentation.md b/docs/tutorials/segmentation/annotate_segmentation.md index b19ede869..2aae181fb 100644 --- a/docs/tutorials/segmentation/annotate_segmentation.md +++ b/docs/tutorials/segmentation/annotate_segmentation.md @@ -100,16 +100,16 @@ def circularity(perimeter, area): image = data.coins()[50:-50, 50:-50] label_image = segment(image) -# create the properties dictionary -properties = regionprops_table( +# create the features dictionary +feature = regionprops_table( label_image, properties=('label', 'bbox', 'perimeter', 'area') ) -properties['circularity'] = circularity( - properties['perimeter'], properties['area'] +features['circularity'] = circularity( + features['perimeter'], features['area'] ) # create the bounding box rectangles -bbox_rects = make_bbox([properties[f'bbox-{i}'] for i in range(4)]) +bbox_rects = make_bbox([features[f'bbox-{i}'] for i in range(4)]) # specify the display parameters for the text text_parameters = { @@ -130,7 +130,7 @@ shapes_layer = viewer.add_shapes( bbox_rects, face_color='transparent', edge_color='green', - properties=properties, + features=features, text=text_parameters, name='bounding box', ) @@ -193,13 +193,13 @@ napari.run() Next, we use [`regionprops_table`](https://scikit-image.org/docs/dev/api/skimage.measure.html#regionprops-table) from skimage to quantify some parameters of each detection object (e.g., area and perimeter). ```python -# create the properties dictionary -properties = regionprops_table( +# create the features dictionary +features = regionprops_table( label_image, properties=('label', 'bbox', 'perimeter', 'area') ) ``` -Conveniently, `regionprops_table()` returns a dictionary in the same format as the napari layer properties dictionary, so we will be able to use it directly. If we inspect the values of properties, we see each key is the name of the properties and the values are arrays with an element containing the property value for each shape. Note that the bounding boxes have been output as `bbox-0`, `bbox-1`, `bbox-1`, `bbox-2`, `bbox-3` which correspond with the min_row, min_column, max_row, amnd max_column of each bounding box, respectively. +Conveniently, `regionprops_table()` returns a dictionary that can be used as input for a napari layer's features table, so we will be able to use it directly. If we inspect the values of features, we see each key is the name of the feature and the values are arrays with an element containing the feature value for each shape. Note that the bounding boxes have been output as `bbox-0`, `bbox-1`, `bbox-1`, `bbox-2`, `bbox-3` which correspond with the min_row, min_column, max_row, and max_column of each bounding box, respectively. ```python { @@ -242,11 +242,11 @@ def circularity(perimeter, area): return circularity ``` -We can then calculate the circularity of each region and save it as a property. +We can then calculate the circularity of each region and save it as a feature. ```python -properties['circularity'] = circularity( - properties['perimeter'], properties['area'] +features['circularity'] = circularity( + features['perimeter'], features['area'] ) ``` @@ -282,11 +282,11 @@ def make_bbox(bbox_extents): return bbox_rect ``` -Finally, we can use an list comprension to pass the bounding box extents to `make_bbox()` and calculate the bounding box corners required by the `Shapes` layer. +Finally, we can use an list comprehension to pass the bounding box extents to `make_bbox()` and calculate the bounding box corners required by the `Shapes` layer. ```python # create the bounding box rectangles -bbox_rects = make_bbox([properties[f'bbox-{i}'] for i in range(4)]) +bbox_rects = make_bbox([features[f'bbox-{i}'] for i in range(4)]) ``` ## Visualizing the segmentation results @@ -320,30 +320,30 @@ Next, we will use the Shapes layer to overlay the bounding boxes for each detect The first positional argument (`bbox_rects`) contains the bounding boxes we created above. We specified that the face of each bounding box has no color (`face_color='transparent'`) and the edges of the bounding box are green (`edge_color='green'`). Finally, the name of the layer displayed in the layer list in the napari GUI is `bounding box` (`name='bounding box'`). ## Annotating shapes with text -We can further annotate our analysis by using text to display properties of each segmentation. The code to create a shapes layer with text is pasted here and explained below. +We can further annotate our analysis by using text to display features of each segmentation. The code to create a shapes layer with text is pasted here and explained below. ```python shapes_layer = viewer.add_shapes( bbox_rects, face_color='transparent', edge_color='green', - properties=properties, + features=features, text=text_parameters, name='bounding box' ) ``` -We will use `Shapes.properties` to store the annotations for each bounding box. The properties are definined as a dictionary where each key is the name of the property (i.e., label, circularity) and the values are arrays where each element contains the value for the corresponding shape (i.e., index matched to the Shape data). As a reminder, we created `labels` and `circularity` above and each is a list containing where each element is property value for the corresponding (i.e., index matched) shape. +We will use `Shapes.features` to store the annotations for each bounding box. The features are defined as a table where each column is the name of the feature (i.e., label, circularity) and the values are rows where each element contains the value for the corresponding shape (i.e., index matched to the Shape data). As a reminder, we created `labels` and `circularity` above and each is a list containing where each element is feature value for the corresponding (i.e., index matched) shape. ```python -# create the properties dictionary -properties = { +# create the features table +features = { 'label': labels, 'circularity': circularity, } ``` -Each bounding box can be annotated with text drawn from the layer `properties`. To specity the text and display properties of the text, we pass a dictionary with the text parameters (`text_parameters`). We define `text_parameters` as: +Each bounding box can be annotated with text drawn from the layer `features`. To specify the text and display properties of the text, we pass a dictionary with the text parameters (`text_parameters`). We define `text_parameters` as: ```python text_parameters = { @@ -355,7 +355,7 @@ text_parameters = { } ``` -The `text` key specifies pattern for the text to be displayed. If `text` is set to the name of a `properties` key, the value for that property will be displayed. napari text also accepts f-string-like syntax, as used here. napari will substitute each pair of curly braces(`{}`) with the values from the property specified inside of the curley braces. For numbers, the precision can be specified in the same style as f-strings. Additionally, napari recognizes standard special characters such as `\n` for new line. +The `string` key specifies pattern for the text to be displayed. If `string` is set to the name of a `feature` column, the value for that feature will be displayed. napari text also accepts f-string-like syntax, as used here. napari will substitute each pair of curly braces(`{}`) with the values from the feature specified inside of the curly braces. For numbers, the precision can be specified in the same style as f-strings. Additionally, napari recognizes standard special characters such as `\n` for new line. As an example, if a given object has a `label=1` and `circularity=0.8322940`, the resulting text string would be: @@ -369,8 +369,8 @@ We set the text to green (`'color': 'green'`) with a font size of 12 (`'size': 1 All together, the visualization code is: ```python -# create the properties dictionary -properties = { +# create the features table +features = { 'label': labels, 'circularity': circularity, } @@ -394,7 +394,7 @@ shapes_layer = viewer.add_shapes( bbox_rects, face_color='transparent', edge_color='green', - properties=properties, + features=features, text=text_parameters, name='bounding box' ) diff --git a/docs/tutorials/tracking/cell_tracking.md b/docs/tutorials/tracking/cell_tracking.md index 4db2ee146..6706f7608 100644 --- a/docs/tutorials/tracking/cell_tracking.md +++ b/docs/tutorials/tracking/cell_tracking.md @@ -145,9 +145,9 @@ def root(node: int): roots = {k: root(k) for k in full_graph.keys()} ``` -The `Tracks` layer enables the vertices of the tracks to be colored by user specified properties. Here, we will create a property which represents the `root_id` of each tree, so that cells with a common ancestor are colored the same: +The `Tracks` layer enables the vertices of the tracks to be colored by user specified features. Here, we will create a feature which represents the `root_id` of each tree, so that cells with a common ancestor are colored the same: ```python -properties = {'root_id': [roots[idx] for idx in data[:, 0]]} +features = {'root_id': [roots[idx] for idx in data[:, 0]]} ``` ### Visualizing the tracks with napari @@ -172,7 +172,7 @@ We can now visualize the full, linked tracks in napari! ```python viewer = napari.Viewer() viewer.add_image(timelapse, scale=SCALE, name='Fluo-N3DH-CE') -viewer.add_tracks(data, properties=properties, graph=graph, scale=SCALE, name='tracks') +viewer.add_tracks(data, features=features, graph=graph, scale=SCALE, name='tracks') napari.run() ``` @@ -223,18 +223,18 @@ with btrack.BayesianTracker() as tracker: tracker.optimize() # get the tracks in a format for napari visualization - data, properties, graph = tracker.to_napari(ndim=2) + data, features, graph = tracker.to_napari(ndim=2) ``` We set the configuration of the tracker using a configuration file using the `.configure_from_file()` method. An example configuration file can be found [here](https://github.com/quantumjot/BayesianTracker/blob/main/models/cell_config.json). Next, the objects are linked into tracks using the `.track_interactive()` method. The `step_size` argument specifies how many steps are taken before reporting the tracking statistics. The `.optimize()` method then performs a global optimization on the dataset and creates lineage trees automatically. -Finally, the `.to_napari()` method returns the track vertices, track properties and graph in a format that can be directly visualized using the napari `Tracks` layer: +Finally, the `.to_napari()` method returns the track vertices, track features and graph in a format that can be directly visualized using the napari `Tracks` layer: ```python viewer = napari.Viewer() -viewer.add_tracks(data, properties=properties, graph=graph) +viewer.add_tracks(data, features=features, graph=graph) napari.run() ```