Ejemplo n.º 1
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
def generate_fmaps(pa_ap_sefms, params, config, distcorr_dir):
    """
    Generate fieldmaps via topup for use with asl_reg. 

    Args: 
        asl_vol0: path to image of stacked blipped images (ie, PEdir as vol0,
            (oPEdir as vol1), in this case stacked as pa then ap)
        params: path to text file for topup --datain, PE directions/times
        config: path to text file for topup --config, other args 
        distcorr_dir: directory in which to put output
    
    Returns: 
        n/a, files 'fmap, fmapmag, fmapmagbrain.nii.gz' will be created in output dir
    """

    pwd = os.getcwd()
    os.chdir(distcorr_dir)

    # Run topup to get fmap in Hz
    topup_fmap = op.join(distcorr_dir, 'topup_fmap_hz.nii.gz')
    cmd = (("topup --imain={} --datain={}".format(pa_ap_sefms, params) +
            " --config={} --out=topup".format(config)) +
           " --fout={} --iout={}".format(
               topup_fmap, op.join(distcorr_dir, 'corrected_sefms.nii.gz')))
    sp.run(cmd, shell=True)

    fmap, fmapmag, fmapmagbrain = [
        op.join(distcorr_dir, '{}.nii.gz'.format(s))
        for s in ['fmap', 'fmapmag', 'fmapmagbrain']
    ]

    # Convert fmap from Hz to rad/s
    fmap_spc = rt.ImageSpace(topup_fmap)
    fmap_arr_hz = nb.load(topup_fmap).get_data()
    fmap_arr = fmap_arr_hz * 2 * np.pi
    fmap_spc.save_image(fmap_arr, fmap)

    # Mean across volumes of corrected sefms to get fmapmag
    fmapmag_arr = nb.load(op.join(distcorr_dir,
                                  "corrected_sefms.nii.gz")).get_data()
    fmapmag_arr = fmapmag_arr.mean(-1)
    fmap_spc.save_image(fmapmag_arr, fmapmag)

    # Run BET on fmapmag to get brain only version
    bet(fmap_spc.make_nifti(fmapmag_arr), output=fmapmagbrain)

    os.chdir(pwd)
Ejemplo n.º 4
0
def run(wsp):
    """
    Do initialization on supplied structural data - copy relevant image and do brain extraction

    FIXME copy across all supplied structural data
    """
    wsp.log.write("\nInitialising structural data\n")
    wsp.sub("structural")

    if wsp.fslanat:
        wsp.log.write(
            " - Using FSL_ANAT output directory for structural data: %s\n" %
            wsp.fslanat)
        biascorr = os.path.join(wsp.fslanat, "T1_biascorr")
        biascorr_brain = os.path.join(wsp.fslanat, "T1_biascorr_brain")
        if glob.glob(biascorr + ".*") and glob.glob(biascorr_brain + ".*"):
            wsp.log.write(" - Using bias-corrected structural images\n")
            wsp.structural.struc = Image(biascorr)
            wsp.structural.brain = Image(biascorr_brain)
        else:
            wsp.log.write(" - Using non bias-corrected structural images\n")
            wsp.structural.struc = Image(os.path.join(wsp.fslanat, "T1"))
            wsp.structural.brain = Image(os.path.join(wsp.fslanat, "T1_brain"))
    elif wsp.struc:
        wsp.log.write(" - Using structural image provided by user: %s\n" %
                      wsp.struc.name)
        wsp.structural.struc = wsp.struc
        wsp.structural.brain = wsp.struc_brain
    #elif wsp.structural.struc_lores
    #    wsp.log.write("Low-resolution tructural image: %s\n" % wsp.structural.struc_lores.name)
    else:
        wsp.log.write(
            " - No structural data supplied - output will be native space only\n"
        )

    if wsp.structural.struc is not None and wsp.structural.brain is None:
        wsp.log.write(" - Brain-extracting structural image\n")
        bet_result = fsl.bet(wsp.structural.struc,
                             output=fsl.LOAD,
                             seg=True,
                             mask=True,
                             log=wsp.fsllog)
        wsp.structural.brain = bet_result["output"]
        #wsp.structural.brain_mask = bet_result["output_mask"]

    if wsp.structural.brain is not None and wsp.structural.brain_mask is None:
        # FIXME - for now get the mask by binarising the brain image for compatibility with oxford_asl
        # although this gives slightly different results compared to using the mask returned by BET
        wsp.structural.brain_mask = Image(
            (wsp.structural.brain.data != 0).astype(np.int),
            header=wsp.structural.struc.header)

    if wsp.structural.struc is not None:
        segment(wsp)
Ejemplo n.º 5
0
def get_mask(wsp):
    """
    Generate the mask to use - if it has not been provided by the user
    """
    if wsp.mask is None:
        wsp.datamean = Image(np.mean(wsp.dscdata.data, axis=-1),
                             header=wsp.dscdata.header)
        bet_result = fsl.bet(wsp.datamean,
                             seg=False,
                             mask=True,
                             output=fsl.LOAD,
                             log=wsp.fsllog)
        wsp.mask = bet_result["output_mask"]
        wsp.log.write(
            "  - Mask generated by running BET on time-averaged DSC data\n")
    else:
        wsp.log.write("  - Mask supplied by user\n")
Ejemplo n.º 6
0
def generate_fmaps(pa_ap_sefms,
                   params,
                   config,
                   distcorr_dir,
                   gdc_warp,
                   interpolation=3):
    """
    Generate fieldmaps via topup for use with asl_reg. 

    Args: 
        asl_vol0: path to image of stacked blipped images (ie, PEdir as vol0,
            (oPEdir as vol1), in this case stacked as pa then ap)
        params: path to text file for topup --datain, PE directions/times
        config: path to text file for topup --config, other args 
        distcorr_dir: directory in which to put output
        gdc_warp: path to gradient_unwarp's gradient distortion correction warp
        interpolation: order of interpolation to be used when applying registrations, 
            default=3
    
    Returns: 
        n/a, files 'fmap, fmapmag, fmapmagbrain.nii.gz' will be created in output dir
    """
    # set up logger
    logger = logging.getLogger("HCPASL.distortion_estimation")
    logger.info("Running generate_fmaps()")
    logger.info(f"Spin Echo Field Maps: {pa_ap_sefms}")
    logger.info(f"Topup param file: {params}")
    logger.info(f"Topup config file: {config}")
    logger.info(f"Topup output directory: {distcorr_dir}")
    logger.info(f"Gradient distortion correction warp: {gdc_warp}")
    logger.info(f"Interpolation order: {interpolation}")

    pwd = os.getcwd()
    os.chdir(distcorr_dir)

    # apply gradient distortion correction to stacked SEFMs
    gdc = rt.NonLinearRegistration.from_fnirt(coefficients=str(gdc_warp),
                                              src=str(pa_ap_sefms),
                                              ref=str(pa_ap_sefms),
                                              intensity_correct=True)
    gdc_corr_pa_ap_sefms = gdc.apply_to_image(src=str(pa_ap_sefms),
                                              ref=str(pa_ap_sefms),
                                              order=interpolation,
                                              cores=1)
    gdc_corr_pa_ap_sefms_name = distcorr_dir / "merged_sefms_gdc.nii.gz"
    nb.save(gdc_corr_pa_ap_sefms, gdc_corr_pa_ap_sefms_name)

    # Run topup to get fmap in Hz
    topup_fmap = op.join(distcorr_dir, 'topup_fmap_hz.nii.gz')
    topup_cmd = [
        "topup", f"--imain={gdc_corr_pa_ap_sefms_name}", f"--datain={params}",
        f"--config={config}", f"--out=topup",
        f"--iout={op.join(distcorr_dir, 'corrected_sefms.nii.gz')}",
        f"--fout={topup_fmap}",
        f"--dfout={op.join(distcorr_dir, 'WarpField')}",
        f"--rbmout={op.join(distcorr_dir, 'MotionMatrix')}",
        f"--jacout={op.join(distcorr_dir, 'Jacobian')}", "--verbose"
    ]
    logger.info(f"Topup command: {' '.join(topup_cmd)}")
    process = sp.Popen(topup_cmd, stdout=sp.PIPE)
    while 1:
        retcode = process.poll()
        line = process.stdout.readline().decode("utf-8")
        logger.info(line)
        if line == "" and retcode is not None:
            break
    if retcode != 0:
        logger.info(f"retcode={retcode}")
        logger.exception("Process failed.")

    fmap, fmapmag, fmapmagbrain = [
        op.join(distcorr_dir, '{}.nii.gz'.format(s))
        for s in ['fmap', 'fmapmag', 'fmapmagbrain']
    ]

    # Convert fmap from Hz to rad/s
    logger.info("Converting fieldmap from Hz to rad/s.")
    fmap_spc = rt.ImageSpace(topup_fmap)
    fmap_arr_hz = nb.load(topup_fmap).get_data()
    fmap_arr = fmap_arr_hz * 2 * np.pi
    fmap_spc.save_image(fmap_arr, fmap)

    # Apply gdc warp from gradient_unwarp and topup's EPI-DC
    # warp (just generated) in one interpolation step
    logger.info(
        "Applying gdc and epi-dc to fieldmap images in one interpolation step."
    )
    pa_ap_sefms_gdc_dc = apply_gdc_and_topup(pa_ap_sefms,
                                             distcorr_dir,
                                             gdc_warp,
                                             interpolation=interpolation)

    # Mean across volumes of corrected sefms to get fmapmag
    logger.info(
        "Taking mean of corrected fieldmap images to get fmapmag.nii.gz")
    fmapmag_img = nb.nifti1.Nifti1Image(
        pa_ap_sefms_gdc_dc.get_fdata().mean(-1),
        affine=pa_ap_sefms_gdc_dc.affine)
    nb.save(fmapmag_img, fmapmag)

    # Run BET on fmapmag to get brain only version
    logger.info("Running BET on fmapmag for brain-extracted version.")
    bet(fmapmag, output=fmapmagbrain)

    os.chdir(pwd)
Ejemplo n.º 7
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)
Ejemplo n.º 8
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).")
    parser.add_argument(
        "-t",
        "--target",
        help="Which space we want to register to. Can be either 'asl' for " +
        "registration to the first volume of the ASL series or " +
        "'structural' for registration to the T1w image. Default " +
        " is 'asl'.",
        default="asl")

    args = parser.parse_args()
    study_dir = args.study_dir
    sub_id = args.sub_number
    grad_coefficients = args.grads
    target = args.target

    # For debug, re-use existing intermediate files
    force_refresh = True

    # Input, output and intermediate directories
    # Create if they do not already exist.
    sub_base = op.abspath(op.join(study_dir, sub_id))
    grad_coefficients = op.abspath(grad_coefficients)
    pvs_dir = op.join(sub_base, "T1w", "ASL", "PVEs")
    t1_asl_dir = op.join(sub_base, "T1w", "ASL")
    distcorr_dir = op.join(sub_base, "ASL", "TIs", "SecondPass", "DistCorr")
    reg_dir = op.join(sub_base, 'T1w', 'ASL', 'reg')
    t1_dir = op.join(sub_base, "T1w")
    asl_dir = op.join(sub_base, "ASL", "TIs", "SecondPass", "STCorr2")
    asl_out_dir = op.join(t1_asl_dir, "TIs", "DistCorr")
    calib_out_dir = op.join(t1_asl_dir, "Calib", "Calib0", "DistCorr")
    [
        os.makedirs(d, exist_ok=True) for d in [
            pvs_dir, t1_asl_dir, distcorr_dir, reg_dir, asl_out_dir,
            calib_out_dir
        ]
    ]

    # Images required for processing
    asl = op.join(asl_dir, "tis_stcorr.nii.gz")
    struct = op.join(t1_dir, "T1w_acpc_dc_restore.nii.gz")
    struct_brain = op.join(t1_dir, "T1w_acpc_dc_restore_brain.nii.gz")
    struct_brain_mask = op.join(t1_dir,
                                "T1w_acpc_dc_restore_brain_mask.nii.gz")
    asl_vol0 = op.join(asl_dir, "tis_stcorr_vol1.nii.gz")
    if (not op.exists(asl_vol0) or force_refresh) and target == 'asl':
        cmd = "fslroi {} {} 0 1".format(asl, asl_vol0)
        sp.run(cmd.split(" "), check=True)

    # Create ASL-gridded version of T1 image
    t1_asl_grid = op.join(t1_dir, "ASL", "reg",
                          "ASL_grid_T1w_acpc_dc_restore.nii.gz")
    if (not op.exists(t1_asl_grid) or force_refresh) and target == 'asl':
        asl_spc = rt.ImageSpace(asl)
        t1_spc = rt.ImageSpace(struct)
        t1_asl_grid_spc = t1_spc.resize_voxels(asl_spc.vox_size /
                                               t1_spc.vox_size)
        nb.save(
            rt.Registration.identity().apply_to_image(struct, t1_asl_grid_spc),
            t1_asl_grid)

    # Create ASL-gridded version of T1 image
    t1_asl_grid_mask = op.join(
        reg_dir, "ASL_grid_T1w_acpc_dc_restore_brain_mask.nii.gz")
    if (not op.exists(t1_asl_grid_mask) or force_refresh) and target == 'asl':
        asl_spc = rt.ImageSpace(asl)
        t1_spc = rt.ImageSpace(struct_brain)
        t1_asl_grid_spc = t1_spc.resize_voxels(asl_spc.vox_size /
                                               t1_spc.vox_size)
        t1_mask = binarise_image(struct_brain)
        t1_mask_asl_grid = rt.Registration.identity().apply_to_array(
            t1_mask, t1_spc, t1_asl_grid_spc)
        # Re-binarise downsampled mask and save
        t1_asl_grid_mask_array = binary_fill_holes(
            t1_mask_asl_grid > 0.25).astype(np.float32)
        t1_asl_grid_spc.save_image(t1_asl_grid_mask_array, t1_asl_grid_mask)

    # MCFLIRT ASL using the calibration as reference
    calib = op.join(sub_base, 'ASL', 'Calib', 'Calib0', 'MTCorr',
                    'calib0_mtcorr.nii.gz')
    asl = op.join(sub_base, 'ASL', 'TIs', 'tis.nii.gz')
    mcdir = op.join(sub_base, 'ASL', 'TIs', 'SecondPass', 'MoCo',
                    'asln2m0.mat')
    asl2calib_mc = rt.MotionCorrection.from_mcflirt(mcdir, asl, calib)

    # Rebase the motion correction to target volume 0 of ASL
    # The first registration in the series gives us ASL-calibration transform
    calib2asl0 = asl2calib_mc[0].inverse()
    asl_mc = rt.chain(asl2calib_mc, calib2asl0)

    # Generate the gradient distortion correction warp
    gdc_path = op.join(distcorr_dir, 'fullWarp_abs.nii.gz')
    if (not op.exists(gdc_path) or force_refresh) and target == 'asl':
        generate_gdc_warp(asl_vol0, grad_coefficients, distcorr_dir)
    gdc = rt.NonLinearRegistration.from_fnirt(gdc_path,
                                              asl_vol0,
                                              asl_vol0,
                                              intensity_correct=True,
                                              constrain_jac=(0.01, 100))

    # Stack the cblipped images together for use with topup
    pa_sefm, ap_sefm = find_field_maps(study_dir, sub_id)
    pa_ap_sefms = op.join(distcorr_dir, 'merged_sefms.nii.gz')
    if (not op.exists(pa_ap_sefms) or force_refresh) and target == 'asl':
        rt.ImageSpace.save_like(
            pa_sefm,
            np.stack(
                (nb.load(pa_sefm).get_data(), nb.load(ap_sefm).get_data()),
                axis=-1), pa_ap_sefms)
    topup_params = op.join(distcorr_dir, 'topup_params.txt')
    generate_topup_params(topup_params)
    topup_config = "b02b0.cnf"  # Note this file doesn't exist in scope,
    # but topup knows where to find it

    # Generate fieldmaps for use with asl_reg (via topup)
    fmap, fmapmag, fmapmagbrain = [
        op.join(distcorr_dir, '{}.nii.gz'.format(s))
        for s in ['fmap', 'fmapmag', 'fmapmagbrain']
    ]
    if ((not all([op.exists(p) for p in [fmap, fmapmag, fmapmagbrain]]))
            or force_refresh) and target == 'asl':
        generate_fmaps(pa_ap_sefms, topup_params, topup_config, distcorr_dir)

    # get linear registration from asl to structural
    if target == 'asl':
        unreg_img = asl_vol0
    elif target == 'structural':
        # register perfusion-weighted image to structural instead of asl 0
        unreg_img = op.join(sub_base, "ASL", "TIs", "SecondPass", "OxfordASL",
                            "native_space", "perfusion.nii.gz")

    # apply gdc to unreg_img before getting registration to structural
    # only apply to asl_vol0 as perfusion image has already had gdc applied
    distcorr_out_dir = asl_out_dir if target == 'structural' else distcorr_dir
    gdc_tis_vol1_name = op.join(distcorr_out_dir, "gdc_tis_vol1.nii.gz")
    if (not op.exists(gdc_tis_vol1_name) or force_refresh) and target == 'asl':
        gdc_tis_vol1 = gdc.apply_to_image(src=unreg_img, ref=unreg_img)
        unreg_img = gdc_tis_vol1_name
        nb.save(gdc_tis_vol1, unreg_img)

    # Initial (linear) asl to structural registration, via first round of asl_reg
    asl2struct_initial_path = op.join(
        reg_dir,
        'asl2struct_init.mat' if target == 'asl' else 'asl2struct_final.mat')
    if not op.exists(asl2struct_initial_path) or force_refresh:
        generate_asl2struct_initial(unreg_img, reg_dir, struct, struct_brain)
        asl2struct_initial_path_temp = op.join(reg_dir, 'asl2struct.mat')
        os.replace(asl2struct_initial_path_temp, asl2struct_initial_path)
    asl2struct_initial = rt.Registration.from_flirt(asl2struct_initial_path,
                                                    src=unreg_img,
                                                    ref=struct)

    # Get brain mask in asl space
    if target == 'asl':
        mask_name = op.join(reg_dir, "asl_vol1_mask_init.nii.gz")
    else:
        mask_name = op.join(reg_dir, "asl_vol1_mask_final.nii.gz")
    if not op.exists(mask_name) or force_refresh:
        asl_mask = generate_asl_mask(struct_brain, unreg_img,
                                     asl2struct_initial)
        rt.ImageSpace.save_like(unreg_img, asl_mask, mask_name)

    # Brain extract volume 0 of asl series
    gdc_unreg_img_brain = op.join(sub_base, "ASL", "TIs", "SecondPass",
                                  "DistCorr", "gdc_tis_vol1_brain.nii.gz")
    if (not op.exists(gdc_unreg_img_brain)
            or force_refresh) and target == 'asl':
        bet(unreg_img, gdc_unreg_img_brain)
        unreg_img = gdc_unreg_img_brain

    # Generate a binary WM mask in the space of the T1 (using FS' aparc+aseg)
    wmmask = op.join(sub_base, "T1w", "wmmask.nii.gz")
    if (not op.exists(wmmask) or force_refresh) and target == 'asl':
        aparc_seg = op.join(t1_dir, "aparc+aseg.nii.gz")
        wmmask_img = generate_wmmask(aparc_seg)
        rt.ImageSpace.save_like(struct, wmmask_img, wmmask)

    # Generate the EPI distortion correction warp via asl_reg --final
    epi_dc_path = op.join(
        distcorr_dir, 'asl2struct_warp_init.nii.gz'
        if target == 'asl' else 'asl2struct_warp_final.nii.gz')
    if not op.exists(epi_dc_path) or force_refresh:
        epi_dc_path_temp = op.join(distcorr_dir, 'asl2struct_warp.nii.gz')
        generate_epidc_warp(unreg_img, struct, struct_brain, mask_name, wmmask,
                            asl2struct_initial, fmap, fmapmag, fmapmagbrain,
                            distcorr_dir)
        # rename warp so it isn't overwritten
        os.replace(epi_dc_path_temp, epi_dc_path)
    epi_dc = rt.NonLinearRegistration.from_fnirt(epi_dc_path,
                                                 mask_name,
                                                 struct,
                                                 intensity_correct=True,
                                                 constrain_jac=(0.01, 100))

    # if ending in asl space, chain struct2asl transformation
    if target == 'asl':
        struct2asl_aslreg = op.join(distcorr_out_dir, "struct2asl.mat")
        struct2asl_aslreg = rt.Registration.from_flirt(struct2asl_aslreg,
                                                       src=struct,
                                                       ref=asl)
        epi_dc = rt.chain(epi_dc, struct2asl_aslreg)

    # Final ASL transforms: moco, grad dc,
    # epi dc (incorporating asl->struct reg)
    asl = op.join(asl_dir, "tis_stcorr.nii.gz")
    reference = t1_asl_grid if target == 'structural' else asl
    asl_outpath = op.join(distcorr_out_dir, "tis_distcorr.nii.gz")
    if not op.exists(asl_outpath) or force_refresh:
        asl2struct_mc_dc = rt.chain(asl_mc, gdc, epi_dc)
        asl_corrected = asl2struct_mc_dc.apply_to_image(src=asl,
                                                        ref=reference,
                                                        cores=mp.cpu_count())
        nb.save(asl_corrected, asl_outpath)

    # Final calibration transforms: calib->asl, grad dc,
    # epi dc (incorporating asl->struct reg)
    calib_outpath = op.join(calib_out_dir, "calib0_dcorr.nii.gz")
    if (not op.exists(calib_outpath)
            or force_refresh) and target == 'structural':
        calib2struct_dc = rt.chain(calib2asl0, gdc, epi_dc)
        calib_corrected = calib2struct_dc.apply_to_image(src=calib,
                                                         ref=reference)

        nb.save(calib_corrected, calib_outpath)

    # Final scaling factors transforms: moco, grad dc,
    # epi dc (incorporating asl->struct reg)
    sfs_name = op.join(asl_dir, "combined_scaling_factors.nii.gz")
    sfs_outpath = op.join(distcorr_out_dir, "combined_scaling_factors.nii.gz")
    if not op.exists(sfs_outpath) or force_refresh:
        # don't chain transformations together if we don't have to
        try:
            asl2struct_mc_dc
        except NameError:
            asl2struct_mc_dc = rt.chain(asl_mc, gdc, epi_dc)
        sfs_corrected = asl2struct_mc_dc.apply_to_image(src=sfs_name,
                                                        ref=reference,
                                                        cores=mp.cpu_count())
        nb.save(sfs_corrected, sfs_outpath)

    # create ti image in asl space
    slicedt = 0.059
    tis = [1.7, 2.2, 2.7, 3.2, 3.7]
    sliceband = 10
    ti_asl = op.join(sub_base, "ASL", "TIs", "timing_img.nii.gz")
    if (not op.exists(ti_asl) or force_refresh) and target == 'asl':
        create_ti_image(asl, tis, sliceband, slicedt, ti_asl)

    # transform ti image into t1 space
    ti_t1 = op.join(t1_asl_dir, "timing_img.nii.gz")
    if (not op.exists(ti_t1) or force_refresh) and target == 'structural':
        asl2struct = op.join(distcorr_dir, "asl2struct.mat")
        asl2struct = rt.Registration.from_flirt(asl2struct,
                                                src=asl,
                                                ref=struct)
        ti_t1_img = asl2struct.apply_to_image(src=ti_asl, ref=reference)
        nb.save(ti_t1_img, ti_t1)
Ejemplo n.º 9
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)
Ejemplo n.º 10
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)
Ejemplo n.º 11
0
def test_bet():
    with asrt.disabled(), run.dryrun(), mockFSLDIR(bin=('bet', )) as fsldir:
        bet = op.join(fsldir, 'bin', 'bet')
        result = fw.bet('input', 'output', mask=True, c=(10, 20, 30))
        expected = (bet + ' input output', ('-m', '-c 10 20 30'))
        assert checkResult(result.stdout[0], *expected, stripdir=[2])
Ejemplo n.º 12
0
def brain(wsp, img, thresh=0.5):
    """
    Extract brain from wholehead image
    """
    bet_result = fsl.bet(img, seg=True, mask=False, fracintensity=thresh, output=fsl.LOAD, log=wsp.fsllog)
    return bet_result["output"]