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 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 stats_per_label(values, labels, include_labels=[], exclude_labels=[], weights=[], precision=1): """ Compute various statistical measures across vertices per label, optionally using weights (such as surface area per vertex). Example (area-weighted mean): average value = sum(a_i * v_i) / total_surface_area, where *a_i* and *v_i* are the area and value for each vertex *i*. Note :: This function is different than means_per_label() in two ways: 1. It computes more than simply the (weighted) mean and sdev. 2. It only accepts 1-D arrays of values. Reference --------- Weighted skewness and kurtosis unbiased by sample size Lorenzo Rimoldini, arXiv:1304.6564 (2013) http://arxiv.org/abs/1304.6564 Parameters ---------- values : numpy array of individual or lists of integers or floats values for all vertices labels : list or array of integers label for each value include_labels : list of integers labels to include exclude_labels : list of integers labels to be excluded weights : numpy array of floats weights to compute weighted statistical measures precision : integer number of decimal places to consider weights Returns ------- medians : list of floats median for each label mads : list of floats median absolute deviation for each label means : list of floats mean for each label sdevs : list of floats standard deviation for each label skews : list of floats skew for each label kurts : list of floats kurtosis value for each label lower_quarts : list of floats lower quartile for each label upper_quarts : list of floats upper quartile for each label label_list : list of integers list of unique labels Examples -------- >>> import os >>> from mindboggle.utils.io_vtk import read_scalars >>> from mindboggle.utils.compute import stats_per_label >>> data_path = os.environ['MINDBOGGLE_DATA'] >>> values_file = os.path.join(data_path, 'arno', 'shapes', 'lh.pial.mean_curvature.vtk') >>> area_file = os.path.join(data_path, 'arno', 'shapes', 'lh.pial.area.vtk') >>> labels_file = os.path.join(data_path, 'arno', 'labels', 'lh.labels.DKT25.manual.vtk') >>> values, name = read_scalars(values_file, True, True) >>> areas, name = read_scalars(area_file, True, True) >>> labels, name = read_scalars(labels_file) >>> include_labels = [] >>> exclude_labels = [-1] >>> weights = areas >>> precision = 1 >>> stats_per_label(values, labels, include_labels, exclude_labels, weights, precision) """ import numpy as np from scipy.stats import skew, kurtosis, scoreatpercentile from mindboggle.utils.compute import weighted_to_repeated_values, median_abs_dev # Make sure arguments are numpy arrays: if not isinstance(values, np.ndarray): values = np.asarray(values) if not isinstance(weights, np.ndarray): weights = np.asarray(weights) # Initialize all statistical lists: if include_labels: label_list = include_labels else: label_list = np.unique(labels) label_list = [int(x) for x in label_list if int(x) not in exclude_labels] medians = [] mads = [] means = [] sdevs = [] skews = [] kurts = [] lower_quarts = [] upper_quarts = [] # Extract all vertex indices for each label: for label in label_list: I = [i for i, x in enumerate(labels) if x == label] if I: # Get the vertex values: X = values[I] if len([x for x in X if x != 0]): # If there are as many weights as values, apply the weights to the values: if np.size(weights) == np.size(values): W = weights[I] sumW = np.sum(W) # If the sum of the weights and the standard deviation is non-zero, # compute all statistics of the weighted values: if sumW > 0: Xdiff = X - np.mean(X) Xstd = np.sqrt(np.sum(W * Xdiff**2) / sumW) means.append(np.sum(W * X) / sumW) sdevs.append(Xstd) if Xstd > 0: skews.append( (np.sum(W * Xdiff**3) / sumW) / Xstd**3) kurts.append((np.sum(W * Xdiff**4) / sumW) / Xstd**4 - 3) else: skews.append(skew(X)) kurts.append(kurtosis(X)) X = weighted_to_repeated_values(X, W, precision) # If the sum of the weights equals zero, simply compute the statistics: else: means.append(np.mean(X)) sdevs.append(np.std(X)) skews.append(skew(X)) kurts.append(kurtosis(X)) # If there are no (or not enough) weights, simply compute the statistics: else: means.append(np.mean(X)) sdevs.append(np.std(X)) skews.append(skew(X)) kurts.append(kurtosis(X)) # Compute median, median absolute deviation, and lower and upper quartiles: if np.size(X): medians.append(np.median(X)) mads.append(median_abs_dev(X)) lower_quarts.append(scoreatpercentile(X, 25)) upper_quarts.append(scoreatpercentile(X, 75)) # If the weights are all smaller than the precision, then X will disappear, # so set the above statistics (in the 'if' block) to zero: else: medians.append(0) mads.append(0) lower_quarts.append(0) upper_quarts.append(0) # If all values are equal to zero, set all statistics to zero: else: medians.append(0) mads.append(0) means.append(0) sdevs.append(0) skews.append(0) kurts.append(0) lower_quarts.append(0) upper_quarts.append(0) # If there are no vertices for the label, set all statistics to zero: else: medians.append(0) mads.append(0) means.append(0) sdevs.append(0) skews.append(0) kurts.append(0) lower_quarts.append(0) upper_quarts.append(0) return medians, mads, means, sdevs, skews, kurts, \ lower_quarts, upper_quarts, label_list
def stats_per_label(values, labels, exclude_labels, weights=[], precision=1): """ Compute various statistical measures across vertices per label, optionally using weights (such as surface area per vertex). Example (area-weighted mean): average value = sum(a_i * v_i) / total_surface_area, where *a_i* and *v_i* are the area and value for each vertex *i*. Note :: This function is different than means_per_label() in two ways: 1. It computes more than simply the (weighted) mean and sdev. 2. It only accepts 1-D arrays of values. Reference --------- Weighted skewness and kurtosis unbiased by sample size Lorenzo Rimoldini, arXiv:1304.6564 (2013) http://arxiv.org/abs/1304.6564 Parameters ---------- values : numpy array of individual or lists of integers or floats values for all vertices labels : list or array of integers label for each value exclude_labels : list of integers labels to be excluded weights : numpy array of floats weights to compute weighted statistical measures precision : integer number of decimal places to consider weights Returns ------- medians : list of floats median for each label mads : list of floats median absolute deviation for each label means : list of floats mean for each label sdevs : list of floats standard deviation for each label skews : list of floats skew for each label kurts : list of floats kurtosis value for each label lower_quarts : list of floats lower quartile for each label upper_quarts : list of floats upper quartile for each label label_list : list of integers list of unique labels Examples -------- >>> import os >>> from mindboggle.utils.io_vtk import read_scalars >>> from mindboggle.utils.compute import stats_per_label >>> data_path = os.environ['MINDBOGGLE_DATA'] >>> values_file = os.path.join(data_path, 'arno', 'shapes', 'lh.pial.mean_curvature.vtk') >>> area_file = os.path.join(data_path, 'arno', 'shapes', 'lh.pial.area.vtk') >>> labels_file = os.path.join(data_path, 'arno', 'labels', 'lh.labels.DKT25.manual.vtk') >>> values, name = read_scalars(values_file, True, True) >>> areas, name = read_scalars(area_file, True, True) >>> labels, name = read_scalars(labels_file) >>> exclude_labels = [-1] >>> weights = areas >>> precision = 1 >>> stats_per_label(values, labels, exclude_labels, weights, precision) """ import numpy as np from scipy.stats import skew, kurtosis, scoreatpercentile from mindboggle.utils.compute import weighted_to_repeated_values, median_abs_dev # Make sure arguments are numpy arrays: if not isinstance(values, np.ndarray): values = np.asarray(values) if not isinstance(weights, np.ndarray): weights = np.asarray(weights) # Initialize all statistical lists: label_list = np.unique(labels) label_list = [int(x) for x in label_list if int(x) not in exclude_labels] medians = [] mads = [] means = [] sdevs = [] skews = [] kurts = [] lower_quarts = [] upper_quarts = [] # Extract all vertex indices for each label: for label in label_list: I = [i for i,x in enumerate(labels) if x == label] if I: # Get the vertex values: X = values[I] if len([x for x in X if x != 0]): # If there are as many weights as values, apply the weights to the values: if np.size(weights) == np.size(values): W = weights[I] sumW = np.sum(W) # If the sum of the weights and the standard deviation is non-zero, # compute all statistics of the weighted values: if sumW > 0: Xdiff = X - np.mean(X) Xstd = np.sqrt(np.sum(W * Xdiff**2) / sumW) means.append(np.sum(W * X) / sumW) sdevs.append(Xstd) if Xstd > 0: skews.append((np.sum(W * Xdiff**3) / sumW) / Xstd**3) kurts.append((np.sum(W * Xdiff**4) / sumW) / Xstd**4 - 3) else: skews.append(skew(X)) kurts.append(kurtosis(X)) X = weighted_to_repeated_values(X, W, precision) # If the sum of the weights equals zero, simply compute the statistics: else: means.append(np.mean(X)) sdevs.append(np.std(X)) skews.append(skew(X)) kurts.append(kurtosis(X)) # If there are no (or not enough) weights, simply compute the statistics: else: means.append(np.mean(X)) sdevs.append(np.std(X)) skews.append(skew(X)) kurts.append(kurtosis(X)) # Compute median, median absolute deviation, and lower and upper quartiles: if np.size(X): medians.append(np.median(X)) mads.append(median_abs_dev(X)) lower_quarts.append(scoreatpercentile(X, 25)) upper_quarts.append(scoreatpercentile(X, 75)) # If the weights are all smaller than the precision, then X will disappear, # so set the above statistics (in the 'if' block) to zero: else: medians.append(0) mads.append(0) lower_quarts.append(0) upper_quarts.append(0) # If all values are equal to zero, set all statistics to zero: else: medians.append(0) mads.append(0) means.append(0) sdevs.append(0) skews.append(0) kurts.append(0) lower_quarts.append(0) upper_quarts.append(0) # If there are no vertices for the label, set all statistics to zero: else: medians.append(0) mads.append(0) means.append(0) sdevs.append(0) skews.append(0) kurts.append(0) lower_quarts.append(0) upper_quarts.append(0) return medians, mads, means, sdevs, skews, kurts, \ lower_quarts, upper_quarts, label_list
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