.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "_gallery/selection_tools/visibility_selector.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr__gallery_selection_tools_visibility_selector.py: Visibility and Highlight Selector ================================= Example with an image that contains time-varying signals. An ``ImageHighlightSelector`` is created with pre-loaded options for either contour outlines or filled masks that spatially denote a unique signal in the image. A ``VisiblitySelector`` is used on a LineCollection. When the image is clicked, the closest spatial signal is highlighted and the corresponding line is made visible. Shift + click to multi-select signals. .. GENERATED FROM PYTHON SOURCE LINES 10-186 .. image-sg:: /_gallery/selection_tools/images/sphx_glr_visibility_selector_001.webp :alt: visibility selector :srcset: /_gallery/selection_tools/images/sphx_glr_visibility_selector_001.webp :class: sphx-glr-single-img .. code-block:: Python # test_example = false from functools import partial import numpy as np from scipy.ndimage import binary_erosion import fastplotlib as fpl import cmap as cmap_lib n_t = 500 n_y, n_x = 128, 128 n_circles = 32 radius = 4 # diameter 5 rng = np.random.default_rng(0) # Random circle centers centers = rng.integers(0, [n_y, n_x], size=(n_circles, 2)) yy, xx = np.ogrid[:n_y, :n_x] movies_sessions = list() contours_sessions = list() signals_sessions = list() centers_per_session = list() indices_per_session = list() # just generate multi-session toy data for session_index in range(3): masks = [] contours = [] # perimeter pixel coordinates per circle for cy, cx in centers: mask = (yy - cy) ** 2 + (xx - cx) ** 2 <= radius**2 masks.append(mask) # Perimeter = filled mask minus its erosion perimeter = mask # & ~binary_erosion(mask) contours.append(np.argwhere(perimeter)) # shape (K, 2), columns are [y, x] images = np.zeros((n_t, n_y, n_x), dtype=np.float32) t = np.linspace(0, 10 * np.pi, n_t) phases = 2 * np.pi * np.arange(n_circles) / n_circles signals = list() for j, mask in enumerate(masks): signal = np.sin(t + phases[j]).astype(np.float32) # (n_t,) noise = rng.normal(0, 0.05, (n_t, mask.sum())).astype(np.float32) # (n_t, K) signal = signal[:, None] + noise images[:, mask] += signal signals.append(signal.mean(axis=1)) signals = np.stack(signals) # just to create diff indices per session local_indices = np.roll(np.arange(n_circles), shift=session_index) indices_per_session.append(local_indices) movies_sessions.append(images) # re-order stuff in local index order centers_per_session.append(centers[local_indices]) contours_sessions.append([contours[i] for i in local_indices]) signals_sessions.append(signals[local_indices]) # Just NDWidget & figure stuff extents = { "images-0": (0, 0.33, 0, 0.33), "signals-0": (0.33, 1, 0, 0.33), "images-1": (0, 0.33, 0.33, 0.67), "signals-1": (0.33, 1, 0.33, 0.67), "images-2": (0, 0.33, 0.67, 1), "signals-2": (0.33, 1, 0.67, 1), } ref_range = {"time": (0, n_t, 1)} ndw = fpl.NDWidget( ref_range, extents=extents, controller_ids=[ ("images-0", "images-1", "images-2"), ], size=(1300, 1000) ) # create selection vector sv = fpl.SelectionVector() # mapping to go from master index -> per session index for a given session # this must be a vector -> vector mapping since multiple things can be selected def master_to_local_index(session_id: int, selection_indices: list[int]) -> list[int]: return [i + session_id for i in selection_indices] # image click changes the selection, can change the selection vector in any other way too def image_clicked(session, ev): col, row = ev.pick_info["index"] local_index = np.argmin( np.linalg.norm(centers_per_session[session] - np.array([row, col]), axis=1) ) # inverse transform, local scalar index -> master index master_index = local_index - session print(local_index, master_index) global sv if "Shift" in ev.modifiers: sv.append(master_index) else: # just one item selected sv.selection = [master_index] for subplot in ndw.figure: if "signals" in subplot.name: subplot.auto_scale() # iterate through all the toy data, create NDGraphics and selectors # for session_index, (indices, movie, contours, signals) in enumerate( # zip(indices_per_session, movies_sessions, contours_sessions, signals_sessions) # ): # # create NDImage, nothing special here # ndi = ndw[f"images-{session_index}"].add_nd_image( # movie, # dims=("time", "m", "n"), # display_dims=list("mn"), # ) # ndi.graphic.cmap = "gray" # # create ND Timeseries, again nothing special # ndt = ndw[f"signals-{session_index}"].add_nd_timeseries( # fpl.utils.heatmap_to_positions(signals, xvals=np.arange(0, n_t)), # dims=("l", "time", "d"), # display_dims=("l", "time", "d"), # x_range_mode="fixed", # display_window=None, # ) # # # Create selectors # # image highlight selector for this session # image_selector = fpl.ImageHighlightSelector( # lut="tab10", # selection_options={"pixels": contours}, # pre-loaded selection options # options_alpha=0.1, # unselected contours shown with low alpha # options_color="w", # unselected contours shown this color # lut_wrap="repeat", # cycles through tab10 colormap if you select > 10 items # alpha=0.7, # highlight alpha # ) # # # selector that toggles visibility of lines in the line stack # # use same lut as the image highlight # traces_visible_selector = fpl.VisibilitySelector( # ndt.graphic, lut="tab10", lut_wrap="repeat" # ) # # # target graphic, you can also add more target graphics later # # as long as they are in the same "selection space", ex: each movie for single-session # # each selector manages ONE buffer, so the same pixels will be highlighted on all graphics # # targetted by a selector. # image_selector.add_graphic(ndi.graphic) # # when image is double clicked, calls the handler # ndi.graphic.add_event_handler(partial(image_clicked, session_index), "double_click") # # # add selectors to SelectionVector # # with mapping that defines how to map from master index to local index for this session # mapping = partial(master_to_local_index, session_index) # sv.add_selector((image_selector, mapping)) # sv.add_selector((traces_visible_selector, mapping)) ndw.show() figure = ndw.figure fpl.loop.run() .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.934 seconds) .. _sphx_glr_download__gallery_selection_tools_visibility_selector.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: visibility_selector.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: visibility_selector.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_