def rescale_by_neighborhood(input_vtk, indices=[], nedges=10, p=99, set_max_to_1=True, save_file=False, output_filestring='rescaled_scalars', background_value=-1): """ Rescale the scalar values of a VTK file by a percentile value in each vertex's surface mesh neighborhood. Parameters ---------- input_vtk : string name of VTK file with a scalar value for each vertex indices : list of integers (optional) indices of scalars to normalize nedges : integer number or edges from vertex, defining the size of its neighborhood p : float in range of [0,100] percentile used to normalize each scalar set_max_to_1 : Boolean set all rescaled values greater than 1 to 1.0? save_file : Boolean save output VTK file? output_filestring : string (if save_file) name of output file background_value : integer background value Returns ------- rescaled_scalars : list of floats rescaled scalar values rescaled_scalars_file : string (if save_file) name of output VTK file with rescaled scalar values Examples -------- >>> import os >>> from mindboggle.guts.mesh import rescale_by_neighborhood >>> from mindboggle.mio.vtks import read_scalars, rewrite_scalars >>> from mindboggle.mio.plots import plot_surfaces >>> path = os.environ['MINDBOGGLE_DATA'] >>> input_vtk = os.path.join(path, 'arno', 'shapes', 'lh.pial.travel_depth.vtk') >>> indices = [] >>> nedges = 10 >>> p = 99 >>> set_max_to_1 = True >>> save_file = True >>> output_filestring = 'rescaled_scalars' >>> background_value = -1 >>> # >>> rescaled_scalars, rescaled_scalars_file = rescale_by_neighborhood(input_vtk, >>> indices, nedges, p, set_max_to_1, save_file, output_filestring, background_value) >>> # >>> # View rescaled scalar values per fold: >>> folds_file = os.path.join(path, 'arno', 'features', 'folds.vtk') >>> folds, name = read_scalars(folds_file) >>> # >>> rewrite_scalars(rescaled_scalars_file, rescaled_scalars_file, >>> rescaled_scalars, 'rescaled_depths', folds) >>> plot_surfaces(rescaled_scalars_file) """ import os import numpy as np from mindboggle.mio.vtks import read_scalars, rewrite_scalars from mindboggle.guts.mesh import find_neighbors_from_file, find_neighborhood # Load scalars and vertex neighbor lists: scalars, name = read_scalars(input_vtk, True, True) if not indices: indices = [i for i, x in enumerate(scalars) if x != background_value] print(" Rescaling {0} scalar values by neighborhood...".format( len(indices))) neighbor_lists = find_neighbors_from_file(input_vtk) # Loop through vertices: rescaled_scalars = scalars.copy() for index in indices: # Determine the scalars in the vertex's neighborhood: neighborhood = find_neighborhood(neighbor_lists, [index], nedges) # Compute a high neighborhood percentile to normalize vertex's value: normalization_factor = np.percentile(scalars[neighborhood], p) rescaled_scalar = scalars[index] / normalization_factor rescaled_scalars[index] = rescaled_scalar # Make any rescaled value greater than 1 equal to 1: if set_max_to_1: rescaled_scalars[[x for x in indices if rescaled_scalars[x] > 1.0]] = 1 rescaled_scalars = rescaled_scalars.tolist() #------------------------------------------------------------------------- # Return rescaled scalars and file name #------------------------------------------------------------------------- if save_file: rescaled_scalars_file = os.path.join(os.getcwd(), output_filestring + '.vtk') rewrite_scalars(input_vtk, rescaled_scalars_file, rescaled_scalars, 'rescaled_scalars') if not os.path.exists(rescaled_scalars_file): raise (IOError(rescaled_scalars_file + " not found")) else: rescaled_scalars_file = None return rescaled_scalars, rescaled_scalars_file
def concatenate_sulcus_scalars(scalar_files, fold_files, label_files, background_value=-1): """ Prepare data for estimating scalar distributions along and outside fundi. Extract (e.g., depth, curvature) scalar values in folds, along sulcus label boundaries as well as outside the sulcus label boundaries. Concatenate these scalar values across multiple files. Parameters ---------- scalar_files : list of strings names of surface mesh VTK files with scalar values to concatenate fold_files : list of strings (corr. to each list in scalar_files) VTK files with fold numbers as scalars (-1 for non-fold vertices) label_files : list of strings (corr. to fold_files) VTK files with label numbers (-1 for unlabeled vertices) background_value : integer or float background value Returns ------- border_scalars : list of floats concatenated scalar values within folds along sulcus label boundaries nonborder_scalars : list of floats concatenated scalar values within folds outside sulcus label boundaries Examples -------- >>> # Concatenate (duplicate) depth scalars: >>> import numpy as np >>> from mindboggle.shapes.likelihood import concatenate_sulcus_scalars >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> depth_file = fetch_data(urls['left_travel_depth'], '', '.vtk') >>> labels_file = fetch_data(urls['left_freesurfer_labels'], '', '.vtk') >>> folds_file = fetch_data(urls['left_folds'], '', '.vtk') >>> scalar_files = [depth_file, depth_file] >>> fold_files = [folds_file, folds_file] >>> label_files = [labels_file, labels_file] >>> background_value = -1 >>> border, nonborder = concatenate_sulcus_scalars(scalar_files, ... fold_files, label_files, background_value) >>> print(np.array_str(np.array(border[0:5]), ... precision=5, suppress_small=True)) [ 3.48284 2.57157 4.27596 4.56549 3.84881] >>> print(np.array_str(np.array(nonborder[0:5]), ... precision=5, suppress_small=True)) [ 2.87204 2.89388 3.55364 2.81681 3.70736] """ import numpy as np from mindboggle.mio.vtks import read_scalars from mindboggle.guts.mesh import find_neighbors_from_file from mindboggle.guts.segment import extract_borders from mindboggle.mio.labels import DKTprotocol dkt = DKTprotocol() # Prepare (non-unique) list of sulcus label pairs: protocol_label_pairs = [x for lst in dkt.sulcus_label_pair_lists for x in lst] border_scalars = [] nonborder_scalars = [] # Loop through files with the scalar values: for ifile, scalar_file in enumerate(scalar_files): #print(scalar_file) # Load scalars, folds, and labels: folds_file = fold_files[ifile] labels_file = label_files[ifile] scalars, name = read_scalars(scalar_file, True, True) if scalars.shape: folds, name = read_scalars(folds_file) labels, name = read_scalars(labels_file) indices_folds = [i for i,x in enumerate(folds) if x != background_value] neighbor_lists = find_neighbors_from_file(labels_file) # Find all label border pairs within the folds: indices_label_pairs, label_pairs, unique_pairs = extract_borders( indices_folds, labels, neighbor_lists, ignore_values=[-1], return_label_pairs=True) indices_label_pairs = np.array(indices_label_pairs) # Find vertices with label pairs in the sulcus labeling protocol: Ipairs_in_protocol = [i for i,x in enumerate(label_pairs) if x in protocol_label_pairs] indices_label_pairs = indices_label_pairs[Ipairs_in_protocol] indices_outside_pairs = list(frozenset(indices_folds).difference( indices_label_pairs)) # Store scalar values in folds along label border pairs: border_scalars.extend(scalars[indices_label_pairs].tolist()) # Store scalar values in folds outside label border pairs: nonborder_scalars.extend(scalars[indices_outside_pairs].tolist()) return border_scalars, nonborder_scalars
def concatenate_sulcus_scalars(scalar_files, fold_files, label_files, background_value=-1): """ Prepare data for estimating scalar distributions along and outside fundi. Extract (e.g., depth, curvature) scalar values in folds, along sulcus label boundaries as well as outside the sulcus label boundaries. Concatenate these scalar values across multiple files. Parameters ---------- scalar_files : list of strings names of surface mesh VTK files with scalar values to concatenate fold_files : list of strings (corr. to each list in scalar_files) VTK files with fold numbers as scalars (-1 for non-fold vertices) label_files : list of strings (corr. to fold_files) VTK files with label numbers (-1 for unlabeled vertices) background_value : integer or float background value Returns ------- border_scalars : list of floats concatenated scalar values within folds along sulcus label boundaries nonborder_scalars : list of floats concatenated scalar values within folds outside sulcus label boundaries Examples -------- >>> # Concatenate (duplicate) depth scalars: >>> import numpy as np >>> from mindboggle.shapes.likelihood import concatenate_sulcus_scalars >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> depth_file = fetch_data(urls['left_travel_depth'], '', '.vtk') >>> labels_file = fetch_data(urls['left_freesurfer_labels'], '', '.vtk') >>> folds_file = fetch_data(urls['left_folds'], '', '.vtk') >>> scalar_files = [depth_file, depth_file] >>> fold_files = [folds_file, folds_file] >>> label_files = [labels_file, labels_file] >>> background_value = -1 >>> border, nonborder = concatenate_sulcus_scalars(scalar_files, ... fold_files, label_files, background_value) >>> print(np.array_str(np.array(border[0:5]), ... precision=5, suppress_small=True)) [ 3.48284 2.57157 4.27596 4.56549 3.84881] >>> print(np.array_str(np.array(nonborder[0:5]), ... precision=5, suppress_small=True)) [ 2.87204 2.89388 3.55364 2.81681 3.70736] """ import numpy as np from mindboggle.mio.vtks import read_scalars from mindboggle.guts.mesh import find_neighbors_from_file from mindboggle.guts.segment import extract_borders from mindboggle.mio.labels import DKTprotocol dkt = DKTprotocol() # Prepare (non-unique) list of sulcus label pairs: protocol_label_pairs = [ x for lst in dkt.sulcus_label_pair_lists for x in lst ] border_scalars = [] nonborder_scalars = [] # Loop through files with the scalar values: for ifile, scalar_file in enumerate(scalar_files): #print(scalar_file) # Load scalars, folds, and labels: folds_file = fold_files[ifile] labels_file = label_files[ifile] scalars, name = read_scalars(scalar_file, True, True) if scalars.shape: folds, name = read_scalars(folds_file) labels, name = read_scalars(labels_file) indices_folds = [ i for i, x in enumerate(folds) if x != background_value ] neighbor_lists = find_neighbors_from_file(labels_file) # Find all label border pairs within the folds: indices_label_pairs, label_pairs, unique_pairs = extract_borders( indices_folds, labels, neighbor_lists, ignore_values=[-1], return_label_pairs=True) indices_label_pairs = np.array(indices_label_pairs) # Find vertices with label pairs in the sulcus labeling protocol: Ipairs_in_protocol = [ i for i, x in enumerate(label_pairs) if x in protocol_label_pairs ] indices_label_pairs = indices_label_pairs[Ipairs_in_protocol] indices_outside_pairs = list( frozenset(indices_folds).difference(indices_label_pairs)) # Store scalar values in folds along label border pairs: border_scalars.extend(scalars[indices_label_pairs].tolist()) # Store scalar values in folds outside label border pairs: nonborder_scalars.extend(scalars[indices_outside_pairs].tolist()) return border_scalars, nonborder_scalars
def extract_fundi(folds, curv_file, depth_file, min_separation=10, erode_ratio=0.1, erode_min_size=1, save_file=False, output_file='', background_value=-1, verbose=False): """ Extract fundi from folds. A fundus is a branching curve that runs along the deepest and most highly curved portions of a fold. This function extracts one fundus from each fold by finding the deepest vertices inside the fold, finding endpoints along the edge of the fold, and connecting the former to the latter with tracks that run along deep and curved paths (through vertices with high values of travel depth multiplied by curvature), and a final filtration step. The deepest vertices are those with values at least two median absolute deviations above the median (non-zero) value, with the higher value chosen if two of the vertices are within (a default of) 10 edges from each other (to reduce the number of possible fundus paths as well as computation time). To find the endpoints, the find_outer_endpoints function propagates multiple tracks from seed vertices at median depth in the fold through concentric rings toward the fold’s edge, selecting maximal values within each ring, and terminating at candidate endpoints. The final endpoints are those candidates at the end of tracks that have a high median value, with the higher value chosen if two candidate endpoints are within (a default of) 10 edges from each other (otherwise, the resulting fundi can have spurious branching at the fold’s edge). The connect_points_erosion function connects the deepest fold vertices to the endpoints with a skeleton of 1-vertex-thick curves by erosion. It erodes by iteratively removing simple topological points and endpoints in order of lowest to highest values, where a simple topological point is a vertex that when added to or removed from an object on a surface mesh (such as a fundus curve) does not alter the object's topology. Steps :: 1. Find fundus endpoints (outer anchors) with find_outer_endpoints(). 2. Include inner anchor points. 3. Connect anchor points using connect_points_erosion(); inner anchors are removed if they result in endpoints. Note :: Follow this with segment_by_region() to segment fundi by sulci. Parameters ---------- folds : numpy array or list of integers fold number for each vertex curv_file : string surface mesh file in VTK format with mean curvature values depth_file : string surface mesh file in VTK format with rescaled depth values likelihoods : list of integers fundus likelihood value for each vertex min_separation : integer minimum number of edges between inner/outer anchor points erode_ratio : float fraction of indices to test for removal at each iteration in connect_points_erosion() save_file : bool save output VTK file? output_file : string output VTK file background_value : integer or float background value verbose : bool print statements? Returns ------- fundus_per_fold : list of integers fundus numbers for all vertices, labeled by fold (-1 for non-fundus vertices) n_fundi_in_folds : integer number of fundi fundus_per_fold_file : string (if save_file) output VTK file with fundus numbers (-1 for non-fundus vertices) Examples -------- >>> # Extract fundus from one or more folds: >>> import numpy as np >>> from mindboggle.mio.vtks import read_scalars >>> from mindboggle.features.fundi import extract_fundi >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> curv_file = fetch_data(urls['left_mean_curvature'], '', '.vtk') >>> depth_file = fetch_data(urls['left_travel_depth'], '', '.vtk') >>> folds_file = fetch_data(urls['left_folds'], '', '.vtk') >>> folds, name = read_scalars(folds_file, True, True) >>> # Limit number of folds to speed up the test: >>> limit_folds = True >>> if limit_folds: ... fold_numbers = [4] #[4, 6] ... i0 = [i for i,x in enumerate(folds) if x not in fold_numbers] ... folds[i0] = -1 >>> min_separation = 10 >>> erode_ratio = 0.10 >>> erode_min_size = 10 >>> save_file = True >>> output_file = 'extract_fundi_fold4.vtk' >>> background_value = -1 >>> verbose = False >>> o1, o2, fundus_per_fold_file = extract_fundi(folds, curv_file, ... depth_file, min_separation, erode_ratio, erode_min_size, ... save_file, output_file, background_value, verbose) >>> lens = [len([x for x in o1 if x == y]) ... for y in np.unique(o1) if y != background_value] >>> lens[0:10] # [66, 2914, 100, 363, 73, 331, 59, 30, 1, 14] # (if not limit_folds) [73] View result without background (skip test): >>> from mindboggle.mio.plots import plot_surfaces # doctest: +SKIP >>> from mindboggle.mio.vtks import rewrite_scalars # doctest: +SKIP >>> rewrite_scalars(fundus_per_fold_file, ... 'extract_fundi_fold4_no_background.vtk', o1, ... 'fundus_per_fold', folds) # doctest: +SKIP >>> plot_surfaces('extract_fundi_fold4_no_background.vtk') # doctest: +SKIP """ # Extract a skeleton to connect endpoints in a fold: import os import numpy as np from time import time from mindboggle.mio.vtks import read_scalars, read_vtk, rewrite_scalars from mindboggle.guts.compute import median_abs_dev from mindboggle.guts.paths import find_max_values from mindboggle.guts.mesh import find_neighbors_from_file from mindboggle.guts.mesh import find_complete_faces from mindboggle.guts.paths import find_outer_endpoints from mindboggle.guts.paths import connect_points_erosion if isinstance(folds, list): folds = np.array(folds) # Load values, inner anchor threshold, and neighbors: if os.path.isfile(curv_file): points, indices, lines, faces, curvs, scalar_names, npoints, \ input_vtk = read_vtk(curv_file, True, True) else: raise IOError("{0} doesn't exist!".format(curv_file)) if os.path.isfile(curv_file): depths, name = read_scalars(depth_file, True, True) else: raise IOError("{0} doesn't exist!".format(depth_file)) values = curvs * depths values0 = [x for x in values if x > 0] thr = np.median(values0) + 2 * median_abs_dev(values0) neighbor_lists = find_neighbors_from_file(curv_file) # ------------------------------------------------------------------------ # Loop through folds: # ------------------------------------------------------------------------ t1 = time() skeletons = [] unique_fold_IDs = [x for x in np.unique(folds) if x != background_value] if verbose: if len(unique_fold_IDs) == 1: print("Extract a fundus from 1 fold...") else: print("Extract a fundus from each of {0} folds...". format(len(unique_fold_IDs))) for fold_ID in unique_fold_IDs: indices_fold = [i for i,x in enumerate(folds) if x == fold_ID] if indices_fold: if verbose: print(' Fold {0}:'.format(int(fold_ID))) # ---------------------------------------------------------------- # Find outer anchor points on the boundary of the surface region, # to serve as fundus endpoints: # ---------------------------------------------------------------- outer_anchors, tracks = find_outer_endpoints(indices_fold, neighbor_lists, values, depths, min_separation, background_value, verbose) # ---------------------------------------------------------------- # Find inner anchor points: # ---------------------------------------------------------------- inner_anchors = find_max_values(points, values, min_separation, thr) # ---------------------------------------------------------------- # Connect anchor points to create skeleton: # ---------------------------------------------------------------- B = background_value * np.ones(npoints) B[indices_fold] = 1 skeleton = connect_points_erosion(B, neighbor_lists, outer_anchors, inner_anchors, values, erode_ratio, erode_min_size, [], '', background_value, verbose) if skeleton: skeletons.extend(skeleton) ## --------------------------------------------------------------- ## Remove fundus vertices if they make complete triangle faces: ## --------------------------------------------------------------- #Iremove = find_complete_faces(skeletons, faces) #if Iremove: # skeletons = list(frozenset(skeletons).difference(Iremove)) indices_skel = [x for x in skeletons if folds[x] != background_value] fundus_per_fold = background_value * np.ones(npoints) fundus_per_fold[indices_skel] = folds[indices_skel] n_fundi_in_folds = len([x for x in np.unique(fundus_per_fold) if x != background_value]) if n_fundi_in_folds == 1: sdum = 'fold fundus' else: sdum = 'fold fundi' if verbose: print(' ...Extracted {0} {1}; {2} total ({3:.2f} seconds)'. format(n_fundi_in_folds, sdum, n_fundi_in_folds, time() - t1)) # ------------------------------------------------------------------------ # Return fundi, number of fundi, and file name: # ------------------------------------------------------------------------ fundus_per_fold_file = None if n_fundi_in_folds > 0: fundus_per_fold = [int(x) for x in fundus_per_fold] if save_file: if output_file: fundus_per_fold_file = output_file else: fundus_per_fold_file = os.path.join(os.getcwd(), 'fundus_per_fold.vtk') rewrite_scalars(curv_file, fundus_per_fold_file, fundus_per_fold, 'fundi', [], background_value) if not os.path.exists(fundus_per_fold_file): raise IOError(fundus_per_fold_file + " not found") return fundus_per_fold, n_fundi_in_folds, fundus_per_fold_file
def concatenate_sulcus_scalars(scalar_files, fold_files, label_files): """ Prepare data for estimating scalar distributions along and outside fundi. Extract (e.g., depth, curvature) scalar values in folds, along sulcus label boundaries as well as outside the sulcus label boundaries. Concatenate these scalar values across multiple files. Parameters ---------- scalar_files : list of strings names of surface mesh VTK files with scalar values to concatenate fold_files : list of strings (corr. to each list in scalar_files) VTK files with fold numbers as scalars (-1 for non-fold vertices) label_files : list of strings (corr. to fold_files) VTK files with label numbers (-1 for unlabeled vertices) Returns ------- border_scalars : list of floats concatenated scalar values within folds along sulcus label boundaries nonborder_scalars : list of floats concatenated scalar values within folds outside sulcus label boundaries Examples -------- >>> # Concatenate (duplicate) depth scalars: >>> import os >>> from mindboggle.shapes.likelihood import concatenate_sulcus_scalars >>> path = os.environ['MINDBOGGLE_DATA'] >>> depth_file = os.path.join(path, 'arno', 'shapes', 'depth_rescaled.vtk') >>> folds_file = os.path.join(path, 'arno', 'features', 'folds.vtk') >>> labels_file = os.path.join(path, 'arno', 'labels', 'lh.labels.DKT25.manual.vtk') >>> scalar_files = [depth_file, depth_file] >>> fold_files = [folds_file, folds_file] >>> label_files = [labels_file, labels_file] >>> # >>> S = concatenate_sulcus_scalars(scalar_files, fold_files, label_files) """ import numpy as np from mindboggle.mio.vtks import read_scalars from mindboggle.guts.mesh import find_neighbors_from_file from mindboggle.guts.segment import extract_borders from mindboggle.mio.labels import DKTprotocol dkt = DKTprotocol() # Prepare (non-unique) list of sulcus label pairs: protocol_label_pairs = [x for lst in dkt.sulcus_label_pair_lists for x in lst] border_scalars = [] nonborder_scalars = [] # Loop through files with the scalar values: for ifile, scalar_file in enumerate(scalar_files): print(scalar_file) # Load scalars, folds, and labels: folds_file = fold_files[ifile] labels_file = label_files[ifile] scalars, name = read_scalars(scalar_file, True, True) if scalars.shape: folds, name = read_scalars(folds_file) labels, name = read_scalars(labels_file) indices_folds = [i for i,x in enumerate(folds) if x != -1] neighbor_lists = find_neighbors_from_file(labels_file) # Find all label border pairs within the folds: indices_label_pairs, label_pairs, unique_pairs = extract_borders( indices_folds, labels, neighbor_lists, ignore_values=[-1], return_label_pairs=True) indices_label_pairs = np.array(indices_label_pairs) # Find vertices with label pairs in the sulcus labeling protocol: Ipairs_in_protocol = [i for i,x in enumerate(label_pairs) if x in protocol_label_pairs] indices_label_pairs = indices_label_pairs[Ipairs_in_protocol] indices_outside_pairs = list(frozenset(indices_folds).difference( indices_label_pairs)) # Store scalar values in folds along label border pairs: border_scalars.extend(scalars[indices_label_pairs].tolist()) # Store scalar values in folds outside label border pairs: nonborder_scalars.extend(scalars[indices_outside_pairs].tolist()) return border_scalars, nonborder_scalars
def extract_fundi(folds, curv_file, depth_file, min_separation=10, erode_ratio=0.1, erode_min_size=1, save_file=False, verbose=False): """ Extract fundi from folds. A fundus is a branching curve that runs along the deepest and most highly curved portions of a fold. Steps :: 1. Find fundus endpoints (outer anchors) with find_outer_anchors(). 2. Include inner anchor points. 3. Connect anchor points using connect_points_erosion(); inner anchors are removed if they result in endpoints. Parameters ---------- folds : numpy array or list of integers fold number for each vertex curv_file : string surface mesh file in VTK format with mean curvature values depth_file : string surface mesh file in VTK format with rescaled depth values likelihoods : list of integers fundus likelihood value for each vertex min_separation : integer minimum number of edges between inner/outer anchor points erode_ratio : float fraction of indices to test for removal at each iteration in connect_points_erosion() save_file : bool save output VTK file? verbose : bool print statements? Returns ------- fundus_per_fold : list of integers fundus numbers for all vertices, labeled by fold (-1 for non-fundus vertices) n_fundi_in_folds : integer number of fundi fundus_per_fold_file : string (if save_file) output VTK file with fundus numbers (-1 for non-fundus vertices) Examples -------- >>> # Extract fundus from one or more folds: >>> single_fold = True >>> from mindboggle.mio.vtks import read_scalars >>> from mindboggle.features.fundi import extract_fundi >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> curv_file = fetch_data(urls['left_mean_curvature']) >>> depth_file = fetch_data(urls['left_travel_depth']) >>> folds_file = fetch_data(urls['left_folds']) >>> folds, name = read_scalars(folds_file, True, True) >>> if single_fold: ... fold_number = 2 #11 ... folds[folds != fold_number] = -1 >>> min_separation = 10 >>> erode_ratio = 0.10 >>> erode_min_size = 10 >>> save_file = True >>> verbose = False >>> o1, o2, fundus_per_fold_file = extract_fundi(folds, curv_file, ... depth_file, min_separation, erode_ratio, erode_min_size, ... save_file, verbose) >>> if single_fold: ... lens = [len([x for x in o1 if x == 2])] ... else: ... lens = [len([x for x in o1 if x == y]) for y in range(o2)] >>> lens[0:10] [115] View result (skip test): >>> from mindboggle.mio.plots import plot_surfaces >>> plot_surfaces(fundus_per_fold_file) # doctest: +SKIP """ # Extract a skeleton to connect endpoints in a fold: import os import numpy as np from time import time from mindboggle.mio.vtks import read_scalars, read_vtk, rewrite_scalars from mindboggle.guts.compute import median_abs_dev from mindboggle.guts.paths import find_max_values from mindboggle.guts.mesh import find_neighbors_from_file from mindboggle.guts.mesh import find_complete_faces from mindboggle.guts.paths import find_outer_anchors from mindboggle.guts.paths import connect_points_erosion if isinstance(folds, list): folds = np.array(folds) # Load values, inner anchor threshold, and neighbors: points, indices, lines, faces, curvs, scalar_names, npoints, \ input_vtk = read_vtk(curv_file, True, True) depths, name = read_scalars(depth_file, True, True) values = curvs * depths values0 = [x for x in values if x > 0] thr = np.median(values0) + 2 * median_abs_dev(values0) neighbor_lists = find_neighbors_from_file(curv_file) #------------------------------------------------------------------------- # Loop through folds: #------------------------------------------------------------------------- t1 = time() skeletons = [] unique_fold_IDs = [x for x in np.unique(folds) if x != -1] if verbose: if len(unique_fold_IDs) == 1: print("Extract a fundus from 1 fold...") else: print("Extract a fundus from each of {0} folds...".format( len(unique_fold_IDs))) for fold_ID in unique_fold_IDs: indices_fold = [i for i, x in enumerate(folds) if x == fold_ID] if indices_fold: if verbose: print(' Fold {0}:'.format(int(fold_ID))) #----------------------------------------------------------------- # Find outer anchor points on the boundary of the surface region, # to serve as fundus endpoints: #----------------------------------------------------------------- verbose = False outer_anchors, tracks = find_outer_anchors(indices_fold, neighbor_lists, values, depths, min_separation, verbose) #----------------------------------------------------------------- # Find inner anchor points: #----------------------------------------------------------------- inner_anchors = find_max_values(points, values, min_separation, thr) #----------------------------------------------------------------- # Connect anchor points to create skeleton: #----------------------------------------------------------------- B = -1 * np.ones(npoints) B[indices_fold] = 1 skeleton = connect_points_erosion(B, neighbor_lists, outer_anchors, inner_anchors, values, erode_ratio, erode_min_size, save_steps=[], save_vtk='', verbose=False) if skeleton: skeletons.extend(skeleton) #----------------------------------------------------------------- # Remove fundus vertices if they complete triangle faces: #----------------------------------------------------------------- Iremove = find_complete_faces(skeletons, faces) if Iremove: skeletons = list(frozenset(skeletons).difference(Iremove)) indices_skel = [x for x in skeletons if folds[x] != -1] fundus_per_fold = -1 * np.ones(npoints) fundus_per_fold[indices_skel] = folds[indices_skel] n_fundi_in_folds = len([x for x in np.unique(fundus_per_fold) if x != -1]) if n_fundi_in_folds == 1: sdum = 'fold fundus' else: sdum = 'fold fundi' if verbose: print(' ...Extracted {0} {1}; {2} total ({3:.2f} seconds)'.format( n_fundi_in_folds, sdum, n_fundi_in_folds, time() - t1)) #------------------------------------------------------------------------- # Return fundi, number of fundi, and file name: #------------------------------------------------------------------------- if n_fundi_in_folds > 0: fundus_per_fold = [int(x) for x in fundus_per_fold] if save_file: fundus_per_fold_file = os.path.join(os.getcwd(), 'fundus_per_fold.vtk') rewrite_scalars(curv_file, fundus_per_fold_file, fundus_per_fold, 'fundi', folds) if not os.path.exists(fundus_per_fold_file): raise IOError(fundus_per_fold_file + " not found") else: fundus_per_fold_file = None return fundus_per_fold, n_fundi_in_folds, fundus_per_fold_file
def extract_fundi(folds, curv_file, depth_file, min_separation=10, erode_ratio=0.1, erode_min_size=1, save_file=False, output_file='', background_value=-1, verbose=False): """ Extract fundi from folds. A fundus is a branching curve that runs along the deepest and most highly curved portions of a fold. This function extracts one fundus from each fold by finding the deepest vertices inside the fold, finding endpoints along the edge of the fold, and connecting the former to the latter with tracks that run along deep and curved paths (through vertices with high values of travel depth multiplied by curvature), and a final filtration step. The deepest vertices are those with values at least two median absolute deviations above the median (non-zero) value, with the higher value chosen if two of the vertices are within (a default of) 10 edges from each other (to reduce the number of possible fundus paths as well as computation time). To find the endpoints, the find_outer_endpoints function propagates multiple tracks from seed vertices at median depth in the fold through concentric rings toward the fold’s edge, selecting maximal values within each ring, and terminating at candidate endpoints. The final endpoints are those candidates at the end of tracks that have a high median value, with the higher value chosen if two candidate endpoints are within (a default of) 10 edges from each other (otherwise, the resulting fundi can have spurious branching at the fold’s edge). The connect_points_erosion function connects the deepest fold vertices to the endpoints with a skeleton of 1-vertex-thick curves by erosion. It erodes by iteratively removing simple topological points and endpoints in order of lowest to highest values, where a simple topological point is a vertex that when added to or removed from an object on a surface mesh (such as a fundus curve) does not alter the object's topology. Steps :: 1. Find fundus endpoints (outer anchors) with find_outer_endpoints(). 2. Include inner anchor points. 3. Connect anchor points using connect_points_erosion(); inner anchors are removed if they result in endpoints. Note :: Follow this with segment_by_region() to segment fundi by sulci. Parameters ---------- folds : numpy array or list of integers fold number for each vertex curv_file : string surface mesh file in VTK format with mean curvature values depth_file : string surface mesh file in VTK format with rescaled depth values likelihoods : list of integers fundus likelihood value for each vertex min_separation : integer minimum number of edges between inner/outer anchor points erode_ratio : float fraction of indices to test for removal at each iteration in connect_points_erosion() save_file : bool save output VTK file? output_file : string output VTK file background_value : integer or float background value verbose : bool print statements? Returns ------- fundus_per_fold : list of integers fundus numbers for all vertices, labeled by fold (-1 for non-fundus vertices) n_fundi_in_folds : integer number of fundi fundus_per_fold_file : string (if save_file) output VTK file with fundus numbers (-1 for non-fundus vertices) Examples -------- >>> # Extract fundus from one or more folds: >>> import numpy as np >>> from mindboggle.mio.vtks import read_scalars >>> from mindboggle.features.fundi import extract_fundi >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> curv_file = fetch_data(urls['left_mean_curvature'], '', '.vtk') >>> depth_file = fetch_data(urls['left_travel_depth'], '', '.vtk') >>> folds_file = fetch_data(urls['left_folds'], '', '.vtk') >>> folds, name = read_scalars(folds_file, True, True) >>> # Limit number of folds to speed up the test: >>> limit_folds = True >>> if limit_folds: ... fold_numbers = [4] #[4, 6] ... i0 = [i for i,x in enumerate(folds) if x not in fold_numbers] ... folds[i0] = -1 >>> min_separation = 10 >>> erode_ratio = 0.10 >>> erode_min_size = 10 >>> save_file = True >>> output_file = 'extract_fundi_fold4.vtk' >>> background_value = -1 >>> verbose = False >>> o1, o2, fundus_per_fold_file = extract_fundi(folds, curv_file, ... depth_file, min_separation, erode_ratio, erode_min_size, ... save_file, output_file, background_value, verbose) >>> lens = [len([x for x in o1 if x == y]) ... for y in np.unique(o1) if y != background_value] >>> lens[0:10] # [66, 2914, 100, 363, 73, 331, 59, 30, 1, 14] # (if not limit_folds) [73] View result without background (skip test): >>> from mindboggle.mio.plots import plot_surfaces # doctest: +SKIP >>> from mindboggle.mio.vtks import rewrite_scalars # doctest: +SKIP >>> rewrite_scalars(fundus_per_fold_file, ... 'extract_fundi_fold4_no_background.vtk', o1, ... 'fundus_per_fold', folds) # doctest: +SKIP >>> plot_surfaces('extract_fundi_fold4_no_background.vtk') # doctest: +SKIP """ # Extract a skeleton to connect endpoints in a fold: import os import numpy as np from time import time from mindboggle.mio.vtks import read_scalars, read_vtk, rewrite_scalars from mindboggle.guts.compute import median_abs_dev from mindboggle.guts.paths import find_max_values from mindboggle.guts.mesh import find_neighbors_from_file #from mindboggle.guts.mesh import find_complete_faces from mindboggle.guts.paths import find_outer_endpoints from mindboggle.guts.paths import connect_points_erosion if isinstance(folds, list): folds = np.array(folds) # Load values, inner anchor threshold, and neighbors: if os.path.isfile(curv_file): points, indices, lines, faces, curvs, scalar_names, npoints, \ input_vtk = read_vtk(curv_file, True, True) else: raise IOError("{0} doesn't exist!".format(curv_file)) if os.path.isfile(curv_file): depths, name = read_scalars(depth_file, True, True) else: raise IOError("{0} doesn't exist!".format(depth_file)) values = curvs * depths values0 = [x for x in values if x > 0] thr = np.median(values0) + 2 * median_abs_dev(values0) neighbor_lists = find_neighbors_from_file(curv_file) # ------------------------------------------------------------------------ # Loop through folds: # ------------------------------------------------------------------------ t1 = time() skeletons = [] unique_fold_IDs = [x for x in np.unique(folds) if x != background_value] if verbose: if len(unique_fold_IDs) == 1: print("Extract a fundus from 1 fold...") else: print("Extract a fundus from each of {0} folds...".format( len(unique_fold_IDs))) for fold_ID in unique_fold_IDs: indices_fold = [i for i, x in enumerate(folds) if x == fold_ID] if indices_fold: if verbose: print(' Fold {0}:'.format(int(fold_ID))) # ---------------------------------------------------------------- # Find outer anchor points on the boundary of the surface region, # to serve as fundus endpoints: # ---------------------------------------------------------------- outer_anchors, tracks = find_outer_endpoints( indices_fold, neighbor_lists, values, depths, min_separation, background_value, verbose) # ---------------------------------------------------------------- # Find inner anchor points: # ---------------------------------------------------------------- inner_anchors = find_max_values(points, values, min_separation, thr) # ---------------------------------------------------------------- # Connect anchor points to create skeleton: # ---------------------------------------------------------------- B = background_value * np.ones(npoints) B[indices_fold] = 1 skeleton = connect_points_erosion(B, neighbor_lists, outer_anchors, inner_anchors, values, erode_ratio, erode_min_size, [], '', background_value, verbose) if skeleton: skeletons.extend(skeleton) ## --------------------------------------------------------------- ## Remove fundus vertices if they make complete triangle faces: ## --------------------------------------------------------------- #Iremove = find_complete_faces(skeletons, faces) #if Iremove: # skeletons = list(frozenset(skeletons).difference(Iremove)) indices_skel = [x for x in skeletons if folds[x] != background_value] fundus_per_fold = background_value * np.ones(npoints) fundus_per_fold[indices_skel] = folds[indices_skel] n_fundi_in_folds = len( [x for x in np.unique(fundus_per_fold) if x != background_value]) if n_fundi_in_folds == 1: sdum = 'fold fundus' else: sdum = 'fold fundi' if verbose: print(' ...Extracted {0} {1}; {2} total ({3:.2f} seconds)'.format( n_fundi_in_folds, sdum, n_fundi_in_folds, time() - t1)) # ------------------------------------------------------------------------ # Return fundi, number of fundi, and file name: # ------------------------------------------------------------------------ fundus_per_fold_file = None if n_fundi_in_folds > 0: fundus_per_fold = [int(x) for x in fundus_per_fold] if save_file: if output_file: fundus_per_fold_file = output_file else: fundus_per_fold_file = os.path.join(os.getcwd(), 'fundus_per_fold.vtk') rewrite_scalars(curv_file, fundus_per_fold_file, fundus_per_fold, 'fundi', [], background_value) if not os.path.exists(fundus_per_fold_file): raise IOError(fundus_per_fold_file + " not found") return fundus_per_fold, n_fundi_in_folds, fundus_per_fold_file
def extract_fundi(folds, curv_file, depth_file, min_separation=10, erode_ratio=0.1, erode_min_size=1, save_file=False): """ Extract fundi from folds. A fundus is a branching curve that runs along the deepest and most highly curved portions of a fold. Steps :: 1. Find fundus endpoints (outer anchors) with find_outer_anchors(). 2. Include inner anchor points. 3. Connect anchor points using connect_points_erosion(); inner anchors are removed if they result in endpoints. Parameters ---------- folds : numpy array or list of integers fold number for each vertex curv_file : string surface mesh file in VTK format with mean curvature values depth_file : string surface mesh file in VTK format with rescaled depth values likelihoods : list of integers fundus likelihood value for each vertex min_separation : integer minimum number of edges between inner/outer anchor points erode_ratio : float fraction of indices to test for removal at each iteration in connect_points_erosion() save_file : Boolean save output VTK file? Returns ------- fundus_per_fold : list of integers fundus numbers for all vertices, labeled by fold (-1 for non-fundus vertices) n_fundi_in_folds : integer number of fundi fundus_per_fold_file : string (if save_file) output VTK file with fundus numbers (-1 for non-fundus vertices) Examples -------- >>> # Extract fundus from one or more folds: >>> single_fold = True >>> import os >>> from mindboggle.mio.vtks import read_scalars >>> from mindboggle.features.fundi import extract_fundi >>> from mindboggle.mio.plots import plot_surfaces >>> path = os.environ['MINDBOGGLE_DATA'] >>> curv_file = os.path.join(path, 'arno', 'shapes', 'lh.pial.mean_curvature.vtk') >>> depth_file = os.path.join(path, 'arno', 'shapes', 'travel_depth_rescaled.vtk') >>> folds_file = os.path.join(path, 'arno', 'features', 'folds.vtk') >>> folds, name = read_scalars(folds_file, True, True) >>> if single_fold: >>> fold_number = 2 #11 >>> folds[folds != fold_number] = -1 >>> min_separation = 10 >>> erode_ratio = 0.10 >>> erode_min_size = 10 >>> save_file = True >>> o1, o2, fundus_per_fold_file = extract_fundi(folds, curv_file, ... depth_file, min_separation, erode_ratio, erode_min_size, save_file) >>> # >>> # View: >>> plot_surfaces(fundi_file) """ # Extract a skeleton to connect endpoints in a fold: import os import numpy as np from time import time from mindboggle.mio.vtks import read_scalars, read_vtk, rewrite_scalars from mindboggle.guts.compute import median_abs_dev from mindboggle.guts.paths import find_max_values from mindboggle.guts.mesh import find_neighbors_from_file, find_complete_faces from mindboggle.guts.paths import find_outer_anchors, connect_points_erosion if isinstance(folds, list): folds = np.array(folds) # Load values, inner anchor threshold, and neighbors: points, indices, lines, faces, curvs, scalar_names, npoints, \ input_vtk = read_vtk(curv_file, True, True) depths, name = read_scalars(depth_file, True, True) values = curvs * depths values0 = [x for x in values if x > 0] thr = np.median(values0) + 2 * median_abs_dev(values0) neighbor_lists = find_neighbors_from_file(curv_file) #------------------------------------------------------------------------- # Loop through folds: #------------------------------------------------------------------------- t1 = time() skeletons = [] unique_fold_IDs = [x for x in np.unique(folds) if x != -1] if len(unique_fold_IDs) == 1: print("Extract a fundus from 1 fold...") else: print("Extract a fundus from each of {0} folds...". format(len(unique_fold_IDs))) for fold_ID in unique_fold_IDs: indices_fold = [i for i,x in enumerate(folds) if x == fold_ID] if indices_fold: print(' Fold {0}:'.format(int(fold_ID))) #----------------------------------------------------------------- # Find outer anchor points on the boundary of the surface region, # to serve as fundus endpoints: #----------------------------------------------------------------- outer_anchors, tracks = find_outer_anchors(indices_fold, neighbor_lists, values, depths, min_separation) #----------------------------------------------------------------- # Find inner anchor points: #----------------------------------------------------------------- inner_anchors = find_max_values(points, values, min_separation, thr) #----------------------------------------------------------------- # Connect anchor points to create skeleton: #----------------------------------------------------------------- B = -1 * np.ones(npoints) B[indices_fold] = 1 skeleton = connect_points_erosion(B, neighbor_lists, outer_anchors, inner_anchors, values, erode_ratio, erode_min_size, save_steps=[], save_vtk='') if skeleton: skeletons.extend(skeleton) #----------------------------------------------------------------- # Remove fundus vertices if they complete triangle faces: #----------------------------------------------------------------- Iremove = find_complete_faces(skeletons, faces) if Iremove: skeletons = list(frozenset(skeletons).difference(Iremove)) indices_skel = [x for x in skeletons if folds[x] != -1] fundus_per_fold = -1 * np.ones(npoints) fundus_per_fold[indices_skel] = folds[indices_skel] n_fundi_in_folds = len([x for x in np.unique(fundus_per_fold) if x != -1]) if n_fundi_in_folds == 1: sdum = 'fold fundus' else: sdum = 'fold fundi' print(' ...Extracted {0} {1}; {2} total ({3:.2f} seconds)'. format(n_fundi_in_folds, sdum, n_fundi_in_folds, time() - t1)) #------------------------------------------------------------------------- # Return fundi, number of fundi, and file name: #------------------------------------------------------------------------- if n_fundi_in_folds > 0: fundus_per_fold = [int(x) for x in fundus_per_fold] if save_file: fundus_per_fold_file = os.path.join(os.getcwd(), 'fundus_per_fold.vtk') rewrite_scalars(curv_file, fundus_per_fold_file, fundus_per_fold, 'fundi', folds) if not os.path.exists(fundus_per_fold_file): raise(IOError(fundus_per_fold_file + " not found")) else: fundus_per_fold_file = None return fundus_per_fold, n_fundi_in_folds, fundus_per_fold_file
def rescale_by_neighborhood(input_vtk, indices=[], nedges=10, p=99, set_max_to_1=True, save_file=False, output_filestring='rescaled_scalars', background_value=-1): """ Rescale the scalar values of a VTK file by a percentile value in each vertex's surface mesh neighborhood. Parameters ---------- input_vtk : string name of VTK file with a scalar value for each vertex indices : list of integers (optional) indices of scalars to normalize nedges : integer number or edges from vertex, defining the size of its neighborhood p : float in range of [0,100] percentile used to normalize each scalar set_max_to_1 : Boolean set all rescaled values greater than 1 to 1.0? save_file : Boolean save output VTK file? output_filestring : string (if save_file) name of output file background_value : integer background value Returns ------- rescaled_scalars : list of floats rescaled scalar values rescaled_scalars_file : string (if save_file) name of output VTK file with rescaled scalar values Examples -------- >>> import os >>> from mindboggle.guts.mesh import rescale_by_neighborhood >>> from mindboggle.mio.vtks import read_scalars, rewrite_scalars >>> from mindboggle.mio.plots import plot_surfaces >>> path = os.environ['MINDBOGGLE_DATA'] >>> input_vtk = os.path.join(path, 'arno', 'shapes', 'lh.pial.travel_depth.vtk') >>> indices = [] >>> nedges = 10 >>> p = 99 >>> set_max_to_1 = True >>> save_file = True >>> output_filestring = 'rescaled_scalars' >>> background_value = -1 >>> # >>> rescaled_scalars, rescaled_scalars_file = rescale_by_neighborhood(input_vtk, >>> indices, nedges, p, set_max_to_1, save_file, output_filestring, background_value) >>> # >>> # View rescaled scalar values per fold: >>> folds_file = os.path.join(path, 'arno', 'features', 'folds.vtk') >>> folds, name = read_scalars(folds_file) >>> # >>> rewrite_scalars(rescaled_scalars_file, rescaled_scalars_file, >>> rescaled_scalars, 'rescaled_depths', folds) >>> plot_surfaces(rescaled_scalars_file) """ import os import numpy as np from mindboggle.mio.vtks import read_scalars, rewrite_scalars from mindboggle.guts.mesh import find_neighbors_from_file, find_neighborhood # Load scalars and vertex neighbor lists: scalars, name = read_scalars(input_vtk, True, True) if not indices: indices = [i for i,x in enumerate(scalars) if x != background_value] print(" Rescaling {0} scalar values by neighborhood...".format(len(indices))) neighbor_lists = find_neighbors_from_file(input_vtk) # Loop through vertices: rescaled_scalars = scalars.copy() for index in indices: # Determine the scalars in the vertex's neighborhood: neighborhood = find_neighborhood(neighbor_lists, [index], nedges) # Compute a high neighborhood percentile to normalize vertex's value: normalization_factor = np.percentile(scalars[neighborhood], p) rescaled_scalar = scalars[index] / normalization_factor rescaled_scalars[index] = rescaled_scalar # Make any rescaled value greater than 1 equal to 1: if set_max_to_1: rescaled_scalars[[x for x in indices if rescaled_scalars[x] > 1.0]] = 1 rescaled_scalars = rescaled_scalars.tolist() #------------------------------------------------------------------------- # Return rescaled scalars and file name #------------------------------------------------------------------------- if save_file: rescaled_scalars_file = os.path.join(os.getcwd(), output_filestring + '.vtk') rewrite_scalars(input_vtk, rescaled_scalars_file, rescaled_scalars, 'rescaled_scalars') if not os.path.exists(rescaled_scalars_file): raise(IOError(rescaled_scalars_file + " not found")) else: rescaled_scalars_file = None return rescaled_scalars, rescaled_scalars_file
def concatenate_sulcus_scalars(scalar_files, fold_files, label_files): """ Prepare data for estimating scalar distributions along and outside fundi. Extract (e.g., depth, curvature) scalar values in folds, along sulcus label boundaries as well as outside the sulcus label boundaries. Concatenate these scalar values across multiple files. Parameters ---------- scalar_files : list of strings names of surface mesh VTK files with scalar values to concatenate fold_files : list of strings (corr. to each list in scalar_files) VTK files with fold numbers as scalars (-1 for non-fold vertices) label_files : list of strings (corr. to fold_files) VTK files with label numbers (-1 for unlabeled vertices) Returns ------- border_scalars : list of floats concatenated scalar values within folds along sulcus label boundaries nonborder_scalars : list of floats concatenated scalar values within folds outside sulcus label boundaries Examples -------- >>> # Concatenate (duplicate) depth scalars: >>> import os >>> from mindboggle.shapes.likelihood import concatenate_sulcus_scalars >>> path = os.environ['MINDBOGGLE_DATA'] >>> depth_file = os.path.join(path, 'arno', 'shapes', 'depth_rescaled.vtk') >>> folds_file = os.path.join(path, 'arno', 'features', 'folds.vtk') >>> labels_file = os.path.join(path, 'arno', 'labels', 'lh.labels.DKT25.manual.vtk') >>> scalar_files = [depth_file, depth_file] >>> fold_files = [folds_file, folds_file] >>> label_files = [labels_file, labels_file] >>> # >>> S = concatenate_sulcus_scalars(scalar_files, fold_files, label_files) """ import numpy as np from mindboggle.mio.vtks import read_scalars from mindboggle.guts.mesh import find_neighbors_from_file from mindboggle.guts.segment import extract_borders from mindboggle.mio.labels import DKTprotocol dkt = DKTprotocol() # Prepare (non-unique) list of sulcus label pairs: protocol_label_pairs = [ x for lst in dkt.sulcus_label_pair_lists for x in lst ] border_scalars = [] nonborder_scalars = [] # Loop through files with the scalar values: for ifile, scalar_file in enumerate(scalar_files): print(scalar_file) # Load scalars, folds, and labels: folds_file = fold_files[ifile] labels_file = label_files[ifile] scalars, name = read_scalars(scalar_file, True, True) if scalars.shape: folds, name = read_scalars(folds_file) labels, name = read_scalars(labels_file) indices_folds = [i for i, x in enumerate(folds) if x != -1] neighbor_lists = find_neighbors_from_file(labels_file) # Find all label border pairs within the folds: indices_label_pairs, label_pairs, unique_pairs = extract_borders( indices_folds, labels, neighbor_lists, ignore_values=[-1], return_label_pairs=True) indices_label_pairs = np.array(indices_label_pairs) # Find vertices with label pairs in the sulcus labeling protocol: Ipairs_in_protocol = [ i for i, x in enumerate(label_pairs) if x in protocol_label_pairs ] indices_label_pairs = indices_label_pairs[Ipairs_in_protocol] indices_outside_pairs = list( frozenset(indices_folds).difference(indices_label_pairs)) # Store scalar values in folds along label border pairs: border_scalars.extend(scalars[indices_label_pairs].tolist()) # Store scalar values in folds outside label border pairs: nonborder_scalars.extend(scalars[indices_outside_pairs].tolist()) return border_scalars, nonborder_scalars
def rescale_by_neighborhood(input_vtk, indices=[], nedges=10, p=99, set_max_to_1=True, save_file=False, output_filestring='rescaled_scalars', background_value=-1): """ Rescale the scalar values of a VTK file by a percentile value in each vertex's surface mesh neighborhood. Parameters ---------- input_vtk : string name of VTK file with a scalar value for each vertex indices : list of integers (optional) indices of scalars to normalize nedges : integer number or edges from vertex, defining the size of its neighborhood p : float in range of [0,100] percentile used to normalize each scalar set_max_to_1 : bool set all rescaled values greater than 1 to 1.0? save_file : bool save output VTK file? output_filestring : string (if save_file) name of output file background_value : integer background value Returns ------- rescaled_scalars : list of floats rescaled scalar values rescaled_scalars_file : string (if save_file) name of output VTK file with rescaled scalar values Examples -------- >>> import numpy as np >>> from mindboggle.guts.mesh import rescale_by_neighborhood >>> from mindboggle.mio.vtks import read_scalars >>> from mindboggle.mio.plots import plot_surfaces >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> input_vtk = fetch_data(urls['left_travel_depth']) >>> indices = [] >>> nedges = 10 >>> p = 99 >>> set_max_to_1 = True >>> save_file = True >>> output_filestring = 'rescaled_scalars' >>> background_value = -1 >>> rescaled, rescaled_file = rescale_by_neighborhood(input_vtk, ... indices, nedges, p, set_max_to_1, save_file, output_filestring, ... background_value) >>> scalars1, name = read_scalars(input_vtk) >>> print('{0:0.5f}, {1:0.5f}'.format(max(scalars1), max(rescaled))) 34.95560, 1.00000 >>> print('{0:0.5f}, {1:0.5f}'.format(np.mean(scalars1), np.mean(rescaled))) 7.43822, 0.44950 View rescaled scalar values on surface (skip test): >>> from mindboggle.mio.plots import plot_surfaces # doctest: +SKIP >>> plot_surfaces(rescaled_file) # doctest: +SKIP """ import os import numpy as np from mindboggle.mio.vtks import read_scalars, rewrite_scalars from mindboggle.guts.mesh import find_neighbors_from_file, find_neighborhood # Load scalars and vertex neighbor lists: scalars, name = read_scalars(input_vtk, True, True) if not indices: indices = [i for i, x in enumerate(scalars) if x != background_value] #print(" Rescaling {0} scalar values by neighborhood...".format(len(indices))) neighbor_lists = find_neighbors_from_file(input_vtk) # Loop through vertices: rescaled_scalars = scalars.copy() for index in indices: # Determine the scalars in the vertex's neighborhood: neighborhood = find_neighborhood(neighbor_lists, [index], nedges) # Compute a high neighborhood percentile to normalize vertex's value: normalization_factor = np.percentile(scalars[neighborhood], p) rescaled_scalar = scalars[index] / normalization_factor rescaled_scalars[index] = rescaled_scalar # Make any rescaled value greater than 1 equal to 1: if set_max_to_1: rescaled_scalars[[x for x in indices if rescaled_scalars[x] > 1.0]] = 1 rescaled_scalars = rescaled_scalars.tolist() #------------------------------------------------------------------------- # Return rescaled scalars and file name #------------------------------------------------------------------------- if save_file: rescaled_scalars_file = os.path.join(os.getcwd(), output_filestring + '.vtk') rewrite_scalars(input_vtk, rescaled_scalars_file, rescaled_scalars, 'rescaled_scalars') if not os.path.exists(rescaled_scalars_file): raise IOError(rescaled_scalars_file + " not found") else: rescaled_scalars_file = None return rescaled_scalars, rescaled_scalars_file