def create_bingham_slicer(data, orientation, slice_index, sphere, color_per_lobe=False): """ Create a bingham fit slicer using a combination of odf_slicer actors Parameters ---------- data: ndarray (X, Y, Z, 9 * nb_lobes) The Bingham volume. orientation: string One of 'sagittal', 'coronal', 'axial'. slice_index: int Index of the slice of interest along the chosen orientation. sphere: DIPY Sphere Sphere used for visualization. color_per_lobe: bool, optional If true, each Bingham distribution is colored using a disting color. Else, Bingham distributions are colored by their orientation. Return ------ actors: list of fury odf_slicer actors ODF slicer actors representing the Bingham distributions. """ shape = data.shape nb_lobes = shape[-1] // NB_PARAMS nb_vertices = len(sphere.vertices) colors = [ c * 255 for i, c in zip(range(nb_lobes), distinguishable_colormap()) ] # lmax norm for normalization lmaxnorm = np.max(np.abs(data[..., ::NB_PARAMS]), axis=-1) sf = np.zeros((shape[0], shape[1], shape[2], nb_vertices)) actors = [] for nn in range(nb_lobes): nn_dat = data[..., nn * NB_PARAMS:(nn + 1) * NB_PARAMS] for ii in range(shape[0]): for jj in range(shape[1]): for kk in range(shape[2]): params = nn_dat[ii, jj, kk] fit = BinghamDistribution(params[0], params[1:4], params[4:7], params[7], params[8]) sf[ii, jj, kk, :] = fit.evaluate(sphere.vertices) # (1, N) sf[lmaxnorm > 0] /= lmaxnorm[lmaxnorm > 0][:, None] color = colors[nn] if color_per_lobe else None odf_actor = actor.odf_slicer(sf, sphere=sphere, norm=False, colormap=color) set_display_extent(odf_actor, orientation, shape[:3], slice_index) actors.append(odf_actor) return actors
def create_bingham_slicer(data, orientation, slice_index, sphere, color_per_lobe=False): """ Create a bingham fit slicer using a combination of odf_slicer actors Parameters ---------- data: ndarray (X, Y, Z, 9 * nb_lobes) The Bingham volume. orientation: str Name of the axis to visualize. Choices are axial, coronal and sagittal. slice_index: int Index of the slice of interest along the chosen orientation. sphere: DIPY Sphere Sphere used for visualization. color_per_lobe: bool, optional If true, each Bingham distribution is colored using a disting color. Else, Bingham distributions are colored by their orientation. Return ------ actors: list of fury odf_slicer actors ODF slicer actors representing the Bingham distributions. """ shape = data.shape nb_lobes = shape[-2] colors = [ c * 255 for i, c in zip(range(nb_lobes), distinguishable_colormap()) ] # lmax norm for normalization lmaxnorm = np.max(np.abs(data[..., 0]), axis=-1) bingham_sf = bingham_to_sf(data, sphere.vertices) actors = [] for nn in range(nb_lobes): sf = bingham_sf[..., nn, :] sf[lmaxnorm > 0] /= lmaxnorm[lmaxnorm > 0][:, None] color = colors[nn] if color_per_lobe else None odf_actor = actor.odf_slicer(sf, sphere=sphere, norm=False, colormap=color) set_display_extent(odf_actor, orientation, shape[:3], slice_index) actors.append(odf_actor) return actors
def test_grid_ui1(interactive=False): vol1 = np.zeros((100, 100, 100)) vol1[25:75, 25:75, 25:75] = 100 colors = distinguishable_colormap(nb_colors=3) contour_actor1 = actor.contour_from_roi(vol1, np.eye(4), colors[0], 1.) vol2 = np.zeros((100, 100, 100)) vol2[25:75, 25:75, 25:75] = 100 contour_actor2 = actor.contour_from_roi(vol2, np.eye(4), colors[1], 1.) vol3 = np.zeros((100, 100, 100)) vol3[25:75, 25:75, 25:75] = 100 contour_actor3 = actor.contour_from_roi(vol3, np.eye(4), colors[2], 1.) scene = window.Scene() actors = [] texts = [] actors.append(contour_actor1) text_actor1 = actor.text_3d('cube 1', justification='center') texts.append(text_actor1) actors.append(contour_actor2) text_actor2 = actor.text_3d('cube 2', justification='center') texts.append(text_actor2) actors.append(contour_actor3) text_actor3 = actor.text_3d('cube 3', justification='center') texts.append(text_actor3) actors.append(shallow_copy(contour_actor1)) text_actor1 = actor.text_3d('cube 4', justification='center') texts.append(text_actor1) actors.append(shallow_copy(contour_actor2)) text_actor2 = actor.text_3d('cube 5', justification='center') texts.append(text_actor2) actors.append(shallow_copy(contour_actor3)) text_actor3 = actor.text_3d('cube 6', justification='center') texts.append(text_actor3) actors.append(shallow_copy(contour_actor1)) text_actor1 = actor.text_3d('cube 7', justification='center') texts.append(text_actor1) actors.append(shallow_copy(contour_actor2)) text_actor2 = actor.text_3d('cube 8', justification='center') texts.append(text_actor2) actors.append(shallow_copy(contour_actor3)) text_actor3 = actor.text_3d('cube 9', justification='center') texts.append(text_actor3) counter = itertools.count() show_m = window.ShowManager(scene) show_m.initialize() def timer_callback(_obj, _event): nonlocal show_m, counter cnt = next(counter) show_m.scene.zoom(1) show_m.render() if cnt == 10: show_m.exit() # show the grid with the captions grid_ui = ui.GridUI(actors=actors, captions=texts, caption_offset=(0, -50, 0), cell_padding=(60, 60), dim=(3, 3), rotation_axis=(1, 0, 0)) scene.add(grid_ui) show_m.add_timer_callback(True, 200, timer_callback) show_m.start() arr = window.snapshot(scene) report = window.analyze_snapshot(arr) npt.assert_equal(report.objects > 9, True)
def test_grid_ui2(interactive=False): vol1 = np.zeros((100, 100, 100)) vol1[25:75, 25:75, 25:75] = 100 colors = distinguishable_colormap(nb_colors=3) contour_actor1 = actor.contour_from_roi(vol1, np.eye(4), colors[0], 1.) vol2 = np.zeros((100, 100, 100)) vol2[25:75, 25:75, 25:75] = 100 contour_actor2 = actor.contour_from_roi(vol2, np.eye(4), colors[1], 1.) vol3 = np.zeros((100, 100, 100)) vol3[25:75, 25:75, 25:75] = 100 contour_actor3 = actor.contour_from_roi(vol3, np.eye(4), colors[2], 1.) scene = window.Scene() actors = [] texts = [] actors.append(contour_actor1) text_actor1 = actor.text_3d('cube 1', justification='center') texts.append(text_actor1) actors.append(contour_actor2) text_actor2 = actor.text_3d('cube 2', justification='center') texts.append(text_actor2) actors.append(contour_actor3) text_actor3 = actor.text_3d('cube 3', justification='center') texts.append(text_actor3) actors.append(shallow_copy(contour_actor1)) text_actor1 = actor.text_3d('cube 4', justification='center') texts.append(text_actor1) actors.append(shallow_copy(contour_actor2)) text_actor2 = actor.text_3d('cube 5', justification='center') texts.append(text_actor2) actors.append(shallow_copy(contour_actor3)) text_actor3 = actor.text_3d('cube 6', justification='center') texts.append(text_actor3) actors.append(shallow_copy(contour_actor1)) text_actor1 = actor.text_3d('cube 7', justification='center') texts.append(text_actor1) actors.append(shallow_copy(contour_actor2)) text_actor2 = actor.text_3d('cube 8', justification='center') texts.append(text_actor2) actors.append(shallow_copy(contour_actor3)) text_actor3 = actor.text_3d('cube 9', justification='center') texts.append(text_actor3) # this needs to happen automatically when start() ends. # for act in actors: # act.RemoveAllObservers() filename = "test_grid_ui" recording_filename = pjoin(DATA_DIR, filename + ".log.gz") expected_events_counts_filename = pjoin(DATA_DIR, filename + ".json") current_size = (900, 600) scene = window.Scene() show_manager = window.ShowManager(scene, size=current_size, title="FURY GridUI") show_manager.initialize() grid_ui2 = ui.GridUI(actors=actors, captions=texts, caption_offset=(0, -50, 0), cell_padding=(60, 60), dim=(3, 3), rotation_axis=None) scene.add(grid_ui2) event_counter = EventCounter() event_counter.monitor(grid_ui2) if interactive: show_manager.start() recording = False if recording: # Record the following events # 1. Left click on top left box (will rotate the box) show_manager.record_events_to_file(recording_filename) # print(list(event_counter.events_counts.items())) event_counter.save(expected_events_counts_filename) else: show_manager.play_events_from_file(recording_filename) expected = EventCounter.load(expected_events_counts_filename) event_counter.check_counts(expected)
def test_grid_ui(interactive=False): vol1 = np.zeros((100, 100, 100)) vol1[25:75, 25:75, 25:75] = 100 colors = distinguishable_colormap(nb_colors=3) contour_actor1 = actor.contour_from_roi(vol1, np.eye(4), colors[0], 1.) vol2 = np.zeros((100, 100, 100)) vol2[25:75, 25:75, 25:75] = 100 contour_actor2 = actor.contour_from_roi(vol2, np.eye(4), colors[1], 1.) vol3 = np.zeros((100, 100, 100)) vol3[25:75, 25:75, 25:75] = 100 contour_actor3 = actor.contour_from_roi(vol3, np.eye(4), colors[2], 1.) scene = window.Scene() actors = [] texts = [] actors.append(contour_actor1) text_actor1 = actor.text_3d('cube 1', justification='center') texts.append(text_actor1) actors.append(contour_actor2) text_actor2 = actor.text_3d('cube 2', justification='center') texts.append(text_actor2) actors.append(contour_actor3) text_actor3 = actor.text_3d('cube 3', justification='center') texts.append(text_actor3) actors.append(shallow_copy(contour_actor1)) text_actor1 = actor.text_3d('cube 4', justification='center') texts.append(text_actor1) actors.append(shallow_copy(contour_actor2)) text_actor2 = actor.text_3d('cube 5', justification='center') texts.append(text_actor2) actors.append(shallow_copy(contour_actor3)) text_actor3 = actor.text_3d('cube 6', justification='center') texts.append(text_actor3) actors.append(shallow_copy(contour_actor1)) text_actor1 = actor.text_3d('cube 7', justification='center') texts.append(text_actor1) actors.append(shallow_copy(contour_actor2)) text_actor2 = actor.text_3d('cube 8', justification='center') texts.append(text_actor2) actors.append(shallow_copy(contour_actor3)) text_actor3 = actor.text_3d('cube 9', justification='center') texts.append(text_actor3) counter = itertools.count() show_m = window.ShowManager(scene) show_m.initialize() def timer_callback(_obj, _event): cnt = next(counter) show_m.scene.zoom(1) show_m.render() if cnt == 10: show_m.exit() show_m.destroy_timers() # show the grid with the captions grid_ui = ui.GridUI(actors=actors, captions=texts, caption_offset=(0, -50, 0), cell_padding=(60, 60), dim=(3, 3), rotation_axis=(1, 0, 0)) scene.add(grid_ui) show_m.add_timer_callback(True, 200, timer_callback) show_m.start() arr = window.snapshot(scene) report = window.analyze_snapshot(arr) npt.assert_equal(report.objects > 9, True) # this needs to happen automatically when start() ends. for act in actors: act.RemoveAllObservers() filename = "test_grid_ui" recording_filename = pjoin(DATA_DIR, filename + ".log.gz") expected_events_counts_filename = pjoin(DATA_DIR, filename + ".json") current_size = (900, 600) scene = window.Scene() show_manager = window.ShowManager(scene, size=current_size, title="FURY GridUI") show_manager.initialize() grid_ui2 = ui.GridUI(actors=actors, captions=texts, caption_offset=(0, -50, 0), cell_padding=(60, 60), dim=(3, 3), rotation_axis=None) scene.add(grid_ui2) event_counter = EventCounter() event_counter.monitor(grid_ui2) if interactive: show_manager.start() recording = False if recording: # Record the following events # 1. Left click on top left box (will rotate the box) show_manager.record_events_to_file(recording_filename) # print(list(event_counter.events_counts.items())) event_counter.save(expected_events_counts_filename) else: show_manager.play_events_from_file(recording_filename) expected = EventCounter.load(expected_events_counts_filename) event_counter.check_counts(expected)
edges = np.array(network.edges()) positions = view_size * \ np.random.random((vertices_count, 3)) - view_size / 2.0 ############################################################################### # We attribute a color to each category of our dataset which correspond to our # nodes colors. category2index = { category: i for i, category in enumerate(np.unique(categories)) } index2category = np.unique(categories) category_colors = cmap.distinguishable_colormap(nb_colors=len(index2category)) colors = np.array( [category_colors[category2index[category]] for category in categories]) ############################################################################### # We define our node size radii = 1 + np.random.rand(len(positions)) ############################################################################### # Let's create our edges now. They will indicate a citation between two nodes. # The colors of each edge are interpolated between the two endpoints. edges_colors = [] for source, target in edges:
def add_cluster_actors(self, scene, tractograms, threshold, enable_callbacks=True): """ Add streamline actors to the scene Parameters ---------- scene : Scene tractograms : list list of tractograms threshold : float Cluster threshold enable_callbacks : bool Enable callbacks for selecting clusters """ color_gen = distinguishable_colormap() for (t, sft) in enumerate(tractograms): streamlines = sft.streamlines if self.random_colors: colors = next(color_gen) else: colors = None if not self.world_coords: # TODO we need to read the affine of a tractogram # from a StatefullTractogram msg = 'Currently native coordinates are not supported' msg += ' for streamlines' raise ValueError(msg) if self.cluster: print(' Clustering threshold {} \n'.format(threshold)) clusters = qbx_and_merge(streamlines, [40, 30, 25, 20, threshold]) self.tractogram_clusters[t] = clusters centroids = clusters.centroids print(' Number of centroids is {}'.format(len(centroids))) sizes = np.array([len(c) for c in clusters]) linewidths = np.interp(sizes, [sizes.min(), sizes.max()], [0.1, 2.]) centroid_lengths = np.array([length(c) for c in centroids]) print(' Minimum number of streamlines in cluster {}' .format(sizes.min())) print(' Maximum number of streamlines in cluster {}' .format(sizes.max())) print(' Construct cluster actors') for (i, c) in enumerate(centroids): centroid_actor = actor.streamtube([c], colors, linewidth=linewidths[i], lod=False) scene.add(centroid_actor) self.mem.centroid_actors.append(centroid_actor) cluster_actor = actor.line(clusters[i], lod=False) cluster_actor.GetProperty().SetRenderLinesAsTubes(1) cluster_actor.GetProperty().SetLineWidth(6) cluster_actor.GetProperty().SetOpacity(1) cluster_actor.VisibilityOff() scene.add(cluster_actor) self.mem.cluster_actors.append(cluster_actor) # Every centroid actor (cea) is paired to a cluster actor # (cla). self.cea[centroid_actor] = { 'cluster_actor': cluster_actor, 'cluster': i, 'tractogram': t, 'size': sizes[i], 'length': centroid_lengths[i], 'selected': 0, 'expanded': 0} self.cla[cluster_actor] = { 'centroid_actor': centroid_actor, 'cluster': i, 'tractogram': t, 'size': sizes[i], 'length': centroid_lengths[i], 'selected': 0, 'highlighted': 0} apply_shader(self, cluster_actor) apply_shader(self, centroid_actor) else: streamline_actor = actor.line(streamlines, colors=colors) streamline_actor.GetProperty().SetEdgeVisibility(1) streamline_actor.GetProperty().SetRenderLinesAsTubes(1) streamline_actor.GetProperty().SetLineWidth(6) streamline_actor.GetProperty().SetOpacity(1) scene.add(streamline_actor) self.mem.streamline_actors.append(streamline_actor) if not enable_callbacks: return def left_click_centroid_callback(obj, event): self.cea[obj]['selected'] = not self.cea[obj]['selected'] self.cla[self.cea[obj]['cluster_actor']]['selected'] = \ self.cea[obj]['selected'] self.show_m.render() def left_click_cluster_callback(obj, event): if self.cla[obj]['selected']: self.cla[obj]['centroid_actor'].VisibilityOn() ca = self.cla[obj]['centroid_actor'] self.cea[ca]['selected'] = 0 obj.VisibilityOff() self.cea[ca]['expanded'] = 0 self.show_m.render() for cl in self.cla: cl.AddObserver('LeftButtonPressEvent', left_click_cluster_callback, 1.0) self.cla[cl]['centroid_actor'].AddObserver( 'LeftButtonPressEvent', left_click_centroid_callback, 1.0)
def __init__(self, tractograms=None, images=None, pams=None, cluster=False, cluster_thr=15.0, random_colors=None, length_gt=0, length_lt=1000, clusters_gt=0, clusters_lt=10000, world_coords=True, interactive=True, out_png='tmp.png', recorded_events=None, return_showm=False, bg_color=(0, 0, 0), order_transparent=True, buan=False, buan_colors=None, roi_images=False, roi_colors=(1, 0, 0)): """Interactive medical visualization - Invert the Horizon! Parameters ---------- tractograms : sequence of StatefulTractograms StatefulTractograms are used for making sure that the coordinate systems are correct images : sequence of tuples Each tuple contains data and affine pams : sequence of PeakAndMetrics Contains peak directions and spherical harmonic coefficients cluster : bool Enable QuickBundlesX clustering cluster_thr : float Distance threshold used for clustering. Default value 15.0 for small animal data you may need to use something smaller such as 2.0. The threshold is in mm. For this parameter to be active ``cluster`` should be enabled. random_colors : string, optional Given multiple tractograms and/or ROIs then each tractogram and/or ROI will be shown with a different color. If no value is provided, both the tractograms and the ROIs will have a different random color generated from a distinguishable colormap. If the effect should only be applied to one of the 2 types, then use the options 'tracts' and 'rois' for the tractograms and the ROIs respectively. length_gt : float Clusters with average length greater than ``length_gt`` amount in mm will be shown. length_lt : float Clusters with average length less than ``length_lt`` amount in mm will be shown. clusters_gt : int Clusters with size greater than ``clusters_gt`` will be shown. clusters_lt : int Clusters with size less than ``clusters_lt`` will be shown. world_coords : bool Show data in their world coordinates (not native voxel coordinates) Default True. interactive : bool Allow user interaction. If False then Horizon goes on stealth mode and just saves pictures. out_png : string Filename of saved picture. recorded_events : string File path to replay recorded events return_showm : bool Return ShowManager object. Used only at Python level. Can be used for extending Horizon's cababilities externally and for testing purposes. bg_color : ndarray or list or tuple Define the background color of the scene. Default is black (0, 0, 0) order_transparent : bool Default True. Use depth peeling to sort transparent objects. If True also enables anti-aliasing. buan : bool, optional Enables BUAN framework visualization. Default is False. buan_colors : list, optional List of colors for bundles. roi_images : bool, optional Displays binary images as contours. Default is False. roi_colors : ndarray or list or tuple, optional Define the colors of the roi images. Default is red (1, 0, 0) References ---------- .. [Horizon_ISMRM19] Garyfallidis E., M-A. Cote, B.Q. Chandio, S. Fadnavis, J. Guaje, R. Aggarwal, E. St-Onge, K.S. Juneja, S. Koudoro, D. Reagan, DIPY Horizon: fast, modular, unified and adaptive visualization, Proceedings of: International Society of Magnetic Resonance in Medicine (ISMRM), Montreal, Canada, 2019. """ self.cluster = cluster self.cluster_thr = cluster_thr self.random_colors = random_colors self.length_lt = length_lt self.length_gt = length_gt self.clusters_lt = clusters_lt self.clusters_gt = clusters_gt self.world_coords = world_coords self.interactive = interactive self.prng = np.random.RandomState(27) self.tractograms = tractograms or [] self.out_png = out_png self.images = images or [] self.pams = pams or [] self.cea = {} # holds centroid actors self.cla = {} # holds cluster actors self.tractogram_clusters = {} self.recorded_events = recorded_events self.show_m = None self.return_showm = return_showm self.bg_color = bg_color self.order_transparent = order_transparent self.buan = buan self.buan_colors = buan_colors self.roi_images = roi_images self.roi_colors = roi_colors if self.random_colors is not None: self.color_gen = distinguishable_colormap() if not self.random_colors: self.random_colors = ['tracts', 'rois'] else: self.random_colors = []