def select_largest(points, faces, exclude_labels=[-1], areas=None, reindex=True): """ Select the largest segment (connected set of indices) in a mesh. In case a surface patch is fragmented, we select the largest fragment, remove extraneous triangular faces, and reindex indices. Parameters ---------- points : list of lists of 3 floats x,y,z coordinates for each vertex of the structure faces : list of lists of 3 integers 3 indices to vertices that form a triangle on the mesh exclude_labels : list of integers background values to exclude areas : numpy array or list of floats (or None) surface area scalar values for all vertices reindex : Boolean reindex indices in faces? Returns ------- points : list of lists of 3 floats x,y,z coordinates for each vertex of the structure faces : list of lists of 3 integers 3 indices to vertices that form a triangle on the mesh Examples -------- >>> # Spectrum for one label (artificial composite), two fragments: >>> import os >>> import numpy as np >>> from mindboggle.utils.io_vtk import read_scalars, read_vtk, write_vtk >>> from mindboggle.utils.mesh import remove_faces >>> from mindboggle.utils.segment import select_largest >>> path = os.environ['MINDBOGGLE_DATA'] >>> label_file = os.path.join(path, 'arno', 'labels', 'lh.labels.DKT31.manual.vtk') >>> area_file = os.path.join(path, 'arno', 'shapes', 'lh.pial.area.vtk') >>> exclude_labels = [-1] >>> faces, lines, indices, points, u1, labels, u2,u3 = read_vtk(label_file, >>> return_first=True, return_array=True) >>> I19 = [i for i,x in enumerate(labels) if x==19] # pars orbitalis >>> I22 = [i for i,x in enumerate(labels) if x==22] # postcentral >>> I19.extend(I22) >>> faces = remove_faces(faces, I19) >>> areas, u1 = read_scalars(area_file, True, True) >>> reindex = True >>> # >>> points, faces = select_largest(points, faces, exclude_labels, areas, >>> reindex) >>> # View: >>> from mindboggle.utils.plots import plot_vtk >>> scalars = np.zeros(np.shape(labels)) >>> scalars[I19] = 1 >>> vtk_file = 'test_two_labels.vtk' >>> write_vtk(vtk_file, points, indices, lines, faces, >>> scalars, scalar_names='scalars') >>> plot_vtk(vtk_file) """ import numpy as np from mindboggle.utils.mesh import find_neighbors, remove_faces, \ reindex_faces_points from mindboggle.utils.segment import segment # Areas: use_area = False if isinstance(areas, np.ndarray) and np.shape(areas): use_area = True elif isinstance(areas, list) and len(areas): areas = np.array(areas) use_area = True # Check to see if there are enough points: min_npoints = 4 npoints = len(points) if npoints < min_npoints or len(faces) < min_npoints: print("The input size {0} ({1} faces) should be much larger " "than {2}". format(npoints, len(faces), min_npoints)) return None else: #--------------------------------------------------------------------- # Segment the indices into connected sets of indices: #--------------------------------------------------------------------- # Construct neighbor lists: neighbor_lists = find_neighbors(faces, npoints) # Determine the indices: indices = [x for sublst in faces for x in sublst] # Segment: segments = segment(indices, neighbor_lists, min_region_size=1, seed_lists=[], keep_seeding=False, spread_within_labels=False, labels=[], label_lists=[], values=[], max_steps='', verbose=False) #--------------------------------------------------------------------- # Select the largest segment (connected set of indices): #--------------------------------------------------------------------- unique_segments = [x for x in np.unique(segments) if x not in exclude_labels] if len(unique_segments) > 1: select_indices = [] max_segment_area = 0 for segment_number in unique_segments: segment_indices = [i for i,x in enumerate(segments) if x == segment_number] if use_area: segment_area = np.sum(areas[segment_indices]) else: segment_area = len(segment_indices) if segment_area > max_segment_area: select_indices = segment_indices max_segment_area = len(select_indices) print('Maximum size of {0} segments: {1} vertices'. format(len(unique_segments), len(select_indices))) #----------------------------------------------------------------- # Extract points and renumber faces for the selected indices: #----------------------------------------------------------------- faces = remove_faces(faces, select_indices) else: select_indices = indices # Alert if the number of indices is small: if len(select_indices) < min_npoints: print("The input size {0} is too small.".format(len(select_indices))) return None elif faces: #----------------------------------------------------------------- # Reindex indices in faces: #----------------------------------------------------------------- if reindex: faces, points = reindex_faces_points(faces, points) return points, faces else: points = np.array(points) points = points[select_indices].tolist() return points, faces else: return None
def plot_mask_surface(vtk_file, mask_file='', nonmask_value=-1, masked_output='', remove_nonmask=False, program='vtkviewer', use_colormap=False, colormap_file=''): """ Use vtkviewer or mayavi2 to visualize VTK surface mesh data. If a mask_file is provided, a temporary masked file is saved, and it is this file that is viewed. If using vtkviewer, can optionally provide colormap file or set $COLORMAP environment variable. Parameters ---------- vtk_file : string name of VTK surface mesh file mask_file : string name of VTK surface mesh file to mask vtk_file vertices nonmask_value : integer nonmask (usually background) value masked_output : string temporary masked output file name remove_nonmask : Boolean remove vertices that are not in mask? (otherwise assign nonmask_value) program : string {'vtkviewer', 'mayavi2'} program to visualize VTK file use_colormap : Boolean use Paraview-style XML colormap file set by $COLORMAP env variable? colormap_file : string use colormap in given file if use_colormap==True? if empty and use_colormap==True, use file set by $COLORMAP environment variable Examples -------- >>> import os >>> from mindboggle.utils.plots import plot_mask_surface >>> path = os.environ['MINDBOGGLE_DATA'] >>> vtk_file = os.path.join(path, 'arno', 'labels', 'lh.labels.DKT31.manual.vtk') >>> mask_file = os.path.join(path, 'test_one_label.vtk') >>> nonmask_value = 0 #-1 >>> masked_output = '' >>> remove_nonmask = True >>> program = 'vtkviewer' >>> use_colormap = True >>> colormap_file = '' #'/software/mindboggle_tools/colormap.xml' >>> plot_mask_surface(vtk_file, mask_file, nonmask_value, masked_output, remove_nonmask, program, use_colormap, colormap_file) """ import os import numpy as np from mindboggle.utils.mesh import remove_faces, reindex_faces_points from mindboggle.utils.utils import execute from mindboggle.utils.plots import plot_surfaces from mindboggle.utils.io_vtk import read_scalars, rewrite_scalars, \ read_vtk, write_vtk #------------------------------------------------------------------------- # Filter mesh with non-background values from a second (same-size) mesh: #------------------------------------------------------------------------- if mask_file: mask, name = read_scalars(mask_file, True, True) if not masked_output: masked_output = os.path.join(os.getcwd(), 'temp.vtk') file_to_plot = masked_output #--------------------------------------------------------------------- # Remove nonmask-valued vertices: #--------------------------------------------------------------------- if remove_nonmask: #----------------------------------------------------------------- # Load VTK files: #----------------------------------------------------------------- faces, lines, indices, points, npoints, scalars, scalar_names, \ o1 = read_vtk(vtk_file, True, True) #----------------------------------------------------------------- # Find mask indices, remove nonmask faces, and reindex: #----------------------------------------------------------------- Imask = [i for i,x in enumerate(mask) if x != nonmask_value] mask_faces = remove_faces(faces, Imask) mask_faces, points, \ original_indices = reindex_faces_points(mask_faces, points) #----------------------------------------------------------------- # Write VTK file with scalar values: #----------------------------------------------------------------- if np.ndim(scalars) == 1: scalar_type = type(scalars[0]).__name__ elif np.ndim(scalars) == 2: scalar_type = type(scalars[0][0]).__name__ else: print("Undefined scalar type!") write_vtk(file_to_plot, points, [], [], mask_faces, scalars[original_indices].tolist(), scalar_names, scalar_type=scalar_type) else: scalars, name = read_scalars(vtk_file, True, True) scalars[mask == nonmask_value] = nonmask_value rewrite_scalars(vtk_file, file_to_plot, scalars) else: file_to_plot = vtk_file #------------------------------------------------------------------------- # Display with vtkviewer.py: #------------------------------------------------------------------------- if program == 'vtkviewer': plot_surfaces(file_to_plot, use_colormap=use_colormap, colormap_file=colormap_file) #------------------------------------------------------------------------- # Display with mayavi2: #------------------------------------------------------------------------- elif program == 'mayavi2': cmd = ["mayavi2", "-d", file_to_plot, "-m", "Surface", "&"] execute(cmd, 'os')
def spectrum_per_label(vtk_file, spectrum_size=10, exclude_labels=[-1], normalization='area', area_file='', largest_segment=True): """ Compute Laplace-Beltrami spectrum per labeled region in a file. Parameters ---------- vtk_file : string name of VTK surface mesh file containing index scalars (labels) spectrum_size : integer number of eigenvalues to be computed (the length of the spectrum) exclude_labels : list of integers labels to be excluded normalization : string the method used to normalize eigenvalues ('area' or None) if "area", use area of the 2D structure as in Reuter et al. 2006 area_file : string name of VTK file with surface area scalar values largest_segment : Boolean compute spectrum only for largest segment with a given label? Returns ------- spectrum_lists : list of lists first eigenvalues for each label's Laplace-Beltrami spectrum label_list : list of integers list of unique labels for which spectra are obtained Examples -------- >>> # Uncomment "if label==22:" below to run example: >>> # Spectrum for Twins-2-1 left postcentral (22) pial surface: >>> import os >>> from mindboggle.shapes.laplace_beltrami import spectrum_per_label >>> path = os.environ['MINDBOGGLE_DATA'] >>> vtk_file = os.path.join(path, 'arno', 'labels', 'lh.labels.DKT31.manual.vtk') >>> area_file = os.path.join(path, 'arno', 'shapes', 'lh.pial.area.vtk') >>> spectrum_size = 6 >>> exclude_labels = [0] #[-1] >>> largest_segment = True >>> spectrum_per_label(vtk_file, spectrum_size, exclude_labels, None, >>> area_file, largest_segment) ([[6.3469513010430304e-18, 0.0005178862383467463, 0.0017434911095630772, 0.003667561767487686, 0.005429017880363784, 0.006309346984678924]], [22]) """ from mindboggle.utils.io_vtk import read_vtk, read_scalars from mindboggle.utils.mesh import remove_faces, reindex_faces_points from mindboggle.shapes.laplace_beltrami import fem_laplacian,\ spectrum_of_largest # Read VTK surface mesh file: faces, u1, u2, points, u4, labels, u5, u6 = read_vtk(vtk_file) # Area file: if area_file: areas, u1 = read_scalars(area_file) else: areas = None # Loop through labeled regions: ulabels = [] [ulabels.append(int(x)) for x in labels if x not in ulabels if x not in exclude_labels] label_list = [] spectrum_lists = [] for label in ulabels: #if label == 22: # print("DEBUG: COMPUTE FOR ONLY ONE LABEL") # Determine the indices per label: Ilabel = [i for i,x in enumerate(labels) if x == label] print('{0} vertices for label {1}'.format(len(Ilabel), label)) # Remove background faces: pick_faces = remove_faces(faces, Ilabel) pick_faces, pick_points, o1 = reindex_faces_points(pick_faces, points) # Compute Laplace-Beltrami spectrum for the label: if largest_segment: exclude_labels_inner = [-1] spectrum = spectrum_of_largest(pick_points, pick_faces, spectrum_size, exclude_labels_inner, normalization, areas) else: spectrum = fem_laplacian(pick_points, pick_faces, spectrum_size, normalization) # Append to a list of lists of spectra: spectrum_lists.append(spectrum) label_list.append(label) return spectrum_lists, label_list
def explode_scalars(input_indices_vtk, input_values_vtk='', output_stem='', exclude_values=[-1], background_value=-1, output_scalar_name='scalars', remove_background_faces=True, reindex=True): """ Write out a separate VTK file for each integer (not in exclude_values) in (the first) scalar list of an input VTK file. Optionally write the values drawn from a second VTK file, remove background values, and reindex indices. Parameters ---------- input_indices_vtk : string path of the input VTK file that contains indices as scalars (assumes that the scalars are a list of floats or integers) input_values_vtk : string path of the input VTK file that contains values as scalars output_stem : string path and stem of the output VTK file exclude_values : list or array values to exclude background_value : integer or float background value in output VTK files remove_background_faces : Boolean remove all faces whose three vertices are not all a given index? reindex : Boolean reindex all indices in faces? Examples -------- >>> # Example 1: explode sulci with thickness values >>> import os >>> from mindboggle.utils.io_vtk import explode_scalars >>> from mindboggle.utils.plots import plot_surfaces >>> path = os.environ['MINDBOGGLE_DATA'] >>> input_indices_vtk = os.path.join(path, 'arno', 'features', 'sulci.vtk') >>> input_values_vtk = os.path.join(path, 'arno', 'shapes', 'lh.pial.travel_depth.vtk') >>> output_stem = 'sulci_depth' >>> # >>> explode_scalars(input_indices_vtk, input_values_vtk, output_stem) >>> # >>> # View: >>> example_vtk = os.path.join(os.getcwd(), output_stem + '0.vtk') >>> plot_surfaces(example_vtk) >>> # >>> # Example 2: explode labels >>> import os >>> from mindboggle.utils.io_vtk import explode_scalars >>> from mindboggle.utils.plots import plot_surfaces >>> path = os.environ['MINDBOGGLE_DATA'] >>> input_values_vtk = os.path.join(path, 'arno', 'labels', >>> 'lh.labels.DKT25.manual.vtk') >>> input_indices_vtk = input_values_vtk >>> output_stem = 'label' >>> exclude_values = [-1] >>> background_value = -1, >>> output_scalar_name = 'scalars' >>> remove_background_faces = True >>> reindex = True >>> # >>> explode_scalars(input_indices_vtk, input_values_vtk, output_stem, >>> exclude_values, background_value, >>> output_scalar_name, remove_background_faces, reindex) >>> # View: >>> example_vtk = os.path.join(os.getcwd(), output_stem + '2.vtk') >>> plot_surfaces(example_vtk) """ import os import numpy as np from mindboggle.utils.io_vtk import read_scalars, read_vtk, write_vtk from mindboggle.utils.mesh import reindex_faces_points, remove_faces # Load VTK file: faces, lines, indices, points, npoints, scalars, scalar_names, \ foo1 = read_vtk(input_indices_vtk, True, True) print("Explode the scalar list in {0}". format(os.path.basename(input_indices_vtk))) if input_values_vtk != input_indices_vtk: values, name = read_scalars(input_values_vtk, True, True) print("Explode the scalar list of values in {0} " "with the scalar list of indices in {1}". format(os.path.basename(input_values_vtk), os.path.basename(input_indices_vtk))) else: values = np.copy(scalars) # Loop through unique (non-excluded) scalar values: unique_scalars = np.unique(scalars) if all(unique_scalars==np.round(unique_scalars)): unique_scalars = [int(x) for x in unique_scalars if x not in exclude_values] else: unique_scalars = [x for x in unique_scalars if x not in exclude_values] for scalar in unique_scalars: # Remove background (keep only faces with the scalar): if remove_background_faces: scalar_indices = [i for i,x in enumerate(scalars) if x == scalar] scalar_faces = remove_faces(faces, scalar_indices) else: scalar_faces = faces # Reindex: if reindex: scalar_faces, select_points, \ o1 = reindex_faces_points(scalar_faces, points) else: select_points = points # Create array and indices for scalar value: if reindex: len_indices = len(select_points) select_values = scalar * np.ones(len_indices) else: select_values = np.copy(values) select_values[scalars != scalar] = background_value len_indices = len([i for i,x in enumerate(select_values) if x != background_value]) print(" Scalar {0}: {1} vertices".format(scalar, len_indices)) # Write VTK file with scalar values (list of values): if np.ndim(select_values) == 1: scalar_type = type(select_values[0]).__name__ elif np.ndim(select_values) == 2: scalar_type = type(select_values[0][0]).__name__ else: print("Undefined scalar type!") output_vtk = os.path.join(os.getcwd(), output_stem + str(scalar) + '.vtk') write_vtk(output_vtk, select_points, indices, lines, scalar_faces, select_values.tolist(), output_scalar_name, scalar_type=scalar_type)
def spectrum_of_largest(points, faces, n_eigenvalues=6, exclude_labels=[-1], normalization=None, areas=None): """ Compute Laplace-Beltrami spectrum on largest connected segment. In case a surface patch is fragmented, we select the largest fragment, remove extraneous triangular faces, and reindex indices. Parameters ---------- points : list of lists of 3 floats x,y,z coordinates for each vertex of the structure faces : list of lists of 3 integers 3 indices to vertices that form a triangle on the mesh n_eigenvalues : integer number of eigenvalues to be computed (the length of the spectrum) exclude_labels : list of integers background values to exclude normalization : string the method used to normalize eigenvalues ('area' or None) if "area", use area of the 2D structure as in Reuter et al. 2006 areas : numpy array or list of floats (or None) surface area scalar values for all vertices Returns ------- spectrum : list first n_eigenvalues eigenvalues for Laplace-Beltrami spectrum Examples -------- >>> # Spectrum for one label (artificial composite), two fragments: >>> import os >>> import numpy as np >>> from mindboggle.utils.io_vtk import read_scalars, read_vtk, write_vtk >>> from mindboggle.utils.mesh import remove_faces >>> from mindboggle.shapes.laplace_beltrami import spectrum_of_largest >>> path = os.environ['MINDBOGGLE_DATA'] >>> label_file = os.path.join(path, 'arno', 'labels', 'lh.labels.DKT25.manual.vtk') >>> area_file = os.path.join(path, 'arno', 'shapes', 'lh.pial.area.vtk') >>> n_eigenvalues = 6 >>> exclude_labels = [0] #[-1] >>> normalization = None >>> faces, lines, indices, points, foo1, labels, foo2, foo3 = read_vtk(label_file, >>> return_first=True, return_array=True) >>> I2 = [i for i,x in enumerate(labels) if x==2] # cingulate >>> I22 = [i for i,x in enumerate(labels) if x==22] # postcentral >>> I2.extend(I22) >>> faces = remove_faces(faces, I2) >>> areas, u1 = read_scalars(area_file, True, True) >>> # >>> spectrum_of_largest(points, faces, n_eigenvalues, exclude_labels, >>> normalization, areas) >>> # >>> # View: >>> from mindboggle.utils.plots import plot_vtk >>> scalars = np.zeros(np.shape(labels)) >>> scalars[I2] = 1 >>> vtk_file = 'test_two_labels.vtk' >>> write_vtk(vtk_file, points, indices, lines, faces, >>> scalars, scalar_names='scalars') >>> plot_vtk(vtk_file) Load "Labels" scalars from lh.labels.DKT25.manual.vtk Reduced 290134 to 29728 triangular faces Load "scalars" scalars from lh.pial.area.vtk 2 segments Reduced 29728 to 14498 triangular faces Compute linear FEM Laplace-Beltrami spectrum [-8.764053090852845e-18, 0.00028121452203987146, 0.0010941205613292243, 0.0017301461686759188, 0.0034244633555606295, 0.004280982704174599] """ from scipy.sparse.linalg import eigsh, lobpcg import numpy as np from mindboggle.utils.mesh import find_neighbors, remove_faces, \ reindex_faces_points from mindboggle.utils.segment import segment from mindboggle.shapes.laplace_beltrami import fem_laplacian # Areas: use_area = False if isinstance(areas, np.ndarray) and np.shape(areas): use_area = True elif isinstance(areas, list) and len(areas): areas = np.array(areas) use_area = True # Check to see if there are enough points: min_npoints = n_eigenvalues npoints = len(points) if npoints < min_npoints or len(faces) < min_npoints: print("The input size {0} ({1} faces) should be much larger " "than n_eigenvalues {2}". format(npoints, len(faces), n_eigenvalues)) return None else: #--------------------------------------------------------------------- # Segment the indices into connected sets of indices: #--------------------------------------------------------------------- # Construct neighbor lists: neighbor_lists = find_neighbors(faces, npoints) # Determine the indices: indices = [x for sublst in faces for x in sublst] # Segment: segments = segment(indices, neighbor_lists, min_region_size=1, seed_lists=[], keep_seeding=False, spread_within_labels=False, labels=[], label_lists=[], values=[], max_steps='', verbose=False) #--------------------------------------------------------------------- # Select the largest segment (connected set of indices): #--------------------------------------------------------------------- unique_segments = [x for x in np.unique(segments) if x not in exclude_labels] if len(unique_segments) > 1: select_indices = [] max_segment_area = 0 for segment_number in unique_segments: segment_indices = [i for i,x in enumerate(segments) if x == segment_number] if use_area: segment_area = np.sum(areas[segment_indices]) else: segment_area = len(segment_indices) if segment_area > max_segment_area: select_indices = segment_indices max_segment_area = len(select_indices) print('Maximum size of {0} segments: {1} vertices'. format(len(unique_segments), len(select_indices))) #----------------------------------------------------------------- # Extract points and renumber faces for the selected indices: #----------------------------------------------------------------- faces = remove_faces(faces, select_indices) else: select_indices = indices # Alert if the number of indices is small: if len(select_indices) < min_npoints: print("The input size {0} is too small.".format(len(select_indices))) return None elif faces: #----------------------------------------------------------------- # Reindex indices in faces: #----------------------------------------------------------------- faces, points = reindex_faces_points(faces, points) #----------------------------------------------------------------- # Compute spectrum: #----------------------------------------------------------------- spectrum = fem_laplacian(points, faces, n_eigenvalues, normalization) return spectrum else: return None