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
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)
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
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
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)
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
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
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
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