Example #1
0
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 evaluate_deep_features(features_file, labels_file, sulci_file='', hemi='',
                           excludeIDs=[-1], output_vtk_name='', verbose=True):
    """
    Evaluate deep surface features by computing the minimum distance from each
    label border vertex to all of the feature vertices in the same sulcus,
    and from each feature vertex to all of the label border vertices in the
    same sulcus.  The label borders run along the deepest parts of sulci
    and correspond to fundi in the DKT cortical labeling protocol.

    Parameters
    ----------
    features_file : string
        VTK surface file with feature numbers for vertex scalars
    labels_file : string
        VTK surface file with label numbers for vertex scalars
    sulci_file : string
        VTK surface file with sulcus numbers for vertex scalars
    excludeIDs : list of integers
        feature/sulcus/label IDs to exclude (background set to -1)
    output_vtk_name : Boolean
        if not empty, output a VTK file beginning with output_vtk_name that
        contains a surface with mean distances as scalars
    verbose : Boolean
        print mean distances to standard output?

    Returns
    -------
    feature_to_border_mean_distances : numpy array [number of features x 1]
        mean distance from each feature to sulcus label border
    feature_to_border_sd_distances : numpy array [number of features x 1]
        standard deviations of feature-to-border distances
    feature_to_border_distances_vtk : string
        VTK surface file containing feature-to-border distances
    border_to_feature_mean_distances : numpy array [number of features x 1]
        mean distances from each sulcus label border to feature
    border_to_feature_sd_distances : numpy array [number of features x 1]
        standard deviations of border-to-feature distances
    border_to_feature_distances_vtk : string
        VTK surface file containing border-to-feature distances

    """
    import os
    import sys
    import numpy as np
    from mindboggle.mio.vtks import read_vtk, read_scalars, write_vtk
    from mindboggle.guts.mesh import find_neighbors, remove_faces
    from mindboggle.guts.segment import extract_borders
    from mindboggle.guts.compute import source_to_target_distances
    from mindboggle.mio.labels import DKTprotocol

    dkt = DKTprotocol()
    #-------------------------------------------------------------------------
    # Load labels, features, and sulci:
    #-------------------------------------------------------------------------
    faces, lines, indices, points, npoints, labels, scalar_names, \
        input_vtk = read_vtk(labels_file, True, True)
    features, name = read_scalars(features_file, True, True)
    if sulci_file:
        sulci, name = read_scalars(sulci_file, True, True)
        # List of indices to sulcus vertices:
        sulcus_indices = [i for i,x in enumerate(sulci) if x != -1]
        segmentIDs = sulci
        sulcus_faces = remove_faces(faces, sulcus_indices)
    else:
        sulcus_indices = range(len(labels))
        segmentIDs = []
        sulcus_faces = faces

    #-------------------------------------------------------------------------
    # Prepare neighbors, label pairs, border IDs, and outputs:
    #-------------------------------------------------------------------------
    # Calculate neighbor lists for all points:
    print('Find neighbors for all vertices...')
    neighbor_lists = find_neighbors(faces, npoints)

    # Find label border points in any of the sulci:
    print('Find label border points in any of the sulci...')
    border_indices, border_label_tuples, unique_border_label_tuples = \
        extract_borders(sulcus_indices, labels, neighbor_lists,
                        ignore_values=[], return_label_pairs=True)
    if not len(border_indices):
        sys.exit('There are no label border points!')

    # Initialize an array of label border IDs
    # (label border vertices that define sulci in the labeling protocol):
    print('Build an array of label border IDs...')
    label_borders = -1 * np.ones(npoints)

    if hemi == 'lh':
        nsulcus_lists = len(dkt.left_sulcus_label_pair_lists)
    else:
        nsulcus_lists = len(dkt.right_sulcus_label_pair_lists)
    feature_to_border_mean_distances = -1 * np.ones(nsulcus_lists)
    feature_to_border_sd_distances = -1 * np.ones(nsulcus_lists)
    border_to_feature_mean_distances = -1 * np.ones(nsulcus_lists)
    border_to_feature_sd_distances = -1 * np.ones(nsulcus_lists)
    feature_to_border_distances_vtk = ''
    border_to_feature_distances_vtk = ''

    #-------------------------------------------------------------------------
    # Loop through sulci:
    #-------------------------------------------------------------------------
    # For each list of sorted label pairs (corresponding to a sulcus):
    for isulcus, label_pairs in enumerate(dkt.sulcus_label_pair_lists):

        # Keep the border points with label pair labels:
        label_pair_border_indices = [x for i,x in enumerate(border_indices)
                          if np.unique(border_label_tuples[i]).tolist()
                          in label_pairs]

        # Store the points as sulcus IDs in the border IDs array:
        if label_pair_border_indices:
            label_borders[label_pair_border_indices] = isulcus

    if len(np.unique(label_borders)) > 1:

        #---------------------------------------------------------------------
        # Construct a feature-to-border distance matrix and VTK file:
        #---------------------------------------------------------------------
        # Construct a distance matrix:
        print('Construct a feature-to-border distance matrix...')
        sourceIDs = features
        targetIDs = label_borders
        distances, distance_matrix = source_to_target_distances(
            sourceIDs, targetIDs, points, segmentIDs, excludeIDs)

        # Compute mean distances for each feature:
        nfeatures = min(np.shape(distance_matrix)[1], nsulcus_lists)
        for ifeature in range(nfeatures):
            feature_distances = [x for x in distance_matrix[:, ifeature]
                                 if x != -1]
            feature_to_border_mean_distances[ifeature] = \
                np.mean(feature_distances)
            feature_to_border_sd_distances[ifeature] = \
                np.std(feature_distances)

        if verbose:
            print('Feature-to-border mean distances:')
            print(feature_to_border_mean_distances)
            print('Feature-to-border standard deviations of distances:')
            print(feature_to_border_sd_distances)

        # Write resulting feature-label border distances to VTK file:
        if output_vtk_name:
            feature_to_border_distances_vtk = os.path.join(os.getcwd(),
                output_vtk_name + '_feature_to_border_mean_distances.vtk')
            print('Write feature-to-border distances to {0}...'.
                  format(feature_to_border_distances_vtk))
            write_vtk(feature_to_border_distances_vtk, points,
                      [], [], sulcus_faces, [distances],
                      ['feature-to-border_distances'], 'float')

        #---------------------------------------------------------------------
        # Construct a border-to-feature distance matrix and VTK file:
        #---------------------------------------------------------------------
        # Construct a distance matrix:
        print('Construct a border-to-feature distance matrix...')
        sourceIDs = label_borders
        targetIDs = features
        distances, distance_matrix = source_to_target_distances(
            sourceIDs, targetIDs, points, segmentIDs, excludeIDs)

        # Compute mean distances for each feature:
        nfeatures = min(np.shape(distance_matrix)[1], nsulcus_lists)
        for ifeature in range(nfeatures):
            border_distances = [x for x in distance_matrix[:, ifeature]
                                if x != -1]
            border_to_feature_mean_distances[ifeature] = \
                np.mean(border_distances)
            border_to_feature_sd_distances[ifeature] = \
                np.std(border_distances)

        if verbose:
            print('border-to-feature mean distances:')
            print(border_to_feature_mean_distances)
            print('border-to-feature standard deviations of distances:')
            print(border_to_feature_sd_distances)

        # Write resulting feature-label border distances to VTK file:
        if output_vtk_name:
            border_to_feature_distances_vtk = os.path.join(os.getcwd(),
                output_vtk_name + '_border_to_feature_mean_distances.vtk')
            print('Write border-to-feature distances to {0}...'.
                  format(border_to_feature_distances_vtk))
            write_vtk(border_to_feature_distances_vtk, points,
                      [], [], sulcus_faces, [distances],
                      ['border-to-feature_distances'], 'float')

    #-------------------------------------------------------------------------
    # Return outputs:
    #-------------------------------------------------------------------------
    return feature_to_border_mean_distances, feature_to_border_sd_distances,\
           feature_to_border_distances_vtk,\
           border_to_feature_mean_distances, border_to_feature_sd_distances,\
           border_to_feature_distances_vtk
Example #3
0
def label_adjacency_matrix(label_file, ignore_values=[-1, 999], add_value=0,
                           save_table=True, output_format='csv',
                           verbose=True):
    """
    Extract surface or volume label boundaries, find unique label pairs,
    and write adjacency matrix (useful for constructing a colormap).

    Each row of the (upper triangular) adjacency matrix corresponds to an
    index to a unique label, where each column has a 1 if the label indexed
    by that column is adjacent to the label indexed by the row.

    Parameters
    ----------
    label_file : string
        path to VTK surface file or nibabel-readable volume file with labels
    ignore_values : list of integers
        labels to ignore
    add_value : integer
        value to add to labels
    matrix : pandas dataframe
        adjacency matrix
    save_table : Boolean
        output table file?
    output_format : string
        format of adjacency table file name (currently only 'csv')
    verbose : Boolean
        print to stdout?

    Returns
    -------
    labels : list
        label numbers
    matrix : pandas DataFrame
        adjacency matrix
    output_table : string
        adjacency table file name

    Examples
    --------
    >>> from mindboggle.mio.colors import label_adjacency_matrix
    >>> from mindboggle.mio.fetch_data import prep_tests
    >>> urls, fetch_data = prep_tests()
    >>> ignore_values = [-1, 0]
    >>> add_value = 0
    >>> save_table = False
    >>> output_format = 'csv'
    >>> verbose = False
    >>> label_file = fetch_data(urls['left_manual_labels'], '', '.vtk')
    >>> labels, matrix, output_table = label_adjacency_matrix(label_file,
    ...     ignore_values, add_value, save_table, output_format, verbose)
    >>> matrix.lookup([20,21,22,23,24,25,26,27,28,29],
    ...               [35,35,35,35,35,35,35,35,35,35])
    array([ 0.,  1.,  0.,  0.,  0.,  0.,  0.,  1.,  1.,  1.])

    >>> label_file = fetch_data(urls['freesurfer_labels'], '', '.nii.gz')
    >>> labels, matrix, output_table = label_adjacency_matrix(label_file,
    ...     ignore_values, add_value, save_table, output_format, verbose)
    >>> matrix.lookup([4,5,7,8,10,11,12,13,14,15], [4,4,4,4,4,4,4,4,4,4])
    array([ 1.,  1.,  0.,  0.,  0.,  1.,  0.,  0.,  1.,  0.])

    """
    import numpy as np
    import pandas as pd
    from nibabel import load
    from scipy import ndimage

    from mindboggle.guts.mesh import find_neighbors
    from mindboggle.guts.segment import extract_borders
    from mindboggle.mio.vtks import read_vtk

    # Use Mindboggle's extract_borders() function for surface VTK files:
    if label_file.endswith('.vtk'):
        f1,f2,f3, faces, labels, f4, npoints, f5 = read_vtk(label_file,
                                                            True, True)
        neighbor_lists = find_neighbors(faces, npoints)
        return_label_pairs = True
        indices_borders, label_pairs, f1 = extract_borders(list(range(npoints)),
            labels, neighbor_lists, ignore_values, return_label_pairs)

        output_table = 'adjacent_surface_labels.' + output_format

    # Use scipy to dilate volume files to find neighboring labels:
    elif label_file.endswith('.nii.gz'):

        L = load(label_file).get_data()
        unique_volume_labels = np.unique(L)

        label_pairs = []
        for label in unique_volume_labels:

            if label not in ignore_values:

                B = L * np.logical_xor(ndimage.binary_dilation(L==int(label)),
                                       (L==int(label)))
                neighbor_labels = np.unique(np.ravel(B))

                for neigh in neighbor_labels:
                    if neigh > 0 and neigh in unique_volume_labels:
                    #        and neigh%2==(int(label)%2):
                        label_pairs.append([int(label), int(neigh)])

        output_table = 'adjacent_volume_labels.' + output_format

    else:
        raise IOError("Use appropriate input file type.")

    # Find unique pairs (or first two of each list):
    pairs = []
    for pair in label_pairs:
        new_pair = [int(pair[0]) + add_value,
                    int(pair[1]) + add_value]
        if new_pair not in pairs:
            pairs.append(new_pair)

    # Write adjacency matrix:
    unique_labels = np.unique(pairs)
    nlabels = np.size(unique_labels)
    matrix = np.zeros((nlabels, nlabels))
    for pair in pairs:
        index1 = [i for i, x in enumerate(unique_labels) if x == pair[0]]
        index2 = [i for i, x in enumerate(unique_labels) if x == pair[1]]
        matrix[index1, index2] = 1

    df1 = pd.DataFrame({'ID': unique_labels}, index=None)
    df2 = pd.DataFrame(matrix, index=None)
    df2.columns = unique_labels
    matrix = pd.concat([df1, df2], axis=1)

    if save_table:
        if output_format == 'csv':
            matrix.to_csv(output_table, index=False)
            if verbose:
                print("Adjacency matrix saved to {0}".format(output_table))
        else:
            raise IOError("Set appropriate output file format.")
    else:
        output_table = None

    labels = list(unique_labels)

    return labels, matrix, output_table
def evaluate_deep_features(features_file,
                           labels_file,
                           sulci_file='',
                           hemi='',
                           excludeIDs=[-1],
                           output_vtk_name='',
                           verbose=True):
    """
    Evaluate deep surface features by computing the minimum distance from each
    label border vertex to all of the feature vertices in the same sulcus,
    and from each feature vertex to all of the label border vertices in the
    same sulcus.  The label borders run along the deepest parts of sulci
    and correspond to fundi in the DKT cortical labeling protocol.

    Parameters
    ----------
    features_file : string
        VTK surface file with feature numbers for vertex scalars
    labels_file : string
        VTK surface file with label numbers for vertex scalars
    sulci_file : string
        VTK surface file with sulcus numbers for vertex scalars
    excludeIDs : list of integers
        feature/sulcus/label IDs to exclude (background set to -1)
    output_vtk_name : Boolean
        if not empty, output a VTK file beginning with output_vtk_name that
        contains a surface with mean distances as scalars
    verbose : Boolean
        print mean distances to standard output?

    Returns
    -------
    feature_to_border_mean_distances : numpy array [number of features x 1]
        mean distance from each feature to sulcus label border
    feature_to_border_sd_distances : numpy array [number of features x 1]
        standard deviations of feature-to-border distances
    feature_to_border_distances_vtk : string
        VTK surface file containing feature-to-border distances
    border_to_feature_mean_distances : numpy array [number of features x 1]
        mean distances from each sulcus label border to feature
    border_to_feature_sd_distances : numpy array [number of features x 1]
        standard deviations of border-to-feature distances
    border_to_feature_distances_vtk : string
        VTK surface file containing border-to-feature distances

    """
    import os
    import sys
    import numpy as np
    from mindboggle.mio.vtks import read_vtk, read_scalars, write_vtk
    from mindboggle.guts.mesh import find_neighbors, remove_faces
    from mindboggle.guts.segment import extract_borders
    from mindboggle.guts.compute import source_to_target_distances
    from mindboggle.mio.labels import DKTprotocol

    dkt = DKTprotocol()
    #-------------------------------------------------------------------------
    # Load labels, features, and sulci:
    #-------------------------------------------------------------------------
    faces, lines, indices, points, npoints, labels, scalar_names, \
        input_vtk = read_vtk(labels_file, True, True)
    features, name = read_scalars(features_file, True, True)
    if sulci_file:
        sulci, name = read_scalars(sulci_file, True, True)
        # List of indices to sulcus vertices:
        sulcus_indices = [i for i, x in enumerate(sulci) if x != -1]
        segmentIDs = sulci
        sulcus_faces = remove_faces(faces, sulcus_indices)
    else:
        sulcus_indices = range(len(labels))
        segmentIDs = []
        sulcus_faces = faces

    #-------------------------------------------------------------------------
    # Prepare neighbors, label pairs, border IDs, and outputs:
    #-------------------------------------------------------------------------
    # Calculate neighbor lists for all points:
    print('Find neighbors for all vertices...')
    neighbor_lists = find_neighbors(faces, npoints)

    # Find label border points in any of the sulci:
    print('Find label border points in any of the sulci...')
    border_indices, border_label_tuples, unique_border_label_tuples = \
        extract_borders(sulcus_indices, labels, neighbor_lists,
                        ignore_values=[], return_label_pairs=True)
    if not len(border_indices):
        sys.exit('There are no label border points!')

    # Initialize an array of label border IDs
    # (label border vertices that define sulci in the labeling protocol):
    print('Build an array of label border IDs...')
    label_borders = -1 * np.ones(npoints)

    if hemi == 'lh':
        nsulcus_lists = len(dkt.left_sulcus_label_pair_lists)
    else:
        nsulcus_lists = len(dkt.right_sulcus_label_pair_lists)
    feature_to_border_mean_distances = -1 * np.ones(nsulcus_lists)
    feature_to_border_sd_distances = -1 * np.ones(nsulcus_lists)
    border_to_feature_mean_distances = -1 * np.ones(nsulcus_lists)
    border_to_feature_sd_distances = -1 * np.ones(nsulcus_lists)
    feature_to_border_distances_vtk = ''
    border_to_feature_distances_vtk = ''

    #-------------------------------------------------------------------------
    # Loop through sulci:
    #-------------------------------------------------------------------------
    # For each list of sorted label pairs (corresponding to a sulcus):
    for isulcus, label_pairs in enumerate(dkt.sulcus_label_pair_lists):

        # Keep the border points with label pair labels:
        label_pair_border_indices = [
            x for i, x in enumerate(border_indices)
            if np.unique(border_label_tuples[i]).tolist() in label_pairs
        ]

        # Store the points as sulcus IDs in the border IDs array:
        if label_pair_border_indices:
            label_borders[label_pair_border_indices] = isulcus

    if len(np.unique(label_borders)) > 1:

        #---------------------------------------------------------------------
        # Construct a feature-to-border distance matrix and VTK file:
        #---------------------------------------------------------------------
        # Construct a distance matrix:
        print('Construct a feature-to-border distance matrix...')
        sourceIDs = features
        targetIDs = label_borders
        distances, distance_matrix = source_to_target_distances(
            sourceIDs, targetIDs, points, segmentIDs, excludeIDs)

        # Compute mean distances for each feature:
        nfeatures = min(np.shape(distance_matrix)[1], nsulcus_lists)
        for ifeature in range(nfeatures):
            feature_distances = [
                x for x in distance_matrix[:, ifeature] if x != -1
            ]
            feature_to_border_mean_distances[ifeature] = \
                np.mean(feature_distances)
            feature_to_border_sd_distances[ifeature] = \
                np.std(feature_distances)

        if verbose:
            print('Feature-to-border mean distances:')
            print(feature_to_border_mean_distances)
            print('Feature-to-border standard deviations of distances:')
            print(feature_to_border_sd_distances)

        # Write resulting feature-label border distances to VTK file:
        if output_vtk_name:
            feature_to_border_distances_vtk = os.path.join(
                os.getcwd(),
                output_vtk_name + '_feature_to_border_mean_distances.vtk')
            print('Write feature-to-border distances to {0}...'.format(
                feature_to_border_distances_vtk))
            write_vtk(feature_to_border_distances_vtk, points, [], [],
                      sulcus_faces, [distances],
                      ['feature-to-border_distances'], 'float')

        #---------------------------------------------------------------------
        # Construct a border-to-feature distance matrix and VTK file:
        #---------------------------------------------------------------------
        # Construct a distance matrix:
        print('Construct a border-to-feature distance matrix...')
        sourceIDs = label_borders
        targetIDs = features
        distances, distance_matrix = source_to_target_distances(
            sourceIDs, targetIDs, points, segmentIDs, excludeIDs)

        # Compute mean distances for each feature:
        nfeatures = min(np.shape(distance_matrix)[1], nsulcus_lists)
        for ifeature in range(nfeatures):
            border_distances = [
                x for x in distance_matrix[:, ifeature] if x != -1
            ]
            border_to_feature_mean_distances[ifeature] = \
                np.mean(border_distances)
            border_to_feature_sd_distances[ifeature] = \
                np.std(border_distances)

        if verbose:
            print('border-to-feature mean distances:')
            print(border_to_feature_mean_distances)
            print('border-to-feature standard deviations of distances:')
            print(border_to_feature_sd_distances)

        # Write resulting feature-label border distances to VTK file:
        if output_vtk_name:
            border_to_feature_distances_vtk = os.path.join(
                os.getcwd(),
                output_vtk_name + '_border_to_feature_mean_distances.vtk')
            print('Write border-to-feature distances to {0}...'.format(
                border_to_feature_distances_vtk))
            write_vtk(border_to_feature_distances_vtk, points, [], [],
                      sulcus_faces, [distances],
                      ['border-to-feature_distances'], 'float')

    #-------------------------------------------------------------------------
    # Return outputs:
    #-------------------------------------------------------------------------
    return feature_to_border_mean_distances, feature_to_border_sd_distances,\
           feature_to_border_distances_vtk,\
           border_to_feature_mean_distances, border_to_feature_sd_distances,\
           border_to_feature_distances_vtk
Example #5
0
def extract_sulci(labels_file, folds_or_file, hemi, min_boundary=1,
                  sulcus_names=[]):
    """
    Identify sulci from folds in a brain surface according to a labeling
    protocol that includes a list of label pairs defining each sulcus.

    A fold is a group of connected, deep vertices.

    Steps for each fold ::

        1. Remove fold if it has fewer than two labels.
        2. Remove fold if its labels do not contain a sulcus label pair.
        3. Find vertices with labels that are in only one of the fold's
           label boundary pairs. Assign the vertices the sulcus with the label
           pair if they are connected to the label boundary for that pair.
        4. If there are remaining vertices, segment into sets of vertices
           connected to label boundaries, and assign a unique ID to each set.

    Parameters
    ----------
    labels_file : string
        file name for surface mesh VTK containing labels for all vertices
    folds_or_file : list or string
        fold number for each vertex / name of VTK file containing fold scalars
    hemi : string
        hemisphere abbreviation in {'lh', 'rh'} for sulcus labels
    min_boundary : integer
        minimum number of vertices for a sulcus label boundary segment
    sulcus_names : list of strings
        names of sulci

    Returns
    -------
    sulci : list of integers
        sulcus numbers for all vertices (-1 for non-sulcus vertices)
    n_sulci : integers
        number of sulci
    sulci_file : string
        output VTK file with sulcus numbers (-1 for non-sulcus vertices)

    Examples
    --------
    >>> import os
    >>> from mindboggle.mio.vtks import read_scalars, rewrite_scalars
    >>> from mindboggle.features.sulci import extract_sulci
    >>> from mindboggle.mio.plots import plot_surfaces
    >>> path = os.environ['MINDBOGGLE_DATA']
    >>> # Load labels, folds, neighbor lists, and sulcus names and label pairs
    >>> labels_file = os.path.join(path, 'arno', 'labels', 'relabeled_lh.DKTatlas40.gcs.vtk')
    >>> folds_file = os.path.join(path, 'arno', 'features', 'folds.vtk')
    >>> folds_or_file, name = read_scalars(folds_file)
    >>> hemi = 'lh'
    >>> min_boundary = 10
    >>> sulcus_names = []
    >>> #
    >>> sulci, n_sulci, sulci_file = extract_sulci(labels_file, folds_or_file, hemi, min_boundary, sulcus_names)
    >>> # View:
    >>> plot_surfaces('sulci.vtk')

    """
    import os
    from time import time
    import numpy as np

    from mindboggle.mio.vtks import read_scalars, read_vtk, rewrite_scalars
    from mindboggle.guts.mesh import find_neighbors
    from mindboggle.guts.segment import extract_borders, propagate, segment
    from mindboggle.mio.labels import DKTprotocol


    # Load fold numbers if folds_or_file is a string:
    if isinstance(folds_or_file, str):
        folds, name = read_scalars(folds_or_file)
    elif isinstance(folds_or_file, list):
        folds = folds_or_file

    dkt = DKTprotocol()

    if hemi == 'lh':
        pair_lists = dkt.left_sulcus_label_pair_lists
    elif hemi == 'rh':
        pair_lists = dkt.right_sulcus_label_pair_lists
    else:
        print("Warning: hemisphere not properly specified ('lh' or 'rh').")

    # Load points, faces, and neighbors:
    faces, o1, o2, points, npoints, labels, o3, o4 = read_vtk(labels_file)
    neighbor_lists = find_neighbors(faces, npoints)

    # Array of sulcus IDs for fold vertices, initialized as -1.
    # Since we do not touch gyral vertices and vertices whose labels
    # are not in the label list, or vertices having only one label,
    # their sulcus IDs will remain -1:
    sulci = -1 * np.ones(npoints)

    #-------------------------------------------------------------------------
    # Loop through folds
    #-------------------------------------------------------------------------
    fold_numbers = [int(x) for x in np.unique(folds) if x != -1]
    n_folds = len(fold_numbers)
    print("Extract sulci from {0} folds...".format(n_folds))
    t0 = time()
    for n_fold in fold_numbers:
        fold = [i for i,x in enumerate(folds) if x == n_fold]
        len_fold = len(fold)

        # List the labels in this fold:
        fold_labels = [labels[x] for x in fold]
        unique_fold_labels = [int(x) for x in np.unique(fold_labels)
                              if x != -1]

        #---------------------------------------------------------------------
        # NO MATCH -- fold has fewer than two labels
        #---------------------------------------------------------------------
        if len(unique_fold_labels) < 2:
            # Ignore: sulci already initialized with -1 values:
            if not unique_fold_labels:
                print("  Fold {0} ({1} vertices): "
                      "NO MATCH -- fold has no labels".
                      format(n_fold, len_fold))
            else:
                print("  Fold {0} ({1} vertices): "
                  "NO MATCH -- fold has only one label ({2})".
                  format(n_fold, len_fold, unique_fold_labels[0]))
            # Ignore: sulci already initialized with -1 values

        else:
            # Find all label boundary pairs within the fold:
            indices_fold_pairs, fold_pairs, unique_fold_pairs = \
                extract_borders(fold, labels, neighbor_lists,
                                ignore_values=[], return_label_pairs=True)

            # Find fold label pairs in the protocol (pairs are already sorted):
            fold_pairs_in_protocol = [x for x in unique_fold_pairs
                                      if x in dkt.unique_sulcus_label_pairs]

            if unique_fold_labels:
                print("  Fold {0} labels: {1} ({2} vertices)".format(n_fold,
                      ', '.join([str(x) for x in unique_fold_labels]),
                      len_fold))
            #-----------------------------------------------------------------
            # NO MATCH -- fold has no sulcus label pair
            #-----------------------------------------------------------------
            if not fold_pairs_in_protocol:
                print("  Fold {0}: NO MATCH -- fold has no sulcus label pair".
                      format(n_fold, len_fold))

            #-----------------------------------------------------------------
            # Possible matches
            #-----------------------------------------------------------------
            else:
                print("  Fold {0} label pairs in protocol: {1}".format(n_fold,
                      ', '.join([str(x) for x in fold_pairs_in_protocol])))

                # Labels in the protocol (includes repeats across label pairs):
                labels_in_pairs = [x for lst in fold_pairs_in_protocol
                                   for x in lst]

                # Labels that appear in one or more sulcus label boundary:
                unique_labels = []
                nonunique_labels = []
                for label in np.unique(labels_in_pairs):
                    if len([x for x in labels_in_pairs if x == label]) == 1:
                        unique_labels.append(label)
                    else:
                        nonunique_labels.append(label)

                #-------------------------------------------------------------
                # Vertices whose labels are in only one sulcus label pair
                #-------------------------------------------------------------
                # Find vertices with a label that is in only one of the fold's
                # label pairs (the other label in the pair can exist in other
                # pairs). Assign the vertices the sulcus with the label pair
                # if they are connected to the label boundary for that pair.
                #-------------------------------------------------------------
                if unique_labels:

                    for pair in fold_pairs_in_protocol:

                        # If one or both labels in label pair is/are unique:
                        unique_labels_in_pair = [x for x in pair
                                                 if x in unique_labels]
                        n_unique = len(unique_labels_in_pair)
                        if n_unique:

                            ID = None
                            for i, pair_list in enumerate(pair_lists):
                                if not isinstance(pair_list, list):
                                    pair_list = [pair_list]
                                if pair in pair_list:
                                    ID = i
                                    break
                            if ID:
                                # Seeds from label boundary vertices
                                # (fold_pairs and pair already sorted):
                                indices_pair = [x for i,x
                                    in enumerate(indices_fold_pairs)
                                    if fold_pairs[i] == pair]

                                # Vertices with unique label(s) in pair:
                                indices_unique_labels = [fold[i]
                                     for i,x in enumerate(fold_labels)
                                     if x in dkt.unique_sulcus_label_pairs]

                                # Propagate from seeds to labels in label pair:
                                sulci2 = segment(indices_unique_labels,
                                                 neighbor_lists,
                                                 min_region_size=1,
                                                 seed_lists=[indices_pair],
                                                 keep_seeding=False,
                                                 spread_within_labels=True,
                                                 labels=labels)
                                sulci[sulci2 != -1] = ID

                                # Print statement:
                                if n_unique == 1:
                                    ps1 = '1 label'
                                else:
                                    ps1 = 'Both labels'
                                if len(sulcus_names):
                                    ps2 = sulcus_names[ID]
                                else:
                                    ps2 = ''
                                print("    {0} unique to one fold pair: "
                                      "{1} {2}".
                                      format(ps1, ps2, unique_labels_in_pair))

                #-------------------------------------------------------------
                # Vertex labels shared by multiple label pairs
                #-------------------------------------------------------------
                # Propagate labels from label borders to vertices with labels
                # that are shared by multiple label pairs in the fold.
                #-------------------------------------------------------------
                if len(nonunique_labels):
                    # For each label shared by different label pairs:
                    for label in nonunique_labels:
                        # Print statement:
                        print("    Propagate sulcus borders with label {0}".
                              format(int(label)))

                        # Construct seeds from label boundary vertices:
                        seeds = -1 * np.ones(len(points))

                        for ID, pair_list in enumerate(pair_lists):
                            if not isinstance(pair_list, list):
                                pair_list = [pair_list]
                            label_pairs = [x for x in pair_list if label in x]
                            for label_pair in label_pairs:
                                indices_pair = [x for i,x
                                    in enumerate(indices_fold_pairs)
                                    if np.sort(fold_pairs[i]).
                                    tolist() == label_pair]
                                if indices_pair:

                                    # Do not include short boundary segments:
                                    if min_boundary > 1:
                                        indices_pair2 = []
                                        seeds2 = segment(indices_pair,
                                                         neighbor_lists)
                                        useeds2 = [x for x in
                                                   np.unique(seeds2)
                                                   if x != -1]
                                        for seed2 in useeds2:
                                            iseed2 = [i for i,x
                                                      in enumerate(seeds2)
                                                      if x == seed2]
                                            if len(iseed2) >= min_boundary:
                                                indices_pair2.extend(iseed2)
                                            else:
                                                if len(iseed2) == 1:
                                                    print("    Remove "
                                                          "assignment "
                                                          "of ID {0} from "
                                                          "1 vertex".
                                                          format(seed2))
                                                else:
                                                    print("    Remove "
                                                          "assignment "
                                                          "of ID {0} from "
                                                          "{1} vertices".
                                                          format(seed2,
                                                                 len(iseed2)))
                                        indices_pair = indices_pair2

                                    # Assign sulcus IDs to seeds:
                                    seeds[indices_pair] = ID

                        # Identify vertices with the label:
                        label_array = -1 * np.ones(len(points))
                        indices_label = [fold[i] for i,x
                                         in enumerate(fold_labels)
                                         if x == label]
                        if len(indices_label):
                            label_array[indices_label] = 1

                            # Propagate from seeds to vertices with label:
                            #indices_seeds = []
                            #for seed in range(int(max(seeds))+1):
                            #    indices_seeds.append([i for i,x
                            #                          in enumerate(seeds)
                            #                          if x == seed])
                            #sulci2 = segment(indices_label, neighbor_lists,
                            #                 50, indices_seeds, False, True,
                            #                 labels)
                            sulci2 = propagate(points, faces,
                                               label_array, seeds, sulci,
                                               max_iters=10000,
                                               tol=0.001, sigma=5)
                            sulci[sulci2 != -1] = sulci2[sulci2 != -1]

    #-------------------------------------------------------------------------
    # Print out assigned sulci
    #-------------------------------------------------------------------------
    sulcus_numbers = [int(x) for x in np.unique(sulci) if x != -1]
                      # if not np.isnan(x)]
    n_sulci = len(sulcus_numbers)
    print("Extracted {0} sulci from {1} folds ({2:.1f}s):".
          format(n_sulci, n_folds, time()-t0))
    if sulcus_names:
        for sulcus_number in sulcus_numbers:
            print("  {0}: {1}".format(sulcus_number,
                                      sulcus_names[sulcus_number]))
    elif sulcus_numbers:
        print("  " + ", ".join([str(x) for x in sulcus_numbers]))

    #-------------------------------------------------------------------------
    # Print out unresolved sulci
    #-------------------------------------------------------------------------
    unresolved = [i for i in range(len(pair_lists))
                  if i not in sulcus_numbers]
    if len(unresolved) == 1:
        print("The following sulcus is unaccounted for:")
    else:
        print("The following {0} sulci are unaccounted for:".
              format(len(unresolved)))
    if sulcus_names:
        for sulcus_number in unresolved:
            print("  {0}: {1}".format(sulcus_number,
                                      sulcus_names[sulcus_number]))
    else:
        print("  " + ", ".join([str(x) for x in unresolved]))

    #-------------------------------------------------------------------------
    # Return sulci, number of sulci, and file name
    #-------------------------------------------------------------------------
    sulci = [int(x) for x in sulci]
    sulci_file = os.path.join(os.getcwd(), 'sulci.vtk')
    rewrite_scalars(labels_file, sulci_file, sulci, 'sulci', sulci)

    if not os.path.exists(sulci_file):
        raise(IOError(sulci_file + " not found"))

    return sulci, n_sulci, sulci_file
Example #6
0
def extract_sulci(labels_file,
                  folds_or_file,
                  hemi,
                  min_boundary=1,
                  sulcus_names=[],
                  save_file=False,
                  output_file='',
                  background_value=-1,
                  verbose=False):
    """
    Identify sulci from folds in a brain surface according to a labeling
    protocol that includes a list of label pairs defining each sulcus.

    Since folds are defined as deep, connected areas of a surface, and since
    folds may be connected to each other in ways that differ across brains,
    there usually does not exist a one-to-one mapping between folds of one
    brain and those of another.  To address the correspondence problem then,
    we need to find just those portions of the folds that correspond across
    brains. To accomplish this, Mindboggle segments folds into sulci, which
    do have a one-to-one correspondence across non-pathological brains.
    Mindboggle defines a sulcus as a folded portion of cortex whose opposing
    banks are labeled with one or more sulcus label pairs in the DKT labeling
    protocol, where each label pair is unique to one sulcus and represents
    a boundary between two adjacent gyri, and each vertex has one gyrus label.

    This function assigns vertices in a fold to a sulcus in one of two cases.
    In the first case, vertices whose labels are in only one label pair in
    the fold are assigned to the label pair’s sulcus if they are connected
    through similarly labeled vertices to the boundary between the two labels.
    In the second case, the segment_regions function propagates labels from
    label borders to vertices whose labels are in multiple label pairs in the
    fold.

    Steps for each fold ::

        1. Remove fold if it has fewer than two labels.
        2. Remove fold if its labels do not contain a sulcus label pair.
        3. Find vertices with labels that are in only one of the fold's
           label boundary pairs. Assign the vertices the sulcus with the label
           pair if they are connected to the label boundary for that pair.
        4. If there are remaining vertices, segment into sets of vertices
           connected to label boundaries, and assign a unique ID to each set.

    Parameters
    ----------
    labels_file : string
        file name for surface mesh VTK containing labels for all vertices
    folds_or_file : numpy array, list or string
        fold number for each vertex / name of VTK file containing fold scalars
    hemi : string
        hemisphere abbreviation in {'lh', 'rh'} for sulcus labels
    min_boundary : integer
        minimum number of vertices for a sulcus label boundary segment
    sulcus_names : list of strings
        names of sulci
    save_file : bool
        save output VTK file?
    output_file : string
        name of output file in VTK format
    background_value : integer or float
        background value
    verbose : bool
        print statements?

    Returns
    -------
    sulci : list of integers
        sulcus numbers for all vertices (-1 for non-sulcus vertices)
    n_sulci : integers
        number of sulci
    sulci_file : string
        output VTK file with sulcus numbers (-1 for non-sulcus vertices)

    Examples
    --------
    >>> # Example 1: Extract sulcus from a fold with one sulcus label pair:
    >>> import numpy as np
    >>> from mindboggle.features.sulci import extract_sulci
    >>> from mindboggle.mio.vtks import read_scalars
    >>> from mindboggle.mio.fetch_data import prep_tests
    >>> urls, fetch_data = prep_tests()
    >>> # Load labels, folds, neighbor lists, and sulcus names and label pairs
    >>> labels_file = fetch_data(urls['left_freesurfer_labels'], '', '.vtk')
    >>> folds_file = fetch_data(urls['left_folds'], '', '.vtk')
    >>> folds_or_file, name = read_scalars(folds_file, True, True)
    >>> save_file = True
    >>> output_file = 'extract_sulci_fold4_1sulcus.vtk'
    >>> background_value = -1
    >>> # 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_or_file) if x not in fold_numbers]
    ...     folds_or_file[i0] = background_value
    >>> hemi = 'lh'
    >>> min_boundary = 10
    >>> sulcus_names = []
    >>> verbose = False
    >>> sulci, n_sulci, sulci_file = extract_sulci(labels_file, folds_or_file,
    ...     hemi, min_boundary, sulcus_names, save_file, output_file,
    ...     background_value, verbose)
    >>> n_sulci  # 23 # (if not limit_folds)
    1
    >>> lens = [len([x for x in sulci if x==y])
    ...         for y in np.unique(sulci) if y != -1]
    >>> lens[0:10]  # [6358, 3288, 7612, 5205, 4414, 6251, 3493, 2566, 4436, 739] # (if not limit_folds)
    [1151]

    View result without background (skip test):

    >>> from mindboggle.mio.plots import plot_surfaces # doctest: +SKIP
    >>> from mindboggle.mio.vtks import rewrite_scalars # doctest: +SKIP
    >>> output = 'extract_sulci_fold4_1sulcus_no_background.vtk'
    >>> rewrite_scalars(sulci_file, output, sulci,
    ...                 'sulci', sulci) # doctest: +SKIP
    >>> plot_surfaces(output) # doctest: +SKIP

    Example 2:  Extract sulcus from a fold with multiple sulcus label pairs:

    >>> folds_or_file, name = read_scalars(folds_file, True, True)
    >>> output_file = 'extract_sulci_fold7_2sulci.vtk'
    >>> # Limit number of folds to speed up the test:
    >>> limit_folds = True
    >>> if limit_folds:
    ...     fold_numbers = [7] #[4, 6]
    ...     i0 = [i for i,x in enumerate(folds_or_file) if x not in fold_numbers]
    ...     folds_or_file[i0] = background_value
    >>> sulci, n_sulci, sulci_file = extract_sulci(labels_file, folds_or_file,
    ...     hemi, min_boundary, sulcus_names, save_file, output_file,
    ...     background_value, verbose)
    >>> n_sulci  # 23 # (if not limit_folds)
    2
    >>> lens = [len([x for x in sulci if x==y])
    ...         for y in np.unique(sulci) if y != -1]
    >>> lens[0:10]  # [6358, 3288, 7612, 5205, 4414, 6251, 3493, 2566, 4436, 739] # (if not limit_folds)
    [369, 93]

    View result without background (skip test):

    >>> from mindboggle.mio.plots import plot_surfaces # doctest: +SKIP
    >>> from mindboggle.mio.vtks import rewrite_scalars # doctest: +SKIP
    >>> output = 'extract_sulci_fold7_2sulci_no_background.vtk'
    >>> rewrite_scalars(sulci_file, output, sulci,
    ...                 'sulci', sulci) # doctest: +SKIP
    >>> plot_surfaces(output) # doctest: +SKIP

    """
    import os
    from time import time
    import numpy as np

    from mindboggle.mio.vtks import read_scalars, read_vtk, rewrite_scalars
    from mindboggle.guts.mesh import find_neighbors
    from mindboggle.guts.segment import extract_borders, propagate, segment_regions
    from mindboggle.mio.labels import DKTprotocol

    # Load fold numbers if folds_or_file is a string:
    if isinstance(folds_or_file, str):
        folds, name = read_scalars(folds_or_file)
    elif isinstance(folds_or_file, list):
        folds = folds_or_file
    elif isinstance(folds_or_file, np.ndarray):
        folds = folds_or_file.tolist()

    dkt = DKTprotocol()

    if hemi == 'lh':
        pair_lists = dkt.left_sulcus_label_pair_lists
    elif hemi == 'rh':
        pair_lists = dkt.right_sulcus_label_pair_lists
    else:
        raise IOError(
            "Warning: hemisphere not properly specified ('lh' or 'rh').")

    # Load points, faces, and neighbors:
    points, indices, lines, faces, labels, scalar_names, npoints, \
            input_vtk = read_vtk(labels_file)
    neighbor_lists = find_neighbors(faces, npoints)

    # Array of sulcus IDs for fold vertices, initialized as -1.
    # Since we do not touch gyral vertices and vertices whose labels
    # are not in the label list, or vertices having only one label,
    # their sulcus IDs will remain -1:
    sulci = background_value * np.ones(npoints)

    # ------------------------------------------------------------------------
    # Loop through folds
    # ------------------------------------------------------------------------
    fold_numbers = [int(x) for x in np.unique(folds) if x != background_value]
    n_folds = len(fold_numbers)
    if verbose:
        print("Extract sulci from {0} folds...".format(n_folds))
    t0 = time()
    for n_fold in fold_numbers:
        fold_indices = [i for i, x in enumerate(folds) if x == n_fold]
        len_fold = len(fold_indices)

        # List the labels in this fold:
        fold_labels = [labels[x] for x in fold_indices]
        unique_fold_labels = [
            int(x) for x in np.unique(fold_labels) if x != background_value
        ]

        # --------------------------------------------------------------------
        # NO MATCH -- fold has fewer than two labels
        # --------------------------------------------------------------------
        if verbose and len(unique_fold_labels) < 2:
            # Ignore: sulci already initialized with -1 values:
            if not unique_fold_labels:
                print("  Fold {0} ({1} vertices): "
                      "NO MATCH -- fold has no labels".format(
                          n_fold, len_fold))
            else:
                print("  Fold {0} ({1} vertices): "
                      "NO MATCH -- fold has only one label ({2})".format(
                          n_fold, len_fold, unique_fold_labels[0]))
            # Ignore: sulci already initialized with -1 values

        else:
            # Find all label boundary pairs within the fold:
            indices_fold_pairs, fold_pairs, unique_fold_pairs = \
                extract_borders(fold_indices, labels, neighbor_lists,
                                ignore_values=[], return_label_pairs=True)

            # Find fold label pairs in the protocol (pairs are already sorted):
            fold_pairs_in_protocol = [
                x for x in unique_fold_pairs
                if x in dkt.unique_sulcus_label_pairs
            ]

            if verbose and unique_fold_labels:
                print("  Fold {0} labels: {1} ({2} vertices)".format(
                    n_fold, ', '.join([str(x) for x in unique_fold_labels]),
                    len_fold))
            # ----------------------------------------------------------------
            # NO MATCH -- fold has no sulcus label pair
            # ----------------------------------------------------------------
            if verbose and not fold_pairs_in_protocol:
                print("  Fold {0}: NO MATCH -- fold has no sulcus label pair".
                      format(n_fold, len_fold))

            # ----------------------------------------------------------------
            # Possible matches
            # ----------------------------------------------------------------
            else:
                if verbose:
                    print("  Fold {0} label pairs in protocol: {1}".format(
                        n_fold,
                        ', '.join([str(x) for x in fold_pairs_in_protocol])))

                # Labels in the protocol (includes repeats across label pairs):
                labels_in_pairs = [
                    x for lst in fold_pairs_in_protocol for x in lst
                ]

                # Labels that appear in one or more sulcus label boundary:
                unique_labels = []
                nonunique_labels = []
                for label in np.unique(labels_in_pairs):
                    if len([x for x in labels_in_pairs if x == label]) == 1:
                        unique_labels.append(label)
                    else:
                        nonunique_labels.append(label)

                # ------------------------------------------------------------
                # Vertices whose labels are in only one sulcus label pair
                # ------------------------------------------------------------
                # Find vertices with a label that is in only one of the fold's
                # label pairs (the other label in the pair can exist in other
                # pairs). Assign the vertices the sulcus with the label pair
                # if they are connected to the label boundary for that pair.
                # ------------------------------------------------------------
                if unique_labels:

                    for pair in fold_pairs_in_protocol:

                        # If one or both labels in label pair is/are unique:
                        unique_labels_in_pair = [
                            x for x in pair if x in unique_labels
                        ]
                        n_unique = len(unique_labels_in_pair)
                        if n_unique:

                            ID = None
                            for i, pair_list in enumerate(pair_lists):
                                if not isinstance(pair_list, list):
                                    pair_list = [pair_list]
                                if pair in pair_list:
                                    ID = i
                                    break
                            if ID:
                                # Seeds from label boundary vertices
                                # (fold_pairs and pair already sorted):
                                indices_pair = [
                                    x for i, x in enumerate(indices_fold_pairs)
                                    if fold_pairs[i] == pair
                                ]

                                # Vertices with unique label(s) in pair:
                                indices_unique_labels = [
                                    fold_indices[i]
                                    for i, x in enumerate(fold_labels)
                                    if x in unique_labels_in_pair
                                ]
                                #dkt.unique_sulcus_label_pairs]

                                # Propagate sulcus ID from seeds to vertices
                                # with "unique" labels (only exist in one
                                # label pair in a fold); propagation ensures
                                # that sulci consist of contiguous vertices
                                # for each label boundary:
                                sulci2 = segment_regions(
                                    indices_unique_labels,
                                    neighbor_lists,
                                    min_region_size=1,
                                    seed_lists=[indices_pair],
                                    keep_seeding=False,
                                    spread_within_labels=True,
                                    labels=labels,
                                    label_lists=[],
                                    values=[],
                                    max_steps='',
                                    background_value=background_value,
                                    verbose=False)

                                sulci[sulci2 != background_value] = ID

                                # Print statement:
                                if verbose:
                                    if n_unique == 1:
                                        ps1 = 'One label'
                                    else:
                                        ps1 = 'Both labels'
                                    if len(sulcus_names):
                                        ps2 = sulcus_names[ID]
                                    else:
                                        ps2 = ''
                                    print("    {0} unique to one fold pair: "
                                          "{1} {2}".format(
                                              ps1, ps2, unique_labels_in_pair))

                # ------------------------------------------------------------
                # Vertex labels shared by multiple label pairs
                # ------------------------------------------------------------
                # Propagate labels from label borders to vertices with labels
                # that are shared by multiple label pairs in the fold.
                # ------------------------------------------------------------
                if len(nonunique_labels):
                    # For each label shared by different label pairs:
                    for label in nonunique_labels:
                        # Print statement:
                        if verbose:
                            print(
                                "    Propagate sulcus borders with label {0}".
                                format(int(label)))

                        # Construct seeds from label boundary vertices:
                        seeds = background_value * np.ones(npoints)

                        for ID, pair_list in enumerate(pair_lists):
                            if not isinstance(pair_list, list):
                                pair_list = [pair_list]
                            label_pairs = [x for x in pair_list if label in x]
                            for label_pair in label_pairs:
                                indices_pair = [
                                    x for i, x in enumerate(indices_fold_pairs)
                                    if np.sort(fold_pairs[i]).tolist() ==
                                    label_pair
                                ]
                                if indices_pair:

                                    # Do not include short boundary segments:
                                    if min_boundary > 1:
                                        indices_pair2 = []
                                        seeds2 = segment_regions(
                                            indices_pair, neighbor_lists, 1,
                                            [], False, False, [], [], [], '',
                                            background_value, verbose)

                                        useeds2 = [
                                            x for x in np.unique(seeds2)
                                            if x != background_value
                                        ]
                                        for seed2 in useeds2:
                                            iseed2 = [
                                                i for i, x in enumerate(seeds2)
                                                if x == seed2
                                            ]
                                            if len(iseed2) >= min_boundary:
                                                indices_pair2.extend(iseed2)
                                            elif verbose:
                                                if len(iseed2) == 1:
                                                    print("    Remove "
                                                          "assignment "
                                                          "of ID {0} from "
                                                          "1 vertex".format(
                                                              seed2))
                                                else:
                                                    print(
                                                        "    Remove "
                                                        "assignment "
                                                        "of ID {0} from "
                                                        "{1} vertices".format(
                                                            seed2,
                                                            len(iseed2)))
                                        indices_pair = indices_pair2

                                    # Assign sulcus IDs to seeds:
                                    seeds[indices_pair] = ID

                        # Identify vertices with the label:
                        indices_label = [
                            fold_indices[i] for i, x in enumerate(fold_labels)
                            if x == label
                        ]
                        if len(indices_label):

                            # Propagate sulcus ID from seeds to vertices
                            # with a given shared label:
                            seg_vs_prop = False
                            if seg_vs_prop:
                                indices_seeds = []
                                for seed in [
                                        x for x in np.unique(seeds)
                                        if x != background_value
                                ]:
                                    indices_seeds.append([
                                        i for i, x in enumerate(seeds)
                                        if x == seed
                                    ])

                                sulci2 = segment_regions(
                                    indices_label, neighbor_lists, 50,
                                    indices_seeds, False, True, labels, [], [],
                                    '', background_value, verbose)
                            else:
                                label_array = background_value * \
                                              np.ones(npoints)
                                label_array[indices_label] = 1
                                sulci2 = propagate(
                                    points,
                                    faces,
                                    label_array,
                                    seeds,
                                    sulci,
                                    max_iters=10000,
                                    tol=0.001,
                                    sigma=5,
                                    background_value=background_value,
                                    verbose=verbose)
                            sulci[sulci2 != background_value] = \
                                sulci2[sulci2 != background_value]

    sulcus_numbers = [
        int(x) for x in np.unique(sulci) if x != background_value
    ]
    n_sulci = len(sulcus_numbers)

    # ------------------------------------------------------------------------
    # Print statements
    # ------------------------------------------------------------------------
    if verbose:
        if n_sulci == 1:
            sulcus_str = 'sulcus'
        else:
            sulcus_str = 'sulci'
        if n_folds == 1:
            folds_str = 'fold'
        else:
            folds_str = 'folds'
        print("Extracted {0} {1} from {2} {3} ({4:.1f}s):".format(
            n_sulci, sulcus_str, n_folds, folds_str,
            time() - t0))
        if sulcus_names:
            for sulcus_number in sulcus_numbers:
                print("  {0}: {1}".format(sulcus_number,
                                          sulcus_names[sulcus_number]))
        elif sulcus_numbers:
            print("  " + ", ".join([str(x) for x in sulcus_numbers]))

        unresolved = [
            i for i in range(len(pair_lists)) if i not in sulcus_numbers
        ]
        if len(unresolved) == 1:
            print("The following sulcus is unaccounted for:")
        else:
            print("The following {0} sulci are unaccounted for:".format(
                len(unresolved)))
        if sulcus_names:
            for sulcus_number in unresolved:
                print("  {0}: {1}".format(sulcus_number,
                                          sulcus_names[sulcus_number]))
        else:
            print("  " + ", ".join([str(x) for x in unresolved]))

    # ------------------------------------------------------------------------
    # Return sulci, number of sulci, and file name
    # ------------------------------------------------------------------------
    sulci = [int(x) for x in sulci]

    sulci_file = os.path.join(os.getcwd(), 'sulci.vtk')
    rewrite_scalars(labels_file, sulci_file, sulci, 'sulci', [],
                    background_value)

    if not os.path.exists(sulci_file):
        raise IOError(sulci_file + " not found")

    return sulci, n_sulci, sulci_file
Example #7
0
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 realign_boundaries_to_fundus_lines(surf_file,
                                       init_label_file,
                                       fundus_lines_file,
                                       thickness_file,
                                       out_label_file=None):
    """
    Fix label boundaries to fundus lines.

    Parameters
    ----------
    surf_file : file containing the surface geometry in vtk format
    init_label_file : file containing scalars that represent the
                      initial guess at labels
    fundus_lines_file : file containing scalars representing fundus lines.
    thickness_file: file containing cortical thickness scalar data
    (for masking out the medial wall only)
    out_label_file : if specified, the realigned labels will be writen to
                     this file

    Returns
    -------
    numpy array representing the realigned label for each surface vertex.
    """

    import numpy as np
    from mindboggle.guts.segment import extract_borders
    import mindboggle.guts.graph as go
    from mindboggle.mio.vtks import read_vtk, read_scalars, write_vtk
    from mindboggle.guts.mesh import find_neighbors
    import propagate_fundus_lines

    ## read files
    points, indices, lines, faces, scalars, scalar_names, num_points, \
        input_vtk = read_vtk(surf_file, return_first=True, return_array=True)
    indices = range(num_points)

    init_labels, _ = read_scalars(init_label_file,
                                  return_first=True,
                                  return_array=True)

    fundus_lines, _ = read_scalars(fundus_lines_file,
                                   return_first=True,
                                   return_array=True)

    thickness, _ = read_scalars(thickness_file,
                                return_first=True,
                                return_array=True)

    # remove labels from vertices with zero thickness (get around
    # DKT40 annotations having the label '3' for all the Corpus
    # Callosum vertices).
    cc_inds = [x for x in indices if thickness[x] < 0.001]
    init_labels[cc_inds] = 0

    ## setup seeds from initial label boundaries
    neighbor_lists = find_neighbors(faces, num_points)

    # extract all vertices that are on a boundary between labels
    boundary_indices, label_pairs, _ = extract_borders(indices,
                                                       init_labels,
                                                       neighbor_lists,
                                                       return_label_pairs=True)

    # split boundary vertices into segments with common boundary pairs.
    boundary_segments = {}
    for boundary_index, label_pair in zip(boundary_indices, label_pairs):
        key = ((label_pair[0],
                label_pair[1]) if label_pair[0] < label_pair[1] else
               (label_pair[1], label_pair[0]))
        if key not in boundary_segments:
            boundary_segments[key] = []

        boundary_segments[key].append(boundary_index)

    boundary_matrix, boundary_matrix_keys = _build_boundary_matrix(
        boundary_segments, num_points)

    # build the affinity matrix
    affinity_matrix = go.weight_graph(np.array(points),
                                      indices,
                                      np.array(faces),
                                      sigma=10,
                                      add_to_graph=False)

    ## propagate boundaries to fundus line vertices
    learned_matrix = _propagate_labels(affinity_matrix, boundary_matrix,
                                       boundary_indices, 100, 1)

    # assign labels to fundus line vertices based on highest probability
    new_boundaries = -1 * np.ones(init_labels.shape)
    fundus_line_indices = [i for i, x in enumerate(fundus_lines) if x > 0.5]

    # tile the surface into connected components delimited by fundus lines
    closed_fundus_lines, _, _ = propagate_fundus_lines.propagate_fundus_lines(
        points, faces, fundus_line_indices, thickness)

    closed_fundus_line_indices = np.where(closed_fundus_lines > 0)[0]

    # split surface into connected components
    connected_component_faces = _remove_boundary_faces(
        points, faces, closed_fundus_line_indices)

    # label components based on most probable label assignment
    new_labels = _label_components(connected_component_faces, num_points,
                                   boundary_indices, learned_matrix,
                                   boundary_matrix_keys)

    # propagate new labels to fill holes
    label_matrix, label_map = _build_label_matrix(new_labels)
    new_learned_matrix = _propagate_labels(
        affinity_matrix, label_matrix,
        [i for i in range(num_points) if new_labels[i] >= 0], 100, 1)

    # assign most probable labels
    for idx in [i for i in range(num_points) if new_labels[i] == -1]:
        max_idx = np.argmax(new_learned_matrix[idx])
        new_labels[idx] = label_map[max_idx]

    # save
    if out_label_file is not None:
        write_vtk(out_label_file,
                  points,
                  faces=faces,
                  scalars=[int(x) for x in new_labels],
                  scalar_type='int')

    return new_labels
Example #9
0
def extract_sulci(
    labels_file,
    folds_or_file,
    hemi,
    min_boundary=1,
    sulcus_names=[],
    save_file=False,
    output_file="",
    background_value=-1,
    verbose=False,
):
    """
    Identify sulci from folds in a brain surface according to a labeling
    protocol that includes a list of label pairs defining each sulcus.

    Since folds are defined as deep, connected areas of a surface, and since
    folds may be connected to each other in ways that differ across brains,
    there usually does not exist a one-to-one mapping between folds of one
    brain and those of another.  To address the correspondence problem then,
    we need to find just those portions of the folds that correspond across
    brains. To accomplish this, Mindboggle segments folds into sulci, which
    do have a one-to-one correspondence across non-pathological brains.
    Mindboggle defines a sulcus as a folded portion of cortex whose opposing
    banks are labeled with one or more sulcus label pairs in the DKT labeling
    protocol, where each label pair is unique to one sulcus and represents
    a boundary between two adjacent gyri, and each vertex has one gyrus label.

    This function assigns vertices in a fold to a sulcus in one of two cases.
    In the first case, vertices whose labels are in only one label pair in
    the fold are assigned to the label pair’s sulcus if they are connected
    through similarly labeled vertices to the boundary between the two labels.
    In the second case, the segment_regions function propagates labels from
    label borders to vertices whose labels are in multiple label pairs in the
    fold.

    Steps for each fold ::

        1. Remove fold if it has fewer than two labels.
        2. Remove fold if its labels do not contain a sulcus label pair.
        3. Find vertices with labels that are in only one of the fold's
           label boundary pairs. Assign the vertices the sulcus with the label
           pair if they are connected to the label boundary for that pair.
        4. If there are remaining vertices, segment into sets of vertices
           connected to label boundaries, and assign a unique ID to each set.

    Parameters
    ----------
    labels_file : string
        file name for surface mesh VTK containing labels for all vertices
    folds_or_file : numpy array, list or string
        fold number for each vertex / name of VTK file containing fold scalars
    hemi : string
        hemisphere abbreviation in {'lh', 'rh'} for sulcus labels
    min_boundary : integer
        minimum number of vertices for a sulcus label boundary segment
    sulcus_names : list of strings
        names of sulci
    save_file : bool
        save output VTK file?
    output_file : string
        name of output file in VTK format
    background_value : integer or float
        background value
    verbose : bool
        print statements?

    Returns
    -------
    sulci : list of integers
        sulcus numbers for all vertices (-1 for non-sulcus vertices)
    n_sulci : integers
        number of sulci
    sulci_file : string
        output VTK file with sulcus numbers (-1 for non-sulcus vertices)

    Examples
    --------
    >>> # Example 1: Extract sulcus from a fold with one sulcus label pair:
    >>> import numpy as np
    >>> from mindboggle.features.sulci import extract_sulci
    >>> from mindboggle.mio.vtks import read_scalars
    >>> from mindboggle.mio.fetch_data import prep_tests
    >>> urls, fetch_data = prep_tests()
    >>> # Load labels, folds, neighbor lists, and sulcus names and label pairs
    >>> labels_file = fetch_data(urls['left_freesurfer_labels'], '', '.vtk')
    >>> folds_file = fetch_data(urls['left_folds'], '', '.vtk')
    >>> folds_or_file, name = read_scalars(folds_file, True, True)
    >>> save_file = True
    >>> output_file = 'extract_sulci_fold4_1sulcus.vtk'
    >>> background_value = -1
    >>> # 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_or_file) if x not in fold_numbers]
    ...     folds_or_file[i0] = background_value
    >>> hemi = 'lh'
    >>> min_boundary = 10
    >>> sulcus_names = []
    >>> verbose = False
    >>> sulci, n_sulci, sulci_file = extract_sulci(labels_file, folds_or_file,
    ...     hemi, min_boundary, sulcus_names, save_file, output_file,
    ...     background_value, verbose)
    >>> n_sulci  # 23 # (if not limit_folds)
    1
    >>> lens = [len([x for x in sulci if x==y])
    ...         for y in np.unique(sulci) if y != -1]
    >>> lens[0:10]  # [6358, 3288, 7612, 5205, 4414, 6251, 3493, 2566, 4436, 739] # (if not limit_folds)
    [1151]

    View result without background (skip test):

    >>> from mindboggle.mio.plots import plot_surfaces # doctest: +SKIP
    >>> from mindboggle.mio.vtks import rewrite_scalars # doctest: +SKIP
    >>> output = 'extract_sulci_fold4_1sulcus_no_background.vtk'
    >>> rewrite_scalars(sulci_file, output, sulci,
    ...                 'sulci', sulci) # doctest: +SKIP
    >>> plot_surfaces(output) # doctest: +SKIP

    Example 2:  Extract sulcus from a fold with multiple sulcus label pairs:

    >>> folds_or_file, name = read_scalars(folds_file, True, True)
    >>> output_file = 'extract_sulci_fold7_2sulci.vtk'
    >>> # Limit number of folds to speed up the test:
    >>> limit_folds = True
    >>> if limit_folds:
    ...     fold_numbers = [7] #[4, 6]
    ...     i0 = [i for i,x in enumerate(folds_or_file) if x not in fold_numbers]
    ...     folds_or_file[i0] = background_value
    >>> sulci, n_sulci, sulci_file = extract_sulci(labels_file, folds_or_file,
    ...     hemi, min_boundary, sulcus_names, save_file, output_file,
    ...     background_value, verbose)
    >>> n_sulci  # 23 # (if not limit_folds)
    2
    >>> lens = [len([x for x in sulci if x==y])
    ...         for y in np.unique(sulci) if y != -1]
    >>> lens[0:10]  # [6358, 3288, 7612, 5205, 4414, 6251, 3493, 2566, 4436, 739] # (if not limit_folds)
    [369, 93]

    View result without background (skip test):

    >>> from mindboggle.mio.plots import plot_surfaces # doctest: +SKIP
    >>> from mindboggle.mio.vtks import rewrite_scalars # doctest: +SKIP
    >>> output = 'extract_sulci_fold7_2sulci_no_background.vtk'
    >>> rewrite_scalars(sulci_file, output, sulci,
    ...                 'sulci', sulci) # doctest: +SKIP
    >>> plot_surfaces(output) # doctest: +SKIP

    """
    import os
    from time import time
    import numpy as np

    from mindboggle.mio.vtks import read_scalars, read_vtk, rewrite_scalars
    from mindboggle.guts.mesh import find_neighbors
    from mindboggle.guts.segment import extract_borders, propagate, segment_regions
    from mindboggle.mio.labels import DKTprotocol

    # Load fold numbers if folds_or_file is a string:
    if isinstance(folds_or_file, str):
        folds, name = read_scalars(folds_or_file)
    elif isinstance(folds_or_file, list):
        folds = folds_or_file
    elif isinstance(folds_or_file, np.ndarray):
        folds = folds_or_file.tolist()

    dkt = DKTprotocol()

    if hemi == "lh":
        pair_lists = dkt.left_sulcus_label_pair_lists
    elif hemi == "rh":
        pair_lists = dkt.right_sulcus_label_pair_lists
    else:
        raise IOError("Warning: hemisphere not properly specified ('lh' or 'rh').")

    # Load points, faces, and neighbors:
    points, indices, lines, faces, labels, scalar_names, npoints, input_vtk = read_vtk(labels_file)
    neighbor_lists = find_neighbors(faces, npoints)

    # Array of sulcus IDs for fold vertices, initialized as -1.
    # Since we do not touch gyral vertices and vertices whose labels
    # are not in the label list, or vertices having only one label,
    # their sulcus IDs will remain -1:
    sulci = background_value * np.ones(npoints)

    # ------------------------------------------------------------------------
    # Loop through folds
    # ------------------------------------------------------------------------
    fold_numbers = [int(x) for x in np.unique(folds) if x != background_value]
    n_folds = len(fold_numbers)
    if verbose:
        print("Extract sulci from {0} folds...".format(n_folds))
    t0 = time()
    for n_fold in fold_numbers:
        fold_indices = [i for i, x in enumerate(folds) if x == n_fold]
        len_fold = len(fold_indices)

        # List the labels in this fold:
        fold_labels = [labels[x] for x in fold_indices]
        unique_fold_labels = [int(x) for x in np.unique(fold_labels) if x != background_value]

        # --------------------------------------------------------------------
        # NO MATCH -- fold has fewer than two labels
        # --------------------------------------------------------------------
        if verbose and len(unique_fold_labels) < 2:
            # Ignore: sulci already initialized with -1 values:
            if not unique_fold_labels:
                print("  Fold {0} ({1} vertices): " "NO MATCH -- fold has no labels".format(n_fold, len_fold))
            else:
                print(
                    "  Fold {0} ({1} vertices): "
                    "NO MATCH -- fold has only one label ({2})".format(n_fold, len_fold, unique_fold_labels[0])
                )
            # Ignore: sulci already initialized with -1 values

        else:
            # Find all label boundary pairs within the fold:
            indices_fold_pairs, fold_pairs, unique_fold_pairs = extract_borders(
                fold_indices, labels, neighbor_lists, ignore_values=[], return_label_pairs=True
            )

            # Find fold label pairs in the protocol (pairs are already sorted):
            fold_pairs_in_protocol = [x for x in unique_fold_pairs if x in dkt.unique_sulcus_label_pairs]

            if verbose and unique_fold_labels:
                print(
                    "  Fold {0} labels: {1} ({2} vertices)".format(
                        n_fold, ", ".join([str(x) for x in unique_fold_labels]), len_fold
                    )
                )
            # ----------------------------------------------------------------
            # NO MATCH -- fold has no sulcus label pair
            # ----------------------------------------------------------------
            if verbose and not fold_pairs_in_protocol:
                print("  Fold {0}: NO MATCH -- fold has no sulcus label pair".format(n_fold, len_fold))

            # ----------------------------------------------------------------
            # Possible matches
            # ----------------------------------------------------------------
            else:
                if verbose:
                    print(
                        "  Fold {0} label pairs in protocol: {1}".format(
                            n_fold, ", ".join([str(x) for x in fold_pairs_in_protocol])
                        )
                    )

                # Labels in the protocol (includes repeats across label pairs):
                labels_in_pairs = [x for lst in fold_pairs_in_protocol for x in lst]

                # Labels that appear in one or more sulcus label boundary:
                unique_labels = []
                nonunique_labels = []
                for label in np.unique(labels_in_pairs):
                    if len([x for x in labels_in_pairs if x == label]) == 1:
                        unique_labels.append(label)
                    else:
                        nonunique_labels.append(label)

                # ------------------------------------------------------------
                # Vertices whose labels are in only one sulcus label pair
                # ------------------------------------------------------------
                # Find vertices with a label that is in only one of the fold's
                # label pairs (the other label in the pair can exist in other
                # pairs). Assign the vertices the sulcus with the label pair
                # if they are connected to the label boundary for that pair.
                # ------------------------------------------------------------
                if unique_labels:

                    for pair in fold_pairs_in_protocol:

                        # If one or both labels in label pair is/are unique:
                        unique_labels_in_pair = [x for x in pair if x in unique_labels]
                        n_unique = len(unique_labels_in_pair)
                        if n_unique:

                            ID = None
                            for i, pair_list in enumerate(pair_lists):
                                if not isinstance(pair_list, list):
                                    pair_list = [pair_list]
                                if pair in pair_list:
                                    ID = i
                                    break
                            if ID:
                                # Seeds from label boundary vertices
                                # (fold_pairs and pair already sorted):
                                indices_pair = [x for i, x in enumerate(indices_fold_pairs) if fold_pairs[i] == pair]

                                # Vertices with unique label(s) in pair:
                                indices_unique_labels = [
                                    fold_indices[i] for i, x in enumerate(fold_labels) if x in unique_labels_in_pair
                                ]
                                # dkt.unique_sulcus_label_pairs]

                                # Propagate sulcus ID from seeds to vertices
                                # with "unique" labels (only exist in one
                                # label pair in a fold); propagation ensures
                                # that sulci consist of contiguous vertices
                                # for each label boundary:
                                sulci2 = segment_regions(
                                    indices_unique_labels,
                                    neighbor_lists,
                                    min_region_size=1,
                                    seed_lists=[indices_pair],
                                    keep_seeding=False,
                                    spread_within_labels=True,
                                    labels=labels,
                                    label_lists=[],
                                    values=[],
                                    max_steps="",
                                    background_value=background_value,
                                    verbose=False,
                                )

                                sulci[sulci2 != background_value] = ID

                                # Print statement:
                                if verbose:
                                    if n_unique == 1:
                                        ps1 = "One label"
                                    else:
                                        ps1 = "Both labels"
                                    if len(sulcus_names):
                                        ps2 = sulcus_names[ID]
                                    else:
                                        ps2 = ""
                                    print(
                                        "    {0} unique to one fold pair: "
                                        "{1} {2}".format(ps1, ps2, unique_labels_in_pair)
                                    )

                # ------------------------------------------------------------
                # Vertex labels shared by multiple label pairs
                # ------------------------------------------------------------
                # Propagate labels from label borders to vertices with labels
                # that are shared by multiple label pairs in the fold.
                # ------------------------------------------------------------
                if len(nonunique_labels):
                    # For each label shared by different label pairs:
                    for label in nonunique_labels:
                        # Print statement:
                        if verbose:
                            print("    Propagate sulcus borders with label {0}".format(int(label)))

                        # Construct seeds from label boundary vertices:
                        seeds = background_value * np.ones(npoints)

                        for ID, pair_list in enumerate(pair_lists):
                            if not isinstance(pair_list, list):
                                pair_list = [pair_list]
                            label_pairs = [x for x in pair_list if label in x]
                            for label_pair in label_pairs:
                                indices_pair = [
                                    x
                                    for i, x in enumerate(indices_fold_pairs)
                                    if np.sort(fold_pairs[i]).tolist() == label_pair
                                ]
                                if indices_pair:

                                    # Do not include short boundary segments:
                                    if min_boundary > 1:
                                        indices_pair2 = []
                                        seeds2 = segment_regions(
                                            indices_pair,
                                            neighbor_lists,
                                            1,
                                            [],
                                            False,
                                            False,
                                            [],
                                            [],
                                            [],
                                            "",
                                            background_value,
                                            verbose,
                                        )

                                        useeds2 = [x for x in np.unique(seeds2) if x != background_value]
                                        for seed2 in useeds2:
                                            iseed2 = [i for i, x in enumerate(seeds2) if x == seed2]
                                            if len(iseed2) >= min_boundary:
                                                indices_pair2.extend(iseed2)
                                            elif verbose:
                                                if len(iseed2) == 1:
                                                    print(
                                                        "    Remove "
                                                        "assignment "
                                                        "of ID {0} from "
                                                        "1 vertex".format(seed2)
                                                    )
                                                else:
                                                    print(
                                                        "    Remove "
                                                        "assignment "
                                                        "of ID {0} from "
                                                        "{1} vertices".format(seed2, len(iseed2))
                                                    )
                                        indices_pair = indices_pair2

                                    # Assign sulcus IDs to seeds:
                                    seeds[indices_pair] = ID

                        # Identify vertices with the label:
                        indices_label = [fold_indices[i] for i, x in enumerate(fold_labels) if x == label]
                        if len(indices_label):

                            # Propagate sulcus ID from seeds to vertices
                            # with a given shared label:
                            seg_vs_prop = False
                            if seg_vs_prop:
                                indices_seeds = []
                                for seed in [x for x in np.unique(seeds) if x != background_value]:
                                    indices_seeds.append([i for i, x in enumerate(seeds) if x == seed])

                                sulci2 = segment_regions(
                                    indices_label,
                                    neighbor_lists,
                                    50,
                                    indices_seeds,
                                    False,
                                    True,
                                    labels,
                                    [],
                                    [],
                                    "",
                                    background_value,
                                    verbose,
                                )
                            else:
                                label_array = background_value * np.ones(npoints)
                                label_array[indices_label] = 1
                                sulci2 = propagate(
                                    points,
                                    faces,
                                    label_array,
                                    seeds,
                                    sulci,
                                    max_iters=10000,
                                    tol=0.001,
                                    sigma=5,
                                    background_value=background_value,
                                    verbose=verbose,
                                )
                            sulci[sulci2 != background_value] = sulci2[sulci2 != background_value]

    sulcus_numbers = [int(x) for x in np.unique(sulci) if x != background_value]
    n_sulci = len(sulcus_numbers)

    # ------------------------------------------------------------------------
    # Print statements
    # ------------------------------------------------------------------------
    if verbose:
        if n_sulci == 1:
            sulcus_str = "sulcus"
        else:
            sulcus_str = "sulci"
        if n_folds == 1:
            folds_str = "fold"
        else:
            folds_str = "folds"
        print("Extracted {0} {1} from {2} {3} ({4:.1f}s):".format(n_sulci, sulcus_str, n_folds, folds_str, time() - t0))
        if sulcus_names:
            for sulcus_number in sulcus_numbers:
                print("  {0}: {1}".format(sulcus_number, sulcus_names[sulcus_number]))
        elif sulcus_numbers:
            print("  " + ", ".join([str(x) for x in sulcus_numbers]))

        unresolved = [i for i in range(len(pair_lists)) if i not in sulcus_numbers]
        if len(unresolved) == 1:
            print("The following sulcus is unaccounted for:")
        else:
            print("The following {0} sulci are unaccounted for:".format(len(unresolved)))
        if sulcus_names:
            for sulcus_number in unresolved:
                print("  {0}: {1}".format(sulcus_number, sulcus_names[sulcus_number]))
        else:
            print("  " + ", ".join([str(x) for x in unresolved]))

    # ------------------------------------------------------------------------
    # Return sulci, number of sulci, and file name
    # ------------------------------------------------------------------------
    sulci = [int(x) for x in sulci]

    sulci_file = os.path.join(os.getcwd(), "sulci.vtk")
    rewrite_scalars(labels_file, sulci_file, sulci, "sulci", [], background_value)

    if not os.path.exists(sulci_file):
        raise IOError(sulci_file + " not found")

    return sulci, n_sulci, sulci_file
Example #10
0
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 realign_boundaries_to_fundus_lines(
    surf_file, init_label_file, fundus_lines_file, thickness_file,
    out_label_file=None):
    """
    Fix label boundaries to fundus lines.

    Parameters
    ----------
    surf_file : file containing the surface geometry in vtk format
    init_label_file : file containing scalars that represent the
                      initial guess at labels
    fundus_lines_file : file containing scalars representing fundus lines.
    thickness_file: file containing cortical thickness scalar data
    (for masking out the medial wall only)
    out_label_file : if specified, the realigned labels will be writen to
                     this file

    Returns
    -------
    numpy array representing the realigned label for each surface vertex.
    """

    import numpy as np
    from mindboggle.guts.segment import extract_borders
    import mindboggle.guts.graph as go
    from mindboggle.mio.vtks import read_vtk, read_scalars, write_vtk
    from mindboggle.guts.mesh import find_neighbors
    import propagate_fundus_lines

    ## read files
    faces, _, indices, points, num_points, _, _, _ = read_vtk(
        surf_file, return_first=True, return_array=True)
    indices = range(num_points)

    init_labels, _ = read_scalars(init_label_file,
                                  return_first=True, return_array=True)

    fundus_lines, _ = read_scalars(fundus_lines_file,
                                   return_first=True, return_array=True)

    thickness, _ = read_scalars(thickness_file,
                             return_first=True, return_array=True)

    # remove labels from vertices with zero thickness (get around
    # DKT40 annotations having the label '3' for all the Corpus
    # Callosum vertices).
    cc_inds = [x for x in indices if thickness[x] < 0.001]
    init_labels[cc_inds] = 0

    ## setup seeds from initial label boundaries
    neighbor_lists = find_neighbors(faces, num_points)

    # extract all vertices that are on a boundary between labels
    boundary_indices, label_pairs, _ = extract_borders(
        indices, init_labels, neighbor_lists,
        return_label_pairs=True)

    # split boundary vertices into segments with common boundary pairs.
    boundary_segments = {}
    for boundary_index, label_pair in zip(boundary_indices, label_pairs):
        key = ((label_pair[0], label_pair[1]) if label_pair[0] < label_pair[1]
               else (label_pair[1], label_pair[0]))
        if key not in boundary_segments:
            boundary_segments[key] = []

        boundary_segments[key].append(boundary_index)

    boundary_matrix, boundary_matrix_keys = _build_boundary_matrix(
        boundary_segments, num_points)

    # build the affinity matrix
    affinity_matrix = go.weight_graph(
       np.array(points), indices, np.array(faces), sigma=10, add_to_graph=False)

    ## propagate boundaries to fundus line vertices
    learned_matrix = _propagate_labels(
       affinity_matrix, boundary_matrix, boundary_indices, 100, 1)

    # assign labels to fundus line vertices based on highest probability
    new_boundaries = -1 * np.ones(init_labels.shape)
    fundus_line_indices = [i for i, x in enumerate(fundus_lines) if x > 0.5]

    # tile the surface into connected components delimited by fundus lines
    closed_fundus_lines, _, _ = propagate_fundus_lines.propagate_fundus_lines(
        points, faces, fundus_line_indices, thickness)

    closed_fundus_line_indices = np.where(closed_fundus_lines > 0)[0]

    # split surface into connected components
    connected_component_faces = _remove_boundary_faces(
        points, faces, closed_fundus_line_indices)

    # label components based on most probable label assignment
    new_labels = _label_components(
        connected_component_faces, num_points, boundary_indices, learned_matrix,
        boundary_matrix_keys)

    # propagate new labels to fill holes
    label_matrix, label_map = _build_label_matrix(new_labels)
    new_learned_matrix = _propagate_labels(
        affinity_matrix, label_matrix,
        [i for i in range(num_points) if new_labels[i] >= 0], 100, 1)

    # assign most probable labels
    for idx in [i for i in range(num_points) if new_labels[i] == -1]:
        max_idx = np.argmax(new_learned_matrix[idx])
        new_labels[idx] = label_map[max_idx]

    # save
    if out_label_file is not None:
        write_vtk(out_label_file, points, faces=faces,
                  scalars=[int(x) for x in new_labels], scalar_type='int')

    return new_labels
Example #12
0
def extract_sulci(labels_file, folds_or_file, hemi, min_boundary=1,
                  sulcus_names=[], verbose=False):
    """
    Identify sulci from folds in a brain surface according to a labeling
    protocol that includes a list of label pairs defining each sulcus.

    A fold is a group of connected, deep vertices.

    Steps for each fold ::

        1. Remove fold if it has fewer than two labels.
        2. Remove fold if its labels do not contain a sulcus label pair.
        3. Find vertices with labels that are in only one of the fold's
           label boundary pairs. Assign the vertices the sulcus with the label
           pair if they are connected to the label boundary for that pair.
        4. If there are remaining vertices, segment into sets of vertices
           connected to label boundaries, and assign a unique ID to each set.

    Parameters
    ----------
    labels_file : string
        file name for surface mesh VTK containing labels for all vertices
    folds_or_file : list or string
        fold number for each vertex / name of VTK file containing fold scalars
    hemi : string
        hemisphere abbreviation in {'lh', 'rh'} for sulcus labels
    min_boundary : integer
        minimum number of vertices for a sulcus label boundary segment
    sulcus_names : list of strings
        names of sulci
    verbose : bool
        print statements?

    Returns
    -------
    sulci : list of integers
        sulcus numbers for all vertices (-1 for non-sulcus vertices)
    n_sulci : integers
        number of sulci
    sulci_file : string
        output VTK file with sulcus numbers (-1 for non-sulcus vertices)

    Examples
    --------
    >>> from mindboggle.features.sulci import extract_sulci
    >>> from mindboggle.mio.vtks import read_scalars
    >>> from mindboggle.mio.fetch_data import prep_tests
    >>> urls, fetch_data = prep_tests()
    >>> # Load labels, folds, neighbor lists, and sulcus names and label pairs
    >>> labels_file = fetch_data(urls['left_freesurfer_labels'])
    >>> folds_file = fetch_data(urls['left_folds'])
    >>> folds_or_file, name = read_scalars(folds_file)
    >>> hemi = 'lh'
    >>> min_boundary = 10
    >>> sulcus_names = []
    >>> verbose = False
    >>> sulci, n_sulci, sulci_file = extract_sulci(labels_file, folds_or_file,
    ...     hemi, min_boundary, sulcus_names, verbose)
    >>> n_sulci
    23
    >>> lens = [len([x for x in sulci if x == y]) for y in range(n_sulci)]
    >>> lens[0:10]
    [0, 6573, 3366, 6689, 5358, 4049, 6379, 3551, 2632, 4225]
    >>> lens[10::]
    [754, 3724, 2197, 5823, 1808, 5122, 513, 2153, 1445, 418, 0, 3556, 1221]

    View result (skip test):

    >>> from mindboggle.mio.plots import plot_surfaces
    >>> plot_surfaces('sulci.vtk') # doctest: +SKIP

    """
    import os
    from time import time
    import numpy as np

    from mindboggle.mio.vtks import read_scalars, read_vtk, rewrite_scalars
    from mindboggle.guts.mesh import find_neighbors
    from mindboggle.guts.segment import extract_borders, propagate, segment
    from mindboggle.mio.labels import DKTprotocol


    # Load fold numbers if folds_or_file is a string:
    if isinstance(folds_or_file, str):
        folds, name = read_scalars(folds_or_file)
    elif isinstance(folds_or_file, list):
        folds = folds_or_file

    dkt = DKTprotocol()

    if hemi == 'lh':
        pair_lists = dkt.left_sulcus_label_pair_lists
    elif hemi == 'rh':
        pair_lists = dkt.right_sulcus_label_pair_lists
    else:
        raise IOError("Warning: hemisphere not properly specified ('lh' or 'rh').")

    # Load points, faces, and neighbors:
    points, indices, lines, faces, labels, scalar_names, npoints, \
            input_vtk = read_vtk(labels_file)
    neighbor_lists = find_neighbors(faces, npoints)

    # Array of sulcus IDs for fold vertices, initialized as -1.
    # Since we do not touch gyral vertices and vertices whose labels
    # are not in the label list, or vertices having only one label,
    # their sulcus IDs will remain -1:
    sulci = -1 * np.ones(npoints)

    #-------------------------------------------------------------------------
    # Loop through folds
    #-------------------------------------------------------------------------
    fold_numbers = [int(x) for x in np.unique(folds) if x != -1]
    n_folds = len(fold_numbers)
    if verbose:
        print("Extract sulci from {0} folds...".format(n_folds))
    t0 = time()
    for n_fold in fold_numbers:
        fold = [i for i,x in enumerate(folds) if x == n_fold]
        len_fold = len(fold)

        # List the labels in this fold:
        fold_labels = [labels[x] for x in fold]
        unique_fold_labels = [int(x) for x in np.unique(fold_labels)
                              if x != -1]

        #---------------------------------------------------------------------
        # NO MATCH -- fold has fewer than two labels
        #---------------------------------------------------------------------
        if verbose and len(unique_fold_labels) < 2:
            # Ignore: sulci already initialized with -1 values:
            if not unique_fold_labels:
                print("  Fold {0} ({1} vertices): "
                      "NO MATCH -- fold has no labels".
                      format(n_fold, len_fold))
            else:
                print("  Fold {0} ({1} vertices): "
                  "NO MATCH -- fold has only one label ({2})".
                  format(n_fold, len_fold, unique_fold_labels[0]))
            # Ignore: sulci already initialized with -1 values

        else:
            # Find all label boundary pairs within the fold:
            indices_fold_pairs, fold_pairs, unique_fold_pairs = \
                extract_borders(fold, labels, neighbor_lists,
                                ignore_values=[], return_label_pairs=True)

            # Find fold label pairs in the protocol (pairs are already sorted):
            fold_pairs_in_protocol = [x for x in unique_fold_pairs
                                      if x in dkt.unique_sulcus_label_pairs]

            if verbose and unique_fold_labels:
                print("  Fold {0} labels: {1} ({2} vertices)".format(n_fold,
                      ', '.join([str(x) for x in unique_fold_labels]),
                      len_fold))
            #-----------------------------------------------------------------
            # NO MATCH -- fold has no sulcus label pair
            #-----------------------------------------------------------------
            if verbose and not fold_pairs_in_protocol:
                print("  Fold {0}: NO MATCH -- fold has no sulcus label pair".
                      format(n_fold, len_fold))

            #-----------------------------------------------------------------
            # Possible matches
            #-----------------------------------------------------------------
            else:
                if verbose:
                    print("  Fold {0} label pairs in protocol: {1}".format(n_fold,
                          ', '.join([str(x) for x in fold_pairs_in_protocol])))

                # Labels in the protocol (includes repeats across label pairs):
                labels_in_pairs = [x for lst in fold_pairs_in_protocol
                                   for x in lst]

                # Labels that appear in one or more sulcus label boundary:
                unique_labels = []
                nonunique_labels = []
                for label in np.unique(labels_in_pairs):
                    if len([x for x in labels_in_pairs if x == label]) == 1:
                        unique_labels.append(label)
                    else:
                        nonunique_labels.append(label)

                #-------------------------------------------------------------
                # Vertices whose labels are in only one sulcus label pair
                #-------------------------------------------------------------
                # Find vertices with a label that is in only one of the fold's
                # label pairs (the other label in the pair can exist in other
                # pairs). Assign the vertices the sulcus with the label pair
                # if they are connected to the label boundary for that pair.
                #-------------------------------------------------------------
                if unique_labels:

                    for pair in fold_pairs_in_protocol:

                        # If one or both labels in label pair is/are unique:
                        unique_labels_in_pair = [x for x in pair
                                                 if x in unique_labels]
                        n_unique = len(unique_labels_in_pair)
                        if n_unique:

                            ID = None
                            for i, pair_list in enumerate(pair_lists):
                                if not isinstance(pair_list, list):
                                    pair_list = [pair_list]
                                if pair in pair_list:
                                    ID = i
                                    break
                            if ID:
                                # Seeds from label boundary vertices
                                # (fold_pairs and pair already sorted):
                                indices_pair = [x for i,x
                                    in enumerate(indices_fold_pairs)
                                    if fold_pairs[i] == pair]

                                # Vertices with unique label(s) in pair:
                                indices_unique_labels = [fold[i]
                                     for i,x in enumerate(fold_labels)
                                     if x in dkt.unique_sulcus_label_pairs]

                                # Propagate from seeds to labels in label pair:
                                sulci2 = segment(indices_unique_labels,
                                                 neighbor_lists,
                                                 min_region_size=1,
                                                 seed_lists=[indices_pair],
                                                 keep_seeding=False,
                                                 spread_within_labels=True,
                                                 labels=labels)
                                sulci[sulci2 != -1] = ID

                                # Print statement:
                                if verbose:
                                    if n_unique == 1:
                                        ps1 = '1 label'
                                    else:
                                        ps1 = 'Both labels'
                                    if len(sulcus_names):
                                        ps2 = sulcus_names[ID]
                                    else:
                                        ps2 = ''
                                    print("    {0} unique to one fold pair: "
                                          "{1} {2}".
                                          format(ps1, ps2,
                                                 unique_labels_in_pair))

                #-------------------------------------------------------------
                # Vertex labels shared by multiple label pairs
                #-------------------------------------------------------------
                # Propagate labels from label borders to vertices with labels
                # that are shared by multiple label pairs in the fold.
                #-------------------------------------------------------------
                if len(nonunique_labels):
                    # For each label shared by different label pairs:
                    for label in nonunique_labels:
                        # Print statement:
                        if verbose:
                            print("    Propagate sulcus borders with label {0}".
                                  format(int(label)))

                        # Construct seeds from label boundary vertices:
                        seeds = -1 * np.ones(len(points))

                        for ID, pair_list in enumerate(pair_lists):
                            if not isinstance(pair_list, list):
                                pair_list = [pair_list]
                            label_pairs = [x for x in pair_list if label in x]
                            for label_pair in label_pairs:
                                indices_pair = [x for i,x
                                    in enumerate(indices_fold_pairs)
                                    if np.sort(fold_pairs[i]).
                                    tolist() == label_pair]
                                if indices_pair:

                                    # Do not include short boundary segments:
                                    if min_boundary > 1:
                                        indices_pair2 = []
                                        seeds2 = segment(indices_pair,
                                                         neighbor_lists)
                                        useeds2 = [x for x in
                                                   np.unique(seeds2)
                                                   if x != -1]
                                        for seed2 in useeds2:
                                            iseed2 = [i for i,x
                                                      in enumerate(seeds2)
                                                      if x == seed2]
                                            if len(iseed2) >= min_boundary:
                                                indices_pair2.extend(iseed2)
                                            elif verbose:
                                                if len(iseed2) == 1:
                                                    print("    Remove "
                                                          "assignment "
                                                          "of ID {0} from "
                                                          "1 vertex".
                                                          format(seed2))
                                                else:
                                                    print("    Remove "
                                                          "assignment "
                                                          "of ID {0} from "
                                                          "{1} vertices".
                                                          format(seed2,
                                                                 len(iseed2)))
                                        indices_pair = indices_pair2

                                    # Assign sulcus IDs to seeds:
                                    seeds[indices_pair] = ID

                        # Identify vertices with the label:
                        label_array = -1 * np.ones(len(points))
                        indices_label = [fold[i] for i,x
                                         in enumerate(fold_labels)
                                         if x == label]
                        if len(indices_label):
                            label_array[indices_label] = 1

                            # Propagate from seeds to vertices with label:
                            #indices_seeds = []
                            #for seed in range(int(max(seeds))+1):
                            #    indices_seeds.append([i for i,x
                            #                          in enumerate(seeds)
                            #                          if x == seed])
                            #sulci2 = segment(indices_label, neighbor_lists,
                            #                 50, indices_seeds, False, True,
                            #                 labels)
                            sulci2 = propagate(points, faces,
                                               label_array, seeds, sulci,
                                               max_iters=10000,
                                               tol=0.001, sigma=5)
                            sulci[sulci2 != -1] = sulci2[sulci2 != -1]

    sulcus_numbers = [int(x) for x in np.unique(sulci) if x != -1]
                      # if not np.isnan(x)]
    n_sulci = len(sulcus_numbers)

    #-------------------------------------------------------------------------
    # Print statements
    #-------------------------------------------------------------------------
    if verbose:
        print("Extracted {0} sulci from {1} folds ({2:.1f}s):".
                  format(n_sulci, n_folds, time()-t0))
        if sulcus_names:
            for sulcus_number in sulcus_numbers:
                print("  {0}: {1}".format(sulcus_number,
                                          sulcus_names[sulcus_number]))
        elif sulcus_numbers:
            print("  " + ", ".join([str(x) for x in sulcus_numbers]))

        unresolved = [i for i in range(len(pair_lists))
                      if i not in sulcus_numbers]
        if len(unresolved) == 1:
            print("The following sulcus is unaccounted for:")
        else:
            print("The following {0} sulci are unaccounted for:".
                  format(len(unresolved)))
        if sulcus_names:
            for sulcus_number in unresolved:
                print("  {0}: {1}".format(sulcus_number,
                                          sulcus_names[sulcus_number]))
        else:
            print("  " + ", ".join([str(x) for x in unresolved]))

    #-------------------------------------------------------------------------
    # Return sulci, number of sulci, and file name
    #-------------------------------------------------------------------------
    sulci = [int(x) for x in sulci]
    sulci_file = os.path.join(os.getcwd(), 'sulci.vtk')
    rewrite_scalars(labels_file, sulci_file, sulci, 'sulci', sulci)

    if not os.path.exists(sulci_file):
        raise IOError(sulci_file + " not found")

    return sulci, n_sulci, sulci_file
Example #13
0
def close_surface_pair(faces, points1, points2, scalars, background_value=-1):
    """
    Close a surface patch by connecting its border vertices with
    corresponding vertices in a second surface file.

    Assumes no lines or indices when reading VTK files in.

    Note ::

        Scalar values different than background define the surface patch.
        The two sets of points have a 1-to-1 mapping; they are from
        two surfaces whose corresponding vertices are shifted in position.
        For pial vs. gray-white matter, the two surfaces are not parallel,
        so connecting the vertices leads to intersecting faces.

    Parameters
    ----------
    faces : list of lists of integers
        each sublist contains 3 indices of vertices that form a face
        on a surface mesh
    points1 : list of lists of floats
        each sublist contains 3-D coordinates of a vertex on a surface mesh
    points2 : list of lists of floats
        points from second surface with 1-to-1 correspondence with points1
    scalars : numpy array of integers
        labels used to find foreground vertices
    background_value : integer
        scalar value for background vertices

    Returns
    -------
    closed_faces : list of lists of integers
        indices of vertices that form a face on the closed surface mesh
    closed_points : list of lists of floats
        3-D coordinates from points1 and points2
    closed_scalars : list of integers
        scalar values for points1 and points2

    Examples
    --------
    >>> # Example 1: build a cube by closing two parallel planes:
    >>> import os
    >>> from mindboggle.guts.morph import close_surface_pair
    >>> from mindboggle.mio.plots import plot_surfaces
    >>> from mindboggle.mio.vtks import write_vtk
    >>> # Build plane:
    >>> background_value = -1
    >>> n = 10  # plane edge length
    >>> points1 = []
    >>> for x in range(n):
    >>>     for y in range(n):
    >>>         points1.append([x,y,0])
    >>> points2 = [[x[0],x[1],1] for x in points1]
    >>> scalars = [background_value for x in range(len(points1))]
    >>> p = n*(n-1)/2 - 1
    >>> for i in [p, p+1, p+n, p+n+1]:
    >>>     scalars[i] = 1
    >>> faces = []
    >>> for x in range(n-1):
    >>>     for y in range(n-1):
    >>>         faces.append([x+y*n,x+n+y*n,x+n+1+y*n])
    >>>         faces.append([x+y*n,x+1+y*n,x+n+1+y*n])
    >>> #write_vtk('plane.vtk', points1, [], [], faces, scalars)
    >>> #plot_surfaces('plane.vtk') # doctest: +SKIP
    >>> closed_faces, closed_points, closed_scalars = close_surface_pair(faces, points1, points2, scalars, background_value)
    >>> # View:
    >>> write_vtk('cube.vtk', closed_points, [], [], closed_faces, closed_scalars, 'int')
    >>> plot_surfaces('cube.vtk') # doctest: +SKIP
    >>> #
    >>> # Example 2: Gray and white cortical brain surfaces:
    >>> import os
    >>> from mindboggle.guts.morph import close_surface_pair
    >>> from mindboggle.mio.plots import plot_surfaces
    >>> from mindboggle.mio.vtks import read_scalars, read_vtk, read_points, write_vtk
    >>> path = os.environ['MINDBOGGLE_DATA']
    >>> patch_surface1 = 'fold.pial.vtk'
    >>> whole_surface2 = 'fold.white.vtk'
    >>> # Select a single fold:
    >>> folds_file = os.path.join(path, 'arno', 'features', 'folds.vtk')
    >>> points1 = read_points(folds_file)
    >>> scalars, name = read_scalars(folds_file, True, True)
    >>> fold_number = 11
    >>> scalars[scalars != fold_number] = -1
    >>> white_surface = os.path.join(path, 'arno', 'freesurfer', 'lh.white.vtk')
    >>> points2, indices, lines, faces, scalars, scalar_names, npoints, input_vtk = read_vtk(white_surface)
    >>> background_value = -1
    >>> closed_faces, closed_points, closed_scalars = close_surface_pair(faces, points1, points2, scalars, background_value)
    >>> # View:
    >>> write_vtk('closed.vtk', closed_points, [], [], closed_faces, closed_scalars, name, 'int')
    >>> plot_surfaces('closed.vtk') # doctest: +SKIP

    """
    import sys
    import numpy as np

    from mindboggle.guts.mesh import find_neighbors, remove_faces
    from mindboggle.guts.segment import extract_borders

    if isinstance(scalars, list):
        scalars = np.array(scalars)

    N = len(points1)
    closed_points = points1 + points2

    # Find all vertex neighbors and surface patch border vertices:
    neighbor_lists = find_neighbors(faces, N)
    I = np.where(scalars != background_value)[0]
    scalars[scalars == background_value] = background_value + 1
    scalars[I] = background_value + 2
    scalars = scalars.tolist()
    borders, u1, u2 = extract_borders(range(N), scalars, neighbor_lists)
    if not len(borders):
        sys.exit('There are no border vertices!')
    borders = [x for x in borders if x in I]

    # Reindex copy of faces and combine with original (both zero-index):
    indices = range(N)
    indices2 = range(N, 2 * N)
    reindex = dict([(index, indices2[i]) for i, index in enumerate(indices)])
    faces = remove_faces(faces, I)
    faces2 = [[reindex[i] for i in face] for face in faces]
    closed_faces = faces + faces2

    # Connect border vertices between surface patches and add new faces:
    add_faces = []
    taken_already = []
    for index in borders:
        if index not in taken_already:
            neighbors = list(set(neighbor_lists[index]).intersection(borders))
            taken_already.append(index)
            #taken_already.extend([index] + neighbors)
            for neighbor in neighbors:
                add_faces.append([index, index + N, neighbor])
                add_faces.append([index + N, neighbor, neighbor + N])
    closed_faces = closed_faces + add_faces

    closed_scalars = scalars * 2

    return closed_faces, closed_points, closed_scalars
Example #14
0
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
Example #15
0
def close_surface_pair(faces, points1, points2, scalars, background_value=-1):
    """
    Close a surface patch by connecting its border vertices with
    corresponding vertices in a second surface file.

    Assumes no lines or indices when reading VTK files in.

    Note ::

        Scalar values different than background define the surface patch.
        The two sets of points have a 1-to-1 mapping; they are from
        two surfaces whose corresponding vertices are shifted in position.
        For pial vs. gray-white matter, the two surfaces are not parallel,
        so connecting the vertices leads to intersecting faces.

    Parameters
    ----------
    faces : list of lists of integers
        each sublist contains 3 indices of vertices that form a face
        on a surface mesh
    points1 : list of lists of floats
        each sublist contains 3-D coordinates of a vertex on a surface mesh
    points2 : list of lists of floats
        points from second surface with 1-to-1 correspondence with points1
    scalars : numpy array of integers
        labels used to find foreground vertices
    background_value : integer
        scalar value for background vertices

    Returns
    -------
    closed_faces : list of lists of integers
        indices of vertices that form a face on the closed surface mesh
    closed_points : list of lists of floats
        3-D coordinates from points1 and points2
    closed_scalars : list of integers
        scalar values for points1 and points2

    Examples
    --------
    >>> # Example 1: build a cube by closing two parallel planes:
    >>> import os
    >>> from mindboggle.guts.morph import close_surface_pair
    >>> from mindboggle.mio.plots import plot_surfaces
    >>> from mindboggle.mio.vtks import write_vtk
    >>> # Build plane:
    >>> background_value = -1
    >>> n = 10  # plane edge length
    >>> points1 = []
    >>> for x in range(n):
    >>>     for y in range(n):
    >>>         points1.append([x,y,0])
    >>> points2 = [[x[0],x[1],1] for x in points1]
    >>> scalars = [background_value for x in range(len(points1))]
    >>> p = n*(n-1)/2 - 1
    >>> for i in [p, p+1, p+n, p+n+1]:
    >>>     scalars[i] = 1
    >>> faces = []
    >>> for x in range(n-1):
    >>>     for y in range(n-1):
    >>>         faces.append([x+y*n,x+n+y*n,x+n+1+y*n])
    >>>         faces.append([x+y*n,x+1+y*n,x+n+1+y*n])
    >>> #write_vtk('plane.vtk', points1, [], [], faces, scalars)
    >>> #plot_surfaces('plane.vtk') # doctest: +SKIP
    >>> closed_faces, closed_points, closed_scalars = close_surface_pair(faces, points1, points2, scalars, background_value)
    >>> # View:
    >>> write_vtk('cube.vtk', closed_points, [], [], closed_faces, closed_scalars, 'int')
    >>> plot_surfaces('cube.vtk') # doctest: +SKIP
    >>> #
    >>> # Example 2: Gray and white cortical brain surfaces:
    >>> import os
    >>> from mindboggle.guts.morph import close_surface_pair
    >>> from mindboggle.mio.plots import plot_surfaces
    >>> from mindboggle.mio.vtks import read_scalars, read_vtk, read_points, write_vtk
    >>> path = os.environ['MINDBOGGLE_DATA']
    >>> patch_surface1 = 'fold.pial.vtk'
    >>> whole_surface2 = 'fold.white.vtk'
    >>> # Select a single fold:
    >>> folds_file = os.path.join(path, 'arno', 'features', 'folds.vtk')
    >>> points1 = read_points(folds_file)
    >>> scalars, name = read_scalars(folds_file, True, True)
    >>> fold_number = 11
    >>> scalars[scalars != fold_number] = -1
    >>> white_surface = os.path.join(path, 'arno', 'freesurfer', 'lh.white.vtk')
    >>> faces, u1, u2, points2, N, u3, u4, u5 = read_vtk(white_surface)
    >>> background_value = -1
    >>> closed_faces, closed_points, closed_scalars = close_surface_pair(faces, points1, points2, scalars, background_value)
    >>> # View:
    >>> write_vtk('closed.vtk', closed_points, [], [], closed_faces, closed_scalars, name, 'int')
    >>> plot_surfaces('closed.vtk') # doctest: +SKIP

    """
    import sys
    import numpy as np

    from mindboggle.guts.mesh import find_neighbors, remove_faces
    from mindboggle.guts.segment import extract_borders

    if isinstance(scalars, list):
        scalars = np.array(scalars)

    N = len(points1)
    closed_points = points1 + points2

    # Find all vertex neighbors and surface patch border vertices:
    neighbor_lists = find_neighbors(faces, N)
    I = np.where(scalars != background_value)[0]
    scalars[scalars == background_value] = background_value + 1
    scalars[I] = background_value + 2
    scalars = scalars.tolist()
    borders, u1, u2 = extract_borders(range(N), scalars, neighbor_lists)
    if not len(borders):
        sys.exit('There are no border vertices!')
    borders = [x for x in borders if x in I]

    # Reindex copy of faces and combine with original (both zero-index):
    indices = range(N)
    indices2 = range(N, 2 * N)
    reindex = dict([(index, indices2[i]) for i, index in enumerate(indices)])
    faces = remove_faces(faces, I)
    faces2 = [[reindex[i] for i in face] for face in faces]
    closed_faces = faces + faces2

    # Connect border vertices between surface patches and add new faces:
    add_faces = []
    taken_already = []
    for index in borders:
        if index not in taken_already:
            neighbors = list(set(neighbor_lists[index]).intersection(borders))
            taken_already.append(index)
            #taken_already.extend([index] + neighbors)
            for neighbor in neighbors:
                add_faces.append([index, index + N, neighbor])
                add_faces.append([index + N, neighbor, neighbor + N])
    closed_faces = closed_faces + add_faces

    closed_scalars = scalars * 2

    return closed_faces, closed_points, closed_scalars