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.utils.io_vtk import read_scalars >>> from mindboggle.features.fundi import extract_fundi >>> from mindboggle.utils.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.utils.io_vtk import read_scalars, read_vtk, rewrite_scalars from mindboggle.utils.compute import median_abs_dev from mindboggle.utils.paths import find_max_values from mindboggle.utils.mesh import find_neighbors_from_file, find_complete_faces from mindboggle.utils.paths import find_outer_anchors, connect_points_erosion if isinstance(folds, list): folds = np.array(folds) # Load values, inner anchor threshold, and neighbors: faces, u1, u2, points, npoints, curvs, u3, u4 = 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 = [x for x in skeletons if folds[x] != -1] fundus_per_fold = -1 * np.ones(npoints) fundus_per_fold[indices] = folds[indices] 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 extract_fundi(folds, sulci, 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 sulcus 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(). 4. Segment fundi by sulcus definitions. Possible postprocessing step: smooth with smooth_skeleton(). Parameters ---------- folds : 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 sulci : list of integers sulcus number for each vertex 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 ------- fundi : list of integers fundus numbers for all vertices (-1 for non-fundus vertices) n_fundi : integer number of fundi fundi_file : string (if save_file) name of 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.utils.io_vtk import read_scalars >>> from mindboggle.features.fundi import extract_fundi >>> from mindboggle.utils.plots import plot_vtk >>> path = os.environ['MINDBOGGLE_DATA'] >>> sulci_file = os.path.join(path, 'arno', 'features', 'sulci.vtk') >>> sulci, name = read_scalars(sulci_file, True, True) >>> 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 >>> fundi, n_fundi, fundi_file = extract_fundi(folds, sulci, curv_file, >>> depth_file, min_separation, erode_ratio, erode_min_size, save_file) >>> # >>> # View: >>> plot_vtk(fundi_file) """ # Extract a skeleton to connect endpoints in a fold: import os import numpy as np from time import time from mindboggle.utils.io_vtk import read_scalars, read_vtk, rewrite_scalars from mindboggle.utils.compute import median_abs_dev from mindboggle.utils.paths import find_max_values from mindboggle.utils.mesh import find_neighbors_from_file from mindboggle.utils.paths import find_outer_anchors, connect_points_erosion # Load values, threshold, and neighbors: u1,u2,u3, points, npoints, curvs, u4,u5 = 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 endpoints 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) #------------------------------------------------------------------------- # Create fundi by segmenting skeletons with overlapping sulcus labels: #------------------------------------------------------------------------- fundi = -1 * np.ones(npoints) indices = [x for x in skeletons if sulci[x] != -1] fundi[indices] = sulci[indices] n_fundi = len([x for x in np.unique(fundi) if x != -1]) if n_fundi == 1: sdum = 'fundus' else: sdum = 'fundi' print(' ...Extracted {0} {1} ({2:.2f} seconds)'. format(n_fundi, sdum, time() - t1)) #------------------------------------------------------------------------- # Return fundi, number of fundi, and file name: #------------------------------------------------------------------------- fundi = fundi.tolist() if save_file: fundi_file = os.path.join(os.getcwd(), 'fundi.vtk') rewrite_scalars(curv_file, fundi_file, fundi, 'fundi', folds) else: fundi_file = None return fundi, n_fundi, fundi_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.utils.mesh import rescale_by_neighborhood >>> from mindboggle.utils.io_vtk import read_scalars, rewrite_scalars >>> from mindboggle.utils.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.utils.io_vtk import read_scalars, rewrite_scalars from mindboggle.utils.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 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.utils.mesh import rescale_by_neighborhood >>> from mindboggle.utils.io_vtk import read_scalars, rewrite_scalars >>> from mindboggle.utils.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.utils.io_vtk import read_scalars, rewrite_scalars from mindboggle.utils.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.utils.io_vtk import read_scalars from mindboggle.utils.mesh import find_neighbors_from_file from mindboggle.labels.labels import extract_borders 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) # Prepare (non-unique) list of sulcus label pairs: protocol_label_pairs = [x for lst in 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 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.utils.io_vtk import read_scalars from mindboggle.utils.mesh import find_neighbors_from_file from mindboggle.utils.segment import extract_borders from mindboggle.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): """ 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. 4. Optionally smooth with smooth_skeleton(). 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.utils.io_vtk import read_scalars >>> from mindboggle.features.fundi import extract_fundi >>> from mindboggle.utils.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.utils.io_vtk import read_scalars, read_vtk, rewrite_scalars from mindboggle.utils.compute import median_abs_dev from mindboggle.utils.paths import find_max_values from mindboggle.utils.mesh import find_neighbors_from_file, find_complete_faces from mindboggle.utils.paths import find_outer_anchors, connect_points_erosion if isinstance(folds, list): folds = np.array(folds) # Load values, inner anchor threshold, and neighbors: faces, u1,u2, points, npoints, curvs, u3,u4 = 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 = [x for x in skeletons if folds[x] != -1] fundus_per_fold = -1 * np.ones(npoints) fundus_per_fold[indices] = folds[indices] 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