예제 #1
0
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
예제 #2
0
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
예제 #3
0
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)
예제 #4
0
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[:])
예제 #5
0
    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)
예제 #6
0
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)
예제 #7
0
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)
예제 #8
0
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)
예제 #9
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)
예제 #10
0
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
예제 #11
0
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)
예제 #12
0
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)
예제 #13
0
    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)