From d34ef2d9fe223ba1acd745539df9a9954429d525 Mon Sep 17 00:00:00 2001 From: "Jiaxin (Cindy) Tu" <30844460+cindyhfls@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:29:25 -0500 Subject: [PATCH 1/2] add the make_mask function from DCAN-bold-processing to get subject-specific eroded masks for HCP outputs --- xcp_d/ingression/utils.py | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/xcp_d/ingression/utils.py b/xcp_d/ingression/utils.py index 89d33eb39..77e1a925e 100644 --- a/xcp_d/ingression/utils.py +++ b/xcp_d/ingression/utils.py @@ -396,3 +396,67 @@ def write_json(data, outfile): json.dump(data, f, sort_keys=True, indent=4) return outfile + +def make_masks(segmentation, wm_mask_out, vent_mask_out, **kwargs): + + """ + # make eroded white matter and ventricular masks copied from DCAN-bold-processing + generates ventricular and white matter masks from a Desikan/FreeSurfer + segmentation file. label constraints may be overridden. + :param segmentation: Desikan/FreeSurfer spec segmentation nifti file. [e.g. 'wmparc.%g.nii.gz' % roi_res] + Does not need to be a cifti but must have labels according to FS lookup + table, including cortical parcellations. + :param wm_mask_out: binary white matter mask. [e.g. ‘wm_%gmm_%s_mask_eroded.nii.gz’%(fmri_res, subject)] + :param vent_mask_out: binary ventricular mask. [e.g. ‘vent_%gmm_%s_mask_eroded.nii.gz’%(fmri_res, subject)] + :param kwargs: dictionary of label value overrides. You may override + default label number bounds for white matter and ventricle masks in the + segmentation file. + :return: None + """ + + wd = os.path.dirname(wm_mask_out) + # set parameter defaults + defaults = dict(wm_lt_R=2950, wm_ut_R=3050, wm_lt_L=3950, wm_ut_L=4050, + vent_lt_R=43, vent_ut_R=43, vent_lt_L=4, vent_ut_L=4, + roi_res=2) + # set temporary filenames + tempfiles = { + 'wm_mask_L': os.path.join(wd, 'tmp_left_wm.nii.gz'), + 'wm_mask_R': os.path.join(wd, 'tmp_right_wm.nii.gz'), + 'vent_mask_L': os.path.join(wd, 'tmp_left_vent.nii.gz'), + 'vent_mask_R': os.path.join(wd, 'tmp_right_vent.nii.gz'), + 'wm_mask': os.path.join(wd, 'tmp_wm.nii.gz'), + 'vent_mask': os.path.join(wd, 'tmp_vent.nii.gz') + } + # inputs and outputs + iofiles = { + 'segmentation': segmentation, + 'wm_mask_out': wm_mask_out, + 'vent_mask_out': vent_mask_out + } + # command pipeline + cmdlist = [ + 'fslmaths {segmentation} -thr {wm_lt_R} -uthr {wm_ut_R} {wm_mask_R}', + 'fslmaths {segmentation} -thr {wm_lt_L} -uthr {wm_ut_L} {wm_mask_L}', + 'fslmaths {wm_mask_R} -add {wm_mask_L} -bin {wm_mask}', + 'fslmaths {wm_mask} -kernel gauss {roi_res:g} -ero {wm_mask_out}', + 'fslmaths {segmentation} -thr {vent_lt_R} -uthr {vent_ut_R} ' + '{vent_mask_R}', + 'fslmaths {segmentation} -thr {vent_lt_L} -uthr {vent_ut_L} ' + '{vent_mask_L}', + 'fslmaths {vent_mask_R} -add {vent_mask_L} -bin {vent_mask}', + 'fslmaths {vent_mask} -kernel gauss {roi_res:g} -ero {vent_mask_out}' + ] + + # get params + defaults.update(kwargs) + kwargs.update(defaults) + kwargs.update(iofiles) + kwargs.update(tempfiles) + # format and run commands + for cmdfmt in cmdlist: + cmd = cmdfmt.format(**kwargs) + subprocess.call(cmd.split()) + # cleanup + for key in tempfiles.keys(): + os.remove(tempfiles[key]) From 2211cda7bdbbe1d14a70aaf3b5c12a628f57b40a Mon Sep 17 00:00:00 2001 From: "Jiaxin (Cindy) Tu" <30844460+cindyhfls@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:43:30 -0500 Subject: [PATCH 2/2] Update hcpya.py replace default mask with masks made for individuals --- xcp_d/ingression/hcpya.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/xcp_d/ingression/hcpya.py b/xcp_d/ingression/hcpya.py index dbf7862f1..ee1cd1e10 100644 --- a/xcp_d/ingression/hcpya.py +++ b/xcp_d/ingression/hcpya.py @@ -23,6 +23,7 @@ copy_files_in_dict, plot_bbreg, write_json, + make_masks, ) from xcp_d.utils.filemanip import ensure_list @@ -133,6 +134,8 @@ def convert_hcp_to_bids_single_subject(in_dir, out_dir, sub_ent): sub- └── files └── MNINonLinear + ├── ROIs + ├── wmparc.2.nii.gz # 2 is the roi resolution 2 mm, this will be used for making the subject-specific masks ├── Results │ ├── *__ │ │ ├── SBRef_dc.nii.gz @@ -162,6 +165,7 @@ def convert_hcp_to_bids_single_subject(in_dir, out_dir, sub_ent): ├── aparc+aseg.nii.gz ├── brainmask_fs.nii.gz └── ribbon.nii.gz + """ assert isinstance(in_dir, str) assert os.path.isdir(in_dir), f"Folder DNE: {in_dir}" @@ -179,6 +183,7 @@ def convert_hcp_to_bids_single_subject(in_dir, out_dir, sub_ent): anat_dir_orig = os.path.join(in_dir, sub_id, "MNINonLinear") func_dir_orig = os.path.join(anat_dir_orig, "Results") + ROI_dir_orig = os.path.join(anat_dir_orig,"ROIs") subject_dir_bids = os.path.join(out_dir, sub_ent) anat_dir_bids = os.path.join(subject_dir_bids, "anat") func_dir_bids = os.path.join(subject_dir_bids, "func") @@ -194,9 +199,12 @@ def convert_hcp_to_bids_single_subject(in_dir, out_dir, sub_ent): os.makedirs(func_dir_bids, exist_ok=True) os.makedirs(work_dir, exist_ok=True) + segmentation = os.path.join(anat_dir_orig, "wmparc.2.nii.gz") + + make_masks(segmentation,os.path.join(anat_dir_bids,f"wm_2mm_{sub_id}_mask_eroded.nii.gz"),os.path.join(anat_dir_bids,f"vent_2mm_{sub_id}_mask_eroded.nii.gz"),fmri_res=2,roi_res=2) # Get masks to be used to extract confounds - csf_mask = str(load_data(f"masks/{volspace_ent}_{RES_ENT}_label-CSF_mask.nii.gz")) - wm_mask = str(load_data(f"masks/{volspace_ent}_{RES_ENT}_label-WM_mask.nii.gz")) + csf_mask = str(load_data(os.path.join(anat_dir_bids,f"vent_2mm_{sub_id}_mask_eroded.nii.gz"))) + wm_mask = str(load_data(os.path.join(anat_dir_bids,f"wm_2mm_{sub_id}_mask_eroded.nii.gz"))) # A dictionary of mappings from HCP derivatives to fMRIPrep derivatives. # Values will be lists, to allow one-to-many mappings.