def main(argv): from mindboggle.utils.io_vtk import write_vtk closed_fundus_lines, points, faces = propagate_fundus_lines(argv[1], argv[2], argv[3]) write_vtk(argv[4], points, faces=faces, scalars=closed_fundus_lines, scalar_type='int')
def fundiFromPits(Pits, Maps, Mesh, FundiVTK, SulciThld, SulciMap, Extract_Fundi_on_Map): '''Connecting pits into fundus curves Parameters ============ Pits: list of integers IDs of vertexes that are pits Maps: dictionary of lists of floats Keys are strings, e.g., meancurv, depth, etc. Values are list of per-vertex values that will be used as structure feature vectors, e.g., thickness Mesh: List of two lists of floats/integers The first are points from a VTK file. The second are triangular faces from a VTK file. Vrtx: list of 3-tuples of floats Each element is the X-, Y- and Z-cooridnates of a vertex on the surface, normally pial Fc: list of 3-tuples of integers Each element is the ids of 3 vertexes that form one triangle on the surface SulciThld: a float The number that was used to threhold the surface to get sulci. This is need for loading component file, but it has nothing to do fundi extraction itself Extract_Fundi_on_Map: string The key for the map on which fundi will be built (default: depth) Notes ======== Function and variable names are not fully changed yet. Names like curvature is bad. ''' [Vrtx, Fc] = Mesh scalar_names = [Name for Name in Maps.iterkeys()] scalar_lists = [scalar_list for scalar_list in Maps.itervalues()] LastSlash = len(FundiVTK) - FundiVTK[::-1].find('/') Hemi = FundiVTK[:FundiVTK[LastSlash:].find('.')+LastSlash]# path up to which hemisphere, e.g., /home/data/lh NbrLst = libbasin.vrtxNbrLst(len(Vrtx), Fc, Hemi) FcCmpnt, VrtxCmpnt = libbasin.compnent([], [], [], ".".join([Hemi, SulciMap, str(SulciThld)])) PSegs, NodeColor, FundusLen, FundusID = lineUp(Pits, NbrLst, VrtxCmpnt, Vrtx, Maps[Extract_Fundi_on_Map]) len_scalars = [0 for i in xrange(0,len(NbrLst))] for Key, Value in FundusLen.iteritems(): len_scalars[Key] = Value scalar_names.append('fundusLength') scalar_lists.append(len_scalars) FIDscalars = [-1 for i in xrange(0,len(NbrLst))] # value for gyri is now -1 Forrest 2011-11-01 for Key, Value in FundusID.iteritems(): FIDscalars[Key] = Value scalar_names.append('fundusID') scalar_lists.append(FIDscalars) # io_vtk.write_lines(FundiVTK, Vrtx, Pits, PSegs, scalar_lists, scalar_names) io_vtk.write_vtk(FundiVTK, Vrtx, indices=Pits, lines=PSegs, faces=[], scalars=scalar_lists, scalar_names=scalar_names, scalar_type='int')
def close_surface_pair_from_files(patch_surface1, whole_surface2, background_value=-1, output_vtk=''): """ Close a surface patch by connecting its border vertices with corresponding vertices in a second surface file. Assumes no lines or indices when reading VTK files in. Note :: The first VTK file contains scalar values different than background for a surface patch. The second VTK file contains the (entire) surface whose corresponding vertices are shifted in position. For pial vs. gray-white matter, the two surfaces are not parallel, so connecting the vertices leads to intersecting faces. Parameters ---------- patch_surface1 : string vtk file with surface patch of non-background scalar values whole_surface2 : string second vtk file with 1-to-1 vertex correspondence with patch_surface1 (whole surface so as to derive vertex neighbor lists) background_value : integer scalar value for background vertices output_vtk : string output vtk file name with closed surface patch Returns ------- output_vtk : string output vtk file name with closed surface patch Examples -------- >>> import os >>> from mindboggle.utils.morph import close_surface_pair_from_files >>> from mindboggle.utils.plots import plot_surfaces >>> from mindboggle.utils.io_vtk import read_scalars, read_vtk, read_points, write_vtk >>> path = os.environ['MINDBOGGLE_DATA'] >>> patch_surface1 = 'fold.pial.vtk' >>> whole_surface2 = 'fold.white.vtk' >>> # Select a single fold: >>> folds_file = os.path.join(path, 'arno', 'features', 'folds.vtk') >>> points = read_points(folds_file) >>> folds, name = read_scalars(folds_file, True, True) >>> fold_number = 11 >>> folds[folds != fold_number] = -1 >>> white_surface = os.path.join(path, 'arno', 'freesurfer', 'lh.white.vtk') >>> faces, u1, u2, points2, N, u3, u4, u5 = read_vtk(white_surface) >>> write_vtk(patch_surface1, points, [], [], faces, folds, name) >>> write_vtk(whole_surface2, points2, [], [], faces, folds, name) >>> background_value = -1 >>> output_vtk = '' >>> close_surface_pair_from_files(patch_surface1, whole_surface2, background_value, output_vtk) >>> # View: >>> plot_surfaces('closed.vtk') # doctest: +SKIP """ import os import numpy as np from mindboggle.utils.io_vtk import read_vtk, write_vtk from mindboggle.utils.morph import close_surface_pair # Read VTK surface mesh files: u1, u2, u3, points1, N, scalars, name, u4 = read_vtk(patch_surface1, True, True) faces, u1, u2, points2, N, u3, u4, u5 = read_vtk(whole_surface2, True, True) # Close surface: closed_faces, closed_points, closed_scalars = close_surface_pair(faces, points1, points2, scalars, background_value) # Write output file: if not output_vtk: output_vtk = os.path.join(os.getcwd(), 'closed.vtk') # closed_scalars is a list if np.ndim(closed_scalars) == 1: scalar_type = type(closed_scalars[0]).__name__ elif np.ndim(closed_scalars) == 2: scalar_type = type(closed_scalars[0][0]).__name__ else: print("Undefined scalar type!") write_vtk(output_vtk, closed_points, [], [], closed_faces, closed_scalars, name, scalar_type=scalar_type) return output_vtk
def close_surface_pair_from_files(patch_surface1, whole_surface2, background_value=-1, output_vtk=''): """ Close a surface patch by connecting its border vertices with corresponding vertices in a second surface file. Assumes no lines or indices when reading VTK files in. Note :: The first VTK file contains scalar values different than background for a surface patch. The second VTK file contains the (entire) surface whose corresponding vertices are shifted in position. For pial vs. gray-white matter, the two surfaces are not parallel, so connecting the vertices leads to intersecting faces. Parameters ---------- patch_surface1 : string vtk file with surface patch of non-background scalar values whole_surface2 : string second vtk file with 1-to-1 vertex correspondence with patch_surface1 (whole surface so as to derive vertex neighbor lists) background_value : integer scalar value for background vertices output_vtk : string output vtk file name with closed surface patch Returns ------- output_vtk : string output vtk file name with closed surface patch Examples -------- >>> import os >>> from mindboggle.utils.morph import close_surface_pair_from_files >>> from mindboggle.utils.plots import plot_surfaces >>> from mindboggle.utils.io_vtk import read_scalars, read_vtk, read_points, write_vtk >>> path = os.environ['MINDBOGGLE_DATA'] >>> patch_surface1 = 'fold.pial.vtk' >>> whole_surface2 = 'fold.white.vtk' >>> # Select a single fold: >>> folds_file = os.path.join(path, 'arno', 'features', 'folds.vtk') >>> points = read_points(folds_file) >>> folds, name = read_scalars(folds_file, True, True) >>> fold_number = 11 >>> folds[folds != fold_number] = -1 >>> white_surface = os.path.join(path, 'arno', 'freesurfer', 'lh.white.vtk') >>> faces, u1, u2, points2, N, u3, u4, u5 = read_vtk(white_surface) >>> write_vtk(patch_surface1, points, [], [], faces, folds, name) >>> write_vtk(whole_surface2, points2, [], [], faces, folds, name) >>> background_value = -1 >>> output_vtk = '' >>> close_surface_pair_from_files(patch_surface1, whole_surface2, background_value, output_vtk) >>> # View: >>> plot_surfaces('closed.vtk') # doctest: +SKIP """ import os import numpy as np from mindboggle.utils.io_vtk import read_vtk, write_vtk from mindboggle.utils.morph import close_surface_pair # Read VTK surface mesh files: u1, u2, u3, points1, N, scalars, name, u4 = read_vtk( patch_surface1, True, True) faces, u1, u2, points2, N, u3, u4, u5 = read_vtk(whole_surface2, True, True) # Close surface: closed_faces, closed_points, closed_scalars = close_surface_pair( faces, points1, points2, scalars, background_value) # Write output file: if not output_vtk: output_vtk = os.path.join(os.getcwd(), 'closed.vtk') # closed_scalars is a list if np.ndim(closed_scalars) == 1: scalar_type = type(closed_scalars[0]).__name__ elif np.ndim(closed_scalars) == 2: scalar_type = type(closed_scalars[0][0]).__name__ else: print("Undefined scalar type!") write_vtk(output_vtk, closed_points, [], [], closed_faces, closed_scalars, name, scalar_type=scalar_type) return output_vtk
def realign_boundaries_to_fundus_lines( surf_file, init_label_file, fundus_lines_file, thickness_file, out_label_file=None): """ Fix label boundaries to fundus lines. Parameters ---------- surf_file : file containing the surface geometry in vtk format init_label_file : file containing scalars that represent the initial guess at labels fundus_lines_file : file containing scalars representing fundus lines. thickness_file: file containing cortical thickness scalar data (for masking out the medial wall only) out_label_file : if specified, the realigned labels will be writen to this file Returns ------- numpy array representing the realigned label for each surface vertex. """ import numpy as np from mindboggle.utils.segment import extract_borders import mindboggle.utils.graph as go from mindboggle.utils.io_vtk import read_vtk, read_scalars, write_vtk from mindboggle.utils.mesh import find_neighbors import propagate_fundus_lines ## read files faces, _, indices, points, num_points, _, _, _ = read_vtk( surf_file, return_first=True, return_array=True) indices = range(num_points) init_labels, _ = read_scalars(init_label_file, return_first=True, return_array=True) fundus_lines, _ = read_scalars(fundus_lines_file, return_first=True, return_array=True) thickness, _ = read_scalars(thickness_file, return_first=True, return_array=True) # remove labels from vertices with zero thickness (get around # DKT40 annotations having the label '3' for all the Corpus # Callosum vertices). cc_inds = [x for x in indices if thickness[x] < 0.001] init_labels[cc_inds] = 0 ## setup seeds from initial label boundaries neighbor_lists = find_neighbors(faces, num_points) # extract all vertices that are on a boundary between labels boundary_indices, label_pairs, _ = extract_borders( indices, init_labels, neighbor_lists, return_label_pairs=True) # split boundary vertices into segments with common boundary pairs. boundary_segments = {} for boundary_index, label_pair in zip(boundary_indices, label_pairs): key = ((label_pair[0], label_pair[1]) if label_pair[0] < label_pair[1] else (label_pair[1], label_pair[0])) if key not in boundary_segments: boundary_segments[key] = [] boundary_segments[key].append(boundary_index) boundary_matrix, boundary_matrix_keys = _build_boundary_matrix( boundary_segments, num_points) # build the affinity matrix affinity_matrix = go.weight_graph( np.array(points), indices, np.array(faces), sigma=10, add_to_graph=False) ## propagate boundaries to fundus line vertices learned_matrix = _propagate_labels( affinity_matrix, boundary_matrix, boundary_indices, 100, 1) # assign labels to fundus line vertices based on highest probability new_boundaries = -1 * np.ones(init_labels.shape) fundus_line_indices = [i for i, x in enumerate(fundus_lines) if x > 0.5] # tile the surface into connected components delimited by fundus lines closed_fundus_lines, _, _ = propagate_fundus_lines.propagate_fundus_lines( points, faces, fundus_line_indices, thickness) closed_fundus_line_indices = np.where(closed_fundus_lines > 0)[0] # split surface into connected components connected_component_faces = _remove_boundary_faces( points, faces, closed_fundus_line_indices) # label components based on most probable label assignment new_labels = _label_components( connected_component_faces, num_points, boundary_indices, learned_matrix, boundary_matrix_keys) # propagate new labels to fill holes label_matrix, label_map = _build_label_matrix(new_labels) new_learned_matrix = _propagate_labels( affinity_matrix, label_matrix, [i for i in range(num_points) if new_labels[i] >= 0], 100, 1) # assign most probable labels for idx in [i for i in range(num_points) if new_labels[i] == -1]: max_idx = np.argmax(new_learned_matrix[idx]) new_labels[idx] = label_map[max_idx] # save if out_label_file is not None: write_vtk(out_label_file, points, faces=faces, scalars=[int(x) for x in new_labels], scalar_type='int') return new_labels
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 apply_affine_transform(transform_file, vtk_or_points, transform_format='itk', vtk_file_stem='affine_'): """ Transform coordinates using an affine matrix. Parameters ---------- transform file : string name of affine transform file vtk_or_points : string or list of lists of three integers name of VTK file containing point coordinate data, or the data (if vtk file, assumes scalars are a list of floats or integers) transform_format : string format for transform file (currently 'itk'); complications arise with other formats, such as 'txt' for text, or 'mat' for Matlab format, since software-specific assignment of parameters such as the origin need to be taken into account vtk_file_stem : string save transformed coordinates in a vtk file with this file append (empty string if vtk_or_points is points) Returns ------- affine_points : list of lists of floats transformed coordinates output_file : string or None (if not vtk_file_stem or vtk_or_points is points) name of VTK file containing transformed point data Examples -------- >>> import os >>> from mindboggle.utils.io_vtk import apply_affine_transform >>> from mindboggle.utils.plots import plot_surfaces >>> #path = os.environ['MINDBOGGLE_DATA'] >>> #transform_file = os.path.join(path, 'arno', 'mri', >>> # 't1weighted_brain.MNI152Affine.txt') >>> transform_file = '/Users/arno/mindboggle_working/OASIS-TRT-20-1/Mindboggle/Compose_affine_transform/affine.txt' >>> vtk_or_points = '/Users/arno/mindboggle_working/OASIS-TRT-20-1/Mindboggle/_hemi_lh/Surface_to_vtk/lh.pial.vtk' >>> transform_format = 'itk' >>> vtk_file_stem = True >>> affine_points, output_file = apply_affine_transform(transform_file, vtk_or_points, transform_format, vtk_file_stem) >>> # View >>> plot_surfaces('affine_lh.pial.vtk') """ import os import numpy as np from scipy.io import loadmat from mindboggle.utils.io_vtk import read_vtk, write_vtk, read_itk_transform from mindboggle.utils.ants import antsApplyTransformsToPoints # Read affine transform file: if transform_format == 'itk': #pass transform = read_itk_transform(transform_file) elif transform_format == 'txt': transform = np.loadtxt(transform_file) elif transform_format == 'mat': transform = loadmat(transform_file) else: import sys sys.exit('Transform file format not understood.') # Read VTK file: if isinstance(vtk_or_points, str): faces, lines, indices, points, npoints, scalars, name, \ foo1 = read_vtk(vtk_or_points) points = np.array(points) elif isinstance(vtk_or_points, list): points = np.array(vtk_or_points) vtk_file_stem = '' elif isinstance(vtk_or_points, np.ndarray): points = vtk_or_points.copy() vtk_file_stem = '' # Transform points: if transform_format == 'itk': affine_points = antsApplyTransformsToPoints(points, [transform_file], [0]) #points = np.concatenate((points, # np.ones((np.shape(points)[0],1))), axis=1) #affine_points = np.transpose(np.dot(transform, # np.transpose(points)))[:,0:3] #affine_points = [x.tolist() for x in affine_points] else: points = np.concatenate((points, np.ones((np.shape(points)[0],1))), axis=1) affine_points = np.transpose(np.dot(transform, np.transpose(points)))[:,0:3] affine_points = [x.tolist() for x in affine_points] # Write transformed VTK file: if vtk_file_stem and isinstance(vtk_or_points, str): output_file = os.path.join(os.getcwd(), vtk_file_stem + os.path.basename(vtk_or_points)) if np.size(scalars): 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!") else: scalars = [] scalar_type = 'int' write_vtk(output_file, affine_points, indices, lines, faces, scalars, name, scalar_type) else: output_file = None return affine_points, output_file
def relabel_surface(vtk_file, hemi='', old_labels=[], new_labels=[], output_file=''): """ Relabel surface in a VTK file. Parameters ---------- vtk_file : string input labeled VTK file hemi : string hemisphere ('lh' or 'rh' or '') old_labels : list of integers old labels new_labels : list of integers new labels output_file : string new vtk file name Returns ------- output_file : string new vtk file name Examples -------- >>> import os >>> from mindboggle.labels.relabel import relabel_surface >>> from mindboggle.utils.plots import plot_vtk >>> path = os.environ['MINDBOGGLE_DATA'] >>> vtk_file = os.path.join(path, 'arno', 'labels', 'lh.labels.DKT25.manual.vtk') >>> hemi = 'lh' >>> old_labels = [] >>> new_labels = [] >>> output_file = '' >>> # >>> relabel_surface(vtk_file, hemi, old_labels, new_labels, output_file) >>> # View >>> plot_vtk('relabeled_lh.labels.DKT25.manual.vtk') """ import os import numpy as np from mindboggle.utils.io_vtk import read_vtk, write_vtk # Load labeled vtk surfaces: faces, lines, indices, points, npoints, scalars, \ name, input_vtk = read_vtk(vtk_file, return_first=True, return_array=True) # Add a hemisphere value to each label: if hemi: ulabels = np.unique(scalars) for label in ulabels: I = np.where(scalars == int(label))[0] if hemi == 'lh': scalars[I] = 1000 + int(label) elif hemi == 'rh': scalars[I] = 2000 + int(label) # OR replace each old label with a corresponding new label: else: for ilabel, new_label in enumerate(new_labels): I = np.where(scalars == int(old_labels[ilabel]))[0] scalars[I] = int(new_label) if not output_file: output_file = os.path.join(os.getcwd(), 'relabeled_' + os.path.basename(vtk_file)) write_vtk(output_file, points, indices, lines, faces, [scalars.tolist()], ['Labels']) return output_file
def realign_boundaries_to_fundus_lines( surf_file, init_label_file, fundus_lines_file, out_label_file=None): """ Fix label boundaries to fundus lines. Parameters ---------- surf_file : file containing the surface geometry in vtk format init_label_file : file containing scalars that represent the initial guess at labels fundus_lines_file : file containing scalars representing fundus lines. out_label_file : if specified, the realigned labels will be writen to this file Returns ------- numpy array representing the realigned label for each surface vertex. """ # import os import numpy as np from mindboggle.labels.labels import extract_borders import mindboggle.utils.graph as go from mindboggle.utils.io_vtk import read_vtk, read_scalars, write_vtk # import mindboggle.utils.kernels as kernels from mindboggle.utils.mesh import find_neighbors # from mindboggle.labels.protocol import dkt_protocol # # protocol = 'DKT25' # sulcus_names, sulcus_label_pair_lists, unique_sulcus_label_pairs, \ # label_names, label_numbers, cortex_names, cortex_numbers, \ # noncortex_names, noncortex_numbers = dkt_protocol(protocol) ## read files faces, _, indices, points, num_points, _, _, _ = read_vtk( surf_file, return_first=True, return_array=True) indices = range(num_points) init_labels, _ = read_scalars(init_label_file, return_first=True, return_array=True) fundus_lines, _ = read_scalars(fundus_lines_file, return_first=True, return_array=True) ## setup seeds from initial label boundaries neighbor_lists = find_neighbors(faces, num_points) # extract all vertices that are on a boundary between labels boundary_indices, label_pairs, _ = extract_borders( indices, init_labels, neighbor_lists, return_label_pairs=True) # split boundary vertices into segments with common boundary pairs. boundary_segments = {} for boundary_index, label_pair in zip(boundary_indices, label_pairs): key = ((label_pair[0], label_pair[1]) if label_pair[0] < label_pair[1] else (label_pair[1], label_pair[0])) if key not in boundary_segments: boundary_segments[key] = [] boundary_segments[key].append(boundary_index) boundary_matrix, boundary_matrix_keys = _build_boundary_matrix( boundary_segments, num_points) # build the affinity matrix affinity_matrix = go.weight_graph( np.array(points), indices, np.array(faces), sigma=10, add_to_graph=False) ## propagate boundaries to fundus line vertices learned_matrix = _propagate_labels( affinity_matrix, boundary_matrix, boundary_indices, 1000, 1) # assign labels to fundus line vertices based on highest probability new_boundaries = -1 * np.ones(init_labels.shape) fundus_line_indices = [i for i, x in enumerate(fundus_lines) if x > 0.5] # TODO: this currently only works for fundus lines that tile the # surface into connected components (which is fine when you want # to test this method on fundus lines generated from manual # labeling). However, to work on real data, fundus lines will # need to be connected together using shortest paths. # split surface into connected components connected_component_faces = _remove_boundary_faces( points, faces, fundus_line_indices) # label components based on most probable label assignment new_labels = _label_components( connected_component_faces, num_points, boundary_indices, learned_matrix, boundary_matrix_keys) # propagate new labels to fill holes label_matrix, label_map = _build_label_matrix(new_labels) new_learned_matrix = _propagate_labels( affinity_matrix, label_matrix, [i for i in range(num_points) if new_labels[i] >= 0], 100, 1) # assign most probable labels for idx in [i for i in range(num_points) if new_labels[i] == -1]: max_idx = np.argmax(new_learned_matrix[idx]) new_labels[idx] = label_map[max_idx] # save if out_label_file is not None: write_vtk(out_label_file, points, faces=faces, scalars=new_labels.tolist()) return new_labels
def relabel_surface(vtk_file, hemi='', old_labels=[], new_labels=[], erase_remaining=True, erase_labels=[], erase_value=-1, output_file=''): """ Relabel surface in a VTK file. Parameters ---------- vtk_file : string input labeled VTK file hemi : string hemisphere ('lh' or 'rh' or '') if set, add 1000 to left and 2000 to right hemisphere labels; old_labels : list of integers old labels (empty list if labels drawn from vtk scalars); may be used in conjunction with hemi new_labels : list of integers new labels (empty list if labels drawn from vtk scalars); may be used in conjunction with hemi erase_remaining : Boolean set all values not in old_labels to erase_value? erase_labels : list of integers values to erase (set to erase_value) erase_value : integer set vertices with labels in erase_labels to this value output_file : string new vtk file name Returns ------- output_file : string new vtk file name Examples -------- >>> import os >>> from mindboggle.labels.relabel import relabel_surface >>> from mindboggle.utils.plots import plot_surfaces >>> path = os.environ['MINDBOGGLE_DATA'] >>> vtk_file = os.path.join(path, 'arno', 'labels', 'lh.labels.DKT25.manual.vtk') >>> hemi = 'lh' >>> old_labels = [1003,1009,1030] >>> new_labels = [3,9,30] >>> erase_remaining = True >>> erase_labels = [0] >>> erase_value = -1 >>> output_file = '' >>> # >>> relabel_surface(vtk_file, hemi, old_labels, new_labels, erase_remaining, erase_labels, erase_value, output_file) >>> # View >>> plot_surfaces('relabeled_FreeSurfer_cortex_labels.vtk') """ import os import numpy as np from mindboggle.utils.io_vtk import read_vtk, write_vtk # Load labeled vtk surfaces: faces, lines, indices, points, npoints, scalars, \ name, input_vtk = read_vtk(vtk_file, return_first=True, return_array=True) new_scalars = scalars[:] # Raise an error if inputs set incorrectly: if (new_labels and not old_labels) or \ (hemi and hemi not in ['lh','rh']) or \ (erase_remaining and not old_labels): raise IOError("Please check inputs for relabel_surface().") # Loop through unique labels in scalars: ulabels = np.unique(scalars) for label in ulabels: I = np.where(scalars == label)[0] # If label in erase_labels list, replace with erase_value: if label in erase_labels: new_scalars[I] = erase_value # If label in old_labels list, replace with corresponding new label, # and if hemi set, add 1000 or 2000 to the new label: elif label in old_labels and (len(old_labels) == len(new_labels)): new_label = new_labels[old_labels.index(label)] if hemi == 'lh': new_scalars[I] = 1000 + new_label elif hemi == 'rh': new_scalars[I] = 2000 + new_label else: new_scalars[I] = new_label # If labels not set then optionally add hemi value: elif hemi and not new_labels: if hemi == 'lh': new_scalars[I] = 1000 + label elif hemi == 'rh': new_scalars[I] = 2000 + label # If label unaccounted for and erase_remaining, set to erase_value: elif erase_remaining: new_scalars[I] = erase_value # Ensure that the new scalars are integer values: new_scalars = [int(x) for x in new_scalars] # Write output VTK file: if not output_file: output_file = os.path.join(os.getcwd(), 'relabeled_' + os.path.basename(vtk_file)) write_vtk(output_file, points, indices, lines, faces, [new_scalars], ['Labels'], scalar_type='int') if not os.path.exists(output_file): s = "relabel_surface() did not create " + output_file + "." raise (IOError(s)) return output_file
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 relabel_surface(vtk_file, hemi='', old_labels=[], new_labels=[], output_file=''): """ Relabel surface in a VTK file. Parameters ---------- vtk_file : string input labeled VTK file hemi : string hemisphere ('lh' or 'rh' or '') if set, add 1000 to left and 2000 to right hemisphere labels; old_labels : list of integers old labels (empty list if labels drawn from vtk scalars); may be used in conjunction with hemi new_labels : list of integers new labels (empty list if labels drawn from vtk scalars); may be used in conjunction with hemi output_file : string new vtk file name Returns ------- output_file : string new vtk file name Examples -------- >>> import os >>> from mindboggle.labels.relabel import relabel_surface >>> from mindboggle.utils.plots import plot_vtk >>> path = os.environ['MINDBOGGLE_DATA'] >>> vtk_file = os.path.join(path, 'arno', 'labels', 'lh.labels.DKT25.manual.vtk') >>> hemi = 'lh' >>> old_labels = [] >>> new_labels = [] >>> output_file = '' >>> # >>> relabel_surface(vtk_file, hemi, old_labels, new_labels, output_file) >>> # View >>> plot_vtk('relabeled_lh.labels.DKT25.manual.vtk') """ import os import numpy as np from mindboggle.utils.io_vtk import read_vtk, write_vtk # Load labeled vtk surfaces: faces, lines, indices, points, npoints, scalars, \ name, input_vtk = read_vtk(vtk_file, return_first=True, return_array=True) # Add a hemisphere value to each unique label drawn from scalars: if hemi and not old_labels and not new_labels: ulabels = np.unique(scalars) for label in ulabels: I = np.where(scalars == int(label))[0] if hemi == 'lh': scalars[I] = 1000 + int(label) elif hemi == 'rh': scalars[I] = 2000 + int(label) # OR replace each old label with a corresponding new label # (hemisphere setting optionally adds 1000 or 2000 to the new label): else: for ilabel, new_label in enumerate(new_labels): I = np.where(scalars == int(old_labels[ilabel]))[0] if hemi == 'lh': scalars[I] = 1000 + int(new_label) elif hemi == 'rh': scalars[I] = 2000 + int(new_label) else: scalars[I] = int(new_label) if not output_file: output_file = os.path.join(os.getcwd(), 'relabeled_' + os.path.basename(vtk_file)) write_vtk(output_file, points, indices, lines, faces, [scalars.tolist()], ['Labels']) if not os.path.exists(output_file): raise(IOError(output_file + " not found")) return output_file
def apply_affine_transform(transform_file, vtk_or_points, transform_format='txt', save_file=False): """ Transform coordinates using an affine matrix. Parameters ---------- transform file : string name of ITK affine transform file vtk_or_points : string or list of lists of three integers name of VTK file containing point coordinate data, or the data transform_format : string format for transform file Ex: 'txt' for text, 'itk' for ITK, and 'mat' for Matlab format save_file : Boolean save transformed coordinates in a vtk file? (False if vtk_or_points is points) Returns ------- affine_points : list of lists of floats transformed coordinates output_file : string or None (if save_file==False or vtk_or_points is points) name of VTK file containing transformed point data Examples -------- >>> import os >>> from mindboggle.utils.io_vtk import apply_affine_transform >>> from mindboggle.utils.plots import plot_vtk >>> path = os.environ['MINDBOGGLE_DATA'] >>> transform_file = os.path.join(path, 'arno', 'mri', >>> 't1weighted_brain.MNI152Affine.txt') >>> # 'affine_to_template.mat') >>> transform_format = 'itk' >>> #transform_format = 'mat' >>> vtk_or_points = os.path.join(path, 'arno', 'shapes', 'lh.pial.mean_curvature.vtk') >>> save_file = True >>> # >>> apply_affine_transform(transform_file, vtk_or_points, >>> transform_format, save_file) >>> # View >>> plot_vtk('affine_lh.pial.mean_curvature.vtk') """ import os import numpy as np from scipy.io import loadmat from mindboggle.utils.io_vtk import read_vtk, write_vtk, read_itk_transform # Read ITK affine transform file: if transform_format == 'txt': transform = np.loadtxt(transform_file) elif transform_format == 'mat': transform = loadmat(transform_file) elif transform_format == 'itk': transform = read_itk_transform(transform_file) else: import sys sys.exit('Transform file format not understood.') # Read VTK file: if isinstance(vtk_or_points, str): faces, lines, indices, points, npoints, scalars, name, \ foo1 = read_vtk(vtk_or_points) points = np.array(points) elif isinstance(vtk_or_points, list): points = np.array(vtk_or_points) save_file = False elif isinstance(vtk_or_points, np.ndarray): points = vtk_or_points.copy() save_file = False # Transform points: points = np.concatenate((points, np.ones((np.shape(points)[0],1))), axis=1) affine_points = np.transpose(np.dot(transform, np.transpose(points)))[:,0:3] affine_points.tolist() affine_points = [x.tolist() for x in affine_points] # Write transformed VTK file: if save_file: output_file = os.path.join(os.getcwd(), 'affine_' + os.path.basename(vtk_or_points)) write_vtk(output_file, affine_points, indices, lines, faces, scalars, name) else: output_file = None return affine_points, output_file
def evaluate_deep_features(features_file, labels_file, sulci_file='', hemi='', excludeIDs=[-1], output_vtk_name='', verbose=True): """ Evaluate deep surface features by computing the minimum distance from each label boundary vertex to all of the feature vertices in the same sulcus, and from each feature vertex to all of the label boundary vertices in the same sulcus. The label boundaries run along the deepest parts of sulci and correspond to fundi in the DKT cortical labeling protocol. Parameters ---------- features_file : string VTK surface file with feature numbers for vertex scalars labels_file : string VTK surface file with label numbers for vertex scalars sulci_file : string VTK surface file with sulcus numbers for vertex scalars excludeIDs : list of integers feature/sulcus/label IDs to exclude (background set to -1) output_vtk_name : Boolean if not empty, output a VTK file beginning with output_vtk_name that contains a surface with mean distances as scalars verbose : Boolean print mean distances to standard output? Returns ------- feature_to_fundus_mean_distances : numpy array [number of features x 1] mean distance from each feature to sulcus label boundary ("fundus") feature_to_fundus_sd_distances : numpy array [number of features x 1] standard deviations of feature-to-fundus distances feature_to_fundus_mean_distances_vtk : string VTK surface file containing feature_to_fundus_mean_distances fundus_to_feature_mean_distances : numpy array [number of features x 1] mean distances from each sulcus label boundary ("fundus") to feature fundus_to_feature_sd_distances : numpy array [number of features x 1] standard deviations of fundus-to-feature distances fundus_to_feature_mean_distances_vtk : string VTK surface file containing fundus_to_feature_mean_distances """ import os import sys import numpy as np from mindboggle.utils.io_vtk import read_vtk, read_scalars, write_vtk from mindboggle.utils.mesh import find_neighbors, remove_faces from mindboggle.utils.segment import extract_borders from mindboggle.utils.compute import source_to_target_distances from mindboggle.LABELS import DKTprotocol dkt = DKTprotocol() #------------------------------------------------------------------------- # Load labels, features, and sulci: #------------------------------------------------------------------------- faces, lines, indices, points, npoints, labels, scalar_names, \ input_vtk = read_vtk(labels_file, True, True) features, name = read_scalars(features_file, True, True) if sulci_file: sulci, name = read_scalars(sulci_file, True, True) # List of indices to sulcus vertices: sulcus_indices = [i for i,x in enumerate(sulci) if x != -1] segmentIDs = sulci sulcus_faces = remove_faces(faces, sulcus_indices) else: sulcus_indices = range(len(labels)) segmentIDs = [] sulcus_faces = faces #------------------------------------------------------------------------- # Prepare neighbors, label pairs, fundus IDs, and outputs: #------------------------------------------------------------------------- # Calculate neighbor lists for all points: print('Find neighbors to all vertices...') neighbor_lists = find_neighbors(faces, npoints) # Find label boundary points in any of the sulci: print('Find label boundary points in any of the sulci...') border_indices, border_label_tuples, unique_border_label_tuples = \ extract_borders(sulcus_indices, labels, neighbor_lists, ignore_values=[], return_label_pairs=True) if not len(border_indices): sys.exit('There are no label boundary points!') # Initialize an array of label boundaries fundus IDs # (label boundary vertices that define sulci in the labeling protocol): print('Build an array of label boundary fundus IDs...') label_boundary_fundi = -1 * np.ones(npoints) if hemi == 'lh': nsulcus_lists = len(dkt.left_sulcus_label_pair_lists) else: nsulcus_lists = len(dkt.right_sulcus_label_pair_lists) feature_to_fundus_mean_distances = -1 * np.ones(nsulcus_lists) feature_to_fundus_sd_distances = -1 * np.ones(nsulcus_lists) fundus_to_feature_mean_distances = -1 * np.ones(nsulcus_lists) fundus_to_feature_sd_distances = -1 * np.ones(nsulcus_lists) feature_to_fundus_mean_distances_vtk = '' fundus_to_feature_mean_distances_vtk = '' #------------------------------------------------------------------------- # Loop through sulci: #------------------------------------------------------------------------- # For each list of sorted label pairs (corresponding to a sulcus): for isulcus, label_pairs in enumerate(dkt.sulcus_label_pair_lists): # Keep the boundary points with label pair labels: fundus_indices = [x for i,x in enumerate(border_indices) if np.unique(border_label_tuples[i]).tolist() in label_pairs] # Store the points as sulcus IDs in the fundus IDs array: if fundus_indices: label_boundary_fundi[fundus_indices] = isulcus if len(np.unique(label_boundary_fundi)) > 1: #--------------------------------------------------------------------- # Construct a feature-to-fundus distance matrix and VTK file: #--------------------------------------------------------------------- # Construct a distance matrix: print('Construct a feature-to-fundus distance matrix...') sourceIDs = features targetIDs = label_boundary_fundi distances, distance_matrix = source_to_target_distances( sourceIDs, targetIDs, points, segmentIDs, excludeIDs) # Compute mean distances for each feature: nfeatures = min(np.shape(distance_matrix)[1], nsulcus_lists) for ifeature in range(nfeatures): feature_distances = [x for x in distance_matrix[:, ifeature] if x != -1] feature_to_fundus_mean_distances[ifeature] = \ np.mean(feature_distances) feature_to_fundus_sd_distances[ifeature] = \ np.std(feature_distances) if verbose: print('Feature-to-fundus mean distances:') print(feature_to_fundus_mean_distances) print('Feature-to-fundus standard deviations of distances:') print(feature_to_fundus_sd_distances) # Write resulting feature-label boundary distances to VTK file: if output_vtk_name: feature_to_fundus_mean_distances_vtk = os.path.join(os.getcwd(), output_vtk_name + '_feature_to_fundus_mean_distances.vtk') print('Write feature-to-fundus distances to {0}...'. format(feature_to_fundus_mean_distances_vtk)) write_vtk(feature_to_fundus_mean_distances_vtk, points, [], [], sulcus_faces, [distances], ['feature-to-fundus_distances'], 'float') #--------------------------------------------------------------------- # Construct a fundus-to-feature distance matrix and VTK file: #--------------------------------------------------------------------- # Construct a distance matrix: print('Construct a fundus-to-feature distance matrix...') sourceIDs = label_boundary_fundi targetIDs = features distances, distance_matrix = source_to_target_distances( sourceIDs, targetIDs, points, segmentIDs, excludeIDs) # Compute mean distances for each feature: nfeatures = min(np.shape(distance_matrix)[1], nsulcus_lists) for ifeature in range(nfeatures): fundus_distances = [x for x in distance_matrix[:, ifeature] if x != -1] fundus_to_feature_mean_distances[ifeature] = \ np.mean(fundus_distances) fundus_to_feature_sd_distances[ifeature] = \ np.std(fundus_distances) if verbose: print('Fundus-to-feature mean distances:') print(fundus_to_feature_mean_distances) print('Fundus-to-feature standard deviations of distances:') print(fundus_to_feature_sd_distances) # Write resulting feature-label boundary distances to VTK file: if output_vtk_name: fundus_to_feature_mean_distances_vtk = os.path.join(os.getcwd(), output_vtk_name + '_fundus_to_feature_mean_distances.vtk') print('Write fundus-to-feature distances to {0}...'. format(fundus_to_feature_mean_distances_vtk)) write_vtk(fundus_to_feature_mean_distances_vtk, points, [], [], sulcus_faces, [distances], ['fundus-to-feature_distances'], 'float') #------------------------------------------------------------------------- # Return outputs: #------------------------------------------------------------------------- return feature_to_fundus_mean_distances, feature_to_fundus_sd_distances,\ feature_to_fundus_mean_distances_vtk,\ fundus_to_feature_mean_distances, fundus_to_feature_sd_distances,\ fundus_to_feature_mean_distances_vtk
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 realign_boundaries_to_fundus_lines(surf_file, init_label_file, fundus_lines_file, thickness_file, out_label_file=None): """ Fix label boundaries to fundus lines. Parameters ---------- surf_file : file containing the surface geometry in vtk format init_label_file : file containing scalars that represent the initial guess at labels fundus_lines_file : file containing scalars representing fundus lines. thickness_file: file containing cortical thickness scalar data (for masking out the medial wall only) out_label_file : if specified, the realigned labels will be writen to this file Returns ------- numpy array representing the realigned label for each surface vertex. """ import numpy as np from mindboggle.utils.segment import extract_borders import mindboggle.utils.graph as go from mindboggle.utils.io_vtk import read_vtk, read_scalars, write_vtk from mindboggle.utils.mesh import find_neighbors import propagate_fundus_lines ## read files faces, _, indices, points, num_points, _, _, _ = read_vtk( surf_file, return_first=True, return_array=True) indices = range(num_points) init_labels, _ = read_scalars(init_label_file, return_first=True, return_array=True) fundus_lines, _ = read_scalars(fundus_lines_file, return_first=True, return_array=True) thickness, _ = read_scalars(thickness_file, return_first=True, return_array=True) # remove labels from vertices with zero thickness (get around # DKT40 annotations having the label '3' for all the Corpus # Callosum vertices). cc_inds = [x for x in indices if thickness[x] < 0.001] init_labels[cc_inds] = 0 ## setup seeds from initial label boundaries neighbor_lists = find_neighbors(faces, num_points) # extract all vertices that are on a boundary between labels boundary_indices, label_pairs, _ = extract_borders(indices, init_labels, neighbor_lists, return_label_pairs=True) # split boundary vertices into segments with common boundary pairs. boundary_segments = {} for boundary_index, label_pair in zip(boundary_indices, label_pairs): key = ((label_pair[0], label_pair[1]) if label_pair[0] < label_pair[1] else (label_pair[1], label_pair[0])) if key not in boundary_segments: boundary_segments[key] = [] boundary_segments[key].append(boundary_index) boundary_matrix, boundary_matrix_keys = _build_boundary_matrix( boundary_segments, num_points) # build the affinity matrix affinity_matrix = go.weight_graph(np.array(points), indices, np.array(faces), sigma=10, add_to_graph=False) ## propagate boundaries to fundus line vertices learned_matrix = _propagate_labels(affinity_matrix, boundary_matrix, boundary_indices, 100, 1) # assign labels to fundus line vertices based on highest probability new_boundaries = -1 * np.ones(init_labels.shape) fundus_line_indices = [i for i, x in enumerate(fundus_lines) if x > 0.5] # tile the surface into connected components delimited by fundus lines closed_fundus_lines, _, _ = propagate_fundus_lines.propagate_fundus_lines( points, faces, fundus_line_indices, thickness) closed_fundus_line_indices = np.where(closed_fundus_lines > 0)[0] # split surface into connected components connected_component_faces = _remove_boundary_faces( points, faces, closed_fundus_line_indices) # label components based on most probable label assignment new_labels = _label_components(connected_component_faces, num_points, boundary_indices, learned_matrix, boundary_matrix_keys) # propagate new labels to fill holes label_matrix, label_map = _build_label_matrix(new_labels) new_learned_matrix = _propagate_labels( affinity_matrix, label_matrix, [i for i in range(num_points) if new_labels[i] >= 0], 100, 1) # assign most probable labels for idx in [i for i in range(num_points) if new_labels[i] == -1]: max_idx = np.argmax(new_learned_matrix[idx]) new_labels[idx] = label_map[max_idx] # save if out_label_file is not None: write_vtk(out_label_file, points, faces=faces, scalars=[int(x) for x in new_labels], scalar_type='int') return new_labels
def evaluate_deep_features(features_file, labels_file, sulci_file='', hemi='', excludeIDs=[-1], output_vtk_name='', verbose=True): """ Evaluate deep surface features by computing the minimum distance from each label border vertex to all of the feature vertices in the same sulcus, and from each feature vertex to all of the label border vertices in the same sulcus. The label borders run along the deepest parts of sulci and correspond to fundi in the DKT cortical labeling protocol. Parameters ---------- features_file : string VTK surface file with feature numbers for vertex scalars labels_file : string VTK surface file with label numbers for vertex scalars sulci_file : string VTK surface file with sulcus numbers for vertex scalars excludeIDs : list of integers feature/sulcus/label IDs to exclude (background set to -1) output_vtk_name : Boolean if not empty, output a VTK file beginning with output_vtk_name that contains a surface with mean distances as scalars verbose : Boolean print mean distances to standard output? Returns ------- feature_to_border_mean_distances : numpy array [number of features x 1] mean distance from each feature to sulcus label border feature_to_border_sd_distances : numpy array [number of features x 1] standard deviations of feature-to-border distances feature_to_border_distances_vtk : string VTK surface file containing feature-to-border distances border_to_feature_mean_distances : numpy array [number of features x 1] mean distances from each sulcus label border to feature border_to_feature_sd_distances : numpy array [number of features x 1] standard deviations of border-to-feature distances border_to_feature_distances_vtk : string VTK surface file containing border-to-feature distances """ import os import sys import numpy as np from mindboggle.utils.io_vtk import read_vtk, read_scalars, write_vtk from mindboggle.utils.mesh import find_neighbors, remove_faces from mindboggle.utils.segment import extract_borders from mindboggle.utils.compute import source_to_target_distances from mindboggle.LABELS import DKTprotocol dkt = DKTprotocol() #------------------------------------------------------------------------- # Load labels, features, and sulci: #------------------------------------------------------------------------- faces, lines, indices, points, npoints, labels, scalar_names, \ input_vtk = read_vtk(labels_file, True, True) features, name = read_scalars(features_file, True, True) if sulci_file: sulci, name = read_scalars(sulci_file, True, True) # List of indices to sulcus vertices: sulcus_indices = [i for i, x in enumerate(sulci) if x != -1] segmentIDs = sulci sulcus_faces = remove_faces(faces, sulcus_indices) else: sulcus_indices = range(len(labels)) segmentIDs = [] sulcus_faces = faces #------------------------------------------------------------------------- # Prepare neighbors, label pairs, border IDs, and outputs: #------------------------------------------------------------------------- # Calculate neighbor lists for all points: print('Find neighbors for all vertices...') neighbor_lists = find_neighbors(faces, npoints) # Find label border points in any of the sulci: print('Find label border points in any of the sulci...') border_indices, border_label_tuples, unique_border_label_tuples = \ extract_borders(sulcus_indices, labels, neighbor_lists, ignore_values=[], return_label_pairs=True) if not len(border_indices): sys.exit('There are no label border points!') # Initialize an array of label border IDs # (label border vertices that define sulci in the labeling protocol): print('Build an array of label border IDs...') label_borders = -1 * np.ones(npoints) if hemi == 'lh': nsulcus_lists = len(dkt.left_sulcus_label_pair_lists) else: nsulcus_lists = len(dkt.right_sulcus_label_pair_lists) feature_to_border_mean_distances = -1 * np.ones(nsulcus_lists) feature_to_border_sd_distances = -1 * np.ones(nsulcus_lists) border_to_feature_mean_distances = -1 * np.ones(nsulcus_lists) border_to_feature_sd_distances = -1 * np.ones(nsulcus_lists) feature_to_border_distances_vtk = '' border_to_feature_distances_vtk = '' #------------------------------------------------------------------------- # Loop through sulci: #------------------------------------------------------------------------- # For each list of sorted label pairs (corresponding to a sulcus): for isulcus, label_pairs in enumerate(dkt.sulcus_label_pair_lists): # Keep the border points with label pair labels: label_pair_border_indices = [ x for i, x in enumerate(border_indices) if np.unique(border_label_tuples[i]).tolist() in label_pairs ] # Store the points as sulcus IDs in the border IDs array: if label_pair_border_indices: label_borders[label_pair_border_indices] = isulcus if len(np.unique(label_borders)) > 1: #--------------------------------------------------------------------- # Construct a feature-to-border distance matrix and VTK file: #--------------------------------------------------------------------- # Construct a distance matrix: print('Construct a feature-to-border distance matrix...') sourceIDs = features targetIDs = label_borders distances, distance_matrix = source_to_target_distances( sourceIDs, targetIDs, points, segmentIDs, excludeIDs) # Compute mean distances for each feature: nfeatures = min(np.shape(distance_matrix)[1], nsulcus_lists) for ifeature in range(nfeatures): feature_distances = [ x for x in distance_matrix[:, ifeature] if x != -1 ] feature_to_border_mean_distances[ifeature] = \ np.mean(feature_distances) feature_to_border_sd_distances[ifeature] = \ np.std(feature_distances) if verbose: print('Feature-to-border mean distances:') print(feature_to_border_mean_distances) print('Feature-to-border standard deviations of distances:') print(feature_to_border_sd_distances) # Write resulting feature-label border distances to VTK file: if output_vtk_name: feature_to_border_distances_vtk = os.path.join( os.getcwd(), output_vtk_name + '_feature_to_border_mean_distances.vtk') print('Write feature-to-border distances to {0}...'.format( feature_to_border_distances_vtk)) write_vtk(feature_to_border_distances_vtk, points, [], [], sulcus_faces, [distances], ['feature-to-border_distances'], 'float') #--------------------------------------------------------------------- # Construct a border-to-feature distance matrix and VTK file: #--------------------------------------------------------------------- # Construct a distance matrix: print('Construct a border-to-feature distance matrix...') sourceIDs = label_borders targetIDs = features distances, distance_matrix = source_to_target_distances( sourceIDs, targetIDs, points, segmentIDs, excludeIDs) # Compute mean distances for each feature: nfeatures = min(np.shape(distance_matrix)[1], nsulcus_lists) for ifeature in range(nfeatures): border_distances = [ x for x in distance_matrix[:, ifeature] if x != -1 ] border_to_feature_mean_distances[ifeature] = \ np.mean(border_distances) border_to_feature_sd_distances[ifeature] = \ np.std(border_distances) if verbose: print('border-to-feature mean distances:') print(border_to_feature_mean_distances) print('border-to-feature standard deviations of distances:') print(border_to_feature_sd_distances) # Write resulting feature-label border distances to VTK file: if output_vtk_name: border_to_feature_distances_vtk = os.path.join( os.getcwd(), output_vtk_name + '_border_to_feature_mean_distances.vtk') print('Write border-to-feature distances to {0}...'.format( border_to_feature_distances_vtk)) write_vtk(border_to_feature_distances_vtk, points, [], [], sulcus_faces, [distances], ['border-to-feature_distances'], 'float') #------------------------------------------------------------------------- # Return outputs: #------------------------------------------------------------------------- return feature_to_border_mean_distances, feature_to_border_sd_distances,\ feature_to_border_distances_vtk,\ border_to_feature_mean_distances, border_to_feature_sd_distances,\ border_to_feature_distances_vtk