def get_ventricular_csf_mask(fslanatdir, interpolation=3): """ Get a ventricular mask in T1 space. Register the ventricles mask from the harvardoxford-subcortical 2mm atlas to T1 space, using the T1_to_MNI_nonlin_coeff.nii.gz in the provided fsl_anat directory. Parameters ---------- fslanatdir: pathlib.Path Path to an fsl_anat output directory. interpolation: int Order of interpolation to use when performing registration. Default is 3. """ # get ventricles mask from Harv-Ox atlases.rescanAtlases() harvox = atlases.loadAtlas('harvardoxford-subcortical', resolution=2.0) vent_img = Image( harvox.data[:,:,:,2] + harvox.data[:,:,:,13], header=harvox.header ) vent_img = fslmaths(vent_img).thr(0.1).bin().ero().run(LOAD) # apply inv(T1->MNI) registration t1_brain = (fslanatdir/"T1_biascorr_brain.nii.gz").resolve(strict=True) struct2mni = (fslanatdir/"T1_to_MNI_nonlin_coeff.nii.gz").resolve(strict=True) mni2struct = invwarp(str(struct2mni), str(t1_brain), LOAD)['out'] vent_t1_img = applywarp(vent_img, str(t1_brain), LOAD, warp=mni2struct)['out'] vent_t1_img = fslmaths(vent_t1_img).thr(0.9).bin().run(LOAD) return vent_t1_img
def brain(image): ''' Brain Extraction with FSL Params: - image: nifti object, scan to brain extract Output: - brain_image: nifti object, extracted brain ''' affine = image.affine header = image.header tmpfile = 'tmpfile.nii.gz' image.to_filename(tmpfile) # FSL calls mask = fslmaths(image).thr('0.000000').uthr( '100.000000').bin().fillh().run() fslmaths(image).mas(mask).run(tmpfile) bet(tmpfile, tmpfile, fracintensity=0.01) mask = fslmaths(tmpfile).bin().fillh().run() image = fslmaths(image).mas(mask).run() image = nib.Nifti1Image(image.get_data(), affine, header) os.remove(tmpfile) return image
def run_fabber_asl(subject_dir, target='structural'): json_dict = load_json(subject_dir) structasl_dir = Path(json_dict['structasl']) brain_mask = structasl_dir / 'reg/ASL_grid_T1w_acpc_dc_restore_brain_mask.nii.gz' timing_image = structasl_dir / 'timing_img.nii.gz' # take mean of data for first 2 iterations beta_perf_name = structasl_dir / 'TIs/Betas/beta_perf.nii.gz' beta_perf_mean_name = structasl_dir / 'TIs/Betas/beta_perf_mean.nii.gz' repeats = [6, 6, 6, 10, 15] mean_call = [ "asl_file", f"--data={beta_perf_name}", "--ntis=5", "--ibf=tis", "--rpts=6,6,6,10,15", "--iaf=diff", "--obf=tis", f"--mean={beta_perf_mean_name}" ] subprocess.run(mean_call, check=True) base_command = [ "fabber_asl", "--model=aslrest", "--method=vb", "--casl", f"--mask={brain_mask}", "--save-mvn", "--overwrite", "--incart", "--inctiss", "--infertiss", "--incbat", "--inferbat", "--noise=white", "--save-mean", "--tau=1.5", "--bat=1.3", "--batsd=1.0", "--allow-bad-voxels", "--convergence=trialmode", "--data-order=singlefile", "--disp=none", "--exch=mix", "--max-iterations=20", "--max-trials=10", f"--tiimg={timing_image}" ] # iteration-specific options for iteration in range(4): it_command = base_command.copy() # data if iteration == 0 or iteration == 1: data = beta_perf_mean_name else: data = beta_perf_name it_command.append(f"--data={data}") # inferart if iteration == 1 or iteration == 3: it_command.append("--inferart") # output dir out_dir = beta_perf_name.parent / f'fabber_{iteration}' it_command.append(f"--output={out_dir}") # continue from mvn if iteration != 0: mvn = beta_perf_name.parent / f'fabber_{iteration - 1}/finalMVN.nii.gz' it_command.append(f"--continue-from-mvn={mvn}") # repeats if iteration >= 2: for n, repeat in enumerate(repeats): it_command.append(f"--rpt{n+1}={repeat}") # run subprocess.run(it_command, check=True) # threshold last run's perfusion estimate mean_ftiss = str(out_dir / 'mean_ftiss.nii.gz') fslmaths(mean_ftiss).thr(0).run(mean_ftiss) # add oxford_asl directory to the json important_names = {"oxford_asl": str(out_dir)} update_json(important_names, json_dict)
def test_fslmaths(): with asrt.disabled(), run.dryrun(), mockFSLDIR( bin=('fslmaths', )) as fsldir: cmd = op.join(fsldir, 'bin', 'fslmaths') result = fw.fslmaths('input') \ .abs().bin().binv().recip().Tmean().Tstd().Tmin().Tmax() \ .fillh().ero().dilM().dilF().add('addim').sub('subim') \ .mul('mulim').div('divim').mas('masim').rem('remim') \ .thr('thrim').uthr('uthrim').inm('inmim').bptf(1, 10) \ .smooth(sigma=6).kernel('3D').fmeanu().run('output') expected = [ cmd, 'input', '-abs', '-bin', '-binv', '-recip', '-Tmean', '-Tstd', '-Tmin', '-Tmax', '-fillh', '-ero', '-dilM', '-dilF', '-add addim', '-sub subim', '-mul mulim', '-div divim', '-mas masim', '-rem remim', '-thr thrim', '-uthr uthrim', '-inm inmim', '-bptf 1 10', '-s 6', '-kernel 3D', '-fmeanu', 'output' ] expected = ' '.join(expected) assert result.stdout[0] == expected # test LOAD output with tempdir() as td, mockFSLDIR(bin=('fslmaths', )) as fsldir: expect = make_random_image(op.join(td, 'output.nii.gz')) with open(op.join(fsldir, 'bin', 'fslmaths'), 'wt') as f: f.write( tw.dedent(""" #!/usr/bin/env python import sys import shutil shutil.copy('{}', sys.argv[2]) """.format(op.join(td, 'output.nii.gz'))).strip()) os.chmod(op.join(fsldir, 'bin', 'fslmaths'), 0o755) got = fw.fslmaths('input').run() assert np.all(expect.dataobj[:] == got.dataobj[:]) got = fw.fslmaths('input').run(fw.LOAD) assert np.all(expect.dataobj[:] == got.dataobj[:])
def run(self, options): from fsl.wrappers import fslmaths cmds = options.pop("cmd", "").split() if cmds[0] == "fslmaths": del cmds[0] input_data = cmds[0] output_data = cmds[-1] if input_data not in self.ivm.data: raise QpException("Input data not found: %s" % input_data) cmds = cmds[1:-1] img = qpdata_to_fslimage(self.ivm.data[input_data]) proc = fslmaths(img) current_method = None current_args = [] for cmd in cmds: if cmd[0] == "-": if current_method is not None: self.debug("Executing %s(%s)", current_method, current_args) proc = current_method(*current_args) elif current_args: self.warn("Discarding args: %s", current_args) cmd = cmd.lstrip("-") try: current_method = getattr(proc, cmd) except AttributeError: self.warn("No such command") current_method = None current_args = [] else: if cmd in self.ivm.data: current_args.append(qpdata_to_fslimage(self.ivm.data[cmd])) else: current_args.append(cmd) if current_method is not None: self.debug("Executing %s(%s)", current_method, current_args) proc = current_method(*current_args) ret = proc.run() self.ivm.add(fslimage_to_qpdata(ret), name=output_data)
def correct_M0(subject_dir, mt_factors): """ Correct the M0 images for a particular subject whose data is stored in `subject_dir`. The corrections to be performed include: - Bias-field correction - Magnetisation Transfer correction Inputs - `subject_dir` = pathlib.Path object specifying the subject's base directory - `mt_factors` = pathlib.Path object specifying the location of empirically estimated MT correction scaling factors """ # load json containing info on where files are stored json_dict = load_json(subject_dir) # do for both m0 images for the subject, calib0 and calib1 calib_names = [json_dict['calib0_img'], json_dict['calib1_img']] for calib_name in calib_names: # get calib_dir and other info calib_path = Path(calib_name) calib_dir = calib_path.parent calib_name_stem = calib_path.stem.split('.')[0] # run BET on m0 image betted_m0 = bet(calib_name, LOAD) # create directories to store results fast_dir = calib_dir / 'FAST' biascorr_dir = calib_dir / 'BiasCorr' mtcorr_dir = calib_dir / 'MTCorr' create_dirs([fast_dir, biascorr_dir, mtcorr_dir]) # estimate bias field on brain-extracted m0 image # run FAST, storing results in directory fast_base = fast_dir / calib_name_stem fast( betted_m0['output'], # output of bet out=str(fast_base), type=3, # image type, 3=PD image b=True, # output estimated bias field nopve=True # don't need pv estimates ) bias_name = fast_dir / f'{calib_name_stem}_bias.nii.gz' # apply bias field to original m0 image (i.e. not BETted) biascorr_name = biascorr_dir / f'{calib_name_stem}_restore.nii.gz' fslmaths(calib_name).div(str(bias_name)).run(str(biascorr_name)) # apply mt_factors to bias-corrected m0 image mtcorr_name = mtcorr_dir / f'{calib_name_stem}_mtcorr.nii.gz' fslmaths(str(biascorr_name)).mul(str(mt_factors)).run(str(mtcorr_name)) # add locations of above files to the json important_names = { f'{calib_name_stem}_bias': str(bias_name), f'{calib_name_stem}_bc': str(biascorr_name), f'{calib_name_stem}_mc': str(mtcorr_name) } update_json(important_names, json_dict)
def main(): # argument handling parser = argparse.ArgumentParser() parser.add_argument("study_dir", help="Path of the base study directory.") parser.add_argument("sub_number", help="Subject number.") parser.add_argument( "-g", "--grads", help="Filename of the gradient coefficients for gradient" + "distortion correction (optional).") args = parser.parse_args() study_dir = args.study_dir sub_num = args.sub_number grad_coeffs = args.grads oph = (study_dir + "/" + sub_num + "/ASL/TIs/DistCorr") outdir = (study_dir + "/" + sub_num + "/T1w/ASL/reg") pve_path = (study_dir + "/" + sub_num + "/T1w/ASL/PVEs") T1w_oph = (study_dir + "/" + sub_num + "/T1w/ASL/TIs/DistCorr") T1w_cal_oph = (study_dir + "/" + sub_num + "/T1w/ASL/Calib/Calib0/DistCorr") need_dirs = [oph, outdir, pve_path, T1w_oph, T1w_cal_oph] for req_dir in need_dirs: Path(req_dir).mkdir(parents=True, exist_ok=True) initial_wd = os.getcwd() print("Pre-distortion correction working directory was: " + initial_wd) print("Changing working directory to: " + oph) os.chdir(oph) # Generate ASL-gridded T1-aligned T1w image for use as a reg reference t1 = (study_dir + "/" + sub_num + "/T1w/T1w_acpc_dc_restore.nii.gz") t1_brain = (study_dir + "/" + sub_num + "/T1w/T1w_acpc_dc_restore_brain.nii.gz") asl = (study_dir + "/" + sub_num + "/ASL/TIs/STCorr/SecondPass/tis_stcorr.nii.gz") t1_asl_res = (study_dir + "/" + sub_num + "/T1w/ASL/reg/ASL_grid_T1w_acpc_dc_restore.nii.gz") asl_v1 = (study_dir + "/" + sub_num + "/ASL/TIs/STCorr/SecondPass/tis_stcorr_vol1.nii.gz") first_asl_call = ("fslroi " + asl + " " + asl_v1 + " 0 1") # print(first_asl_call) sp.run(first_asl_call.split(), check=True, stderr=sp.PIPE, stdout=sp.PIPE) print("Running regtricks bit") t1_spc = rt.ImageSpace(t1) asl_spc = rt.ImageSpace(asl_v1) t1_spc_asl = t1_spc.resize_voxels(asl_spc.vox_size / t1_spc.vox_size) r = rt.Registration.identity() t1_asl = r.apply_to_image(t1, t1_spc_asl) nb.save(t1_asl, t1_asl_res) # Check .grad coefficients are available and call function to generate # GDC warp if they are: if os.path.isfile(grad_coeffs): calc_gdc_warp(asl_v1, grad_coeffs, oph) else: print("Gradient coefficients not available") print("Changing back to original working directory: " + initial_wd) os.chdir(initial_wd) # output file of topup parameters to subject's distortion correction dir pars_filepath = (oph + "/topup_params.txt") produce_topup_params(pars_filepath) # generate EPI distortion correction fieldmaps for use in asl_reg pa_sefm, ap_sefm = find_field_maps(study_dir, sub_num) pa_ap_sefms = (oph + "/merged_sefms.nii.gz") cnf_file = "b02b0.cnf" out_basename = (oph + "/topup_result") topup_fmap = (oph + "/topup_result_fmap_hz.nii.gz") fmap_rads = (oph + "/fmap_rads.nii.gz") fmapmag = (oph + "/fmapmag.nii.gz") fmapmagbrain = (oph + "/fmapmag_brain.nii.gz") calc_fmaps(pa_sefm, ap_sefm, pa_ap_sefms, pars_filepath, cnf_file, oph, out_basename, topup_fmap, fmap_rads, fmapmag, fmapmagbrain) # Calculate initial linear transformation from ASL-space to T1w-space asl_v1_brain = (study_dir + "/" + sub_num + "/ASL/TIs/STCorr/SecondPass/tis_stcorr_vol1_brain.nii.gz") bet_regfrom_call = ("bet " + asl_v1 + " " + asl_v1_brain) # print(bet_regfrom_call) sp.run(bet_regfrom_call.split(), check=True, stderr=sp.PIPE, stdout=sp.PIPE) gen_initial_trans(asl_v1_brain, outdir, t1, t1_brain) # Generate a brain mask in the space of the 1st ASL volume asl2struct = (outdir + "/asl2struct.mat") t1_brain_mask = (outdir + "/T1w_acpc_dc_restore_brain_mask.nii.gz") asl_mask = (outdir + "/asl_vol1_mask.nii.gz") struct2asl = (outdir + "/struct2asl.mat") gen_asl_mask(t1_brain, t1_brain_mask, asl_v1_brain, asl2struct, asl_mask, struct2asl) # brain mask t1_mask = (study_dir + "/" + sub_num + "/T1w/ASL/reg/T1w_acpc_dc_restore_brain_mask.nii.gz") t1_asl_mask_name = ( study_dir + "/" + sub_num + "/T1w/ASL/reg/ASL_grid_T1w_acpc_dc_restore_brain_mask.nii.gz") t1_mask_spc = rt.ImageSpace(t1_mask) t1_mask_spc_asl = t1_mask_spc.resize_voxels(asl_spc.vox_size / t1_mask_spc.vox_size) r = rt.Registration.identity() t1_mask_asl = r.apply_to_image(t1_mask, t1_mask_spc_asl) fslmaths(t1_mask_asl).thr(0.5).bin().run(t1_asl_mask_name) # Generate PVEs aparc_aseg = (study_dir + "/" + sub_num + "/T1w/aparc+aseg.nii.gz") pve_files = (study_dir + "/" + sub_num + "/T1w/ASL/PVEs/pve") gen_pves(Path(t1).parent, asl, pve_files) # Generate WM mask pvwm = (pve_files + "_WM.nii.gz") tissseg = (study_dir + "/" + sub_num + "/T1w/ASL/PVEs/wm_mask.nii.gz") gen_wm_mask(pvwm, tissseg) # Calculate the overall distortion correction warp asl2str_trans = (outdir + "/asl2struct.mat") gdc_warp = (oph + "/gdc_warp.nii.gz") calc_distcorr_warp(asl_v1_brain, oph, t1, t1_brain, asl_mask, tissseg, asl2str_trans, fmap_rads, fmapmag, fmapmagbrain, t1_asl_res, gdc_warp) # Calculate the Jacobian of the distortion correction warp calc_warp_jacobian(oph) # apply the combined distortion correction warp with motion correction # to move asl data, calibrationn images, and scaling factors into # ASL-gridded T1w-aligned space asl_distcorr = (T1w_oph + "/tis_distcorr.nii.gz") moco_xfms = (study_dir + "/" + sub_num + "/ASL/TIs/MoCo/asln2asl0.mat" ) #will this work? concat_xfms = str(Path(moco_xfms).parent / f'{Path(moco_xfms).stem}.cat') # concatenate xfms like in oxford_asl concat_call = f'cat {moco_xfms}/MAT* > {concat_xfms}' sp.run(concat_call, shell=True) # only correcting and transforming the 1st of the calibration images at the moment calib_orig = (study_dir + "/" + sub_num + "/ASL/Calib/Calib0/MTCorr/calib0_mtcorr.nii.gz") calib_distcorr = (study_dir + "/" + sub_num + "/T1w/ASL/Calib/Calib0/DistCorr/calib0_dcorr.nii.gz") calib_inv_xfm = (study_dir + "/" + sub_num + "/ASL/TIs/MoCo/asln2m0.mat/MAT_0000") calib_xfm = (study_dir + "/" + sub_num + "/ASL/TIs/MoCo/calibTOasl1.mat") sfacs_orig = (study_dir + "/" + sub_num + "/ASL/TIs/STCorr/SecondPass/combined_scaling_factors.nii.gz") sfacs_distcorr = (T1w_oph + "/combined_scaling_factors.nii.gz") invert_call = ("convert_xfm -omat " + calib_xfm + " -inverse " + calib_inv_xfm) # print(invert_call) sp.run(invert_call.split(), check=True, stderr=sp.PIPE, stdout=sp.PIPE) apply_distcorr_warp(asl, t1_asl_res, asl_distcorr, oph, concat_xfms, calib_orig, calib_distcorr, calib_xfm, sfacs_orig, sfacs_distcorr)
def setup_mtestimation(subject_dir, rois=[ 'wm', ]): """ Perform the initial processing needed for estimation of the MT Effect. This includes: - Creating sub-directories for storing the results - Finding T1 and mbPCASL directories and scans - Split mbPCASL sequence into its constituent components - Run fsl_anat - Create a json to keep track of important files and directories """ # get subject name subject_name = subject_dir.parts[-1] print(subject_name) # create results directories asl_dir = subject_dir / 'ASL' calib0_dir = asl_dir / 'Calib/Calib0' calib1_dir = asl_dir / 'Calib/Calib1' create_dirs([asl_dir, calib0_dir, calib1_dir]) # obtain calibration images from ASL sequence mbpcasl_dir = list( (subject_dir / f'{subject_name}_V1_B/scans').glob('**/*mbPCASLhr'))[0] mbpcasl = mbpcasl_dir / f'resources/NIFTI/files/{subject_name}_V1_B_mbPCASLhr_PA.nii.gz' calib0_name = calib0_dir / 'calib0.nii.gz' calib1_name = calib1_dir / 'calib1.nii.gz' fslroi(str(mbpcasl), calib0_name, 88, 1) fslroi(str(mbpcasl), calib1_name, 89, 1) # initialise dict json_name = asl_dir / 'ASL.json' important_dict = { "calib_dir": str(calib0_dir.parent), "calib0_dir": str(calib0_dir), "calib1_dir": str(calib1_dir), "calib0_img": str(calib0_name), "calib1_img": str(calib1_dir), "json_name": str(json_name) } # structural directory t1_dir = list( (subject_dir / f'{subject_name}_V1_A/scans').glob('**/*T1w'))[0] struc_dir = t1_dir / 'resources/NIFTI/files' struc_name = list(struc_dir.glob(f'**/{subject_name}_*.nii.gz'))[0] fsl_anat_dir = struc_dir / 'ASL/struc' calib0struct_dir = struc_dir / 'ASL/Calib/Calib0' calib1struct_dir = struc_dir / 'ASL/Calib/Calib1' create_dirs([calib0struct_dir, calib1struct_dir]) # run fsl_anat fsl_anat(str(struc_name), str(fsl_anat_dir), clobber=True, nosubcortseg=True) fsl_anat_dir = fsl_anat_dir.parent / f'{fsl_anat_dir.stem}.anat' t1_name = fsl_anat_dir / 'T1_biascorr.nii.gz' t1_brain_name = fsl_anat_dir / 'T1_biascorr_brain.nii.gz' # get ventricles mask if csf if 'csf' in rois: # initialise atlas list atlases.rescanAtlases() harv_ox_prob_2mm = atlases.loadAtlas('harvardoxford-subcortical', resolution=2.0) vent_img = Image(harv_ox_prob_2mm.data[:, :, :, 2] + harv_ox_prob_2mm.data[:, :, :, 13], header=harv_ox_prob_2mm.header) vent_img = fslmaths(vent_img).thr(0.1).bin().ero().run(LOAD) # we already have registration from T1 to MNI struc2mni_warp = fsl_anat_dir / 'MNI_to_T1_nonlin_field.nii.gz' # apply warp to ventricles image vent_t1_name = fsl_anat_dir / 'ventricles_mask.nii.gz' applywarp(vent_img, str(t1_brain_name), str(vent_t1_name), warp=str(struc2mni_warp)) # re-threshold vent_t1 = fslmaths(str(vent_t1_name)).thr( PVE_THRESHOLDS['csf']).bin().run(LOAD) # mask pve estimate by ventricles mask fslmaths(str(fsl_anat_dir / PVE_NAMES['csf'])).mas(vent_t1).run( str(vent_t1_name)) # bias-field correction for calib_name in (calib0_name, calib1_name): calib_name_stem = calib_name.stem.split('.')[0] # run bet betted_m0 = bet(str(calib_name), LOAD) # create directories to save results fast_dir = calib_name.parent / 'FAST' biascorr_dir = calib_name.parent / 'BiasCorr' create_dirs([fast_dir, biascorr_dir]) # run FAST on brain-extracted m0 image fast_base = fast_dir / calib_name_stem fast(betted_m0['output'], out=str(fast_base), type=3, b=True, nopve=True) bias_name = fast_dir / f'{calib_name_stem}_bias.nii.gz' # apply bias field to original m0 image (i.e. not BETted) biascorr_name = biascorr_dir / f'{calib_name_stem}_restore.nii.gz' fslmaths(str(calib_name)).div(str(bias_name)).run(str(biascorr_name)) # obtain registration from structural to calibration image mask_dir = biascorr_name.parent / 'masks' create_dirs([ mask_dir, ]) cmd = [ 'asl_reg', f'-i {biascorr_name}', f'-s {t1_name}', f'--sbet {t1_brain_name}', f'-o {mask_dir}' ] subprocess.run(" ".join(cmd), shell=True) # apply transformation to the pve map for tissue in rois: roi_dir = mask_dir / tissue create_dirs([ roi_dir, ]) if tissue == 'combined': # check that gm and wm pves already exist gm_pve = mask_dir / 'gm/pve_gm.nii.gz' wm_pve = mask_dir / 'wm/pve_wm.nii.gz' if not (gm_pve.exists() and wm_pve.exists()): raise Exception("WM and GM PVEs don't exist.") gm_mask_name = roi_dir / 'gm_mask.nii.gz' wm_mask_name = roi_dir / 'wm_mask.nii.gz' fslmaths(str(gm_pve)).thr(PVE_THRESHOLDS[tissue]).bin().run( str(gm_mask_name)) fslmaths(str(wm_pve)).thr(PVE_THRESHOLDS[tissue]).bin().run( str(wm_mask_name)) gm_masked_name = roi_dir / f'{calib_name_stem}_gm_masked' wm_masked_name = roi_dir / f'{calib_name_stem}_wm_masked' fslmaths(str(biascorr_name)).mul(str(gm_mask_name)).run( str(gm_masked_name)) fslmaths(str(biascorr_name)).mul(str(wm_mask_name)).run( str(wm_masked_name)) # update dictionary new_dict = { f'{calib_name_stem}_{tissue}_masked': [str(gm_masked_name), str(wm_masked_name)] } else: if tissue == 'csf': pve_struct_name = vent_t1_name else: pve_struct_name = fsl_anat_dir / PVE_NAMES[tissue] pve_asl_name = roi_dir / f'pve_{tissue}.nii.gz' struct2asl_name = mask_dir / 'struct2asl.mat' applyxfm(str(pve_struct_name), str(biascorr_name), str(struct2asl_name), str(pve_asl_name)) # threshold and binarise the ASL-space pve map mask_name = roi_dir / f'{tissue}_mask.nii.gz' fslmaths(str(pve_asl_name)).thr( PVE_THRESHOLDS[tissue]).bin().run(str(mask_name)) # apply the mask to the calibration image masked_name = mask_dir / f'{tissue}_masked.nii.gz' fslmaths(str(biascorr_name)).mul(str(mask_name)).run( str(masked_name)) # update dictionary new_dict = { f'{calib_name_stem}_{tissue}_masked': str(masked_name) } important_dict.update(new_dict) # save json with open(json_name, 'w') as fp: json.dump(important_dict, fp, sort_keys=True, indent=4) return (subject_dir, 0)
def setup_mtestimation(subject_dir, coeffs_path, rois=[ 'wm', ], interpolation=3, ignore_dropouts=False, force_refresh=True): try: # find files and separate calibration images from mbPCASL sequence names_dict = setup(subject_dir) calib0_name, calib1_name = [ names_dict[f"calib{n}_name"] for n in (0, 1) ] calib_stems = [ c.stem.split(".")[0] for c in (calib0_name, calib1_name) ] # create results directory suf = "_ignoredropouts" if ignore_dropouts else "" results_dirs = [ names_dict[f'calib{n}_dir'] / f"SEbased_MT_t1mask{suf}" for n in (0, 1) ] create_dirs(results_dirs) t1_name, t1_brain_name = [ names_dict[name] for name in ("t1_name", "t1_brain_name") ] # generate white matter mask in T1 space for use in BBRegistration wm_mask = names_dict["aslt1_dir"] / "wm_mask.nii.gz" if not wm_mask.exists() or force_refresh: nb.save(generate_tissue_mask(names_dict["aparc_aseg"], "wm"), wm_mask) # setup distortion correction results directories distcorr_dir = names_dict["calib0_dir"].parent / "DistCorr" gdc_dir = distcorr_dir / "gradient_unwarp" topup_dir = distcorr_dir / "topup" calib_distcorr_dirs = [r / "DistCorr" for r in results_dirs] create_dirs((distcorr_dir, gdc_dir, topup_dir, *calib_distcorr_dirs)) # gradient distortion correction gdc_warp = gdc_dir / "fullWarp_abs.nii.gz" if not gdc_warp.exists() or force_refresh: distortion_correction.generate_gdc_warp(names_dict["calib0_name"], coeffs_path, gdc_dir, interpolation) # topup fmap, fmapmag, fmapmagbrain = [ topup_dir / f"fmap{ext}.nii.gz" for ext in ("", "mag", "magbrain") ] if not all([f.exists() for f in (fmap, fmapmag, fmapmagbrain)]) or force_refresh: pa_ap_sefms = topup_dir / "merged_sefms.nii.gz" distortion_correction.stack_fmaps(names_dict["pa_sefm"], names_dict["ap_sefm"], pa_ap_sefms) topup_params = topup_dir / "topup_params.txt" distortion_correction.generate_topup_params(topup_params) topup_config = "b02b0.cnf" distortion_correction.generate_fmaps(pa_ap_sefms, topup_params, topup_config, topup_dir, gdc_warp) # load gdc warp gdc_warp_reg = rt.NonLinearRegistration.from_fnirt( coefficients=str(gdc_warp), src=str(calib0_name), ref=str(calib0_name), intensity_correct=True) # apply gdc and epidc to both calibration images for calib_name, results_dir in zip((calib0_name, calib1_name), calib_distcorr_dirs): # apply gdc to the calibration image calib_name_stem = calib_name.stem.split(".")[0] gdc_calib_name = results_dir / f"{calib_name_stem}_gdc.nii.gz" if not gdc_calib_name.exists() or force_refresh: gdc_calib = gdc_warp_reg.apply_to_image(str(calib_name), str(calib_name), order=interpolation) nb.save(gdc_calib, gdc_calib_name) # estimate initial registration via asl_reg asl_lin_reg = results_dir / "asl_reg_linear" asl_lin_reg.mkdir(exist_ok=True) asl2struct_lin = asl_lin_reg / "asl2struct.mat" if not asl2struct_lin.exists() or force_refresh: linear_asl_reg(gdc_calib_name, asl_lin_reg, t1_name, t1_brain_name, wm_mask) init_linear = rt.Registration.from_flirt(str(asl2struct_lin), src=str(calib_name), ref=str(t1_name)) # run bet - should I instead get mask from T1 here? bet_results = results_dir / "bet" bet_mask = results_dir / "bet_mask.nii.gz" if not bet_mask.exists() or force_refresh: betted_m0 = bet(str(gdc_calib_name), str(bet_results), g=0.2, f=0.2, m=True) # get epi distortion correction warps asl_nonlin_reg = results_dir / "asl_reg_nonlinear" asl_nonlin_reg.mkdir(exist_ok=True) struct2asl, asl2struct_warp = [ asl_nonlin_reg / n for n in ("struct2asl.mat", "asl2struct_warp.nii.gz") ] if not all([f.exists() for f in (asl2struct_warp, struct2asl) ]) or force_refresh: distortion_correction.generate_epidc_warp( str(gdc_calib_name), str(t1_name), t1_brain_name, bet_mask, wm_mask, init_linear, fmap, fmapmag, fmapmagbrain, str(asl_nonlin_reg)) # chain gradient and epi distortion correction warps together asl2struct_warp_reg = rt.NonLinearRegistration.from_fnirt( coefficients=str(asl2struct_warp), src=str(calib_name), ref=str(t1_name), intensity_correct=True) struct2asl_reg = rt.Registration.from_flirt(str(struct2asl), src=str(t1_name), ref=str(calib_name)) dc_warp = rt.chain(gdc_warp_reg, asl2struct_warp_reg, struct2asl_reg) dc_calib_name = results_dir / f"{calib_name_stem}_dc.nii.gz" if not dc_calib_name.exists() or force_refresh: dc_calib = dc_warp.apply_to_image(str(calib_name), str(calib_name), order=interpolation) nb.save(dc_calib, dc_calib_name) # estimate the bias field bias_name = results_dir / f"{calib_name_stem}_bias.nii.gz" sebased_dir = results_dir / "sebased" if not bias_name.exists() or force_refresh: sebased_dir.mkdir(exist_ok=True) bias_field = bias_estimation( dc_calib_name, "sebased", results_dir=sebased_dir, t1_name=t1_name, t1_brain_name=t1_brain_name, aparc_aseg=names_dict["aparc_aseg"], fmapmag=fmapmag, fmapmagbrain=fmapmagbrain, interpolation=interpolation, force_refresh=force_refresh, wmseg_name=wm_mask, struct2asl=struct2asl) nb.save(bias_field, bias_name) else: bias_field = nb.load(bias_name) # bias correct the distortion-corrected calibration image bc_calib_name = results_dir / f"{calib_name_stem}_restore.nii.gz" if not bc_calib_name.exists() or force_refresh: fslmaths(str(dc_calib_name)).div(bias_field).run( str(bc_calib_name)) # create mask directories roi_dirs = [results_dir / "masks" / roi for roi in rois] create_dirs(roi_dirs) # load Dropouts dropouts_inv = nb.load(sebased_dir / "Dropouts_inv.nii.gz") for roi, roi_dir in zip(rois, roi_dirs): # get tissue masks in calibration image space base_mask_call = partial(generate_tissue_mask_in_ref_space, aparc_aseg=names_dict["aparc_aseg"], ref_img=bc_calib_name, struct2ref=struct2asl, order=0) tissues = ("gm", "wm") if roi == "combined" else (roi, ) names = [roi_dir / f"{t}_mask.nii.gz" for t in tissues] if not all(n.exists() for n in names) or force_refresh: if roi == "csf": masks = [ base_mask_call(tissue=t, erode=True) for t in tissues ] else: masks = [base_mask_call(tissue=t) for t in tissues] [nb.save(m, n) for m, n in zip(masks, names)] else: masks = [nb.load(n) for n in names] if ignore_dropouts: # ignore dropout voxels masks = [ nb.nifti1.Nifti1Image( m.get_fdata() * dropouts_inv.get_fdata(), affine=dropouts_inv.affine) for m in masks ] # save [nb.save(m, n) for m, n in zip(masks, names)] # apply tissue masks to bias- and distortion- corrected images calib_masked_names = [ roi_dir / f"{calib_name_stem}_{t}_masked.nii.gz" for t in tissues ] if not all(c.exists() for c in calib_masked_names) or force_refresh: [ fslmaths(str(bc_calib_name)).mul(mask).run(str(name)) for mask, name in zip(masks, calib_masked_names) ] return (subject_dir, 1) except Exception as e: print(e) return (subject_dir, e)
def epi_reg(wsp, epi_img, use_fmap=False, **kwargs): """ Do EPI registration """ struc.segment(wsp) wmseg = wsp.structural.wm_seg bbr_sch = os.path.join(os.environ["FSLDIR"], "etc/flirtsch/bbr.sch") if wsp.asl2struc is None: # Do pre-alignment in the same was as epi_reg wsp.asl2struc = fsl.flirt(epi_img, ref=wsp.structural.brain, omat=fsl.LOAD, dof=6, log=wsp.fsllog)["omat"] if not use_fmap: wsp.log.write(" - Running BBR\n") trans = fsl.flirt(epi_img, ref=wsp.structural.struc, dof=6, cost="bbr", wmseg=wmseg, init=wsp.asl2struc, omat=fsl.LOAD, out=fsl.LOAD, schedule=bbr_sch, log=wsp.fsllog)["omat"] out_img = fsl.applywarp(epi_img, ref=wsp.structural.struc, out=fsl.LOAD, premat=trans, interp="spline", log=wsp.fsllog)["out"] return {"out.nii.gz": out_img, "out": trans} else: dirmap = { "x": (1, "x"), "y": (2, "y"), "z": (3, "z"), "-x": (-1, "x-"), "-y": (-2, "y-"), "-z": (-3, "z-"), "x-": (-1, "x-"), "y-": (-2, "y-"), "z-": (-3, "z-"), } pedir, fdir = dirmap.get(wsp.pedir, (None, None)) if pedir is None: raise ValueError("Invalid phase encode direction specified: %s" % wsp.pedir) if wsp.nofmapreg: wsp.fmap2struc = np.identity(4) wsp.fmapmag_struc = wsp.fmapmag else: # Register fmap to structural image wsp.log.write(" - Registering fieldmap to structural\n") wsp.fmap2struc = fsl.flirt(wsp.fmapmagbrain, ref=wsp.structural.brain, dof=6, omat=fsl.LOAD)["omat"] flirt_result = fsl.flirt(wsp.fmapmag, ref=wsp.structural.struc, dof=6, init=wsp.fmap2struc, omat=fsl.LOAD, out=fsl.LOAD, nosearch=True) wsp.fmap2struc = flirt_result["omat"] wsp.fmapmag_struc = flirt_result["out"] # Unmask the fieldmap (necessary to avoid edge effects) wsp.fmap_mask = fsl.fslmaths(wsp.fmapmagbrain).abs().bin().run() wsp.fmap_mask = fsl.fslmaths(wsp.fmap).abs().bin().mul( wsp.fmap_mask).run() # The direction here should take into account the initial affine (it needs to be the direction in the EPI) wsp.fmap_unmasked = fsl.fugue(loadfmap=wsp.fmap, mask=wsp.fmap_mask, unmaskfmap=True, savefmap=fsl.LOAD, unwarpdir=fdir)["out"] # The following is a NEW HACK to fix extrapolation when fieldmap is too small wsp.fmap_struc_pad0 = fsl.applywarp(wsp.fmap_unmasked, ref=wsp.structural.struc, premat=wsp.fmap2struc, out=fsl.LOAD)["out"] wsp.fmap_struc_innermask = fsl.fslmaths( wsp.fmap_struc_pad0).abs().bin().run() wsp.fmap_struc_dilated = fsl.fugue(loadfmap=wsp.fmap_struc_pad0, mask=wsp.fmap_struc_innermask, unmaskfmap=True, unwarpdir=fdir, savefmap=fsl.LOAD)["savefmap"] wsp.fmap_struc = wsp.fmap_struc_dilated # Run bbr with fieldmap wsp.log.write("Running BBR with fieldmap\n") if not wsp.epi_reg_use_weighting: refweight = None wsp.reg.epi2struc = fsl.flirt(epi_img, ref=wsp.structural.struc, dof=6, cost="bbr", wmseg=wmseg, init=wsp.reg.asl2struc, omat=fsl.LOAD, schedule=bbr_sch, echospacing=wsp.echospacing, pedir=pedir, fieldmap=wsp.fmap_struc, refweight=refweight)["omat"] # Make equivalent warp fields wsp.log.write( "Making warp fields and applying registration to EPI series\n") wsp.reg.struc2epi = np.linalg.inv(wsp.reg.epi2struc) fsl.concatxfm(wsp.reg.struc2epi, wsp.fmap2struc, outmat=fsl.LOAD) fsl.applywarp(wsp.fmap, ref=epi_img, premat=wsp.fmap2epi, out=fsl.LOAD) wsp.fmap2epi_mask = fsl.fslmaths(wsp.fmap2epi).abs().bin().run() # ${vout}_fieldmaprads2epi -abs -bin ${vout}_fieldmaprads2epi_mask ret = fsl.fugue(loadfmap=wsp.fmap2epi, mask=wsp.fmap2epi_mask, saveshift=fsl.LOAD, unmaskshift=True, dwell=wsp.dwell, unwarpdir=fdir) print(ret) wsp.fmap2epi_shift = ret["fieldmaprads2epi_shift"] ret = fsl.convertwarp(wsp.structural.struc, s=wsp.fmap2epi_shift, postmat=wsp.epi2struc, out=fsl.LOAD, shiftdir=fdir, relout=True) print(ret) warp = ret["out"] ret = fsl.applywarp(epi_img, ref=wsp.structural.struc, out=fsl.LOAD, warp1=warp, interp="spline", rel=True) print(ret) return ret["out"], warp
def hcp_asl_moco(subject_dir, mt_factors, superlevel=1, cores=mp.cpu_count(), order=3): """ This function performs the full motion-correction pipeline for the HCP ASL data. The steps of the pipeline include: - Bias-field correction - MT correction - Saturation recovery - Initial slice-timing correction - Motion estimation - Second slice-timing correction - Registration Inputs - `subject_dir` = pathlib.Path object specifying the subject's base directory - `mt_factors` = pathlib.Path object specifying the location of empirically estimated MT correction scaling factors """ # asl sequence parameters ntis = 5 iaf = "tc" ibf = "tis" tis = [1.7, 2.2, 2.7, 3.2, 3.7] rpts = [6, 6, 6, 10, 15] slicedt = 0.059 sliceband = 10 n_slices = 60 # load json containing important file info json_dict = load_json(subject_dir) # create directories for results tis_dir_name = Path(json_dict['TIs_dir']) first_pass_dir = tis_dir_name / 'FirstPass' second_pass_dir = tis_dir_name / 'SecondPass' create_dirs([tis_dir_name, first_pass_dir, second_pass_dir]) # original ASL series and bias field names asl_name = Path(json_dict['ASL_seq']) bias_name = json_dict['calib0_bias'] old_m02asl = first_pass_dir / 'MoCo/m02asln.mat' # iterate over first and second passes for n, iteration in enumerate((first_pass_dir, second_pass_dir)): bcorr_dir = iteration / 'BiasCorr' mtcorr_dir = iteration / 'MTCorr' satrecov_dir = iteration / 'SatRecov' stcorr_dir = iteration / 'STCorr' moco_dir = iteration / 'MoCo' asln2m0_name = moco_dir / 'asln2m0.mat' m02asln_name = moco_dir / 'm02asln.mat' asln2asl0_name = moco_dir / 'asln2asl0.mat' asl02asln_name = moco_dir / 'asl02asln.mat' create_dirs([ bcorr_dir, mtcorr_dir, satrecov_dir, stcorr_dir, moco_dir, asln2m0_name, m02asln_name, asln2asl0_name, asl02asln_name ]) # bias correct the original ASL series bcorr_img = bcorr_dir / 'tis_biascorr.nii.gz' if n == 1: # register bias field to ASL series reg_bias_name = bcorr_dir / 'bias_reg.nii.gz' old_m02asl = rt.MotionCorrection.from_mcflirt( str(old_m02asl), bias_name, bias_name) nib.save( old_m02asl.apply_to_image(bias_name, bias_name, superlevel=superlevel, cores=cores, order=order), str(reg_bias_name)) bias_name = reg_bias_name fslmaths(str(asl_name)).div(str(bias_name)).run(str(bcorr_img)) # apply MT scaling factors to the bias-corrected ASL series mtcorr_name = mtcorr_dir / 'tis_mtcorr.nii.gz' # load mt factors mt_sfs = np.loadtxt(mt_factors) biascorr_img = Image(str(bcorr_img)) assert (len(mt_sfs) == biascorr_img.shape[2]) mtcorr_img = Image(biascorr_img.data * mt_sfs.reshape(1, 1, -1, 1), header=biascorr_img.header) mtcorr_img.save(str(mtcorr_name)) # estimate satrecov model on bias and MT corrected ASL series t1_name = _saturation_recovery(mtcorr_name, satrecov_dir, ntis, iaf, ibf, tis, rpts) t1_filt_name = _fslmaths_med_filter_wrapper(t1_name) # perform slice-time correction using estimated tissue params stcorr_img, stfactors_img = _slicetiming_correction( mtcorr_name, t1_filt_name, tis, rpts, slicedt, sliceband, n_slices) stcorr_name = stcorr_dir / 'tis_stcorr.nii.gz' stcorr_img.save(stcorr_name) stfactors_name = stcorr_dir / 'st_scaling_factors.nii.gz' stfactors_img.save(stfactors_name) # register ASL series to calibration image reg_name = moco_dir / 'initial_registration_TIs.nii.gz' mcflirt(stcorr_img, reffile=json_dict['calib0_mc'], mats=True, out=str(reg_name)) # rename mcflirt matrices directory orig_mcflirt = moco_dir / 'initial_registration_TIs.nii.gz.mat' if asln2m0_name.exists(): shutil.rmtree(asln2m0_name) orig_mcflirt.rename(asln2m0_name) # get motion estimates from ASLn to ASL0 (and their inverses) asl2m0_list = sorted(asln2m0_name.glob('**/MAT*')) m02asl0 = np.linalg.inv(np.loadtxt(asl2m0_list[0])) for n, xform in enumerate(asl2m0_list): if n == 0: fwd_xform = np.eye(4) else: fwd_xform = m02asl0 @ np.loadtxt(xform) inv_xform = np.linalg.inv(fwd_xform) np.savetxt(m02asln_name / xform.stem, np.linalg.inv(np.loadtxt(xform))) np.savetxt(asln2asl0_name / xform.stem, fwd_xform) np.savetxt(asl02asln_name / xform.stem, inv_xform) # register pre-ST-correction ASLn to ASL0 temp_reg_mtcorr = moco_dir / 'temp_reg_tis_mtcorr.nii.gz' asln2m0_moco = rt.MotionCorrection.from_mcflirt(str(asln2m0_name), str(mtcorr_name), json_dict['calib0_mc']) asln2asl0 = rt.chain(asln2m0_moco, asln2m0_moco.transforms[0].inverse()) reg_mtcorr = Image( asln2asl0.apply_to_image(str(mtcorr_name), json_dict['calib0_mc'], superlevel=superlevel, cores=cores, order=order)) reg_mtcorr.save(str(temp_reg_mtcorr)) # estimate satrecov model on motion-corrected data satrecov_dir = iteration / 'SatRecov2' stcorr_dir = iteration / 'STCorr2' create_dirs([satrecov_dir, stcorr_dir]) t1_name = _saturation_recovery(temp_reg_mtcorr, satrecov_dir, ntis, iaf, ibf, tis, rpts) t1_filt_name = _fslmaths_med_filter_wrapper(t1_name) # apply asl0 to asln registrations to new t1 map reg_t1_filt_name = t1_filt_name.parent / f'{t1_filt_name.stem.split(".")[0]}_reg.nii.gz' reg_t1_filt = Image(asln2asl0.inverse().apply_to_image( str(t1_filt_name), json_dict['calib0_mc'], superlevel=superlevel, cores=cores, order=order)) reg_t1_filt.save(str(reg_t1_filt_name)) # perform slice-time correction using estimated tissue params stcorr_img, stfactors_img = _slicetiming_correction( mtcorr_name, reg_t1_filt_name, tis, rpts, slicedt, sliceband, n_slices) # save images stcorr_name = stcorr_dir / 'tis_stcorr.nii.gz' stfactors_name = stcorr_dir / 'st_scaling_factors.nii.gz' stcorr_img.save(str(stcorr_name)) stfactors_img.save(str(stfactors_name)) # combined MT and ST scaling factors combined_factors_name = stcorr_dir / 'combined_scaling_factors.nii.gz' combined_factors_img = Image(stfactors_img.data * mt_sfs.reshape(1, 1, -1, 1), header=stfactors_img.header) combined_factors_img.save(str(combined_factors_name)) # save locations of important files in the json important_names = { 'ASL_stcorr': str(stcorr_name), 'scaling_factors': str(combined_factors_name) } update_json(important_names, json_dict)
def hcp_asl_moco(subject_dir, mt_factors): """ This function performs the full motion-correction pipeline for the HCP ASL data. The steps of the pipeline include: - Bias-field correction - MT correction - Saturation recovery - Initial slice-timing correction - Motion estimation - Second slice-timing correction - Registration Inputs - `subject_dir` = pathlib.Path object specifying the subject's base directory - `mt_factors` = pathlib.Path object specifying the location of empirically estimated MT correction scaling factors """ # asl sequence parameters ntis = 5 iaf = "tc" ibf = "tis" tis = [1.7, 2.2, 2.7, 3.2, 3.7] rpts = [6, 6, 6, 10, 15] slicedt = 0.059 sliceband = 10 n_slices = 60 # load json containing important file info json_dict = load_json(subject_dir) # create directories for results tis_dir_name = Path(json_dict['TIs_dir']) biascorr_dir_name = tis_dir_name / 'BiasCorr' mtcorr_dir_name = tis_dir_name / 'MTCorr' satrecov_dir_name = tis_dir_name / 'SatRecov' stcorr1_dir_name = tis_dir_name / 'STCorr/FirstPass' stcorr2_dir_name = tis_dir_name / 'STCorr/SecondPass' moco_dir_name = tis_dir_name / 'MoCo' asln2m0_name = moco_dir_name / 'asln2m0.mat' asln2asl0_name = moco_dir_name / 'asln2asl0.mat' asl02asln_name = moco_dir_name / 'asl02asln.mat' create_dirs([ biascorr_dir_name, mtcorr_dir_name, satrecov_dir_name, stcorr1_dir_name, stcorr2_dir_name, moco_dir_name, asln2asl0_name, asl02asln_name ]) # bias-correction of original ASL series asl_name = Path(json_dict['ASL_seq']) bias_name = json_dict['calib0_bias'] # possibly take mean of both bias estimates? # possibly some rough registration from M0 to mean of ASL series # if doing the above, is it worth running BET on M0 images again # and changing f parameter so that the brain-mask is larger? biascorr_name = biascorr_dir_name / 'tis_biascorr.nii.gz' fslmaths(str(asl_name)).div(str(bias_name)).run(str(biascorr_name)) # apply MT scaling factors to the bias-corrected ASL series mtcorr_name = mtcorr_dir_name / 'tis_mtcorr.nii.gz' fslmaths(str(biascorr_name)).mul(str(mt_factors)).run(str(mtcorr_name)) # estimate satrecov model on bias-corrected, MT-corrected ASL series t1_name = _saturation_recovery(mtcorr_name, satrecov_dir_name, ntis, iaf, ibf, tis, rpts) # median filter the parameter estimates t1_filt_name = _fslmaths_med_filter_wrapper(t1_name) # perform initial slice-timing correction using estimated tissue params stcorr_img, st_factors_img = _slicetiming_correction(mtcorr_name, t1_filt_name, tis, rpts, slicedt, sliceband, n_slices) stcorr1_name = stcorr1_dir_name / 'tis_stcorr.nii.gz' stcorr_img.save(stcorr1_name) st_factors1_name = stcorr1_dir_name / 'st_scaling_factors.nii.gz' st_factors_img.save(st_factors1_name) # motion estimation from ASL to M0 image reg_name = moco_dir_name / 'initial_registration_TIs.nii.gz' mcflirt(stcorr_img, reffile=json_dict['calib0_mc'], mats=True, out=str(reg_name)) # rename mcflirt matrices directory orig_mcflirt = (moco_dir_name / 'initial_registration_TIs.nii.gz.mat') if asln2m0_name.exists(): shutil.rmtree(asln2m0_name) orig_mcflirt.rename(asln2m0_name) # obtain motion estimates from ASLn to ASL0 (and their inverse) # get list of registration matrices asl2m0_list = sorted(asln2m0_name.glob('**/MAT*')) m02asl0 = np.linalg.inv(np.loadtxt(asl2m0_list[0])) for n, transformation in enumerate(asl2m0_list): if n == 0: forward_trans = np.eye(4) else: forward_trans = m02asl0 @ np.loadtxt(transformation) inv_trans = np.linalg.inv(forward_trans) np.savetxt(asln2asl0_name / transformation.stem, forward_trans) np.savetxt(asl02asln_name / transformation.stem, inv_trans) # apply inverse transformations to parameter estimates to align them with # the individual frames of the ASL series reg_t1_filt_name = t1_filt_name.parent / f'{t1_filt_name.stem.split(".")[0]}_reg.nii.gz' _register_param(t1_filt_name, asl02asln_name, json_dict['calib0_mc'], reg_t1_filt_name) # second slice-timing correction using registered parameter estimates stcorr_img, st_factors_img = _slicetiming_correction(mtcorr_name, reg_t1_filt_name, tis, rpts, slicedt, sliceband, n_slices) # save slice-time corrected image and slice-time correcting scaling factors stcorr2_name = stcorr2_dir_name / 'tis_stcorr.nii.gz' stcorr_img.save(stcorr2_name) st_factors2_name = stcorr2_dir_name / 'st_scaling_factors.nii.gz' st_factors_img.save(st_factors2_name) # also obtain combined MT- and ST- correction scaling factors combined_factors_name = stcorr2_dir_name / 'combined_scaling_factors.nii.gz' fslmaths(st_factors2_name).mul(mt_factors).run(combined_factors_name) # save locations of important files in the json important_names = { 'ASL_stcorr': str(stcorr2_name), 'scaling_factors': str(combined_factors_name) } update_json(important_names, json_dict)
def getPositionAndRotation(self, tempPath): angulationValues = [0] * 3 xStepSize = 0.0 yStepSize = 0.0 xPoints = 0.0 yPoints = 0.0 with open(self.datFile, "rb") as file: for line in file: try: decodedLine = line.decode("utf-8").strip() # position if re.findall("lr_off_center :", decodedLine): self.POS[0] = float(decodedLine.split(':')[1]) elif re.findall("ap_off_center :", decodedLine): self.POS[1] = float(decodedLine.split(':')[1]) elif re.findall("cc_off_center :", decodedLine): self.POS[2] = float(decodedLine.split(':')[1]) # size elif re.findall("lr_size :", decodedLine): self.VOX[0] = float(decodedLine.split(':')[1]) elif re.findall("ap_size :", decodedLine): self.VOX[1] = float(decodedLine.split(':')[1]) elif re.findall("cc_size :", decodedLine): self.VOX[2] = float(decodedLine.split(':')[1]) elif re.findall("dim3_step :", decodedLine): yStepSize = float(decodedLine.split(':')[1]) elif re.findall("dim2_step :", decodedLine): xStepSize = float(decodedLine.split(':')[1]) elif re.findall("dim3_pnts :", decodedLine): yPoints = float(decodedLine.split(':')[1]) elif re.findall("dim2_pnts :", decodedLine): xPoints = float(decodedLine.split(':')[1]) # normal vector to thickness axis # AP PLANE (Y,Z plane) elif re.findall("lr_angulation :", decodedLine): angulationValues[0] = float(decodedLine.split(':')[1]) # Coronal PLANE (X,Z plane) elif re.findall("ap_angulation :", decodedLine): angulationValues[1] = float(decodedLine.split(':')[1]) # Transverse PLANE (X,Y plane) elif re.findall("cc_angulation :", decodedLine): angulationValues[2] = float(decodedLine.split(':')[1]) # rotation about thickness self.ROT = float(decodedLine.split(':')[1]) elif re.findall("rows :", decodedLine): self.singleVoxel = True if float( decodedLine.split(':')[1]) == 1 else False except: continue angulationValuesRadians = numpy.deg2rad(angulationValues) rotationValueRadians = numpy.deg2rad(self.ROT) # #Is the lareg FOV even or odd xEven = False yEven = False # # size of small voxels store in self.VOX originalVoxelSize = self.VOX[:] self.VOX[0] = (xStepSize * 10) self.VOX[1] = (yStepSize * 10) originalVoxelPosition = self.POS[:] originalOutputPath = self.outputPath self.ZED[0] = numpy.sin(angulationValuesRadians[1]) self.ZED[1] = -(numpy.sin(angulationValuesRadians[0]) * numpy.cos(angulationValuesRadians[1])) self.ZED[2] = numpy.cos(angulationValuesRadians[0]) * numpy.cos( angulationValuesRadians[1]) self.tempPath = tempPath numberOfVoxelsX = numpy.ceil(originalVoxelSize[0] / int(self.VOX[0])) numberOfVoxelsY = numpy.ceil(originalVoxelSize[1] / int(self.VOX[1])) tempPos0 = list() tempPos1 = list() if self.singleVoxel is True: self.VOX = originalVoxelSize[:] self.ROT = rotationValueRadians self.outputPath = originalOutputPath + "singleVoxel" self.convertXFM() self.getVoxeContents(True) return rotationMatrixX = numpy.array( [[1, 0, 0], [ 0, numpy.cos(angulationValuesRadians[0]), -numpy.sin(angulationValuesRadians[0]) ], [ 0, numpy.sin(angulationValuesRadians[0]), numpy.cos(angulationValuesRadians[0]) ]]) rotationMatrixY = numpy.array( [[ numpy.cos(angulationValuesRadians[1]), 0, numpy.sin(angulationValuesRadians[1]) ], [0, 1, 0], [ -numpy.sin(angulationValuesRadians[1]), 0, numpy.cos(angulationValuesRadians[1]) ]]) rotationMatrixZ = numpy.array( [[ numpy.cos(-angulationValuesRadians[2]), -numpy.sin(-angulationValuesRadians[2]), 0 ], [ numpy.sin(-angulationValuesRadians[2]), numpy.cos(-angulationValuesRadians[2]), 0 ], [0, 0, 1]]) voxGreyMatter = False voxWhiteMatter = False voxCSF = False tissueTypeFile = open("TissueTypesForEachVoxel.txt", "w+") tissueTypeFile.write("\n (0,0) is at the corner of (L,P)" "\n (0,n) is at the corner of (A,L) " "\n (m,0) is at the corner of (R,P)" "\n (m,n) is at the corner of (A,R)" "\n---------------------\n\n") if numberOfVoxelsX % 2 == 0: xEven = True if numberOfVoxelsY % 2 == 0: yEven = True for i in range(0, int(numberOfVoxelsX)): for j in range(0, int(numberOfVoxelsY)): # if i == 0 or i == 7: # if j== 0 or j ==7: xDisplacement = xStepSize * 10 yDisplacement = yStepSize * 10 # TODO: Find out why we need the -displacement/2 even for the odd number of voxels case if xEven: self.POS[0] = originalVoxelPosition[0] + ( (numberOfVoxelsX * xDisplacement) / 2 - xDisplacement / 2) - i * xDisplacement elif not xEven: self.POS[0] = originalVoxelPosition[0] + ( (numberOfVoxelsX * xDisplacement) / 2 - xDisplacement / 2) - i * xDisplacement self.POS[0] = self.POS[0] - originalVoxelPosition[0] if yEven: self.POS[1] = originalVoxelPosition[1] + ( (numberOfVoxelsY * yDisplacement) / 2 - yDisplacement / 2) - j * yDisplacement elif not yEven: self.POS[1] = originalVoxelPosition[1] + ( (numberOfVoxelsY * yDisplacement) / 2 - yDisplacement / 2) - j * yDisplacement self.POS[1] = self.POS[1] - originalVoxelPosition[1] self.POS[ 2] = originalVoxelPosition[2] - originalVoxelPosition[2] centreOfVoxelPosition = numpy.array(self.POS) self.POS = rotationMatrixZ.dot(centreOfVoxelPosition).tolist() centreOfVoxelPosition = numpy.array(self.POS) self.POS = rotationMatrixY.dot(centreOfVoxelPosition).tolist() centreOfVoxelPosition = numpy.array(self.POS) self.POS = rotationMatrixX.dot(centreOfVoxelPosition).tolist() self.POS[0] = self.POS[0] + originalVoxelPosition[0] self.POS[1] = self.POS[1] + originalVoxelPosition[1] self.POS[2] = self.POS[2] + originalVoxelPosition[2] tempPos1.append(self.POS[1]) print("----------------\n\n _ " + str(i) + "_" + str(j) + "\n\n") self.outputPath = originalOutputPath + "_" + str( i) + "_" + str(j) self.ROT = rotationValueRadians self.convertXFM() continue values = self.getVoxeContents((i == 0 and j == 0)) greyMatter = values[0] whiteMatter = values[1] CSF = values[2] totalVolume = values[3] tissueTypeFile.writelines([ "\tVoxels at position (" + str(i) + "," + str(j) + ")", "\nGrey Matter Percentage: " + greyMatter, "\nWhite Matter Percentage: " + whiteMatter, "\nCSF Percentage: " + CSF, "\nTotal Volume: " + totalVolume, "\n\n" ]) if i == 0 and j == 0: if os.path.exists(originalOutputPath + 'GreyMatterVoxel'): shutil.rmtree(originalOutputPath + 'GreyMatterVoxel') if os.path.exists(originalOutputPath + 'WhiteMatterVoxel'): shutil.rmtree(originalOutputPath + 'WhiteMatterVoxel') if os.path.exists(originalOutputPath + 'CSFVoxel'): shutil.rmtree(originalOutputPath + 'CSFVoxel') os.mkdir(originalOutputPath + 'GreyMatterVoxel') os.mkdir(originalOutputPath + 'WhiteMatterVoxel') os.mkdir(originalOutputPath + 'CSFVoxel') shutil.copy2(self.outputPath + "_vox_final.nii.gz", self.outputPath + "copy_vox_final.nii.gz") shutil.move( self.outputPath + "copy_vox_final.nii.gz", originalOutputPath + 'GreyMatterVoxel/constructedGreyMatterVoxel.nii.gz') shutil.copy2(self.outputPath + "_vox_final.nii.gz", self.outputPath + "copy_vox_final.nii.gz") shutil.move( self.outputPath + "copy_vox_final.nii.gz", originalOutputPath + 'WhiteMatterVoxel/constructedWhiteMatterVoxel.nii.gz') shutil.copy2(self.outputPath + "_vox_final.nii.gz", self.outputPath + "copy_vox_final.nii.gz") shutil.move( self.outputPath + "copy_vox_final.nii.gz", originalOutputPath + 'CSFVoxel/constructedCSFVoxel.nii.gz') voxGreyMatter = fslmaths( originalOutputPath + 'GreyMatterVoxel/constructedGreyMatterVoxel.nii.gz') voxWhiteMatter = fslmaths( originalOutputPath + 'WhiteMatterVoxel/constructedWhiteMatterVoxel.nii.gz') voxCSF = fslmaths(originalOutputPath + 'CSFVoxel/constructedCSFVoxel.nii.gz') voxGreyMatter.mul(greyMatter) voxWhiteMatter.mul(whiteMatter) voxCSF.mul(CSF) voxGreyMatter.run( originalOutputPath + 'GreyMatterVoxel/constructedGreyMatterVoxel.nii.gz') voxWhiteMatter.run( originalOutputPath + 'WhiteMatterVoxel/constructedWhiteMatterVoxel.nii.gz') voxCSF.run(originalOutputPath + 'CSFVoxel/constructedCSFVoxel.nii.gz') continue shutil.copy2(self.outputPath + "_vox_final.nii.gz", originalOutputPath + 'GreyMatterVoxel') shutil.copy2(self.outputPath + "_vox_final.nii.gz", originalOutputPath + 'WhiteMatterVoxel') shutil.copy2(self.outputPath + "_vox_final.nii.gz", originalOutputPath + 'CSFVoxel') currentVoxGreyMatter = fslmaths(originalOutputPath + 'GreyMatterVoxel/' + "_" + str(i) + "_" + str(j) + "_vox_final.nii.gz") currentVoxWhiteMatter = fslmaths(originalOutputPath + 'WhiteMatterVoxel/' + "_" + str(i) + "_" + str(j) + "_vox_final.nii.gz") currentVoxCSF = fslmaths(originalOutputPath + 'CSFVoxel/' + "_" + str(i) + "_" + str(j) + "_vox_final.nii.gz") currentVoxGreyMatter.mul(greyMatter) currentVoxWhiteMatter.mul(whiteMatter) currentVoxCSF.mul(CSF) currentVoxGreyMatter.run(originalOutputPath + 'GreyMatterVoxel/' + "_" + str(i) + "_" + str(j) + "current_vox_final.nii.gz") currentVoxWhiteMatter.run(originalOutputPath + 'WhiteMatterVoxel/' + "_" + str(i) + "_" + str(j) + "current_vox_final.nii.gz") currentVoxCSF.run(originalOutputPath + 'CSFVoxel/' + "_" + str(i) + "_" + str(j) + "current_vox_final.nii.gz") voxGreyMatter = fslmaths( originalOutputPath + 'GreyMatterVoxel/constructedGreyMatterVoxel.nii.gz') voxWhiteMatter = fslmaths( originalOutputPath + 'WhiteMatterVoxel/constructedWhiteMatterVoxel.nii.gz') voxCSF = fslmaths(originalOutputPath + 'CSFVoxel/constructedCSFVoxel.nii.gz') voxGreyMatter.add(originalOutputPath + 'GreyMatterVoxel/' + "_" + str(i) + "_" + str(j) + "current_vox_final.nii.gz") voxWhiteMatter.add(originalOutputPath + 'WhiteMatterVoxel/' + "_" + str(i) + "_" + str(j) + "current_vox_final.nii.gz") voxCSF.add(originalOutputPath + 'CSFVoxel/' + "_" + str(i) + "_" + str(j) + "current_vox_final.nii.gz") voxGreyMatter.run( originalOutputPath + 'GreyMatterVoxel/constructedGreyMatterVoxel.nii.gz') voxWhiteMatter.run( originalOutputPath + 'WhiteMatterVoxel/constructedWhiteMatterVoxel.nii.gz') voxCSF.run(originalOutputPath + 'CSFVoxel/constructedCSFVoxel.nii.gz') tissueTypeFile.close() print(tempPos0) print(tempPos1) print(xEven) print(yEven)