def test_fast(): with asrt.disabled(), run.dryrun(), mockFSLDIR(bin=('fast', )) as fsldir: cmd = op.join(fsldir, 'bin', 'fast') result = fw.fast('input', 'myseg', n_classes=3) expected = [cmd, '--out=myseg', '--class=3', 'input'] assert result.stdout[0] == ' '.join(expected) result = fw.fast(('in1', 'in2', 'in3'), 'myseg', n_classes=3) expected = [cmd, '--out=myseg', '--class=3', 'in1', 'in2', 'in3'] assert result.stdout[0] == ' '.join(expected) result = fw.fast(('in1', 'in2', 'in3'), 'myseg', n_classes=3, verbose=True) expected = [ cmd, '--out=myseg', '--class=3', '--verbose', 'in1', 'in2', 'in3' ] assert result.stdout[0] == ' '.join(expected) result = fw.fast(('in1', 'in2', 'in3'), 'myseg', n_classes=3, a='reg.mat', A=('csf', 'gm', 'wm'), Prior=True) expected = [ cmd, '--out=myseg', '--class=3', '-a', 'reg.mat', '-A', 'csf', 'gm', 'wm', '--Prior', 'in1', 'in2', 'in3' ] assert result.stdout[0] == ' '.join(expected)
def test_fast(): with asrt.disabled(), run.dryrun(), mockFSLDIR(bin=('fast', )) as fsldir: cmd = op.join(fsldir, 'bin', 'fast') result = fw.fast('input', 'myseg', n_classes=3) expected = [cmd, '-v', '--out=myseg', '--class=3', 'input'] assert result.stdout[0] == ' '.join(expected) result = fw.fast(('in1', 'in2', 'in3'), 'myseg', n_classes=3) expected = [cmd, '-v', '--out=myseg', '--class=3', 'in1', 'in2', 'in3'] assert result.stdout[0] == ' '.join(expected)
def bias_estimation_calib(calib_name): """ Estimate the bias field from a calibration image using FAST. Parameters ---------- calib_name: pathlib.Path Path to the calibration image from which to estimate the bias field. Returns ------- bias_field: Nifti1Image """ # perform brain extraction using BET betted_m0 = bet(str(calib_name), LOAD, g=0.2, f=0.2, m=True) # run FAST on BET-ed calibration image fast_results = fast(betted_m0["output"], out=LOAD, type=3, b=True, nopve=True) # extract and return bias field bias_field = fast_results["out_bias"] return bias_field
def segment(wsp): """ Segment the structural image """ init(wsp) if None in (wsp.structural.wm_seg, wsp.structural.gm_seg, wsp.structural.csf_seg): init(wsp) page = wsp.report.page("seg") page.heading("Segmentation of structural image") wsp.log.write("\nGetting structural segmentation\n") if wsp.fslanat: wsp.log.write(" - Using FSL_ANAT output\n") page.text("Segmentation taken from FSL_ANAT output at ``%s``" % wsp.fslanat) wsp.structural.csf_pv = Image(os.path.join(wsp.fslanat, "T1_fast_pve_0")) wsp.structural.gm_pv = Image(os.path.join(wsp.fslanat, "T1_fast_pve_1")) wsp.structural.wm_pv = Image(os.path.join(wsp.fslanat, "T1_fast_pve_2")) try: wsp.structural.bias = Image(os.path.join(wsp.fslanat, "T1_fast_bias")) wsp.log.write(" - Bias field extracted sucessfully\n") except PathError: wsp.log.write(" - No bias field found") elif wsp.fastsrc: # FIXME should be possible to implement this raise NotImplementedError("Specifying FAST output directory") #img = os.path.split(wsp.fastsrc)[1] #wsp.structural.csf_pv = os.path.join(wsp.fastsrc, "%s_pve_0" % img) #wsp.structural.gm_pv = os.path.join(wsp.fastsrc, "%s_pve_1" % img) #wsp.structural.wm_pv = os.path.join(wsp.fastsrc, "%s_pve_2" % img) elif wsp.structural.struc: wsp.log.write(" - Running FAST\n") page.text("FAST run to segment structural image") fast_result = fsl.fast(wsp.structural.brain, out=fsl.LOAD, log=wsp.fsllog) wsp.structural.csf_pv = fast_result["out_pve_0"] wsp.structural.gm_pv = fast_result["out_pve_1"] wsp.structural.wm_pv = fast_result["out_pve_2"] #wsp.bias_struc = fast_result["fast_bias"] else: raise ValueError("No structural data provided - cannot segment") wsp.structural.csf_seg = Image((wsp.structural.csf_pv.data > 0.5).astype(np.int), header=wsp.structural.struc.header) wsp.structural.gm_seg = Image((wsp.structural.gm_pv.data > 0.5).astype(np.int), header=wsp.structural.struc.header) wsp.structural.wm_seg = Image((wsp.structural.wm_pv.data > 0.5).astype(np.int), header=wsp.structural.struc.header) page.heading("Segmentation image", level=1) page.text("CSF partial volume") page.image("csf_pv", LightboxImage(wsp.structural.csf_pv, bgimage=wsp.structural.brain)) page.text("Grey matter partial volume") page.image("gm_pv", LightboxImage(wsp.structural.gm_pv, bgimage=wsp.structural.brain)) page.text("White matter partial volume") page.image("wm_pv", LightboxImage(wsp.structural.wm_pv, bgimage=wsp.structural.brain))
def segment(wsp): """ Segment the structural image """ # Can override segmentation from user-specified maps wsp.structural.wm_seg = wsp.wm_seg wsp.structural.gm_seg = wsp.gm_seg wsp.structural.csf_seg = wsp.csf_seg if None in (wsp.structural.wm_seg, wsp.structural.gm_seg, wsp.structural.csf_seg): page = wsp.report.page("seg") page.heading("Segmentation of structural image") wsp.log.write("\nGetting structural segmentation\n") if wsp.fslanat: wsp.log.write(" - Using FSL_ANAT output from %s\n" % wsp.fslanat) page.text("Segmentation taken from FSL_ANAT output at ``%s``" % wsp.fslanat) wsp.structural.csf_pv = Image( os.path.join(wsp.fslanat, "T1_fast_pve_0")) wsp.structural.gm_pv = Image( os.path.join(wsp.fslanat, "T1_fast_pve_1")) wsp.structural.wm_pv = Image( os.path.join(wsp.fslanat, "T1_fast_pve_2")) try: wsp.structural.bias = Image( os.path.join(wsp.fslanat, "T1_fast_bias")) wsp.log.write(" - Bias field extracted sucessfully\n") except PathError: wsp.log.write(" - No bias field found") elif wsp.fastsrc: wsp.log.write(" - Using FAST output from %s\n" % wsp.fastsrc) page.text("Segmentation taken from FAST output at ``%s``" % wsp.fastsrc) wsp.structural.csf_pv = Image("%s_pve_0" % wsp.fastsrc) wsp.structural.gm_pv = Image("%s_pve_1" % wsp.fastsrc) wsp.structural.wm_pv = Image("%s_pve_2" % wsp.fastsrc) elif wsp.structural.struc: wsp.log.write(" - Running FAST\n") page.text("FAST run to segment structural image") fast_result = fsl.fast(wsp.structural.brain, out=fsl.LOAD, log=wsp.fsllog) wsp.structural.csf_pv = fast_result["out_pve_0"] wsp.structural.gm_pv = fast_result["out_pve_1"] wsp.structural.wm_pv = fast_result["out_pve_2"] #wsp.bias_struc = fast_result["fast_bias"] else: raise ValueError("No structural data provided - cannot segment") if wsp.structural.csf_seg is not None: wsp.structural.csf_seg = Image( (wsp.structural.csf_pv.data > 0.5).astype(np.int), header=wsp.structural.struc.header) if wsp.structural.gm_seg is not None: wsp.structural.gm_seg = Image( (wsp.structural.gm_pv.data > 0.5).astype(np.int), header=wsp.structural.struc.header) if wsp.structural.wm_seg is not None: wsp.structural.wm_seg = Image( (wsp.structural.wm_pv.data > 0.5).astype(np.int), header=wsp.structural.struc.header) page.heading("Segmentation image", level=1) page.text("CSF partial volume") page.image( "csf_pv", LightboxImage(wsp.structural.csf_pv, bgimage=wsp.structural.brain)) page.text("Grey matter partial volume") page.image( "gm_pv", LightboxImage(wsp.structural.gm_pv, bgimage=wsp.structural.brain)) page.text("White matter partial volume") page.image( "wm_pv", LightboxImage(wsp.structural.wm_pv, bgimage=wsp.structural.brain))
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 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)