def test_nodemaker_tools_parlistfile_WB(): """ Test nodemaker_tools_parlistfile_WB functionality """ # Set example inputs base_dir = str(Path(__file__).parent / "examples") parlistfile = f"{base_dir}/miscellaneous/whole_brain_cluster_labels_PCA200.nii.gz" start_time = time.time() [WB_coords, _, _] = nodemaker.get_names_and_coords_of_parcels(parlistfile) print("%s%s%s" % ( 'get_names_and_coords_of_parcels (User-atlas whole-brain version) --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) WB_labels = np.arange(len(WB_coords) + 1)[np.arange(len(WB_coords) + 1) != 0].tolist() start_time = time.time() WB_parcel_list = nodemaker.gen_img_list(parlistfile) [WB_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(WB_parcel_list) print( "%s%s%s" % ('create_parcel_atlas (User-atlas whole-brain version) --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) assert WB_coords is not None assert WB_labels is not None assert WB_parcel_list is not None assert WB_parcels_map_nifti is not None assert parcel_list_exp is not None
def node_gen(coords, parcel_list, labels, dir_path, ID, parc, atlas, uatlas): """ In the case that masking was not applied, this function generate nodes based on atlas definitions established by fetch_nodes_and_labels. Parameters ---------- 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. parcel_list : list List of 3D boolean numpy arrays or binarized Nifti1Images corresponding to ROI masks. labels : list List of string labels corresponding to ROI nodes. dir_path : str Path to directory containing subject derivative data for given run. ID : str A subject id or other unique identifier. parc : bool Indicates whether to use parcels instead of coordinates as ROI nodes. atlas : str Name of a Nilearn-hosted coordinate or parcellation/label-based atlas supported for fetching. See Nilearn's datasets.atlas module for more detailed reference. uatlas : str File path to atlas parcellation Nifti1Image in MNI template space. Returns ------- net_parcels_map_nifti : Nifti1Image A nibabel-based nifti image consisting of a 3D array with integer voxel intensities corresponding to ROI membership. 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. atlas : str Name of a Nilearn-hosted coordinate or parcellation/label-based atlas supported for fetching. See Nilearn's datasets.atlas module for more detailed reference. uatlas : str File path to atlas parcellation Nifti1Image in MNI template space. dir_path : str Path to directory containing subject derivative data for given run. """ try: import cPickle as pickle except ImportError: import _pickle as pickle from pynets.core import nodemaker [net_parcels_map_nifti, _] = nodemaker.create_parcel_atlas(parcel_list) coords = list(tuple(x) for x in coords) assert (len(coords) == len(labels) == len( np.unique(np.asarray(net_parcels_map_nifti.dataobj))[1:])) return net_parcels_map_nifti, coords, labels, atlas, uatlas, dir_path
def test_nodemaker_tools_masking_parlistfile_RSN(): """ Test nodemaker_tools_masking_parlistfile_RSN functionality """ # Set example inputs base_dir = str(Path(__file__).parent/"examples") dir_path = base_dir + '/002/fmri' func_file = dir_path + '/002.nii.gz' parlistfile = base_dir + '/whole_brain_cluster_labels_PCA200.nii.gz' roi = base_dir + '/pDMN_3_bin.nii.gz' network = 'Default' ID = '002' perc_overlap = 0.10 parc = True start_time = time.time() [coords, _, _] = nodemaker.get_names_and_coords_of_parcels(parlistfile) print("%s%s%s" % ('get_names_and_coords_of_parcels --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) labels = np.arange(len(coords) + 1)[np.arange(len(coords) + 1) != 0].tolist() start_time = time.time() parcel_list = nodemaker.gen_img_list(parlistfile) [net_coords, net_parcel_list, net_labels, network] = nodemaker.get_node_membership(network, func_file, coords, labels, parc, parcel_list) print("%s%s%s" % ('get_node_membership --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) start_time = time.time() [net_coords_masked, net_labels_masked, net_parcel_list_masked] = nodemaker.parcel_masker(roi, net_coords, net_parcel_list, net_labels, dir_path, ID, perc_overlap) print("%s%s%s" % ('parcel_masker --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) start_time = time.time() [net_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(net_parcel_list_masked) print("%s%s%s" % ('create_parcel_atlas --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) start_time = time.time() out_path = nodemaker.gen_network_parcels(parlistfile, network, net_labels_masked, dir_path) print("%s%s%s" % ('gen_network_parcels --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) assert coords is not None assert net_coords is not None assert net_labels is not None assert net_parcel_list is not None assert net_coords_masked is not None assert net_labels_masked is not None assert net_parcel_list_masked is not None assert out_path is not None assert net_parcels_map_nifti is not None assert parcel_list_exp is not None assert network is not None
def test_nodemaker_tools_parlistfile_RSN(): """ Test nodemaker_tools_parlistfile_RSN functionality """ import pkg_resources # Set example inputs base_dir = str(Path(__file__).parent / "examples") template = pkg_resources.resource_filename( "pynets", f"templates/MNI152_T1_brain_2mm.nii.gz") dir_path = f"{base_dir}/BIDS/sub-25659/ses-1/func" parlistfile = f"{base_dir}/miscellaneous/whole_brain_cluster_labels_PCA200.nii.gz" network = 'Default' parc = True start_time = time.time() [coords, _, _, _] = nodemaker.get_names_and_coords_of_parcels(parlistfile) print("%s%s%s" % ('get_names_and_coords_of_parcels --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) labels = np.arange(len(coords) + 1)[np.arange(len(coords) + 1) != 0].tolist() start_time = time.time() parcel_list = nodemaker.gen_img_list(parlistfile) [net_coords, net_parcel_list, net_labels, network] = nodemaker.get_node_membership(network, template, coords, labels, parc, parcel_list) print("%s%s%s" % ('get_node_membership --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) start_time = time.time() [net_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(net_parcel_list) print("%s%s%s" % ('create_parcel_atlas --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) start_time = time.time() out_path = nodemaker.gen_network_parcels(parlistfile, network, net_labels, dir_path) print("%s%s%s" % ('gen_network_parcels --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) assert coords is not None assert net_coords is not None assert net_labels is not None assert net_parcel_list is not None assert out_path is not None assert net_parcels_map_nifti is not None assert parcel_list_exp is not None assert network is not None
def test_nodemaker_tools_parlistfile_RSN(): """ Test nodemaker_tools_parlistfile_RSN functionality """ # Set example inputs base_dir = str(Path(__file__).parent / "examples") func_file = f"{base_dir}/BIDS/sub-0025427/ses-1/func/sub-0025427_ses-1_task-rest_space-MNI152NLin2009cAsym_desc-smoothAROMAnonaggr_bold.nii.gz" dir_path = f"{base_dir}/BIDS/sub-0025427/ses-1/func" parlistfile = f"{base_dir}/miscellaneous/whole_brain_cluster_labels_PCA200.nii.gz" network = 'Default' parc = True start_time = time.time() [coords, _, _] = nodemaker.get_names_and_coords_of_parcels(parlistfile) print("%s%s%s" % ('get_names_and_coords_of_parcels --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) labels = np.arange(len(coords) + 1)[np.arange(len(coords) + 1) != 0].tolist() start_time = time.time() parcel_list = nodemaker.gen_img_list(parlistfile) [net_coords, net_parcel_list, net_labels, network] = nodemaker.get_node_membership(network, func_file, coords, labels, parc, parcel_list) print("%s%s%s" % ('get_node_membership --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) start_time = time.time() [net_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(net_parcel_list) print("%s%s%s" % ('create_parcel_atlas --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) start_time = time.time() out_path = nodemaker.gen_network_parcels(parlistfile, network, net_labels, dir_path) print("%s%s%s" % ('gen_network_parcels --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) assert coords is not None assert net_coords is not None assert net_labels is not None assert net_parcel_list is not None assert out_path is not None assert net_parcels_map_nifti is not None assert parcel_list_exp is not None assert network is not None
def test_nodemaker_tools_parlistfile_WB(): """ Test nodemaker_tools_parlistfile_WB functionality """ # Set example inputs parlistfile = pkg_resources.resource_filename( "pynets", "templates/atlases/whole_brain_cluster_labels_PCA200.nii.gz") dir_path = str(tempfile.TemporaryDirectory().name) os.makedirs(dir_path, exist_ok=True) shutil.copy2(parlistfile, f"{dir_path}/{os.path.basename(parlistfile)}") parlistfile = f"{dir_path}/{os.path.basename(parlistfile)}" start_time = time.time() [WB_coords, _, _, _] = nodemaker.get_names_and_coords_of_parcels(parlistfile) print("%s%s%s" % ( "get_names_and_coords_of_parcels (User-atlas " "whole-brain version) --> finished: ", str(np.round(time.time() - start_time, 1)), "s", )) WB_labels = np.arange(len(WB_coords) + 1)[np.arange(len(WB_coords) + 1) != 0].tolist() start_time = time.time() WB_parcel_list = nodemaker.three_to_four_parcellation(parlistfile) [WB_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(WB_parcel_list) print("%s%s%s" % ( "create_parcel_atlas (User-atlas whole-brain version) " "--> finished: ", str(np.round(time.time() - start_time, 1)), "s", )) assert WB_coords is not None assert WB_labels is not None assert WB_parcel_list is not None assert WB_parcels_map_nifti is not None assert parcel_list_exp is not None
def parcellate(self): """ API for performing any of a variety of clustering routines available through NiLearn. """ import gc import time import os from nilearn.regions import Parcellations from pynets.fmri.estimation import fill_confound_nans start = time.time() if (self.clust_type == 'ward') and (self.local_corr != 'allcorr'): if self._local_conn_mat_path is not None: if not os.path.isfile(self._local_conn_mat_path): raise FileNotFoundError('File containing sparse matrix of local connectivity structure not found.') else: raise FileNotFoundError('File containing sparse matrix of local connectivity structure not found.') if self.clust_type == 'complete' or self.clust_type == 'average' or self.clust_type == 'single' or \ self.clust_type == 'ward' or (self.clust_type == 'rena' and self.num_conn_comps == 1) or \ (self.clust_type == 'kmeans' and self.num_conn_comps == 1): self._clust_est = Parcellations(method=self.clust_type, standardize=self._standardize, detrend=self._detrending, n_parcels=self.k, mask=self._clust_mask_corr_img, connectivity=self._local_conn, mask_strategy='background', memory_level=2, random_state=42) if self.conf is not None: import pandas as pd confounds = pd.read_csv(self.conf, sep='\t') if confounds.isnull().values.any(): conf_corr = fill_confound_nans(confounds, self._dir_path) self._clust_est.fit(self._func_img, confounds=conf_corr) else: self._clust_est.fit(self._func_img, confounds=self.conf) else: self._clust_est.fit(self._func_img) self._clust_est.labels_img_.set_data_dtype(np.uint16) nib.save(self._clust_est.labels_img_, self.uatlas) elif self.clust_type == 'ncut': out_img = parcellate_ncut(self._local_conn, self.k, self._clust_mask_corr_img) out_img.set_data_dtype(np.uint16) nib.save(out_img, self.uatlas) elif self.clust_type == 'rena' or self.clust_type == 'kmeans' and self.num_conn_comps > 1: from pynets.core import nodemaker from nilearn.regions import connected_regions, Parcellations from nilearn.image import iter_img, new_img_like from pynets.core.utils import flatten, proportional mask_img_list = [] mask_voxels_dict = dict() for i, mask_img in enumerate(list(iter_img(self._conn_comps))): mask_voxels_dict[i] = np.int(np.sum(np.asarray(mask_img.dataobj))) mask_img_list.append(mask_img) # Allocate k across connected components using Hagenbach-Bischoff Quota based on number of voxels k_list = proportional(self.k, list(mask_voxels_dict.values())) conn_comp_atlases = [] print("%s%s%s" % ('Building ', len(mask_img_list), ' separate atlases with voxel-proportional nclusters ' 'for each connected component...')) for i, mask_img in enumerate(mask_img_list): if k_list[i] == 0: # print('0 voxels in component. Discarding...') continue self._clust_est = Parcellations(method=self.clust_type, standardize=self._standardize, detrend=self._detrending, n_parcels=k_list[i], mask=mask_img, mask_strategy='background', memory_level=2, random_state=42) if self.conf is not None: import pandas as pd confounds = pd.read_csv(self.conf, sep='\t') if confounds.isnull().values.any(): conf_corr = fill_confound_nans(confounds, self._dir_path) self._clust_est.fit(self._func_img, confounds=conf_corr) else: self._clust_est.fit(self._func_img, confounds=self.conf) else: self._clust_est.fit(self._func_img) conn_comp_atlases.append(self._clust_est.labels_img_) # Then combine the multiple atlases, corresponding to each connected component, into a single atlas atlas_of_atlases = [] for atlas in conn_comp_atlases: bna_data = np.around(np.asarray(atlas.dataobj)).astype('uint16') # Get an array of unique parcels bna_data_for_coords_uniq = np.unique(bna_data) # Number of parcels: par_max = len(bna_data_for_coords_uniq) - 1 img_stack = [] for idx in range(1, par_max + 1): roi_img = bna_data == bna_data_for_coords_uniq[idx].astype('uint16') img_stack.append(roi_img.astype('uint16')) img_stack = np.array(img_stack) img_list = [] for idy in range(par_max): img_list.append(new_img_like(atlas, img_stack[idy])) atlas_of_atlases.append(img_list) del img_list, img_stack, bna_data atlas_of_atlases = list(flatten(atlas_of_atlases)) [super_atlas_ward, _] = nodemaker.create_parcel_atlas(atlas_of_atlases) super_atlas_ward.set_data_dtype(np.uint16) nib.save(super_atlas_ward, self.uatlas) del atlas_of_atlases, super_atlas_ward, conn_comp_atlases, mask_img_list, mask_voxels_dict print("%s%s%s" % (self.clust_type, self.k, " clusters: %.2fs" % (time.time() - start))) del self._clust_est self._func_img.uncache() self._clust_mask_corr_img.uncache() gc.collect() return self.uatlas
def test_nodemaker_tools_masking_parlistfile_WB(): """ Test nodemaker_tools_masking_parlistfile_WB functionality """ # Set example inputs base_dir = str(Path(__file__).parent / "examples") dir_path = f"{base_dir}/BIDS/sub-0025427/ses-1/func" parlistfile = f"{base_dir}/miscellaneous/whole_brain_cluster_labels_PCA200.nii.gz" atlas = 'whole_brain_cluster_labels_PCA200' roi = f"{base_dir}/miscellaneous/pDMN_3_bin.nii.gz" ID = '002' parc = True perc_overlap = 0.10 start_time = time.time() [WB_coords, _, _] = nodemaker.get_names_and_coords_of_parcels(parlistfile) print("%s%s%s" % ( 'get_names_and_coords_of_parcels (Masking whole-brain version) --> finished: ', str(np.round(time.time() - start_time, 1)), 's')) WB_labels = np.arange(len(WB_coords) + 1)[np.arange(len(WB_coords) + 1) != 0].tolist() start_time = time.time() WB_parcel_list = nodemaker.gen_img_list(parlistfile) [_, _, WB_parcel_list_masked ] = nodemaker.parcel_masker(roi, WB_coords, WB_parcel_list, WB_labels, dir_path, ID, perc_overlap) print("%s%s%s" % ('parcel_masker (Masking whole-brain version) --> finished: ', np.round(time.time() - start_time, 1), 's')) start_time = time.time() [WB_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(WB_parcel_list_masked) print("%s%s%s" % ('create_parcel_atlas (Masking whole-brain version) --> finished: ', np.round(time.time() - start_time, 1), 's')) start_time = time.time() [WB_net_parcels_map_nifti_unmasked, WB_coords_unmasked, _, _, _, dir_path] = nodemaker.node_gen(WB_coords, WB_parcel_list, WB_labels, dir_path, ID, parc, atlas, parlistfile) print("%s%s%s" % ('node_gen (Masking whole-brain version) --> finished: ', np.round(time.time() - start_time, 1), 's')) start_time = time.time() [ WB_net_parcels_map_nifti_masked, WB_coords_masked, WB_labels_masked, _, _, _ ] = nodemaker.node_gen_masking(roi, WB_coords, WB_parcel_list, WB_labels, dir_path, ID, parc, atlas, parlistfile) print("%s%s%s" % ('node_gen_masking (Masking whole-brain version) --> finished: ', np.round(time.time() - start_time, 1), 's')) assert WB_coords is not None assert WB_labels is not None assert WB_parcel_list is not None assert WB_coords_masked is not None assert WB_labels_masked is not None assert WB_parcel_list_masked is not None assert WB_parcels_map_nifti is not None assert parcel_list_exp is not None assert WB_net_parcels_map_nifti_unmasked is not None assert WB_coords_unmasked is not None assert WB_net_parcels_map_nifti_masked is not None assert WB_coords_masked is not None
def node_gen_masking(roi, coords, parcel_list, labels, dir_path, ID, parc, atlas, uatlas, perc_overlap=0.75, error=2): """ In the case that masking was applied, this function generate nodes based on atlas definitions established by fetch_nodes_and_labels. 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. parcel_list : list List of 3D boolean numpy arrays or binarized Nifti1Images corresponding to ROI masks. labels : list List of string labels corresponding to ROI nodes. dir_path : str Path to directory containing subject derivative data for given run. ID : str A subject id or other unique identifier. parc : bool Indicates whether to use parcels instead of coordinates as ROI nodes. atlas : str Name of a Nilearn-hosted coordinate or parcellation/label-based atlas supported for fetching. See Nilearn's datasets.atlas module for more detailed reference. uatlas : str File path to atlas parcellation Nifti1Image in MNI template space. perc_overlap : float Value 0-1 indicating a threshold of spatial overlap to use as a spatial error cushion in the case of evaluating mask/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 mask/RSN membership from a given list of coordinates. Default is 4. Returns ------- net_parcels_map_nifti : Nifti1Image A nibabel-based nifti image consisting of a 3D array with integer voxel intensities corresponding to ROI membership. 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. atlas : str Name of a Nilearn-hosted coordinate or parcellation/label-based atlas supported for fetching. See Nilearn's datasets.atlas module for more detailed reference. uatlas : str File path to atlas parcellation Nifti1Image in MNI template space. dir_path : str Path to directory containing subject derivative data for given run. """ from pynets.core import nodemaker import os.path as op try: import cPickle as pickle except ImportError: import _pickle as pickle # Mask Parcels if parc is True: # For parcel masking, specify overlap thresh and error cushion in mm voxels [coords, labels, parcel_list_masked ] = nodemaker.parcel_masker(roi, coords, parcel_list, labels, dir_path, ID, perc_overlap) [net_parcels_map_nifti, _] = nodemaker.create_parcel_atlas(parcel_list_masked) # Mask Coordinates else: [coords, labels] = nodemaker.coords_masker(roi, coords, labels, error) # Save coords to pickle coords_path = f"{dir_path}/atlas_coords_{op.basename(roi).split('.')[0]}.pkl" with open(coords_path, 'wb') as f: pickle.dump(coords, f, protocol=2) net_parcels_map_nifti = None # Save labels to pickle labels_path = f"{dir_path}/atlas_labelnames_{op.basename(roi).split('.')[0]}.pkl" with open(labels_path, 'wb') as f: pickle.dump(labels, f, protocol=2) return net_parcels_map_nifti, coords, labels, atlas, uatlas, dir_path
def parcel_masker(roi, coords, parcel_list, labels, dir_path, ID, perc_overlap): """ Evaluate the affinity of any arbitrary list of parcel 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. parcel_list : list List of 3D boolean numpy arrays or binarized Nifti1Images corresponding to ROI masks. labels : list List of string labels corresponding to ROI nodes. dir_path : str Path to directory containing subject derivative data for given run. ID : str A subject id or other unique identifier. perc_overlap : float Value 0-1 indicating a threshold of spatial overlap to use as a spatial error cushion in the case of evaluating ROI-mask membership from a given list of parcel masks. Returns ------- coords_adj : list Filtered list of (x, y, z) tuples in mm-space with a spatial affinity for the specified ROI mask. labels_adj : list Filtered list of string labels corresponding to ROI nodes with a spatial affinity for the specified ROI mask. parcel_list_adj : list Filtered list of 3D boolean numpy arrays or binarized Nifti1Images corresponding to ROI masks with a spatial affinity to the specified ROI mask. """ from pynets.core import nodemaker from nilearn.image import resample_img from nilearn import masking import os.path as op mask_img = nib.load(roi) mask_aff = mask_img.affine mask_data, _ = masking._load_mask_img(roi) mask_img.uncache() i = 0 indices = [] for parcel in parcel_list: parcel_vol = np.zeros(mask_data.shape, dtype=bool) parcel_data_reshaped = np.asarray( resample_img(parcel, target_affine=mask_aff, target_shape=mask_data.shape).dataobj) 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((mask_data.astype('uint16') == 1) & (parcel_vol.astype('uint16') == 1)))) # Count number of total unique voxels within the parcel total_count = len( np.unique(np.where((parcel_vol.astype('uint16') == 1)))) # Calculate % overlap try: overlap = float(overlap_count / total_count) except: print( f"\nWarning: No overlap of parcel {labels[i]} with roi mask!\n" ) overlap = float(0) if overlap >= perc_overlap: print( f"{(100 * overlap):.2f}{'% of parcel '}{labels[i]}{' falls within mask...'}" ) else: indices.append(i) i = i + 1 labels_adj = list(labels) coords_adj = list(tuple(x) for x in coords) parcel_list_adj = parcel_list try: for ix in sorted(indices, reverse=True): print(f"{'Removing: '}{labels_adj[ix]}{' at '}{coords_adj[ix]}") del labels_adj[ix], coords_adj[ix], parcel_list_adj[ix] except RuntimeError: print( 'ERROR: Restrictive masking. No parcels remain after masking with brain mask/roi...' ) # Create a resampled 3D atlas that can be viewed alongside mask img for QA resampled_parcels_nii_path = f"{dir_path}/{ID}_parcels_resampled2roimask_{op.basename(roi).split('.')[0]}.nii.gz" resampled_parcels_map_nifti = resample_img( nodemaker.create_parcel_atlas(parcel_list_adj)[0], target_affine=mask_aff, target_shape=mask_data.shape, interpolation='nearest') nib.save(resampled_parcels_map_nifti, resampled_parcels_nii_path) resampled_parcels_map_nifti.uncache() if not coords_adj: raise ValueError( '\nERROR: ROI mask was likely too restrictive and yielded < 2 remaining parcels' ) return coords_adj, labels_adj, parcel_list_adj
def parcellate(func_boot_img, local_corr, clust_type, _local_conn_mat_path, num_conn_comps, _clust_mask_corr_img, _standardize, _detrending, k, _local_conn, conf, _dir_path, _conn_comps): """ API for performing any of a variety of clustering routines available through NiLearn. """ import time import os import numpy as np from nilearn.regions import Parcellations from pynets.fmri.estimation import fill_confound_nans # from joblib import Memory import tempfile cache_dir = tempfile.mkdtemp() # memory = Memory(cache_dir, verbose=0) start = time.time() if (clust_type == "ward") and (local_corr != "allcorr"): if _local_conn_mat_path is not None: if not os.path.isfile(_local_conn_mat_path): raise FileNotFoundError( "File containing sparse matrix of local connectivity" " structure not found." ) else: raise FileNotFoundError( "File containing sparse matrix of local connectivity" " structure not found." ) if ( clust_type == "complete" or clust_type == "average" or clust_type == "single" or clust_type == "ward" or (clust_type == "rena" and num_conn_comps == 1) or (clust_type == "kmeans" and num_conn_comps == 1) ): _clust_est = Parcellations( method=clust_type, standardize=_standardize, detrend=_detrending, n_parcels=k, mask=_clust_mask_corr_img, connectivity=_local_conn, mask_strategy="background", random_state=42 ) if conf is not None: import pandas as pd import random from nipype.utils.filemanip import fname_presuffix, copyfile out_name_conf = fname_presuffix( conf, suffix=f"_tmp{random.randint(1, 1000)}", newpath=cache_dir ) copyfile( conf, out_name_conf, copy=True, use_hardlink=False) confounds = pd.read_csv(out_name_conf, sep="\t") if confounds.isnull().values.any(): conf_corr = fill_confound_nans(confounds, _dir_path) try: _clust_est.fit(func_boot_img, confounds=conf_corr) except UserWarning: return None os.remove(conf_corr) else: try: _clust_est.fit(func_boot_img, confounds=out_name_conf) except UserWarning: return None os.remove(out_name_conf) else: try: _clust_est.fit(func_boot_img) except UserWarning: return None _clust_est.labels_img_.set_data_dtype(np.uint16) print( f"{clust_type}{k}" f"{(' clusters: %.2fs' % (time.time() - start))}" ) return _clust_est.labels_img_ elif clust_type == "ncut": out_img = parcellate_ncut( _local_conn, k, _clust_mask_corr_img ) out_img.set_data_dtype(np.uint16) print( f"{clust_type}{k}" f"{(' clusters: %.2fs' % (time.time() - start))}" ) return out_img elif ( clust_type == "rena" or clust_type == "kmeans" and num_conn_comps > 1 ): from pynets.core import nodemaker from nilearn.regions import connected_regions, Parcellations from nilearn.image import iter_img, new_img_like from pynets.core.utils import flatten, proportional mask_img_list = [] mask_voxels_dict = dict() for i, mask_img in enumerate(iter_img(_conn_comps)): mask_voxels_dict[i] = np.int( np.sum(np.asarray(mask_img.dataobj))) mask_img_list.append(mask_img) # Allocate k across connected components using Hagenbach-Bischoff # Quota based on number of voxels k_list = proportional(k, list(mask_voxels_dict.values())) conn_comp_atlases = [] print( f"Building {len(mask_img_list)} separate atlases with " f"voxel-proportional k clusters for each " f"connected component...") for i, mask_img in enumerate(iter_img(mask_img_list)): if k_list[i] < 5: print(f"Only {k_list[i]} voxels in component. Discarding...") continue _clust_est = Parcellations( method=clust_type, standardize=_standardize, detrend=_detrending, n_parcels=k_list[i], mask=mask_img, mask_strategy="background", random_state=i ) if conf is not None: import pandas as pd import random from nipype.utils.filemanip import fname_presuffix, copyfile out_name_conf = fname_presuffix( conf, suffix=f"_tmp{random.randint(1, 1000)}", newpath=cache_dir ) copyfile( conf, out_name_conf, copy=True, use_hardlink=False) confounds = pd.read_csv(out_name_conf, sep="\t") if confounds.isnull().values.any(): conf_corr = fill_confound_nans( confounds, _dir_path) try: _clust_est.fit(func_boot_img, confounds=conf_corr) except UserWarning: continue else: try: _clust_est.fit(func_boot_img, confounds=conf) except UserWarning: continue else: try: _clust_est.fit(func_boot_img) except UserWarning: continue conn_comp_atlases.append(_clust_est.labels_img_) # Then combine the multiple atlases, corresponding to each # connected component, into a single atlas atlas_of_atlases = [] for atlas in iter_img(conn_comp_atlases): bna_data = np.around( np.asarray( atlas.dataobj)).astype("uint16") # Get an array of unique parcels bna_data_for_coords_uniq = np.unique(bna_data) # Number of parcels: par_max = len(bna_data_for_coords_uniq) - 1 img_stack = [] for idx in range(1, par_max + 1): roi_img = bna_data == bna_data_for_coords_uniq[idx].astype( "uint16") img_stack.append(roi_img.astype("uint16")) img_stack = np.array(img_stack) img_list = [] for idy in range(par_max): img_list.append(new_img_like(atlas, img_stack[idy])) atlas_of_atlases.append(img_list) del img_list, img_stack, bna_data atlas_of_atlases = list(flatten(atlas_of_atlases)) [super_atlas_ward, _] = nodemaker.create_parcel_atlas( atlas_of_atlases) super_atlas_ward.set_data_dtype(np.uint16) del atlas_of_atlases, conn_comp_atlases, mask_img_list, \ mask_voxels_dict print( f"{clust_type}{k}" f"{(' clusters: %.2fs' % (time.time() - start))}" ) # memory.clear(warn=False) return super_atlas_ward
def test_nodemaker_tools_parlistfile_RSN(): """ Test nodemaker_tools_parlistfile_RSN functionality """ # Set example inputs template = pkg_resources.resource_filename( "pynets", f"templates/standard/MNI152_T1_brain_2mm.nii.gz") dir_path = str(tempfile.TemporaryDirectory().name) parlistfile = pkg_resources.resource_filename( "pynets", "templates/atlases/whole_brain_cluster_labels_PCA200.nii.gz") os.makedirs(dir_path, exist_ok=True) shutil.copy2(parlistfile, f"{dir_path}/{os.path.basename(parlistfile)}") parlistfile = f"{dir_path}/{os.path.basename(parlistfile)}" subnet = "Default" parc = True start_time = time.time() coords = nodemaker.get_names_and_coords_of_parcels(parlistfile)[0] print("%s%s%s" % ( "get_names_and_coords_of_parcels --> finished: ", str(np.round(time.time() - start_time, 1)), "s", )) labels = np.arange(len(coords) + 1)[np.arange(len(coords) + 1) != 0].tolist() start_time = time.time() parcels_4d_img = nodemaker.three_to_four_parcellation(parlistfile) [net_coords, net_parcels_4d_img, net_labels, subnet] = \ nodemaker.get_node_membership(subnet, template, coords, labels, parc, parcels_4d_img ) print("%s%s%s" % ( "get_node_membership --> finished: ", str(np.round(time.time() - start_time, 1)), "s", )) start_time = time.time() [net_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(net_parcels_4d_img) print("%s%s%s" % ( "create_parcel_atlas --> finished: ", str(np.round(time.time() - start_time, 1)), "s", )) start_time = time.time() out_path = nodemaker.gen_network_parcels(parlistfile, subnet, net_labels, dir_path) print("%s%s%s" % ( "gen_network_parcels --> finished: ", str(np.round(time.time() - start_time, 1)), "s", )) assert coords is not None assert net_coords is not None assert net_labels is not None assert net_parcels_4d_img is not None assert out_path is not None assert net_parcels_map_nifti is not None assert parcel_list_exp is not None assert subnet is not None
def test_nodemaker_tools_masking_parlistfile_WB(): """ Test nodemaker_tools_masking_parlistfile_WB functionality """ # Set example inputs parlistfile = pkg_resources.resource_filename( "pynets", "templates/atlases/whole_brain_cluster_labels_PCA200.nii.gz") dir_path = str(tempfile.TemporaryDirectory().name) os.makedirs(dir_path, exist_ok=True) shutil.copy2(parlistfile, f"{dir_path}/{os.path.basename(parlistfile)}") parlistfile = f"{dir_path}/{os.path.basename(parlistfile)}" atlas = "whole_brain_cluster_labels_PCA200" roi = tempfile.NamedTemporaryFile(mode="w+", suffix=".nii.gz").name data_gen.generate_mni_space_img()[1].to_filename(roi) ID = "002" parc = True perc_overlap = 0.10 start_time = time.time() [WB_coords, _, _, _] = nodemaker.get_names_and_coords_of_parcels(parlistfile) print("%s%s%s" % ( "get_names_and_coords_of_parcels (Masking whole-brain " "version) --> finished: ", str(np.round(time.time() - start_time, 1)), "s", )) WB_labels = np.arange(len(WB_coords) + 1)[np.arange(len(WB_coords) + 1) != 0].tolist() start_time = time.time() WB_parcel_list = nodemaker.three_to_four_parcellation(parlistfile) start_time = time.time() [ WB_net_parcels_map_nifti_unmasked, WB_coords_unmasked, _, _, _, dir_path, ] = nodemaker.node_gen(WB_coords, WB_parcel_list, WB_labels, dir_path, ID, parc, atlas, parlistfile) print("%s%s%s" % ( "node_gen (Masking whole-brain version) --> finished: ", np.round(time.time() - start_time, 1), "s", )) start_time = time.time() WB_parcel_list = nodemaker.three_to_four_parcellation(parlistfile) [WB_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(WB_parcel_list) print("%s%s%s" % ( "create_parcel_atlas (Masking whole-brain version) --> finished: ", np.round(time.time() - start_time, 1), "s", )) start_time = time.time() WB_parcel_list = nodemaker.three_to_four_parcellation(parlistfile) [ WB_net_parcels_map_nifti_masked, WB_coords_masked, WB_labels_masked, _, _, _, ] = nodemaker.node_gen_masking( roi, WB_coords, WB_parcel_list, WB_labels, dir_path, ID, parc, atlas, parlistfile, vox_size="2mm", ) WB_parcel_list = nodemaker.three_to_four_parcellation(parlistfile) WB_parcel_list_masked = nodemaker.parcel_masker( roi, WB_coords, WB_parcel_list, WB_labels, dir_path, ID, perc_overlap, vox_size="2mm", )[2] print("%s%s%s" % ( "parcel_masker (Masking whole-brain version) --> " "finished: ", np.round(time.time() - start_time, 1), "s", )) print("%s%s%s" % ( "node_gen_masking (Masking whole-brain version) --> " "finished: ", np.round(time.time() - start_time, 1), "s", )) assert WB_coords is not None assert WB_labels is not None assert WB_parcel_list is not None assert WB_coords_masked is not None assert WB_labels_masked is not None assert WB_parcel_list_masked is not None assert WB_parcels_map_nifti is not None assert parcel_list_exp is not None assert WB_net_parcels_map_nifti_unmasked is not None assert WB_coords_unmasked is not None assert WB_net_parcels_map_nifti_masked is not None assert WB_coords_masked is not None
def test_nodemaker_tools_masking_parlistfile_RSN(): """ Test nodemaker_tools_masking_parlistfile_RSN functionality """ # Set example inputs template = pkg_resources.resource_filename( "pynets", f"templates/standard/MNI152_T1_brain_2mm.nii.gz") tmp = tempfile.TemporaryDirectory() dir_path = str(tmp.name) os.makedirs(dir_path, exist_ok=True) parlistfile = pkg_resources.resource_filename( "pynets", "templates/atlases/whole_brain_cluster_labels_PCA200.nii.gz") shutil.copy2(parlistfile, f"{dir_path}/{os.path.basename(parlistfile)}") parlistfile = f"{dir_path}/{os.path.basename(parlistfile)}" roi = tempfile.NamedTemporaryFile(mode="w+", suffix=".nii.gz").name data_gen.generate_mni_space_img()[1].to_filename(roi) subnet = "Default" ID = "002" perc_overlap = 0.75 parc = True start_time = time.time() coords = nodemaker.get_names_and_coords_of_parcels(parlistfile)[0] print("%s%s%s" % ( "get_names_and_coords_of_parcels --> finished: ", str(np.round(time.time() - start_time, 1)), "s", )) labels = np.arange(len(coords) + 1)[np.arange(len(coords) + 1) != 0].tolist() start_time = time.time() parcels_4d_img = nodemaker.three_to_four_parcellation(parlistfile) [net_coords, net_parcels_4d_img, net_labels, subnet] = \ nodemaker.get_node_membership( subnet, template, coords, labels, parc, parcels_4d_img ) print("%s%s%s" % ( "get_node_membership --> finished: ", str(np.round(time.time() - start_time, 1)), "s", )) start_time = time.time() [ net_coords_masked, net_labels_masked, net_parcels_4d_img_masked, ] = nodemaker.parcel_masker( roi, net_coords, net_parcels_4d_img, net_labels, dir_path, ID, perc_overlap, vox_size="2mm", ) print("%s%s%s" % ( "parcel_masker --> finished: ", str(np.round(time.time() - start_time, 1)), "s", )) start_time = time.time() [net_parcels_map_nifti, parcel_list_exp ] = nodemaker.create_parcel_atlas(net_parcels_4d_img_masked) print("%s%s%s" % ( "create_parcel_atlas --> finished: ", str(np.round(time.time() - start_time, 1)), "s", )) start_time = time.time() out_path = nodemaker.gen_network_parcels(parlistfile, subnet, net_labels_masked, dir_path) print("%s%s%s" % ( "gen_network_parcels --> finished: ", str(np.round(time.time() - start_time, 1)), "s", )) assert coords is not None assert net_coords is not None assert net_labels is not None assert net_parcels_4d_img is not None assert net_coords_masked is not None assert net_labels_masked is not None assert net_parcels_4d_img_masked is not None assert out_path is not None assert net_parcels_map_nifti is not None assert parcel_list_exp is not None assert subnet is not None tmp.cleanup()
def parcellate(self, func_boot_img): """ API for performing any of a variety of clustering routines available through NiLearn. """ import time import os from nilearn.regions import Parcellations from pynets.fmri.estimation import fill_confound_nans start = time.time() if (self.clust_type == "ward") and (self.local_corr != "allcorr"): if self._local_conn_mat_path is not None: if not os.path.isfile(self._local_conn_mat_path): raise FileNotFoundError( "File containing sparse matrix of local connectivity" " structure not found.") else: raise FileNotFoundError( "File containing sparse matrix of local connectivity" " structure not found.") if (self.clust_type == "complete" or self.clust_type == "average" or self.clust_type == "single" or self.clust_type == "ward" or (self.clust_type == "rena" and self.num_conn_comps == 1) or (self.clust_type == "kmeans" and self.num_conn_comps == 1)): _clust_est = Parcellations( method=self.clust_type, standardize=self._standardize, detrend=self._detrending, n_parcels=self.k, mask=self._clust_mask_corr_img, connectivity=self._local_conn, mask_strategy="background", memory_level=2, random_state=42, ) if self.conf is not None: import pandas as pd confounds = pd.read_csv(self.conf, sep="\t") if confounds.isnull().values.any(): conf_corr = fill_confound_nans(confounds, self._dir_path) _clust_est.fit(func_boot_img, confounds=conf_corr) else: _clust_est.fit(func_boot_img, confounds=self.conf) else: _clust_est.fit(func_boot_img) _clust_est.labels_img_.set_data_dtype(np.uint16) print(f"{self.clust_type}{self.k}" f"{(' clusters: %.2fs' % (time.time() - start))}") return _clust_est.labels_img_ elif self.clust_type == "ncut": out_img = parcellate_ncut(self._local_conn, self.k, self._clust_mask_corr_img) out_img.set_data_dtype(np.uint16) print(f"{self.clust_type}{self.k}" f"{(' clusters: %.2fs' % (time.time() - start))}") return out_img elif (self.clust_type == "rena" or self.clust_type == "kmeans" and self.num_conn_comps > 1): from pynets.core import nodemaker from nilearn.regions import connected_regions, Parcellations from nilearn.image import iter_img, new_img_like from pynets.core.utils import flatten, proportional mask_img_list = [] mask_voxels_dict = dict() for i, mask_img in enumerate(list(iter_img(self._conn_comps))): mask_voxels_dict[i] = np.int( np.sum(np.asarray(mask_img.dataobj))) mask_img_list.append(mask_img) # Allocate k across connected components using Hagenbach-Bischoff # Quota based on number of voxels k_list = proportional(self.k, list(mask_voxels_dict.values())) conn_comp_atlases = [] print(f"Building {len(mask_img_list)} separate atlases with " f"voxel-proportional k clusters for each " f"connected component...") for i, mask_img in enumerate(mask_img_list): if k_list[i] == 0: # print('0 voxels in component. Discarding...') continue _clust_est = Parcellations( method=self.clust_type, standardize=self._standardize, detrend=self._detrending, n_parcels=k_list[i], mask=mask_img, mask_strategy="background", memory_level=2, random_state=42, ) if self.conf is not None: import pandas as pd confounds = pd.read_csv(self.conf, sep="\t") if confounds.isnull().values.any(): conf_corr = fill_confound_nans(confounds, self._dir_path) _clust_est.fit(func_boot_img, confounds=conf_corr) else: _clust_est.fit(func_boot_img, confounds=self.conf) else: _clust_est.fit(func_boot_img) conn_comp_atlases.append(_clust_est.labels_img_) # Then combine the multiple atlases, corresponding to each # connected component, into a single atlas atlas_of_atlases = [] for atlas in conn_comp_atlases: bna_data = np.around(np.asarray( atlas.dataobj)).astype("uint16") # Get an array of unique parcels bna_data_for_coords_uniq = np.unique(bna_data) # Number of parcels: par_max = len(bna_data_for_coords_uniq) - 1 img_stack = [] for idx in range(1, par_max + 1): roi_img = bna_data == bna_data_for_coords_uniq[idx].astype( "uint16") img_stack.append(roi_img.astype("uint16")) img_stack = np.array(img_stack) img_list = [] for idy in range(par_max): img_list.append(new_img_like(atlas, img_stack[idy])) atlas_of_atlases.append(img_list) del img_list, img_stack, bna_data atlas_of_atlases = list(flatten(atlas_of_atlases)) [super_atlas_ward, _] = nodemaker.create_parcel_atlas(atlas_of_atlases) super_atlas_ward.set_data_dtype(np.uint16) del atlas_of_atlases, conn_comp_atlases, mask_img_list, \ mask_voxels_dict print(f"{self.clust_type}{self.k}" f"{(' clusters: %.2fs' % (time.time() - start))}") return super_atlas_ward