Example #1
0
def create_spherical_roi_volumes(node_size, coords, template_mask):
    from pynets.nodemaker import get_sphere
    mask_img = nib.load(template_mask)
    mask_aff = mask_img.affine

    print("%s%s" % ('Creating spherical ROI atlas with radius: ', node_size))

    def mmToVox(nib_nifti, mmcoords):
        return nib.affines.apply_affine(np.linalg.inv(nib_nifti.affine),
                                        mmcoords)

    coords_vox = []
    for i in coords:
        coords_vox.append(mmToVox(mask_img, i))
    coords_vox = list(set(list(tuple(x) for x in coords_vox)))

    x_vox = np.diagonal(mask_img.affine[:3, 0:3])[0]
    y_vox = np.diagonal(mask_img.affine[:3, 0:3])[1]
    z_vox = np.diagonal(mask_img.affine[:3, 0:3])[2]
    sphere_vol = np.zeros(mask_img.shape, dtype=bool)
    parcel_list = []
    i = 0
    for coord in coords_vox:
        inds = get_sphere(coord, node_size, (np.abs(x_vox), y_vox, z_vox),
                          mask_img.shape)
        sphere_vol[tuple(inds.T)] = i * 1
        parcel_list.append(
            nib.Nifti1Image(sphere_vol.astype('int'), affine=mask_aff))
        i = i + 1

    par_max = len(coords)
    parc = True
    return parcel_list, par_max, node_size, parc
Example #2
0
def extract_ts_coords_fast(node_size, conf, func_file, coords, dir_path):
    import nibabel as nib
    #import time
    import subprocess
    try:
        from StringIO import StringIO
    except ImportError:
        from io import BytesIO as StringIO
    from pynets.nodemaker import get_sphere
    from pynets.graphestimation import generate_mask_from_voxels, normalize
    #start_time=time.time()
    data = nib.load(func_file)
    activity_data = data.get_data()
    volume_dims = np.shape(activity_data)[0:3]
    ref_affine = data.get_qform()
    ref_header = data.header
    vox_dims = tuple(ref_header['pixdim'][1:4])
    #print('Data loaded: t+'+str(time.time()-start_time)+'s')
    label_mask = np.zeros(volume_dims)
    label_file = "%s%s" % (dir_path, '/label_file_tmp.nii.gz')

    [x_vox, y_vox, z_vox] = vox_dims
    # finding sphere voxels
    print(len(coords))

    def mmToVox(mmcoords):
        voxcoords = ['', '', '']
        voxcoords[0] = int((round(int(mmcoords[0]) / x_vox)) + 45)
        voxcoords[1] = int((round(int(mmcoords[1]) / y_vox)) + 63)
        voxcoords[2] = int((round(int(mmcoords[2]) / z_vox)) + 36)
        return voxcoords

    coords_vox = []
    for i in coords:
        coords_vox.append(mmToVox(i))
    coords = list(tuple(x) for x in coords_vox)

    for coord in range(len(coords)):
        sphere_voxels = get_sphere(coords=coords[coord],
                                   r=node_size,
                                   vox_dims=vox_dims,
                                   dims=volume_dims)
        # creating mask from found voxels
        sphere_mask = generate_mask_from_voxels(sphere_voxels, volume_dims)
        label_mask = label_mask + (coord + 1) * sphere_mask
        #print('Sphere voxels found: t+'+str(time.time()-start_time)+'s')

    nib.save(nib.Nifti1Image(label_mask, ref_affine, header=ref_header),
             label_file)
    #print('Sphere mask saved: t+'+str(time.time()-start_time)+'s')
    cmd = "%s%s%s%s" % ('fslmeants -i ', func_file, ' --label=', label_file)
    stdout_extracted_ts = subprocess.check_output(cmd, shell=True)
    ts_within_nodes = np.loadtxt(StringIO(stdout_extracted_ts))
    ts_within_nodes = normalize(ts_within_nodes)
    #print('Mean time series extracted: '+str(time.time()-start_time)+'s')
    #print('Number of ROIs expected: '+str(len(coords)))
    #print('Number of ROIs found: '+str(ts_within_nodes.shape[1]))
    return (ts_within_nodes)
Example #3
0
def create_spherical_roi_volumes(node_size, coords, template_mask):
    """
    Create volume ROI mask of spheres from a given set of coordinates and radius.

    Parameters
    ----------
    node_size : int
        Spherical centroid node size in the case that coordinate-based centroids
        are used as ROI's for tracking.
    coords : list
        List of (x, y, z) tuples in mm-space corresponding to a coordinate atlas used or
        which represent the center-of-mass of each parcellation node..
    template_mask : str
        Path to binarized version of standard (MNI)-space template Nifti1Image file.

    Returns
    -------
    parcel_list : list
        List of 3D boolean numpy arrays or binarized Nifti1Images corresponding to ROI masks.
    par_max : int
        The maximum label intensity in the parcellation image.
    node_size : int
        Spherical centroid node size in the case that coordinate-based centroids
        are used as ROI's for tracking.
    parc : bool
        Indicates whether to use the raw parcels as ROI nodes instead of coordinates at their center-of-mass.
    """
    from pynets.nodemaker import get_sphere, mmToVox
    mask_img = nib.load(template_mask)
    mask_aff = mask_img.affine

    print("%s%s" % ('Creating spherical ROI atlas with radius: ', node_size))

    coords_vox = []
    for i in coords:
        coords_vox.append(mmToVox(mask_aff, i))
    coords_vox = list(set(list(tuple(x) for x in coords_vox)))

    x_vox = np.diagonal(mask_img.affine[:3, 0:3])[0]
    y_vox = np.diagonal(mask_img.affine[:3, 0:3])[1]
    z_vox = np.diagonal(mask_img.affine[:3, 0:3])[2]
    sphere_vol = np.zeros(mask_img.shape, dtype=bool)
    parcel_list = []
    i = 0
    for coord in coords_vox:
        inds = get_sphere(coord, node_size, (np.abs(x_vox), y_vox, z_vox),
                          mask_img.shape)
        sphere_vol[tuple(inds.T)] = i * 1
        parcel_list.append(
            nib.Nifti1Image(sphere_vol.astype('int'), affine=mask_aff))
        i = i + 1

    par_max = len(coords)
    parc = True
    return parcel_list, par_max, node_size, parc
Example #4
0
def coord_masker(mask, coords, label_names, error):
    from nilearn import masking
    x_vox = np.diagonal(masking._load_mask_img(mask)[1][:3, 0:3])[0]
    y_vox = np.diagonal(masking._load_mask_img(mask)[1][:3, 0:3])[1]
    z_vox = np.diagonal(masking._load_mask_img(mask)[1][:3, 0:3])[2]

    def mmToVox(mmcoords):
        voxcoords = ['', '', '']
        voxcoords[0] = int((round(int(mmcoords[0]) / x_vox)) + 45)
        voxcoords[1] = int((round(int(mmcoords[1]) / y_vox)) + 63)
        voxcoords[2] = int((round(int(mmcoords[2]) / z_vox)) + 36)

        return voxcoords

    mask_data, _ = masking._load_mask_img(mask)
    #    mask_coords = list(zip(*np.where(mask_data == True)))
    coords_vox = []
    for i in coords:
        coords_vox.append(mmToVox(i))
    coords_vox = list(tuple(x) for x in coords_vox)

    bad_coords = []
    for coord in coords_vox:
        sphere_vol = np.zeros(mask_data.shape, dtype=bool)
        sphere_vol[tuple(coord)] = 1
        if (mask_data & sphere_vol).any():
            print("%s%s" % (coord, ' falls within mask...'))
            continue
        inds = get_sphere(coord, error, (np.abs(x_vox), y_vox, z_vox),
                          mask_data.shape)
        sphere_vol[tuple(inds.T)] = 1
        if (mask_data & sphere_vol).any():
            print("%s%s%.2f%s" % (coord, ' is within a + or - ', float(error),
                                  ' mm neighborhood...'))
            continue
        bad_coords.append(coord)

    bad_coords = [x for x in bad_coords if x is not None]
    indices = []
    for bad_coord in bad_coords:
        indices.append(coords_vox.index(bad_coord))

    label_names = list(label_names)
    coords = list(tuple(x) for x in coords)
    for ix in sorted(indices, reverse=True):
        print("%s%s%s%s" % ('Removing: ', label_names[ix], ' at ', coords[ix]))
        label_names.pop(ix)
        coords.pop(ix)
    return coords, label_names
Example #5
0
def get_node_membership(network, func_file, coords, label_names, parc, parcel_list):
    from nilearn.image import resample_img
    from pynets.nodemaker import get_sphere
    import pkg_resources
    import pandas as pd
    ##For parcel membership determination, specify overlap thresh and error cushion in mm voxels
    perc_overlap = 0.75 ##Default is >=90% overlap
    error = 2

    ##Load subject func data
    bna_img = nib.load(func_file)

    x_vox = np.diagonal(bna_img.affine[:3,0:3])[0]
    y_vox = np.diagonal(bna_img.affine[:3,0:3])[1]
    z_vox = np.diagonal(bna_img.affine[:3,0:3])[2]

    ##Determine whether input is from 17-networks or 7-networks
    seven_nets = [ 'Vis', 'SomMot', 'DorsAttn', 'SalVentAttn', 'Limbic', 'Cont', 'Default' ]
    seventeen_nets = ['VisCent', 'VisPeri', 'SomMotA', 'SomMotB', 'DorsAttnA', 'DorsAttnB', 'SalVentAttnA', 'SalVentAttnB', 'LimbicOFC', 'LimbicTempPole', 'ContA', 'ContB', 'ContC', 'DefaultA', 'DefaultB', 'DefaultC', 'TempPar']

    if network in seventeen_nets:
        if x_vox <= 1 and y_vox <= 1 and z_vox <=1:
            par_file = pkg_resources.resource_filename("pynets", "rsnrefs/BIGREF1mm.nii.gz")
        else:
            par_file = pkg_resources.resource_filename("pynets", "rsnrefs/BIGREF2mm.nii.gz")

        ##Grab RSN reference file
        nets_ref_txt = pkg_resources.resource_filename("pynets", "rsnrefs/Schaefer2018_1000_17nets_ref.txt")
    elif network in seven_nets:
        if x_vox <= 1 and y_vox <= 1 and z_vox <=1:
            par_file = pkg_resources.resource_filename("pynets", "rsnrefs/SMALLREF1mm.nii.gz")
        else:
            par_file = pkg_resources.resource_filename("pynets", "rsnrefs/SMALLREF2mm.nii.gz")

        ##Grab RSN reference file
        nets_ref_txt = pkg_resources.resource_filename("pynets", "rsnrefs/Schaefer2018_1000_7nets_ref.txt")
    else:
        nets_ref_txt = None

    if not nets_ref_txt:
        raise ValueError('Network: ' + str(network) + ' not found!\nSee valid network names using the --help flag with pynets_run.py')

    ##Create membership dictionary
    dict_df = pd.read_csv(nets_ref_txt, sep="\t", header=None, names=["Index", "Region", "X", "Y", "Z"])
    dict_df.Region.unique().tolist()
    ref_dict = {v: k for v, k in enumerate(dict_df.Region.unique().tolist())}

    par_img = nib.load(par_file)
    par_data = par_img.get_data()

    RSN_ix=list(ref_dict.keys())[list(ref_dict.values()).index(network)]
    RSNmask = par_data[:,:,:,RSN_ix]

    def mmToVox(mmcoords):
        voxcoords = ['','','']
        voxcoords[0] = int((round(int(mmcoords[0])/x_vox))+45)
        voxcoords[1] = int((round(int(mmcoords[1])/y_vox))+63)
        voxcoords[2] = int((round(int(mmcoords[2])/z_vox))+36)
        return voxcoords

    def VoxTomm(voxcoords):
        mmcoords = ['','','']
        mmcoords[0] = int((round(int(voxcoords[0])-45)*x_vox))
        mmcoords[1] = int((round(int(voxcoords[1])-63)*y_vox))
        mmcoords[2] = int((round(int(voxcoords[2])-36)*z_vox))
        return mmcoords

    coords_vox = []
    for i in coords:
        coords_vox.append(mmToVox(i))
    coords_vox = list(tuple(x) for x in coords_vox)

    if parc == False:
        i = -1
        RSN_parcels = None
        RSN_coords_vox = []
        net_label_names = []
        for coord in coords_vox:
            sphere_vol = np.zeros(RSNmask.shape, dtype=bool)
            sphere_vol[tuple(coord)] = 1
            i = i + 1
            if (RSNmask.astype('bool') & sphere_vol).any():
                print(str(coord) + ' coord falls within ' + network + '...')
                RSN_coords_vox.append(coord)
                net_label_names.append(label_names[i])
                continue
            else:
                inds = get_sphere(coord, error, (np.abs(x_vox), y_vox, z_vox), RSNmask.shape)
                sphere_vol[tuple(inds.T)] = 1
                if (RSNmask.astype('bool') & sphere_vol).any():
                    print(str(coord) + ' coord is within a + or - ' + str(error) + ' mm neighborhood of ' + network + '...')
                    RSN_coords_vox.append(coord)
                    net_label_names.append(label_names[i])
        coords_mm = []
        for i in RSN_coords_vox:
            coords_mm.append(VoxTomm(i))
        coords_mm = list(set(list(tuple(x) for x in coords_mm)))
    else:
        i = 0
        RSN_parcels = []
        coords_with_parc = []
        net_label_names = []
        for parcel in parcel_list:
            parcel_vol = np.zeros(RSNmask.shape, dtype=bool)
            parcel_data_reshaped = resample_img(parcel, target_affine=par_img.affine,
                               target_shape=RSNmask.shape).get_data()
            parcel_vol[parcel_data_reshaped==1] = 1
            
            ##Count number of unique voxels where overlap of parcel and mask occurs
            overlap_count = len(np.unique(np.where((RSNmask.astype('uint8')==1) & (parcel_vol.astype('uint8')==1))))
            
            ##Count number of total unique voxels within the parcel
            total_count = len(np.unique(np.where(((parcel_vol.astype('uint8')==1)))))
       
            ##Calculate % overlap  
            try:
                overlap = float(overlap_count/total_count)
            except:
                overlap = float(0)
            
            if overlap >=perc_overlap:
                print(str(round(100*overlap,1)) + '% of parcel ' + str(label_names[i]) + ' falls within ' + str(network) + ' mask...')
                RSN_parcels.append(parcel)
                coords_with_parc.append(coords[i])
                net_label_names.append(label_names[i])
            i = i + 1
        coords_mm = list(set(list(tuple(x) for x in coords_with_parc)))
        
    return(coords_mm, RSN_parcels, net_label_names, network)
Example #6
0
def coords_masker(roi, coords, label_names, mask, error):
    from nilearn import masking

    if mask and roi == mask:
        error = 0

    mask_data, mask_aff = masking._load_mask_img(roi)
    x_vox = np.diagonal(mask_aff[:3, 0:3])[0]
    y_vox = np.diagonal(mask_aff[:3, 0:3])[1]
    z_vox = np.diagonal(mask_aff[:3, 0:3])[2]

    def mmToVox(mask_aff, mmcoords):
        return nib.affines.apply_affine(np.linalg.inv(mask_aff), mmcoords)


#    mask_coords = list(zip(*np.where(mask_data == True)))

    coords_vox = []
    for i in coords:
        coords_vox.append(mmToVox(mask_aff, i))
    coords_vox = list(
        tuple(map(lambda y: isinstance(y, float) and int(round(y, 0)), x))
        for x in coords_vox)
    bad_coords = []
    for coords in coords_vox:
        sphere_vol = np.zeros(mask_data.shape, dtype=bool)
        sphere_vol[tuple(coords)] = 1
        if (mask_data & sphere_vol).any():
            print("%s%s" % (coords, ' falls within roi mask...'))
            continue
        inds = get_sphere(coords, error, (np.abs(x_vox), y_vox, z_vox),
                          mask_data.shape)
        sphere_vol[tuple(inds.T)] = 1
        if (mask_data & sphere_vol).any():
            print("%s%s%.2f%s" % (coords, ' is within a + or - ', float(error),
                                  ' mm neighborhood...'))
            continue
        bad_coords.append(coords)

    bad_coords = [x for x in bad_coords if x is not None]
    indices = []
    for bad_coords in bad_coords:
        indices.append(coords_vox.index(bad_coords))

    label_names = list(label_names)
    coords = list(tuple(x) for x in coords_vox)
    try:
        for ix in sorted(indices, reverse=True):
            print("%s%s%s%s" %
                  ('Removing: ', label_names[ix], ' at ', coords[ix]))
            label_names.pop(ix)
            coords.pop(ix)
    except RuntimeError:
        print(
            'ERROR: Restrictive masking. No coords remain after masking with brain mask/roi...'
        )

    if len(coords) <= 1:
        raise ValueError(
            '\nERROR: ROI mask was likely too restrictive and yielded < 2 remaining coords'
        )

    return coords, label_names
Example #7
0
def get_node_membership(network,
                        infile,
                        coords,
                        label_names,
                        parc,
                        parcel_list,
                        perc_overlap=0.75,
                        error=2):
    from nilearn.image import resample_img
    from pynets.nodemaker import get_sphere
    import pkg_resources
    import pandas as pd

    # Determine whether input is from 17-networks or 7-networks
    seven_nets = [
        'Vis', 'SomMot', 'DorsAttn', 'SalVentAttn', 'Limbic', 'Cont', 'Default'
    ]
    seventeen_nets = [
        'VisCent', 'VisPeri', 'SomMotA', 'SomMotB', 'DorsAttnA', 'DorsAttnB',
        'SalVentAttnA', 'SalVentAttnB', 'LimbicOFC', 'LimbicTempPole', 'ContA',
        'ContB', 'ContC', 'DefaultA', 'DefaultB', 'DefaultC', 'TempPar'
    ]

    # Load subject func data
    bna_img = nib.load(infile)
    x_vox = np.diagonal(bna_img.affine[:3, 0:3])[0]
    y_vox = np.diagonal(bna_img.affine[:3, 0:3])[1]
    z_vox = np.diagonal(bna_img.affine[:3, 0:3])[2]

    if network in seventeen_nets:
        if x_vox <= 1 and y_vox <= 1 and z_vox <= 1:
            par_file = pkg_resources.resource_filename(
                "pynets", "rsnrefs/BIGREF1mm.nii.gz")
        else:
            par_file = pkg_resources.resource_filename(
                "pynets", "rsnrefs/BIGREF2mm.nii.gz")

        # Grab RSN reference file
        nets_ref_txt = pkg_resources.resource_filename(
            "pynets", "rsnrefs/Schaefer2018_1000_17nets_ref.txt")
    elif network in seven_nets:
        if x_vox <= 1 and y_vox <= 1 and z_vox <= 1:
            par_file = pkg_resources.resource_filename(
                "pynets", "rsnrefs/SMALLREF1mm.nii.gz")
        else:
            par_file = pkg_resources.resource_filename(
                "pynets", "rsnrefs/SMALLREF2mm.nii.gz")

        # Grab RSN reference file
        nets_ref_txt = pkg_resources.resource_filename(
            "pynets", "rsnrefs/Schaefer2018_1000_7nets_ref.txt")
    else:
        nets_ref_txt = None

    if not nets_ref_txt:
        raise ValueError(
            "%s%s%s" %
            ('Network: ', str(network),
             ' not found!\nSee valid network names using the --help '
             'flag with pynets_run.py'))

    # Create membership dictionary
    dict_df = pd.read_csv(nets_ref_txt,
                          sep="\t",
                          header=None,
                          names=["Index", "Region", "X", "Y", "Z"])
    dict_df.Region.unique().tolist()
    ref_dict = {v: k for v, k in enumerate(dict_df.Region.unique().tolist())}
    par_img = nib.load(par_file)
    par_data = par_img.get_fdata()
    RSN_ix = list(ref_dict.keys())[list(ref_dict.values()).index(network)]
    RSNmask = par_data[:, :, :, RSN_ix]

    def mmToVox(nib_nifti, mmcoords):
        return nib.affines.apply_affine(np.linalg.inv(nib_nifti.affine),
                                        mmcoords)

    def VoxTomm(nib_nifti, voxcoords):
        return nib.affines.apply_affine(nib_nifti.affine, voxcoords)

    coords_vox = []
    for i in coords:
        coords_vox.append(mmToVox(bna_img, i))
    coords_vox = list(
        tuple(map(lambda y: isinstance(y, float) and int(round(y, 0)), x))
        for x in coords_vox)
    if parc is False:
        i = -1
        RSN_parcels = None
        RSN_coords_vox = []
        net_label_names = []
        for coords in coords_vox:
            sphere_vol = np.zeros(RSNmask.shape, dtype=bool)
            sphere_vol[tuple(coords)] = 1
            i = i + 1
            if (RSNmask.astype('bool') & sphere_vol).any():
                print("%s%s%s%s" %
                      (coords, ' coords falls within ', network, '...'))
                RSN_coords_vox.append(coords)
                net_label_names.append(label_names[i])
                continue
            else:
                inds = get_sphere(coords, error, (np.abs(x_vox), y_vox, z_vox),
                                  RSNmask.shape)
                sphere_vol[tuple(inds.T)] = 1
                if (RSNmask.astype('bool') & sphere_vol).any():
                    print("%s%s%.2f%s%s%s" %
                          (coords, ' coords is within a + or - ', float(error),
                           ' mm neighborhood of ', network, '...'))
                    RSN_coords_vox.append(coords)
                    net_label_names.append(label_names[i])

        coords_mm = []
        for i in RSN_coords_vox:
            coords_mm.append(VoxTomm(bna_img, i))
        coords_mm = list(set(list(tuple(x) for x in coords_mm)))
    else:
        i = 0
        RSN_parcels = []
        coords_with_parc = []
        net_label_names = []
        for parcel in parcel_list:
            parcel_vol = np.zeros(RSNmask.shape, dtype=bool)
            parcel_data_reshaped = resample_img(
                parcel,
                target_affine=par_img.affine,
                target_shape=RSNmask.shape).get_fdata()
            parcel_vol[parcel_data_reshaped == 1] = 1
            # Count number of unique voxels where overlap of parcel and mask occurs
            overlap_count = len(
                np.unique(
                    np.where((RSNmask.astype('uint8') == 1)
                             & (parcel_vol.astype('uint8') == 1))))
            # Count number of total unique voxels within the parcel
            total_count = len(
                np.unique(np.where((parcel_vol.astype('uint8') == 1))))
            # Calculate % overlap
            try:
                overlap = float(overlap_count / total_count)
            except RuntimeWarning:
                print('\nWarning: No overlap with roi mask!\n')
                overlap = float(0)

            if overlap >= perc_overlap:
                print("%.2f%s%s%s%s%s" %
                      (100 * overlap, '% of parcel ', label_names[i],
                       ' falls within ', str(network), ' mask...'))
                RSN_parcels.append(parcel)
                coords_with_parc.append(coords[i])
                net_label_names.append(label_names[i])
            i = i + 1
        coords_mm = list(set(list(tuple(x) for x in coords_with_parc)))

    bna_img.uncache()
    if len(coords_mm) <= 1:
        raise ValueError(
            "%s%s%s" %
            ('\nERROR: No coords from the specified atlas found within ',
             network, ' network.'))

    return coords_mm, RSN_parcels, net_label_names, network
Example #8
0
def coords_masker(roi, coords, labels, error):
    """
    Evaluate the affinity of any arbitrary list of coordinate nodes for a user-specified ROI mask.

    Parameters
    ----------
    roi : str
        File path to binarized/boolean region-of-interest Nifti1Image file.
    coords : list
        List of (x, y, z) tuples in mm-space corresponding to a coordinate atlas used or
        which represent the center-of-mass of each parcellation node.
    labels : list
        List of string labels corresponding to ROI nodes.
    error : int
        Rounded euclidean distance, in units of voxel number, to use as a spatial error cushion in the case of
        evaluating the spatial affinity of a given list of coordinates to the given ROI mask.

    Returns
    -------
    coords : list
        Filtered list of (x, y, z) tuples in mm-space with a spatial affinity for the specified ROI mask.
    labels : list
        Filtered list of string labels corresponding to ROI nodes with a spatial affinity for the specified ROI mask.
    """
    from nilearn import masking
    from pynets.nodemaker import mmToVox

    mask_data, mask_aff = masking._load_mask_img(roi)
    x_vox = np.diagonal(mask_aff[:3, 0:3])[0]
    y_vox = np.diagonal(mask_aff[:3, 0:3])[1]
    z_vox = np.diagonal(mask_aff[:3, 0:3])[2]

    #    mask_coords = list(zip(*np.where(mask_data == True)))
    coords_vox = []
    for i in coords:
        coords_vox.append(mmToVox(mask_aff, i))
    coords_vox = list(
        tuple(map(lambda y: isinstance(y, float) and int(round(y, 0)), x))
        for x in coords_vox)
    bad_coords = []
    for coords in coords_vox:
        sphere_vol = np.zeros(mask_data.shape, dtype=bool)
        sphere_vol[tuple(coords)] = 1
        if (mask_data & sphere_vol).any():
            print("%s%s" % (coords, ' falls within mask...'))
            continue
        inds = get_sphere(coords, error, (np.abs(x_vox), y_vox, z_vox),
                          mask_data.shape)
        sphere_vol[tuple(inds.T)] = 1
        if (mask_data & sphere_vol).any():
            print("%s%s%.2f%s" % (coords, ' is within a + or - ', float(error),
                                  ' mm neighborhood...'))
            continue
        bad_coords.append(coords)

    bad_coords = [x for x in bad_coords if x is not None]
    indices = []
    for bad_coords in bad_coords:
        indices.append(coords_vox.index(bad_coords))

    labels = list(labels)
    coords = list(tuple(x) for x in coords_vox)
    try:
        for ix in sorted(indices, reverse=True):
            print("%s%s%s%s" % ('Removing: ', labels[ix], ' at ', coords[ix]))
            labels.pop(ix)
            coords.pop(ix)
    except RuntimeError:
        print(
            'ERROR: Restrictive masking. No coords remain after masking with brain mask/roi...'
        )

    if len(coords) <= 1:
        raise ValueError(
            '\nERROR: ROI mask was likely too restrictive and yielded < 2 remaining coords'
        )

    return coords, labels
Example #9
0
def get_node_membership(network,
                        infile,
                        coords,
                        labels,
                        parc,
                        parcel_list,
                        perc_overlap=0.75,
                        error=4):
    """
    Evaluate the affinity of any arbitrary list of coordinate or parcel nodes for a user-specified RSN based on
    Yeo-7 or Yeo-17 definitions.

    Parameters
    ----------
    network : str
        Resting-state network based on Yeo-7 and Yeo-17 naming (e.g. 'Default') used to filter nodes in the study of
        brain subgraphs.
    infile : str
        File path to Nifti1Image object whose affine will provide sampling reference for evaluation spatial proximity.
    coords : list
        List of (x, y, z) tuples in mm-space corresponding to a coordinate atlas used or
        which represent the center-of-mass of each parcellation node.
    labels : list
        List of string labels corresponding to ROI nodes.
    parc : bool
        Indicates whether to use parcels instead of coordinates as ROI nodes.
    parcel_list : list
        List of 3D boolean numpy arrays or binarized Nifti1Images corresponding to ROI masks.
    perc_overlap : float
        Value 0-1 indicating a threshold of spatial overlap to use as a spatial error cushion in the case of
        evaluating RSN membership from a given list of parcel masks. Default is 0.75.
    error : int
        Rounded euclidean distance, in units of voxel number, to use as a spatial error cushion in the case of
        evaluating RSN membership from a given list of coordinates. Default is 4.

    Returns
    -------
    coords_mm : list
        Filtered list of (x, y, z) tuples in mm-space with a spatial affinity for the specified RSN.
    RSN_parcels : list
        Filtered list of 3D boolean numpy arrays or binarized Nifti1Images corresponding to ROI masks with a spatial
        affinity for the specified RSN.
    net_labels : list
        Filtered list of string labels corresponding to ROI nodes with a spatial affinity for the specified RSN.
    network : str
        Resting-state network based on Yeo-7 and Yeo-17 naming (e.g. 'Default') used to filter nodes in the study of
        brain subgraphs.
    """
    from nilearn.image import resample_img
    from pynets.nodemaker import get_sphere, mmToVox, VoxTomm
    import pkg_resources
    import pandas as pd

    # Determine whether input is from 17-networks or 7-networks
    seven_nets = [
        'Vis', 'SomMot', 'DorsAttn', 'SalVentAttn', 'Limbic', 'Cont', 'Default'
    ]
    seventeen_nets = [
        'VisCent', 'VisPeri', 'SomMotA', 'SomMotB', 'DorsAttnA', 'DorsAttnB',
        'SalVentAttnA', 'SalVentAttnB', 'LimbicOFC', 'LimbicTempPole', 'ContA',
        'ContB', 'ContC', 'DefaultA', 'DefaultB', 'DefaultC', 'TempPar'
    ]

    # Load subject func data
    bna_img = nib.load(infile)
    x_vox = np.diagonal(bna_img.affine[:3, 0:3])[0]
    y_vox = np.diagonal(bna_img.affine[:3, 0:3])[1]
    z_vox = np.diagonal(bna_img.affine[:3, 0:3])[2]

    if network in seventeen_nets:
        if x_vox <= 1 and y_vox <= 1 and z_vox <= 1:
            par_file = pkg_resources.resource_filename(
                "pynets", "rsnrefs/BIGREF1mm.nii.gz")
        else:
            par_file = pkg_resources.resource_filename(
                "pynets", "rsnrefs/BIGREF2mm.nii.gz")

        # Grab RSN reference file
        nets_ref_txt = pkg_resources.resource_filename(
            "pynets", "rsnrefs/Schaefer2018_1000_17nets_ref.txt")
    elif network in seven_nets:
        if x_vox <= 1 and y_vox <= 1 and z_vox <= 1:
            par_file = pkg_resources.resource_filename(
                "pynets", "rsnrefs/SMALLREF1mm.nii.gz")
        else:
            par_file = pkg_resources.resource_filename(
                "pynets", "rsnrefs/SMALLREF2mm.nii.gz")

        # Grab RSN reference file
        nets_ref_txt = pkg_resources.resource_filename(
            "pynets", "rsnrefs/Schaefer2018_1000_7nets_ref.txt")
    else:
        nets_ref_txt = None

    if not nets_ref_txt:
        raise ValueError(
            "%s%s%s" %
            ('Network: ', str(network),
             ' not found!\nSee valid network names using the --help '
             'flag with pynets_run.py'))

    # Create membership dictionary
    dict_df = pd.read_csv(nets_ref_txt,
                          sep="\t",
                          header=None,
                          names=["Index", "Region", "X", "Y", "Z"])
    dict_df.Region.unique().tolist()
    ref_dict = {v: k for v, k in enumerate(dict_df.Region.unique().tolist())}
    par_img = nib.load(par_file)
    par_data = par_img.get_fdata()
    RSN_ix = list(ref_dict.keys())[list(ref_dict.values()).index(network)]
    RSNmask = par_data[:, :, :, RSN_ix]
    bna_aff = bna_img.affine

    coords_vox = []
    for i in coords:
        coords_vox.append(mmToVox(bna_aff, i))
    coords_vox = list(
        tuple(map(lambda y: isinstance(y, float) and int(round(y, 0)), x))
        for x in coords_vox)
    if parc is False:
        i = -1
        RSN_parcels = None
        RSN_coords_vox = []
        net_labels = []
        for coords in coords_vox:
            sphere_vol = np.zeros(RSNmask.shape, dtype=bool)
            sphere_vol[tuple(coords)] = 1
            i = i + 1
            if (RSNmask.astype('bool') & sphere_vol).any():
                print("%s%s%s%s" %
                      (coords, ' coords falls within ', network, '...'))
                RSN_coords_vox.append(coords)
                net_labels.append(labels[i])
                continue
            else:
                inds = get_sphere(coords, error, (np.abs(x_vox), y_vox, z_vox),
                                  RSNmask.shape)
                sphere_vol[tuple(inds.T)] = 1
                if (RSNmask.astype('bool') & sphere_vol).any():
                    print("%s%s%.2f%s%s%s" %
                          (coords, ' coords is within a + or - ', float(error),
                           ' mm neighborhood of ', network, '...'))
                    RSN_coords_vox.append(coords)
                    net_labels.append(labels[i])

        coords_mm = []
        for i in RSN_coords_vox:
            coords_mm.append(VoxTomm(bna_aff, i))
        coords_mm = list(set(list(tuple(x) for x in coords_mm)))
    else:
        i = 0
        RSN_parcels = []
        coords_with_parc = []
        net_labels = []
        for parcel in parcel_list:
            parcel_vol = np.zeros(RSNmask.shape, dtype=bool)
            parcel_data_reshaped = resample_img(
                parcel,
                target_affine=par_img.affine,
                target_shape=RSNmask.shape).get_fdata()
            parcel_vol[parcel_data_reshaped == 1] = 1

            # Count number of unique voxels where overlap of parcel and mask occurs
            overlap_count = len(
                np.unique(
                    np.where((RSNmask.astype('uint8') == 1)
                             & (parcel_vol.astype('uint8') == 1))))

            # Count number of total unique voxels within the parcel
            total_count = len(
                np.unique(np.where((parcel_vol.astype('uint8') == 1))))

            # Calculate % overlap
            try:
                overlap = float(overlap_count / total_count)
            except RuntimeWarning:
                print('\nWarning: No overlap with roi mask!\n')
                overlap = float(0)

            if overlap >= perc_overlap:
                print("%.2f%s%s%s%s%s" %
                      (100 * overlap, '% of parcel ', labels[i],
                       ' falls within ', str(network), ' mask...'))
                RSN_parcels.append(parcel)
                coords_with_parc.append(coords[i])
                net_labels.append(labels[i])
            i = i + 1
        coords_mm = list(set(list(tuple(x) for x in coords_with_parc)))

    bna_img.uncache()
    if len(coords_mm) <= 1:
        raise ValueError(
            "%s%s%s" %
            ('\nERROR: No coords from the specified atlas found within ',
             network, ' network.'))

    return coords_mm, RSN_parcels, net_labels, network