def lines_to_vtk_polydata(lines, colors=None): """Create a vtkPolyData with lines and colors. Parameters ---------- lines : list list of N curves represented as 2D ndarrays colors : array (N, 3), list of arrays, tuple (3,), array (K,) If None or False, a standard orientation colormap is used for every line. If one tuple of color is used. Then all streamlines will have the same colour. If an array (N, 3) is given, where N is equal to the number of lines. Then every line is coloured with a different RGB color. If a list of RGB arrays is given then every point of every line takes a different color. If an array (K, 3) is given, where K is the number of points of all lines then every point is colored with a different RGB color. If an array (K,) is given, where K is the number of points of all lines then these are considered as the values to be used by the colormap. If an array (L,) is given, where L is the number of streamlines then these are considered as the values to be used by the colormap per streamline. If an array (X, Y, Z) or (X, Y, Z, 3) is given then the values for the colormap are interpolated automatically using trilinear interpolation. Returns ------- poly_data : vtkPolyData color_is_scalar : bool, true if the color array is a single scalar Scalar array could be used with a colormap lut None if no color was used """ # Get the 3d points_array points_array = np.vstack(lines) # Set Points to vtk array format vtk_points = numpy_to_vtk_points(points_array) # Set Lines to vtk array format vtk_cell_array = numpy_to_vtk_cells(lines) # Create the poly_data poly_data = vtk.vtkPolyData() poly_data.SetPoints(vtk_points) poly_data.SetLines(vtk_cell_array) # Get colors_array (reformat to have colors for each points) # - if/else tested and work in normal simple case nb_points = len(points_array) nb_lines = len(lines) lines_range = range(nb_lines) points_per_line = [len(lines[i]) for i in lines_range] points_per_line = np.array(points_per_line, np.intp) color_is_scalar = False if colors is None or colors is False: # set automatic rgb colors cols_arr = line_colors(lines) colors_mapper = np.repeat(lines_range, points_per_line, axis=0) vtk_colors = numpy_to_vtk_colors(255 * cols_arr[colors_mapper]) else: cols_arr = np.asarray(colors) if cols_arr.dtype == np.object: # colors is a list of colors vtk_colors = numpy_to_vtk_colors(255 * np.vstack(colors)) else: if len(cols_arr) == nb_points: if cols_arr.ndim == 1: # values for every point vtk_colors = numpy_support.numpy_to_vtk(cols_arr, deep=True) color_is_scalar = True elif cols_arr.ndim == 2: # map color to each point vtk_colors = numpy_to_vtk_colors(255 * cols_arr) elif cols_arr.ndim == 1: if len(cols_arr) == nb_lines: # values for every streamline cols_arrx = [] for (i, value) in enumerate(colors): cols_arrx += lines[i].shape[0] * [value] cols_arrx = np.array(cols_arrx) vtk_colors = numpy_support.numpy_to_vtk(cols_arrx, deep=True) color_is_scalar = True else: # the same colors for all points vtk_colors = numpy_to_vtk_colors( np.tile(255 * cols_arr, (nb_points, 1))) elif cols_arr.ndim == 2: # map color to each line colors_mapper = np.repeat(lines_range, points_per_line, axis=0) vtk_colors = numpy_to_vtk_colors(255 * cols_arr[colors_mapper]) else: # colormap # get colors for each vertex cols_arr = map_coordinates_3d_4d(cols_arr, points_array) vtk_colors = numpy_support.numpy_to_vtk(cols_arr, deep=True) color_is_scalar = True vtk_colors.SetName("colors") poly_data.GetPointData().SetScalars(vtk_colors) return poly_data, color_is_scalar
def test_contour_from_roi(): # Render volume scene = window.Scene() data = np.zeros((50, 50, 50)) data[20:30, 25, 25] = 1. data[25, 20:30, 25] = 1. affine = np.eye(4) surface = actor.contour_from_roi(data, affine, color=np.array([1, 0, 1]), opacity=.5) scene.add(surface) scene.reset_camera() scene.reset_clipping_range() # window.show(scene) # Test binarization scene2 = window.Scene() data2 = np.zeros((50, 50, 50)) data2[20:30, 25, 25] = 1. data2[35:40, 25, 25] = 1. affine = np.eye(4) surface2 = actor.contour_from_roi(data2, affine, color=np.array([0, 1, 1]), opacity=.5) scene2.add(surface2) scene2.reset_camera() scene2.reset_clipping_range() # window.show(scene2) arr = window.snapshot(scene, 'test_surface.png', offscreen=True) arr2 = window.snapshot(scene2, 'test_surface2.png', offscreen=True) report = window.analyze_snapshot(arr, find_objects=True) report2 = window.analyze_snapshot(arr2, find_objects=True) npt.assert_equal(report.objects, 1) npt.assert_equal(report2.objects, 2) # test on real streamlines using tracking example from dipy.data import read_stanford_labels from dipy.reconst.shm import CsaOdfModel from dipy.data import default_sphere from dipy.direction import peaks_from_model from dipy.tracking.local import ThresholdTissueClassifier from dipy.tracking import utils from dipy.tracking.local import LocalTracking from fury.colormap import line_colors hardi_img, gtab, labels_img = read_stanford_labels() data = hardi_img.get_data() labels = labels_img.get_data() affine = hardi_img.affine white_matter = (labels == 1) | (labels == 2) csa_model = CsaOdfModel(gtab, sh_order=6) csa_peaks = peaks_from_model(csa_model, data, default_sphere, relative_peak_threshold=.8, min_separation_angle=45, mask=white_matter) classifier = ThresholdTissueClassifier(csa_peaks.gfa, .25) seed_mask = labels == 2 seeds = utils.seeds_from_mask(seed_mask, density=[1, 1, 1], affine=affine) # Initialization of LocalTracking. # The computation happens in the next step. streamlines = LocalTracking(csa_peaks, classifier, seeds, affine, step_size=2) # Compute streamlines and store as a list. streamlines = list(streamlines) # Prepare the display objects. streamlines_actor = actor.line(streamlines, line_colors(streamlines)) seedroi_actor = actor.contour_from_roi(seed_mask, affine, [0, 1, 1], 0.5) # Create the 3d display. r = window.Scene() r2 = window.Scene() r.add(streamlines_actor) arr3 = window.snapshot(r, 'test_surface3.png', offscreen=True) report3 = window.analyze_snapshot(arr3, find_objects=True) r2.add(streamlines_actor) r2.add(seedroi_actor) arr4 = window.snapshot(r2, 'test_surface4.png', offscreen=True) report4 = window.analyze_snapshot(arr4, find_objects=True) # assert that the seed ROI rendering is not far # away from the streamlines (affine error) npt.assert_equal(report3.objects, report4.objects)
def visualize_bundles(sft, affine=None, n_points=None, bundle_dict=None, bundle=None, colors=None, color_by_volume=None, cbv_lims=[None, None], figure=None, background=(1, 1, 1), interact=False, inline=False): """ Visualize bundles in 3D using VTK Parameters ---------- sft : Stateful Tractogram, str A Stateful Tractogram containing streamline information or a path to a trk file In order to visualize individual bundles, the Stateful Tractogram must contain a bundle key in it's data_per_streamline which is a list of bundle `'uid'`. affine : ndarray, optional An affine transformation to apply to the streamlines before visualization. Default: no transform. n_points : int or None n_points to resample streamlines to before plotting. If None, no resampling is done. bundle_dict : dict, optional Keys are names of bundles and values are dicts that should include a key `'uid'` with values as integers for selection from the sft metadata. Default: bundles are either not identified, or identified only as unique integers in the metadata. bundle : str or int, optional The name of a bundle to select from among the keys in `bundle_dict` or an integer for selection from the sft metadata. colors : dict or list If this is a dict, keys are bundle names and values are RGB tuples. If this is a list, each item is an RGB tuple. Defaults to a list with Tableau 20 RGB values if bundle_dict is None, or dict from bundles to Tableau 20 RGB values if bundle_dict is not None. color_by_volume : ndarray or str, optional 3d volume use to shade the bundles. If None, no shading is performed. Only works when using the plotly backend. Default: None cbv_lims : ndarray Of the form (lower bound, upper bound). Shading based on color_by_volume will only differentiate values within these bounds. If lower bound is None, will default to 0. If upper bound is None, will default to the maximum value in color_by_volume. Default: [None, None] background : tuple, optional RGB values for the background. Default: (1, 1, 1), which is white background. figure : fury Scene object, optional If provided, the visualization will be added to this Scene. Default: Initialize a new Scene. interact : bool Whether to provide an interactive VTK window for interaction. Default: False inline : bool Whether to embed the visualization inline in a notebook. Only works in the notebook context. Default: False. Returns ------- Fury Scene object """ if figure is None: figure = window.Scene() figure.SetBackground(background[0], background[1], background[2]) for (sls, color, name, _) in vut.tract_generator( sft, affine, bundle, bundle_dict, colors, n_points): sls = list(sls) if name == "all_bundles": color = line_colors(sls) sl_actor = actor.line(sls, color) figure.add(sl_actor) sl_actor.GetProperty().SetRenderLinesAsTubes(1) sl_actor.GetProperty().SetLineWidth(6) return _inline_interact(figure, inline, interact)
def lines_to_vtk_polydata(lines, colors="RGB"): """Create a vtkPolyData with lines and colors. Parameters ---------- lines : list list of N curves represented as 2D ndarrays colors : array (N, 3), list of arrays, tuple (3,), array (K,), "RGB" If None or False, no coloring is done If "RGB" then a standard orientation colormap is used for every line. If one tuple of color is used. Then all streamlines will have the same colour. If an array (N, 3) is given, where N is equal to the number of lines. Then every line is coloured with a different RGB color. If a list of RGB arrays is given then every point of every line takes a different color. If an array (K, 3) is given, where K is the number of points of all lines then every point is colored with a different RGB color. If an array (K,) is given, where K is the number of points of all lines then these are considered as the values to be used by the colormap. If an array (L,) is given, where L is the number of streamlines then these are considered as the values to be used by the colormap per streamline. If an array (X, Y, Z) or (X, Y, Z, 3) is given then the values for the colormap are interpolated automatically using trilinear interpolation. Returns ------- poly_data : vtkPolyData color_is_scalar : bool, true if the color array is a single scalar Scalar array could be used with a colormap lut None if no color was used """ # Get the 3d points_array points_array = np.vstack(lines) nb_lines = len(lines) nb_points = len(points_array) lines_range = range(nb_lines) # Get lines_array in vtk input format lines_array = [] # Using np.intp (instead of int64), because of a bug in numpy: # https://github.com/nipy/dipy/pull/789 # https://github.com/numpy/numpy/issues/4384 points_per_line = np.zeros([nb_lines], np.intp) current_position = 0 for i in lines_range: current_len = len(lines[i]) points_per_line[i] = current_len end_position = current_position + current_len lines_array += [current_len] lines_array += range(current_position, end_position) current_position = end_position lines_array = np.array(lines_array) # Set Points to vtk array format vtk_points = numpy_to_vtk_points(points_array) # Set Lines to vtk array format vtk_lines = vtk.vtkCellArray() vtk_lines.GetData().DeepCopy(numpy_support.numpy_to_vtk(lines_array)) vtk_lines.SetNumberOfCells(nb_lines) # Create the poly_data poly_data = vtk.vtkPolyData() poly_data.SetPoints(vtk_points) poly_data.SetLines(vtk_lines) # Get colors_array (reformat to have colors for each points) # - if/else tested and work in normal simple case color_is_scalar = False if colors is None or colors is False: # No color array is used return poly_data, None elif isinstance(colors, str) and colors.lower() == "rgb": # set automatic rgb colors cols_arr = line_colors(lines) colors_mapper = np.repeat(lines_range, points_per_line, axis=0) vtk_colors = numpy_to_vtk_colors(255 * cols_arr[colors_mapper]) else: cols_arr = np.asarray(colors) if cols_arr.dtype == np.object: # colors is a list of colors vtk_colors = numpy_to_vtk_colors(255 * np.vstack(colors)) else: if len(cols_arr) == nb_points: if cols_arr.ndim == 1: # values for every point vtk_colors = numpy_support.numpy_to_vtk(cols_arr, deep=True) color_is_scalar = True elif cols_arr.ndim == 2: # map color to each point vtk_colors = numpy_to_vtk_colors(255 * cols_arr) elif cols_arr.ndim == 1: if len(cols_arr) == nb_lines: # values for every streamline cols_arrx = [] for (i, value) in enumerate(colors): cols_arrx += lines[i].shape[0] * [value] cols_arrx = np.array(cols_arrx) vtk_colors = numpy_support.numpy_to_vtk(cols_arrx, deep=True) color_is_scalar = True else: # the same colors for all points vtk_colors = numpy_to_vtk_colors( np.tile(255 * cols_arr, (nb_points, 1))) elif cols_arr.ndim == 2: # map color to each line colors_mapper = np.repeat(lines_range, points_per_line, axis=0) vtk_colors = numpy_to_vtk_colors(255 * cols_arr[colors_mapper]) else: # colormap # get colors for each vertex cols_arr = map_coordinates_3d_4d(cols_arr, points_array) vtk_colors = numpy_support.numpy_to_vtk(cols_arr, deep=True) color_is_scalar = True vtk_colors.SetName("Colors") poly_data.GetPointData().SetScalars(vtk_colors) return poly_data, color_is_scalar
classifier = ThresholdTissueClassifier(csa_peaks.gfa, .25) seed_mask = labels == 2 seeds = utils.seeds_from_mask(seed_mask, density=[1, 1, 1], affine=affine) # Initialization of LocalTracking. The computation happens in the next step. streamlines = LocalTracking(csa_peaks, classifier, seeds, affine, step_size=2) # Compute streamlines and store as a list. streamlines = Streamlines(streamlines) ############################################################################### # We will create a streamline actor from the streamlines. streamlines_actor = actor.line(streamlines, line_colors(streamlines)) ############################################################################### # Next, we create a surface actor from the corpus callosum seed ROI. We # provide the ROI data, the affine, the color in [R,G,B], and the opacity as # a decimal between zero and one. Here, we set the color as blue/green with # 50% opacity. surface_opacity = 0.5 surface_color = [0, 1, 1] seedroi_actor = actor.contour_from_roi(seed_mask, affine, surface_color, surface_opacity) ############################################################################### # Next, we initialize a ''Renderer'' object and add both actors
def visualize_bundles(trk, affine=None, bundle_dict=None, bundle=None, colors=None, scene=None, background=(1, 1, 1), interact=False, inline=False): """ Visualize bundles in 3D using VTK Parameters ---------- trk : str, list, or Streamlines The streamline information affine : ndarray, optional An affine transformation to apply to the streamlines before visualization. Default: no transform. bundle_dict : dict, optional Keys are names of bundles and values are dicts that should include a key `'uid'` with values as integers for selection from the trk metadata. Default: bundles are either not identified, or identified only as unique integers in the metadata. bundle : str or int, optional The name of a bundle to select from among the keys in `bundle_dict` or an integer for selection from the trk metadata. colors : dict or list If this is a dict, keys are bundle names and values are RGB tuples. If this is a list, each item is an RGB tuple. Defaults to a list with Tableau 20 RGB values background : tuple, optional RGB values for the background. Default: (1, 1, 1), which is white background. scene : fury Scene object, optional If provided, the visualization will be added to this Scene. Default: Initialize a new Scene. interact : bool Whether to provide an interactive VTK window for interaction. Default: False inline : bool Whether to embed the visualization inline in a notebook. Only works in the notebook context. Default: False. Returns ------- Fury Scene object """ if isinstance(trk, str): trk = nib.streamlines.load(trk) tg = trk.tractogram else: # Assume these are streamlines (as list or Streamlines object): tg = nib.streamlines.Tractogram(trk) if affine is not None: tg = tg.apply_affine(np.linalg.inv(affine)) streamlines = tg.streamlines if scene is None: scene = window.Scene() scene.SetBackground(background[0], background[1], background[2]) if colors is None: # Use the color dict provided colors = color_dict def _color_selector(bundle_dict, colors, b): """Helper function """ if bundle_dict is None: # We'll choose a color from a rotating list: if isinstance(colors, list): color = colors[np.mod(len(colors), int(b))] else: color_list = colors.values() color = color_list[np.mod(len(colors), int(b))] else: # We have a mapping from UIDs to bundle names: for b_name_iter, b_iter in bundle_dict.items(): if b_iter['uid'] == b: b_name = b_name_iter break color = colors[b_name] return color if list(tg.data_per_streamline.keys()) == []: # There are no bundles in here: streamlines = list(streamlines) # Visualize all the streamlines with directionally assigned RGB: sl_actor = actor.line(streamlines, line_colors(streamlines)) scene.add(sl_actor) sl_actor.GetProperty().SetRenderLinesAsTubes(1) sl_actor.GetProperty().SetLineWidth(6) else: # There are bundles: if bundle is None: # No selection: visualize all of them: for b in np.unique(tg.data_per_streamline['bundle']): idx = np.where(tg.data_per_streamline['bundle'] == b)[0] this_sl = list(streamlines[idx]) color = _color_selector(bundle_dict, colors, b) sl_actor = actor.line(this_sl, color) sl_actor.GetProperty().SetRenderLinesAsTubes(1) sl_actor.GetProperty().SetLineWidth(6) scene.add(sl_actor) else: # Select just one to visualize: if isinstance(bundle, str): # We need to find the UID: uid = bundle_dict[bundle]['uid'] else: # It's already a UID: uid = bundle idx = np.where(tg.data_per_streamline['bundle'] == uid)[0] this_sl = list(streamlines[idx]) color = _color_selector(bundle_dict, colors, uid) sl_actor = actor.line(this_sl, color) sl_actor.GetProperty().SetRenderLinesAsTubes(1) sl_actor.GetProperty().SetLineWidth(6) scene.add(sl_actor) return _inline_interact(scene, inline, interact)