Exemple #1
0
def get_tissrefmask(wsp):
    """
    Calculate a calibration reference mask for a particular known tissue type
    """
    page = wsp.report.page("auto_calib_mask")
    page.heading("Calibration reference region")
    page.text("Reference region was automatically generated for tissue type: %s" % wsp.tissref.upper())
    page.heading("Partial volume map for %s tissue (from structural segmentation)" % wsp.tissref.upper(), level=1)
    wsp.calibration.refpve = getattr(wsp.structural, "%s_pv" % wsp.tissref.lower())
    page.image("refpve", LightboxImage(wsp.calibration.refpve, bgimage=wsp.structural.brain))

    if wsp.tissref == "csf" and not wsp.csfmaskingoff:
        wsp.log.write(" - Doing automatic ventricle selection using standard atlas\n")
        # By deafult now we do FNRIT transformation of ventricle mask
        # FIXME disabled as not being used in ASL_CALIB at present
        #wsp.calibration.struc2stdfnirt = wsp.ifnone("stdmaskfnirt", True)

        # Select ventricles based on standard space atlas
        page.heading("Automatic ventricle selection", level=1)
        page.text("Standard space ventricles mask (from Harvard-Oxford atlas) eroded by 1 pixel")
        atlases = AtlasRegistry()
        atlases.rescanAtlases()
        atlas = atlases.loadAtlas("harvardoxford-subcortical", loadSummary=False, resolution=2)
        ventricles = ((atlas.data[..., 2] + atlas.data[..., 13]) > 0.1).astype(np.int)
        wsp.calibration.ventricles = Image(scipy.ndimage.binary_erosion(ventricles, structure=np.ones([3, 3, 3]), border_value=1).astype(np.int), header=atlas.header)
        std_img = Image(os.path.join(os.environ["FSLDIR"], "data", "standard", 'MNI152_T1_2mm_brain'))
        page.image("ventricles_std", LightboxImage(wsp.calibration.ventricles, bgimage=std_img))

        page.heading("Structural space ventricles mask", level=1)
        # FIXME nearest neighbour interpolation?
        wsp.calibration.ventricles_struc = reg.change_space(wsp, wsp.calibration.ventricles, "struc")
        page.text("This is the above image transformed into structural space. The transformation was obtained by registering the structural image to the standard brain image")
        page.image("ventricles_struc", LightboxImage(wsp.calibration.ventricles_struc, bgimage=wsp.structural.brain))

        wsp.log.write(" - Masking FAST output with standard space derived ventricle mask\n")
        wsp.calibration.refpve_pre_mask = wsp.calibration.refpve
        refpve_data = np.copy(wsp.calibration.refpve.data)
        refpve_data[wsp.calibration.ventricles_struc.data == 0] = 0
        wsp.calibration.refpve = Image(refpve_data, header=wsp.calibration.refpve.header)
        wsp.calibration.refpve_post = wsp.calibration.refpve

        page.heading("Structural space ventricles PVE", level=1)
        page.text("This is the CSF partial volume masked by the ventricles mask. It should select only the ventricles from the original partial volume image.")
        page.image("refpve_post", LightboxImage(wsp.calibration.refpve, bgimage=wsp.structural.brain))

    wsp.log.write(" - Transforming tissue reference mask into ASL space\n")
    # FIXME calibration image may not be in ASL space! Oxford_asl does not handle this currently
    wsp.calibration.refpve_calib = reg.change_space(wsp, wsp.calibration.refpve, "native")
    #wsp.calibration.refpve_calib.data[wsp.calibration.refpve_calib.data < 0.001] = 0 # Better for display
    page.heading("Reference region in ASL space", level=1)
    page.text("Partial volume map")
    page.image("refpve_calib", LightboxImage(wsp.calibration.refpve_calib, bgimage=wsp.calibration.calib_img))

    # Threshold reference mask conservatively to select only reference tissue
    wsp.log.write(" - Thresholding reference mask\n")
    wsp.calibration.refmask = Image((wsp.calibration.refpve_calib.data > 0.9).astype(np.int), header=wsp.calibration.refpve_calib.header)

    page.text("Reference Mask (thresholded at 0.9")
    page.image("refmask", LightboxImage(wsp.calibration.refmask, bgimage=wsp.calibration.calib_img))
Exemple #2
0
def run(wsp):
    # If the user has provided manual PV maps (pvgm and pvgm) then do PVEc, even if they
    # have not explicitly given the --pvcorr option 
    user_pv_flag = ((wsp.pvwm is not None) and (wsp.pvgm is not None))
    if wsp.pvcorr or wsp.surf_pvcorr or user_pv_flag:
        # Partial volume correction is very sensitive to the mask, so recreate it
        # if it came from the structural image as this requires accurate ASL->Struc registration
        if wsp.rois.mask_src == "struc":
            wsp.rois.mask_orig = wsp.rois.mask
            wsp.rois.mask = None
            mask.run(wsp)

        if wsp.pvcorr or user_pv_flag:
            # Do partial volume correction fitting
            #
            # FIXME: We could at this point re-apply all corrections derived from structural space?
            # But would need to make sure corrections module re-transforms things like sensitivity map
            
            # Prepare GM and WM partial volume maps from FAST segmentation
            if user_pv_flag:
                wsp.log.write("\nUsing user-supplied PV estimates\n")
                wsp.structural.wm_pv_asl = wsp.pvwm
                wsp.structural.gm_pv_asl = wsp.pvgm
            else:
                wsp.structural.wm_pv_asl = reg.change_space(wsp, wsp.structural.wm_pv, "native")
                wsp.structural.gm_pv_asl = reg.change_space(wsp, wsp.structural.gm_pv, "native")

            wsp.basil_options = wsp.ifnone("basil_options", {})
            wsp.basil_options.update({"pwm" : wsp.structural.wm_pv_asl, 
                                      "pgm" : wsp.structural.gm_pv_asl})
            basil.run(wsp.sub("basil_pvcorr", prefit=False))
            wsp.basildirs.append("pvcorr")

        if wsp.surf_pvcorr:
            if oxasl_surfpvc is None:
                raise RuntimeError("Surface-based PVC requested but oxasl_surfpvc is not installed")
            if user_pv_flag:
                wsp.log.write(" - WARNING: Performing surface based PVC ignores user-specified PV maps\n")
            # Prepare GM and WM partial volume maps from surface using Toblerone plugin
            # Then reform the ASL ROI mask - Toblerone does not handle the cerebellum so need
            # to mask it out
            oxasl_surfpvc.prepare_surf_pvs(wsp)
            wsp.rois.mask_pvcorr = wsp.rois.mask
            min_pv = 0.01
            new_roi = (wsp.basil_options["pwm"].data > min_pv) | (wsp.basil_options["pgm"].data > min_pv)
            wsp.rois.mask = Image(new_roi.astype(np.int8), header=wsp.rois.mask_pvcorr.header)
        
            basil.run(wsp.sub("basil_surf_pvcorr"), prefit=False)
            wsp.basildirs.append("surf_pvcorr")
Exemple #3
0
def oxasl_add_atlas(wsp, rois, atlas_name, resolution=2, threshold=0.5):
    """
    Get ROIs from an FSL atlas
    
    :param rois: Mapping from name to ROI array which will be updated
    :param mni2struc_warp: Warp image containing MNI->structural space warp
    :param struct2asl_mat: Matrix for struct->ASL transformation
    :param atlas_name: Name of the FSL atlas
    :param resolution: Resolution in mm
    :param threshold: Threshold for probabilistic atlases
    """
    wsp.log.write(
        "\nAdding ROIs from standard atlas: %s (resolution=%imm, thresholding at %.2f)\n"
        % (atlas_name, resolution, threshold))
    registry = atlases.registry
    registry.rescanAtlases()
    desc = registry.getAtlasDescription(atlas_name)
    atlas = registry.loadAtlas(desc.atlasID, resolution=2)
    for label in desc.labels:
        roi_mni = atlas.get(label=label)
        roi_native = reg.change_space(wsp, roi_mni, "native")
        oxasl_add_roi(wsp,
                      rois,
                      label.name,
                      roi_native,
                      threshold=50,
                      roi_mni=roi_mni)
Exemple #4
0
def _calc_slicedt(wsp, options):
    """
    Calculate the slicedt for basil given that we may be quantifying in
    a space other than the usual ASL space

    We do this by generating a slice time offset image and transforming it
    to quantification space. Since this could be rotated wrt to the asl data
    we may need to warn if the resulting image has significant slice time variation
    across X or Y axes
    """
    img_space = wsp.ifnone("image_space", "native")
    if img_space != "native":
        asldata = options["data"]
        _x, _y, z, _t = np.indices(
            list(asldata.data.shape[:3]) + [
                asldata.ntis,
            ])
        print(z.shape)
        tis_arr = np.array(
            asldata.tis) + (z.astype(np.float32) * options["slicedt"])
        print(tis_arr.shape)

        tis_img = Image(tis_arr, header=options["data"].header)
        wsp.tiimg = reg.change_space(wsp, tis_img,
                                     wsp.ifnone("image_space", "native"))

        #print(ztrans.data)
        print(wsp.tiimg.data.shape)
        del options["slicedt"]
        ti_idx = 1
        while "ti%i" % ti_idx in options:
            del options["ti%i" % ti_idx]
            ti_idx += 1

        options["tiimg"] = wsp.tiimg
Exemple #5
0
def correct_img(wsp, img, linear_mat):
    """
    Apply combined warp/linear transformations to an image in ASL space

    :param img: fsl.data.image.Image to correct
    :param linear_mat: img->ASL space linear transformation matrix.
    :return: Corrected Image

    If a jacobian is present, also corrects for quantitative signal magnitude as volume has been locally scaled

    FIXME there are slight differences to oxford_asl here due to use of spline interpolation rather than
    applyxfm4D which uses sinc interpolation.

    Required workspace attributesd
    -----------------------------

     - ``asldata_mean`` : Mean ASL image used as reference space

    Optional workspace attributes
    -----------------------------

     - ``total_warp``      : Combined warp image
     - ``jacobian``        : Jacobian associated with warp image
     - ``senscorr``        : Sensitivity correction
    """
    if wsp.corrected.total_warp is not None:
        img = reg.transform(wsp,
                            img,
                            trans=wsp.corrected.total_warp,
                            ref=wsp.preproc.aslspace,
                            premat=linear_mat)
    else:
        img = reg.transform(wsp,
                            img,
                            trans=linear_mat,
                            ref=wsp.preproc.aslspace)

    if wsp.corrected.jacobian is not None:
        wsp.log.write(
            " - Correcting for local volume scaling using Jacobian\n")
        jdata = wsp.corrected.jacobian.data
        if img.data.ndim == 4:
            # Required to make broadcasting work
            jdata = jdata[..., np.newaxis]
        img = Image(img.data * jdata, header=img.header)

    if wsp.senscorr is not None:
        wsp.log.write(" - Applying sensitivity correction\n")
        sens_data = reg.change_space(wsp, wsp.senscorr.sensitivity, img).data
        if img.ndim == 4:
            sens_data = sens_data[..., np.newaxis]
        img = Image(img.data / sens_data, header=img.header)

    return img
Exemple #6
0
 def __init__(self, wsp, options, desc):
     self.options = dict(options)
     self.desc = desc
     # Need to convert all images to target image space
     for key in list(options.keys()):
         poss_img = self.options[key]
         if isinstance(poss_img, Image):
             image_space = wsp.ifnone("image_space", "native")
             self.options[key] = reg.change_space(wsp,
                                                  poss_img,
                                                  image_space,
                                                  mask=(key == 'mask'))
Exemple #7
0
def get_cblip_correction(wsp):
    """
    Get the cblip based distortion correction warp

    Required workspace attributes
    -----------------------------

     - ``calib`` : Calibration image
     - ``cblip`` : Phase-encode-reversed calibration image
     - ``echospacing`` :
     - ``pedir`` :

    Updated workspace attributes
    ----------------------------

     - ``cblip_warp``    : CBLIP Distortion correction warp image
    """
    if wsp.topup is not None:
        return
    elif wsp.cblip is None:
        wsp.log.write(
            " - No CBLIP images provided for distortion correction\n")
        return

    wsp.sub("topup")
    wsp.log.write(" - Calculating distortion Correction using TOPUP\n")

    topup_params = {
        "x": [[1, 0, 0, -99], [-1, 0, 0, -99]],
        "-x": [[-1, 0, 0, -99], [1, 0, 0, -99]],
        "y": [[0, 1, 0, -99], [0, -1, 0, -99]],
        "-y": [[0, -1, 0, -99], [0, 1, 0, -99]],
        "z": [[0, 0, 1, -99], [0, 0, -1, -99]],
        "-z": [[0, 0, -1, -99], [0, 0, 1, -99]],
    }
    dim_idx = {
        "x": 0,
        "-x": 0,
        "y": 1,
        "-y": 1,
        "z": 2,
        "-z": 2,
    }
    my_topup_params = np.array(topup_params[wsp.pedir], dtype=np.float)
    dimsize = wsp.asldata.shape[dim_idx[wsp.pedir]]
    my_topup_params[:, 3] = wsp.echospacing * (dimsize - 1)
    wsp.topup.params = my_topup_params

    # Run TOPUP to calculate correction
    wsp.topup.calib_blipped = reg.change_space(
        wsp,
        Image(np.stack((wsp.calib.data, wsp.cblip.data), axis=-1),
              header=wsp.calib.header), 'native')
    topup_result = fsl.topup(imain=wsp.topup.calib_blipped,
                             datain=wsp.topup.params,
                             config="b02b0.cnf",
                             out=fsl.LOAD,
                             iout=fsl.LOAD,
                             fout=fsl.LOAD,
                             log=wsp.fsllog)
    wsp.topup.fieldcoef, wsp.topup.movpar = topup_result[
        "out_fieldcoef"], topup_result["out_movpar"]
    wsp.topup.iout = topup_result["iout"]
    wsp.topup.fout = topup_result["fout"]

    page = wsp.report.page("topup")
    page.heading("TOPUP distortion correction", level=0)
    page.text("PE direction: %s" % wsp.pedir)
    page.text("Echo spacing: %f s" % wsp.echospacing)
    page.heading("Correction image", level=1)
    for dim in range(3):
        img = Image(wsp.topup.fieldcoef.data[..., dim],
                    header=wsp.topup.fieldcoef.header)
        page.text("Dimension %i" % dim)
        page.image("fmap_warp%i" % dim, LightboxImage(img))
Exemple #8
0
def run(wsp):
    """ Entry point for OXASL """
    if not wsp.region_analysis:
        return

    if wsp.pvwm is not None:
        wsp.structural.wm_pv_asl = wsp.pvwm
    else:
        wsp.structural.wm_pv_asl = reg.change_space(wsp, wsp.structural.wm_pv,
                                                    "native")

    if wsp.pvwm is not None:
        wsp.structural.gm_pv_asl = wsp.pvgm
    else:
        wsp.structural.gm_pv_asl = reg.change_space(wsp, wsp.structural.gm_pv,
                                                    "native")

    wsp.gm_thresh, wsp.wm_thresh = wsp.ifnone("gm_thresh", 0.8), wsp.ifnone(
        "wm_thresh", 0.9)

    wsp.log.write("\nRegionwise analysis\n")

    wsp.log.write("\nLoading perfusion images\n")
    perfusion_data = oxasl_perfusion_data(wsp)

    rois = []
    wsp.log.write("\nLoading generic ROIs\n")
    oxasl_add_roi(wsp, rois, "10%+GM", wsp.structural.gm_pv_asl, 0.1)
    oxasl_add_roi(wsp, rois, "10%+WM", wsp.structural.wm_pv_asl, 0.1)
    oxasl_add_roi(wsp, rois, "%i%%+GM" % (wsp.gm_thresh * 100),
                  wsp.structural.gm_pv_asl, wsp.gm_thresh)
    oxasl_add_roi(wsp, rois, "%i%%+WM" % (wsp.wm_thresh * 100),
                  wsp.structural.wm_pv_asl, wsp.wm_thresh)

    # Add ROIs from standard atlases
    oxasl_add_atlas(wsp, rois, "harvardoxford-cortical", threshold=0.5)
    oxasl_add_atlas(wsp, rois, "harvardoxford-subcortical", threshold=0.5)

    # Get stats in each ROI. Add name to stats dict to make TSV output easier
    wsp.log.write(
        "\nGetting stats - minimum of %i voxels to report in region\n" %
        wsp.roi_min_nvoxels)
    for item in perfusion_data:
        stats = []
        for roi in rois:
            roi_stats = {"name": roi["name"]}
            get_stats(roi_stats,
                      item["f"].data,
                      item["var"].data,
                      roi["mask_native"].data,
                      mask=item["mask"],
                      min_nvoxels=wsp.roi_min_nvoxels)
            stats.append(roi_stats)
            columns = list(roi_stats.keys())

        setattr(wsp, "roi_stats%s" % item["suffix"],
                pd.DataFrame(stats, columns=columns))

    # Save output masks/PVE maps
    for roi in rois:
        fname = roi["name"].replace(" ", "_").replace(",", "").lower()
        if wsp.save_native_rois and "roi_native" in roi:
            setattr(wsp, fname, roi["roi_native"])
        if wsp.save_native_masks and "mask_native" in roi:
            setattr(wsp, fname, roi["mask_native"])
        if wsp.save_mni_rois and "roi_mni" in roi:
            setattr(wsp, fname, roi["roi_mni"])

    wsp.log.write("\nDONE\n")
Exemple #9
0
def get_m0_refregion(wsp, mode="longtr"):
    """
    Do reference region calibration

    FIXME saturation recovery mode is not functional

    Required workspace attributes
    -----------------------------

     - ``calib`` : Image containing voxelwise M0 map
     - ``refmask`` : Reference region mask in calibration image space. Technically this is not required
                      as the brain mask will be used if not specified, however it is strongly recommended.

    Optional workspace attributes
    -----------------------------

     - ``calib_gain`` : Calibration gain (default 1.0)
     - ``tr`` : Sequence TR (s) (default 3.2)
     - ``te`` : Sequence TE (s) (default 0)
     - ``taq`` : Sequence TAQ (s) (default 0)
     - ``t2star`` : If True, correct for T2* rather than T2 (i.e. use T2* defaults not T2 defaults)
     - ``t1r`` : Reference tissue T1 (default: see ``TISSUE_DEFAULTS``)
     - ``t2r`` : Reference tissue T2/T2* (default: see ``TISSUE_DEFAULTS``)
     - ``pcr`` : Reference tissue partition coefficient (default: see ``TISSUE_DEFAULTS``)
     - ``mask`` : Brain mask in calibration image space
    """
    wsp.log.write(" - Doing reference region calibration\n")
    gain = wsp.ifnone("calib_gain", 1)

    tr = wsp.ifnone("tr", 3.2)
    te = wsp.ifnone("te", 0)
    taq = wsp.ifnone("taq", 0)
    wsp.log.write(" - Acquisition: TE=%f, TR=%f, Readout time (TAQ)=%f\n" % (te, tr, taq))

    t2star = wsp.ifnone("t2star", False)
    if t2star:
        t2b = wsp.ifnone("t2sb", 50)
    else:
        t2b = wsp.ifnone("t2b", 150)

    # Parameters for reference tissue type: T1, T2, T2*, partition coeffs, FAST seg ID
    # Partition coeffs based on Herscovitch and Raichle 1985 with a blood water density of 0.87
    # GM/WM T2* from Foucher 2011 JMRI 34:785-790
    wsp.tissref = wsp.ifnone("tissref", "csf")
    wsp.log.write(" - Using tissue reference type: %s\n" % wsp.tissref)

    t1r, t2r, t2sr, pcr = tissue_defaults(wsp.tissref)
    if t2star:
        t2r = t2sr

    # Command line override of default T1, T2, PC
    t1r_img, t2r_img = False, False

    if wsp.t1r is not None:
        t1r = wsp.t1r
        if isinstance(t1r, Image):
            wsp.log.write(" - Using T1 image for reference region: %s\n" % t1r.name)
            t1r_img = True
        else:
            wsp.log.write(" - Using user-specified T1r value: %f\n" % t1r)

    if wsp.t2r is not None:
        t2r = wsp.t2r
        if isinstance(t2r, Image):
            wsp.log.write(" - Using T2 image for reference region: %s\n" % t2r.name)
            t2r_img = True
        else:
            wsp.log.write(" - Using user-specified T2r value: %f\n" % t2r)

    if wsp.pcr is not None:
        pcr = wsp.pcr
        wsp.log.write(" - Using user-specified partition coefficient: %f\n" % pcr)

    if t1r is None:
        raise ValueError("T1 for reference tissue has not been set")
    if t2r is None:
        if te != 0:
            raise ValueError("T2 for reference tissue has not been set")
        else:
            t2r = 1.0
    if pcr is None:
        raise ValueError("Partition coefficient for reference tissue has not been set")

    wsp.log.write(" - T1r: %f; T2r: %f; T2b: %f; Part co-eff: %f\n" % (t1r, t2r, t2b, pcr))

    # Check the data and masks
    wsp.calibration.calib_img = wsp.corrected.calib
    calib_data = wsp.calibration.calib_img.data

    if wsp.rois is not None and wsp.rois.mask is not None:
        brain_mask = wsp.rois.mask.data
    else:
        brain_mask = np.ones(wsp.calibration.calib_img.shape[:3])

    if wsp.refmask is not None:
        wsp.log.write(" - Using supplied reference tissue mask: %s" % wsp.refmask.name)
        wsp.calibration.refmask = Image(wsp.refmask.data.astype(np.int), header=wsp.refmask.header)
        if wsp.calib_aslreg:
            wsp.log.write(" (Aligned to ASL image already)\n")
            wsp.calibration.refmask_trans = wsp.calibration.refmask
        else:
            wsp.log.write(" (Transforming to ASL image space)\n")
            wsp.calibration.refmask_trans = reg.change_space(wsp, wsp.calibration.refmask, "native", source_space="calib", mask=True)
        refmask = wsp.calibration.refmask_trans.data
    elif wsp.tissref.lower() in ("csf", "wm", "gm"):
        get_tissrefmask(wsp)
        refmask = wsp.calibration.refmask.data

    nonzero = np.count_nonzero(refmask)
    if nonzero < 1:
        raise ValueError("Reference mask does not contain any unmasked voxels")
    else:
        wsp.log.write(" - Number of voxels in tissue reference mask: %i\n" % nonzero)

    ### Sensitivity image calculation (if we have a sensitivity image)
    sens_corr = False
    if wsp.sens:
        wsp.log.write(" - Using sensitivity image: %s\n" % wsp.sens.name)
        sens_corr = True
        sens_data = wsp.sens.data

    wsp.log.write(" - MODE: %s\n" % mode)
    wsp.log.write(" - Calibration gain: %f\n" % gain)

    if mode == "longtr":
        if sens_corr:
            wsp.log.write(" - Applying sensitivity image\n")
            calib_data /= sens_data

        # Mask M0 map with tissue reference
        calib_data[refmask == 0] = 0

        # calcualte T1 of reference region (if a T1 image has been supplied)
        if t1r_img:
            t1r = np.mean(t1r.data[refmask != 0])
            wsp.log.write(" - Calculated T1 of reference tissue: %f\n" % t1r)

        # calcualte T2 of reference region (if a T2 image has been supplied)
        if t2r_img:
            t2r = np.mean(t2r.data[refmask != 0])
            wsp.log.write(" - Calculated T2 of reference tissue: %f\n" % t2r)

        # calculate M0_ref value
        mean_sig = np.mean(calib_data[refmask != 0])
        wsp.log.write(" - mean signal in reference tissue: %f\n" % mean_sig)
        t1_corr = 1 / (1 - math.exp(- (tr - taq) / t1r))
        wsp.log.write(" - T1 correction factor: %f\n" % t1_corr)

    elif mode == "satrecov":
        # Calibration image is control images and we want to do a saturation recovery fit
        # NB only do the fit in the CSF mask
        # FIXME this is not functional at the moment
        options = {
            "data" : calib_data,
            "mask" : refmask,
            "method" : "vb",
            "noise" : "white",
            "model" : "satrecov",
            "t1" : t1r,
        }

        #deal with TIs
        tis = wsp.ifnone("satrecov_tis", [])
        wsp.log.write(" - TIs: %s\n" % str(tis))
        for idx, ti in enumerate(tis):
            options["ti%i" % (idx+1)] = ti

        # Extra options for Look Locker
        if wsp.FA:
            options["FA"] = wsp.FA
        if wsp.calib_nphases:
            options["phases"] = wsp.calib_nphases
        if wsp.lfa:
            options["LFA"] = wsp.lfa

        # Extra sat recovery options
        if wsp.fixa:
            options["fixa"] = True

        # Do fabber within the tissue reference mask with a sensible T1 prior mean
        wsp.log.write(" - Running FABBER within reference tissue mask\n")
        from .wrappers import fabber
        fabber_result = fabber(options)
        mean_m0 = fabber_result["mean_M0t"]

        # Calculate M0 value - this is mean M0 of CSF at the TE of the sequence
        m0_value = np.mean(mean_m0.data[refmask != 0])

        wsp.log.write(" - M0 of reference tissue: %f\n" % m0_value)

        # Save useful results
        wsp.calibration.t1_ref = Image("%s/mean_T1t" % wsp.workdir)
        wsp.calibration.m0_ref = Image("%s/mean_M0t" % wsp.workdir)

        # Do fabber again within whole brain to get estimated T1 of tissue and FA correction (if LL)
        # (note that we do not apply sensitivity correction to the data here - thius is 'built-into' the M0t map)
        wsp.log.write(" - FABBER (again) within whole brain mask\n")
        options["mask"] = brain_mask
        fabber(options)
        # $fabber --data=$calib --mask=$bmask --output=$temp_calib/satrecovT --data-order=singlefile --model=satrecov --noise=white --method=vb $tislist $llopts $sropts

        # save useful results to specified output directory
        #imcp $temp_calib/satrecovT/mean_T1t $outdir/T1t
        #imcp $temp_calib/satrecovT/mean_M0t $outdir/M0t
        #if [ ! -z $lfa ]; then
        #imcp $temp_calib/satrecovT/mean_g $outdir/facorr
    else:
        raise ValueError("Unknown reference region mode: %s (Should be satrecov or longtr)" % mode)

    # Use equation to get the M0 value that is needed

    #  T2 correction
    t2_corr = math.exp(- te / t2b) / math.exp(- te / t2r)
    wsp.log.write(" - T2 correction factor: %f\n" % t2_corr)
    m0 = mean_sig * gain * t1_corr * t2_corr / pcr

    # Apply calibration to input image
    if sens_corr:
        # Apply sensitivity image
        # fslmaths $infile -div $temp_calib/sens $temp_calib/infile
        pass
    else:
        # imcp $infile $temp_calib/infile
        pass

    wsp.log.write(" - M0: %f\n" % m0)

    # Reporting
    page = wsp.report.page("m0")
    page.heading("Reference region M0 calculation")
    page.text("Reference region calibration calculates a single M0 value for a region of known tissue type")
    page.heading("Calculation details", level=1)
    page.table([
        ["Sequence TR (s)", tr],
        ["Sequence TE (ms)", te],
        ["T1 reference tissue (s)", t1r],
        ["T2 reference tissue (ms)", t2r],
        ["Blood T2 (ms)", t2b],
        ["Number of voxels in reference region", np.count_nonzero(refmask)],
        ["Mean signal in reference region", mean_sig],
        ["T1 correction factor", t1_corr],
        ["T2 correction factor", t2_corr],
        ["Partition coefficient", pcr],
        ["Calibration gain", gain],
        ["M0", m0],
    ])
    page.heading("Reference tissue mask", level=1)
    page.image("refmask", LightboxImage(wsp.calibration.refmask, bgimage=wsp.calibration.calib_img))

    return float(m0)
Exemple #10
0
def get_m0_wholebrain(wsp):
    """
    Get a whole-brain M0 value

    This calculates an M0 map for the whole brain using the T1, T2 and PC
    values for each tissue type with the contribution at each voxel weighted
    by the tissue partial volume

    :param wsp: Workspace object
    :return: Image containing voxelwise M0 map

    Required Workspace attributes
    -----------------------------

      - ``calib``     - Calibration Image in ASL native space
      - ``rois.mask`` - Brain mask Image in ASL native space
      - ``struc``     - Structural image
    """
    wsp.log.write("\n - Doing wholebrain region calibration\n")

    tr = wsp.ifnone("tr", 3.2)
    te = wsp.ifnone("te", 0)
    taq = wsp.ifnone("taq", 0)
    wsp.log.write(" - Using TE=%f, TR=%f, Readout time (TAQ)=%f\n" % (te, tr, taq))

    t2star = wsp.ifnone("t2star", False)
    if t2star:
        t2b = wsp.ifnone("t2sb", 50)
    else:
        t2b = wsp.ifnone("t2b", 150)

    # Check the data and masks
    wsp.calibration.calib_img = wsp.corrected.calib
    calib_data = wsp.calibration.calib_img.data

    ### Sensitivity image calculation (if we have a sensitivity image)
    if wsp.sens:
        wsp.log.write(" - Using sensitivity image: %s\n" % wsp.sens.name)
        calib_data /= wsp.sens.data

    m0 = np.zeros(calib_data.shape, dtype=np.float)
    for tiss_type in ("wm", "gm", "csf"):
        pve_struc = getattr(wsp.structural, "%s_pv" % tiss_type)
        wsp.log.write(" - Transforming %s tissue PVE into ASL space\n" % tiss_type)
        pve = reg.change_space(wsp, pve_struc, "native")
        t1r, t2r, t2sr, pcr = tissue_defaults(tiss_type)
        if t2star:
            t2r = t2sr
        t1_corr = 1 / (1 - math.exp(- (tr - taq) / t1r))
        t2_corr = 1 / math.exp(- te / t2r)
        wsp.log.write("Correction factors: T1: %f, T2 %f, PC: %f" % (t1_corr, t2_corr, pcr))
        tiss_m0 = calib_data * t1_corr * t2_corr / pcr
        wsp.log.write(" - Mean %s M0: %f (weighted by PV)\n" % (tiss_type, np.average(tiss_m0, weights=pve.data)))
        tiss_m0 *= pve.data
        setattr(wsp.calibration, "m0_img_%s" % tiss_type, Image(tiss_m0, header=wsp.calibration.calib_img.header))
        m0 += tiss_m0

    m0 = m0 * math.exp(- te / t2b)
    gain = wsp.ifnone("calib_gain", 1)
    wsp.log.write(" - Calibration gain: %f\n" % gain)
    if gain != 1:
        m0 = m0 * gain

    wsp.calibration.m0_img = Image(m0, header=wsp.calibration.calib_img.header)
    m0 = np.mean(m0[brain_mask != 0])
    wsp.log.write(" - M0 of brain: %f\n" % m0)

    # Reporting
    page = wsp.report.page("m0")
    page.heading("Whole-brain M0 calculation")
    page.text("Whole-brain calibration calculates an M0 value for each voxel from the calibration image based on partial volume estimates")
    page.text("- Calibration gain: %f" % gain)
    page.text(" - TR: %f" % tr)
    page.text(" - TE: %f" % te)

    page.heading("M0", level=1)
    page.text("Mean M0 value (within mask): %f" % m0)
    page.image("m0img", LightboxImage(wsp.calibration.m0_img))

    return float(m0)
Exemple #11
0
def run(wsp):
    """
    Get sensitivity correction image

    Required workspace attributes
    -----------------------------

     - ``asldata`` : ASL data

    Optional workspace attributes
    -----------------------------

     - ``isen`` : User supplied sensitivity image
     - ``cact`` : Calibration image. Used in conjunction with ``cref`` to calculate sensitivity map
     - ``calib`` : Calibration image. Used as alternative to cact provided ``mode`` is ``longtr``
     - ``cref`` : Calibration reference image
     - ``senscorr_auto`` : If True, automatically calculate sensitivity correction using FAST
     - ``senscorr_off`` If True, do not apply sensitivity correction

    Updated workspace attributes
    ----------------------------

     - ``sensitivity``    : Sensitivity correction image in ASL space
    """
    wsp.log.write("\nCalculating Sensitivity correction\n")
    sensitivity = None
    bias = None
    if wsp.senscorr_off:
        wsp.log.write(" - Sensitivity correction disabled\n")
    elif wsp.isen is not None:
        wsp.log.write(" - Sensitivity image supplied by user\n")
        sensitivity = wsp.isen
    elif wsp.cact is not None and wsp.cref is not None:
        wsp.log.write(
            " - Sensitivity image calculated from calibration actual and reference images\n"
        )
        cref_data = np.copy(wsp.cref.data)
        cref_data[cref_data == 0] = 1
        sensitivity = Image(wsp.cact.data.astype(np.float) / cref_data,
                            header=wsp.calib.header)
    elif wsp.calib is not None and wsp.cref is not None:
        if wsp.ifnone("mode", "longtr") != "longtr":
            raise ValueError(
                "Calibration reference image specified but calibration image was not in longtr mode - need to provided additional calibration image using the ASL coil"
            )
        wsp.log.write(
            " - Sensitivity image calculated from calibration and reference images\n"
        )
        cref_data = np.copy(wsp.cref.data)
        cref_data[cref_data == 0] = 1
        sensitivity = Image(wsp.calib.data.astype(np.float) / cref_data,
                            header=wsp.calib.header)
    elif wsp.senscorr_auto and wsp.structural.bias is not None:
        wsp.log.write(" - Sensitivity image calculated from bias field\n")
        bias = reg.change_space(wsp, wsp.structural.bias, "native")
        sensitivity = Image(np.reciprocal(bias.data), header=bias.header)
    else:
        wsp.log.write(" - No source of sensitivity correction was found\n")

    if sensitivity is not None:
        sdata = sensitivity.data
        sdata[sdata < 1e-12] = 1
        sdata[np.isnan(sdata)] = 1
        sdata[np.isinf(sdata)] = 1
        wsp.sub("senscorr")
        wsp.senscorr.sensitivity = Image(sdata, header=sensitivity.header)

        page = wsp.report.page("sensitivity")
        page.heading("Sensitivity correction", level=0)
        page.heading("Sensitivity map", level=1)
        page.image("sensitivity", LightboxImage(wsp.senscorr.sensitivity))

    if bias is not None:
        wsp.senscorr.bias = bias
Exemple #12
0
def run(wsp):
    """
    Generate mask for ASL data

    - If a ready-made mask image is provided or has already been generated, this is returned
    - If a structural image is provided this will be used. Brain extraction and registration
      will be performed if required
    - If a calibration image is provided, this is used. It is assumed to be in the same space
      as the ASL data
    - If none of the above are present, the ASL data itself is averaged and brain extracted
      to produce the mask

    Required workspace attributes
    -----------------------------

    Formally there are no required attributes, however at least one image must be provided
    which enables a mask to be generated.

    Optional workspace attributes
    -----------------------------

     - ``asldata`` : ASL data image
     - ``mask``    : Existing brain mask
     - ``struc``   : Structural image (wholehead)
     - ``struc_brain``: Already brain-extracted structural image
     - ``asl2struc`` : Existring ASL->Structural space transformation matrix
     - ``calib``   : Calibration image
     - ``nativeref`` : ASL registration source image
    """
    if wsp.rois is not None and wsp.rois.mask is not None:
        return

    wsp.sub("rois")
    wsp.log.write("\nGenerating ASL data mask\n")

    # Reporting
    page = wsp.report.page("mask")
    page.heading("Mask generation", level=0)

    if wsp.mask is not None:
        wsp.rois.mask_src = "user"
        mask_source = "provided by user (assumed to be ASL space): %s" % wsp.mask.name
        wsp.rois.mask = wsp.mask
    elif wsp.structural is not None and wsp.structural.struc is not None:
        # Preferred option is to use brain extracted structural
        wsp.rois.mask_src = "struc"
        page.heading("Brain extracted structural image", level=1)
        page.image(
            "struc_brain",
            LightboxImage(wsp.structural.brain, bgimage=wsp.structural.struc))
        wsp.rois.mask_struc = wsp.structural.brain_mask
        wsp.rois.mask_asl = reg.change_space(wsp, wsp.structural.brain_mask,
                                             "native")
        wsp.rois.mask = Image(sp.ndimage.morphology.binary_fill_holes(
            (wsp.rois.mask_asl.data > 0.25)).astype(np.int),
                              header=wsp.rois.mask_asl.header)
        mask_source = "generated from brain extracting structural image and registering to ASL space"
    else:
        # Alternatively, use registration image (which will be BETed calibration or mean ASL image)
        wsp.rois.mask_src = "nativeref"
        wsp.rois.mask = Image((wsp.reg.nativeref.data != 0).astype(np.int),
                              header=wsp.reg.nativeref.header)
        mask_source = "generated from brain extracted registration ASL image"

    wsp.log.write(" - Mask %s\n" % mask_source)

    page.heading("Masked ASL brain image", level=1)
    page.text("Mask was %s" % mask_source)
    page.text("PW ASL image masked by ASL-space mask")

    if wsp.asldata.iaf in ("diff", "tc", "ct"):
        page.image(
            "mask_outline",
            LightboxImage(wsp.rois.mask,
                          bgimage=wsp.asldata.perf_weighted(),
                          outline=True))
    else:
        page.image(
            "mask_outline",
            LightboxImage(wsp.rois.mask,
                          bgimage=wsp.asldata.mean(),
                          outline=True))