示例#1
0
    def test_normal_execution(self, mock_loadtxt):
        """ Test the normal behaviour of the function.
        """
        # Set the mocked functions returned values
        bvecs = numpy.array([[0, 0, 0], [0, 0, 0], [1, 0, 0], [0, 1, 0]]).T
        bvals = numpy.array([0, 0, 1500, 150])
        mock_loadtxt.side_effect = lambda x: {"dwi.bval": bvals,
                                              "dwi.bvec": bvecs}[x]

        # Test execution
        output_files = read_bvals_bvecs("dwi.bval", "dwi.bvec", min_bval=100.)
        self.assertTrue(numpy.allclose(output_files[0], bvals))
        self.assertTrue(numpy.allclose(output_files[1], bvecs.T))
        self.assertTrue(output_files[2] == 2)
        self.assertTrue(output_files[3] == 2)
示例#2
0
    def test_normal_execution(self, mock_loadtxt):
        """ Test the normal behaviour of the function.
        """
        # Set the mocked functions returned values
        bvecs = numpy.array([[0, 0, 0], [0, 0, 0], [1, 0, 0], [0, 1, 0]]).T
        bvals = numpy.array([0, 0, 1500, 150])
        mock_loadtxt.side_effect = lambda x: {
            "dwi.bval": bvals,
            "dwi.bvec": bvecs
        }[x]

        # Test execution
        output_files = read_bvals_bvecs("dwi.bval", "dwi.bvec", min_bval=100.)
        self.assertTrue(numpy.allclose(output_files[0], bvals))
        self.assertTrue(numpy.allclose(output_files[1], bvecs.T))
        self.assertTrue(output_files[2] == 2)
        self.assertTrue(output_files[3] == 2)
示例#3
0
def concatenate_volumes(nii_files, bvals_files, bvecs_files, outdir, axis=-1):
    """ Concatenate volumes of different nifti files.

    Parameters
    ----------
    nii_files: array of str
        array containing the different nii files to concatenate.
    bvals_files: list of str
        path to the diffusion b-values files.
    bvecs_files: list of str
        path to the diffusion b-vectors files.
    outdir: str
        subject output directory.
    axis: int, default -1
        the concatenation axis.

    Returns
    -------
    dwi_file: str
        path to the concatenated nii files.
    bval_file: str
        path to the concatenated bval files.
    bvec_file: str
        path to the concatenated bvec files.
    """
    # Concatenate volumes
    data = []
    affines = []
    for path in nii_files:
        im = nibabel.load(path)
        data.append(im.get_data())
        affines.append(im.affine)
    concatenated_volumes = numpy.concatenate(data, axis=axis)

    # Check that affine are the same between volumes
    ref_affine = affines[0]
    for aff in affines:
        if not numpy.allclose(ref_affine, aff):
            raise ValueError("Different affines between DWI volumes: {0}"
                             "...".format(nii_files))

    # Read the bvals and bvecs
    bvals, bvecs, nb_shells, nb_nodiff = read_bvals_bvecs(
        bvals_files, bvecs_files, min_bval=200)

    if nb_nodiff > 1:
        nodiff_indexes = numpy.where(bvals <= 50)[0].tolist()
        b0_array = concatenated_volumes[..., nodiff_indexes[0]]
        b0_array.shape += (1, )
        cpt_delete = 0
        for i in nodiff_indexes:
            concatenated_volumes = numpy.delete(
                concatenated_volumes, i - cpt_delete, axis=3)
            bvals = numpy.delete(bvals, i - cpt_delete, axis=0)
            bvecs = numpy.delete(bvecs, i - cpt_delete, axis=0)
            cpt_delete += 1
        concatenated_volumes = numpy.concatenate(
            (b0_array, concatenated_volumes), axis=3)
        bvals = numpy.concatenate((numpy.array([0]), bvals), axis=0)
        bvecs = numpy.concatenate((numpy.array([[0, 0, 0]]), bvecs), axis=0)

    # Save the results
    dwi_file = os.path.join(outdir, "dwi.nii.gz")
    bval_file = os.path.join(outdir, "dwi.bval")
    bvec_file = os.path.join(outdir, "dwi.bvec")
    concatenated_nii = nibabel.Nifti1Image(concatenated_volumes, ref_affine)
    nibabel.save(concatenated_nii, dwi_file)
    bvals.shape += (1, )
    numpy.savetxt(bval_file, bvals.T, fmt="%f")
    numpy.savetxt(bvec_file, bvecs.T, fmt="%f")

    return dwi_file, bval_file, bvec_file
示例#4
0
def mrtrix_tractogram(
        outdir,
        tempdir,
        subject_id,
        dwi,
        bvals,
        bvecs,
        nb_threads,
        global_tractography=False,
        mtracks=None,
        maxlength=None,
        cutoff=None,
        seed_gmwmi=False,
        sift_mtracks=None,
        sift2=False,
        nodif_brain=None,
        nodif_brain_mask=None,
        fast_t1_brain=None,
        subjects_dir=None,
        mif_gz=True,
        delete_raw_tracks=False,
        delete_dwi_mif=True,
        fs_sh=DEFAULT_FREESURFER_PATH,
        fsl_sh=DEFAULT_FSL_PATH):
    """
    Compute the connectome using MRtrix.

    Requirements:
        - FreeSurfer: result of recon-all on the T1.
        - a T1 parcellation that defines the nodes of the connectome, it has
          to be in the FreeSurfer space (i.e. aligned with
          <subjects dir>/<subject>/mri/brain.mgz), e.g. aparc+aseg from
          FreeSurfer.

    Parameters
    ----------
    outdir: str
        Path to directory where to output.
    tempdir: str
        Path to the directory where temporary directories should be written.
        It should be a partition with 5+ GB available.
    subject_id: str
        Subject identifier.
    dwi: str
        Path to the diffusion-weighted images (Nifti required).
    bvals: str
        Path to the bvalue list.
    bvecs: str
        Path to the list of diffusion-sensitized directions.
    nb_threads: int
        Number of threads.
    global_tractography: bool, default False
        If True run global tractography (tckglobal) instead of local (tckgen).
    mtracks: int, default None
        For non-global tractography only. Number of millions of tracks of the
        raw tractogram.
    maxlength: int, default None
        For non-global tractography only. Max fiber length in mm.
    cutoff: float, default None
        For non-global tractography only.
        FOD amplitude cutoff, stopping criteria.
    seed_gmwmi: bool, default False
        For non-global tractography only.
        Set this option if you want to activate the '-seed_gmwmi' option of
        MRtrix 'tckgen', to seed from the GM/WM interface. Otherwise, and by
        default, the seeding is done in white matter ('-seed_dynamic' option).
    sift_mtracks: int, default None
        For non-global tractography only.
        Number of millions of tracks to keep with SIFT.
        If not set, SIFT is not applied.
    sift2: bool, default False
        For non-global tractography only.
        To activate SIFT2.
    nodif_brain: str, default None
        Diffusion brain-only Nifti volume with bvalue ~ 0. If not passed, it is
        generated automatically by averaging all the b0 volumes of the DWI.
    nodif_brain_mask: str, default None
        Path to the Nifti brain binary mask in diffusion. If not passed, it is
        created with MRtrix 'dwi2mask'.
    fast_t1_brain: str, default None
        By default FSL FAST is run on the FreeSurfer 'brain.mgz'. If you want
        the WM probability map to be computed from another T1, pass the T1
        brain-only volume. Note that it has to be aligned with diffusion.
        This argument is useful for HCP, where some FreeSurfer 'brain.mgz'
        cannot be processed by FSL FAST.
    subjects_dir: str, default None
        Path to the FreeSurfer subjects directory. Required if the environment
        variable $SUBJECTS_DIR is not set.
    mif_gz: bool, default True
        Use compressed MIF files (.mif.gz) instead of .mif to save space.
    delete_raw_tracks: bool, default False
        Delete the raw tracks (<outdir>/<mtracks>M.tck) at the end of
        processing, to save space.
    delete_dwi_mif: bool, default True
        Delete <outdir>/DWI.mif(.gz) at the end of processing, which is a copy
        of the input <dwi> in the MIF format, to save space.
    fs_sh: str, default NeuroSpin path
        Path to the Bash script setting the FreeSurfer environment
    fsl_sh: str, default NeuroSpin path
        Path to the Bash script setting the FSL environment.

    Returns
    -------
    tracks: str
        The generated tractogram.
    sift_tracks: str
        The SIFT filtered tractogram.
    sift2_weights: str
        The SIFT2 tractogram associated weights.
    """
    # -------------------------------------------------------------------------
    # STEP 0 - Check arguments

    # FreeSurfer $SUBJECTS_DIR has to be passed or set as an env variable
    subjects_dir = get_or_check_freesurfer_subjects_dir(subjects_dir)

    # Use compressed MIF files or not
    MIF_EXT = ".mif.gz" if mif_gz else ".mif"

    # Is SIFT to be applied
    sift = (sift_mtracks is not None)

    # Check input and optional paths
    paths_to_check = [dwi, bvals, bvecs, fsl_sh]
    for p in [nodif_brain, nodif_brain_mask, fast_t1_brain]:
        if p is not None:
            paths_to_check.append(p)
    for p in paths_to_check:
        if not os.path.exists(p):
            raise ValueError("File or directory does not exist: %s" % p)

    # Identify whether the DWI acquisition is single or multi-shell
    _, _, nb_shells, _ = read_bvals_bvecs(bvals, bvecs, min_bval=200.)
    is_multi_shell = nb_shells > 1

    # Check compatibility of arguments
    if global_tractography:
        if not is_multi_shell:
            raise ValueError("MRtrix global tractography is only applicable "
                             "to multi shell data.")
        if seed_gmwmi:
            raise ValueError("'seed_gmwmi' cannot be applied when requesting "
                             "global tractography.")
        if sift or sift2:
            raise ValueError("SIFT or SIFT2 are not meant to be used with "
                             "global tractography.")
    else:
        value_of_required_arg = dict(mtracks=mtracks, maxlength=maxlength,
                                     cutoff=cutoff)
        for required_arg, value in value_of_required_arg.items():
            if value is None:
                raise ValueError("When 'global_tractography' is set to False "
                                 "%s is required." % required_arg)

    # Create <outdir> and/or <tempdir> if not existing
    for directory in [outdir, tempdir]:
        if not os.path.isdir(directory):
            os.makedirs(directory)

    # -------------------------------------------------------------------------
    # STEP 1 - Format DWI and compute nodif brain and nodif brain mask if
    # not provided

    # Convert DWI to MRtrix format
    dwi_mif = os.path.join(outdir, "DWI.mif")
    cmd_1a = ["mrconvert", dwi, dwi_mif, "-fslgrad", bvecs, bvals,
              "-datatype", "float32", "-stride", "0,0,0,1",
              "-nthreads", "%i" % nb_threads, "-failonwarn"]
    subprocess.check_call(cmd_1a)

    # If user has not provided a 'nodif_brain_mask', compute one with
    # MRtrix 'dwi2mask'
    if nodif_brain_mask is None:
        nodif_brain_mask = os.path.join(outdir, "nodif_brain_mask.nii.gz")
        cmd_1b = ["dwi2mask", dwi_mif, nodif_brain_mask]
        subprocess.check_call(cmd_1b)

    # If user has not provided a 'nodif_brain', apply 'nodif_brain_mask' to
    # mean b=0 volume
    if nodif_brain is None:
        # Extract b=0 volumes and compute mean b=0 volume
        b0s = os.path.join(outdir, "b0s.nii.gz")
        mean_b0 = os.path.join(outdir, "mean_b0.nii.gz")
        mrtrix_extract_b0s_and_mean_b0(dwi=dwi_mif, b0s=b0s, mean_b0=mean_b0,
                                       nb_threads=nb_threads)
        # Apply nodif_brain_mask to dwi
        nodif_brain = os.path.join(outdir, "nodif_brain.nii.gz")
        cmd_1b = ["mri_mask", mean_b0, nodif_brain_mask, nodif_brain]
        FSWrapper(cmd_1b, shfile=fs_sh)()

    # -------------------------------------------------------------------------
    # STEP 2 - Register DWI to T1 using FreeSurfer bbregister
    # - compute the rigid transformation
    # - apply transformation to align T1 without downsampling
    t1_brain_to_dif, dif2anat_dat, _ = freesurfer_bbregister_t1todif(
            outdir=outdir,
            subject_id=subject_id,
            nodif_brain=nodif_brain,
            subjects_dir=subjects_dir,
            fs_sh=fs_sh,
            fsl_sh=fsl_sh)

    # -------------------------------------------------------------------------
    # STEP 3 - "5 tissue types" segmentation
    # Generate the 5TT image based on a FSL FAST
    five_tissues = os.path.join(outdir, "5TT%s" % MIF_EXT)
    fast_t1_brain = t1_brain_to_dif if fast_t1_brain is None else fast_t1_brain
    cmd_3 = ["5ttgen", "fsl", fast_t1_brain, five_tissues, "-premasked",
             "-tempdir", tempdir, "-nthreads", "%i" % nb_threads]
    FSLWrapper(cmd_3, env=os.environ, shfile=fsl_sh)()

    # -------------------------------------------------------------------------
    # STEP 4 - Estimation of the response function of fibers in each voxel
    if is_multi_shell:
        rf_wm = os.path.join(outdir, "RF_WM.txt")
        rf_gm = os.path.join(outdir, "RF_GM.txt")
        rf_csf = os.path.join(outdir, "RF_CSF.txt")
        cmd_4 = ["dwi2response", "msmt_5tt", dwi_mif, five_tissues,
                 rf_wm, rf_gm, rf_csf]
    else:
        rf = os.path.join(outdir, "RF.txt")
        cmd_4 = ["dwi2response", "tournier", dwi_mif, rf]
    rf_voxels = os.path.join(outdir, "RF_voxels%s" % MIF_EXT)
    cmd_4 += ["-voxels", rf_voxels, "-tempdir", tempdir,
              "-nthreads", "%i" % nb_threads]
    subprocess.check_call(cmd_4)

    # -------------------------------------------------------------------------
    # STEP 5 - Compute FODs
    wm_fods = os.path.join(outdir, "WM_FODs%s" % MIF_EXT)
    if is_multi_shell:
        gm_mif = os.path.join(outdir, "GM%s" % MIF_EXT)
        csf_mif = os.path.join(outdir, "CSF%s" % MIF_EXT)
        cmd_5 = ["dwi2fod", "msmt_csd", dwi_mif, rf_wm, wm_fods,
                 rf_gm, gm_mif, rf_csf, csf_mif]
    else:
        cmd_5 = ["dwi2fod", "csd", dwi_mif, rf, wm_fods]
    cmd_5 += ["-mask", nodif_brain_mask, "-nthreads", "%i" % nb_threads,
              "-failonwarn"]
    subprocess.check_call(cmd_5)

    # -------------------------------------------------------------------------
    # STEP 6 - Image to visualize for multi-shell
    if is_multi_shell:
        wm_fods_vol0 = os.path.join(outdir, "WM_FODs_vol0%s" % MIF_EXT)
        cmd_6a = ["mrconvert", wm_fods, wm_fods_vol0, "-coord", "3", "0",
                  "-nthreads", "%i" % nb_threads]
        subprocess.check_call(cmd_6a)

        tissueRGB_mif = os.path.join(outdir, "tissueRGB%s" % MIF_EXT)
        cmd_6b = ["mrcat", csf_mif, gm_mif, wm_fods_vol0, tissueRGB_mif,
                  "-axis", "3", "-nthreads", "%i" % nb_threads, "-failonwarn"]
        subprocess.check_call(cmd_6b)

    # -------------------------------------------------------------------------
    # STEP 7 - Tractography: tckglobal or tckgen
    if global_tractography:
        tracks = os.path.join(outdir, "global.tck")
        global_fod = os.path.join(outdir, "fod%s" % MIF_EXT)
        fiso_mif = os.path.join(outdir, "fiso%s" % MIF_EXT)
        cmd_7 = ["tckglobal", dwi_mif, rf_wm, "-riso", rf_csf, "-riso", rf_gm,
                 "-mask", nodif_brain_mask, "-niter", "1e8",
                 "-fod", global_fod, "-fiso", fiso_mif, tracks]
    else:
        # Anatomically Constrained Tractography:
        # iFOD2 algorithm with backtracking and crop fibers at GM/WM interface
        tracks = os.path.join(outdir, "%iM.tck" % mtracks)
        cmd_7 = ["tckgen", wm_fods, tracks, "-act", five_tissues, "-backtrack",
                 "-crop_at_gmwmi", "-maxlength", "%i" % maxlength,
                 "-number", "%dM" % mtracks, "-cutoff", "%f" % cutoff]

        # Requested seeding strategy: -seed_gmwmi or -seed_dynamic
        if seed_gmwmi:
            gmwmi_mask = os.path.join(outdir, "gmwmi_mask%s" % MIF_EXT)
            cmd_7b = ["5tt2gmwmi", five_tissues, gmwmi_mask]
            subprocess.check_call(cmd_7b)
            cmd_7 += ["-seed_gmwmi", gmwmi_mask]
        else:
            cmd_7 += ["-seed_dynamic", wm_fods]
    cmd_7 += ["-nthreads", "%i" % nb_threads, "-failonwarn"]
    subprocess.check_call(cmd_7)

    # -------------------------------------------------------------------------
    # STEP 8 - Filter tracts with SIFT if requested
    sift_tracks = None
    if sift:
        sift_tracks = os.path.join(outdir, "%iM_SIFT.tck" % sift_mtracks)
        cmd_8 = ["tcksift", tracks, wm_fods, sift_tracks,
                 "-act", five_tissues, "-term_number", "%iM" % sift_mtracks,
                 "-nthreads", "%i" % nb_threads, "-failonwarn"]
        subprocess.check_call(cmd_8)

    # -------------------------------------------------------------------------
    # STEP 9 - run SIFT2 if requested (compute weights of fibers)
    sift2_weights = None
    if sift2:
        sift2_weights = os.path.join(outdir, "sift2_weights.txt")
        cmd_9 = ["tcksift2", tracks, wm_fods, sift2_weights,
                 "-act", five_tissues,
                 "-nthreads", "%i" % nb_threads, "-failonwarn"]
        subprocess.check_call(cmd_9)

    # -------------------------------------------------------------------------
    # STEP 10 - clean if requested
    if delete_raw_tracks:
        os.remove(tracks)
        tracks = None

    if delete_dwi_mif:
        os.remove(dwi_mif)
        dwi_mif = None
    else:
        if mif_gz:
            subprocess.check_call(["gzip", dwi_mif])
            dwi_mif += ".gz"

    return tracks, sift_tracks, sift2_weights
示例#5
0
def data_import_and_qspace_sampling(
        outdir,
        subject_id,
        dwis,
        bvals,
        bvecs,
        manufacturer,
        flipX=False,
        flipY=False,
        flipZ=False,
        invertX=True,
        invertY=False,
        invertZ=False,
        b0_magnitude=None,
        b0_phase=None,
        phase_axis="y",
        slice_axis="z",
        path_connectomist=DEFAULT_CONNECTOMIST_PATH):
    """ Wrapper to Connectomist's 'DWI & Q-space' tab.

    Parameters
    ----------
    outdir: str
        path to Connectomist's output directory.
    subject_id: str
        the subject code in study.
    dwis: list of str
        path to Nifti diffusion-weighted datasets.
    bvals: list of str
        path to .bval files associated to the Nifti.
    bvecs: list of str
        path to .bvec files associated to the Nifti.
    manufacturer: str
        name of the manufacturer (e.g. "Siemens", "GE", "Philips" or "Bruker").
    flipX, flipY, flipZ: bool (optional, default False)
        if True invert the x, y or z-axis of data.
    invertX: bool (optional, default True)
        if True invert x-axis of diffusion model.
    invertY, invertZ: bool (optional, default False)
        if True invert y or z-axis of diffusion model.
    b0_magnitude: str (optional, default None)
        path to the magnitude fieldmap (if fieldmap-based correction of
        susceptibility distortions is to be used).
    b0_phase: str (optional, default None)
        path to phase fieldmap (if fieldmap-based correction of susceptibility
        distortions is to be used).
    phase_axis: str (optional, default 'y')
        the acquistion phase axis 'x', 'y' or 'z'.
    slice_axis: str (optional, default 'z')
        the acquistion slice axis 'x', 'y' or 'z'.
    path_connectomist: str (optional)
        path to the Connectomist executable.

    Returns
    -------
    outdir: str
        path to Connectomist's output directory.
    """
    # Check input parameters
    for axis in (phase_axis, slice_axis):
        if axis not in AXIS:
            raise ValueError("Invalid axis '{0}'.".format(axis))
    if len(set(map(len, (dwis, bvecs, bvals)))) != 1:
        raise ValueError("The DWIs, BVECs and BVALs must have the same number "
                         "of elements.")

    # Create the Connectomist's import data directory
    dwis, bvals, bvecs, b0_magnitude, b0_phase = gather_and_format_input_files(
        outdir,
        dwis,
        bvals,
        bvecs,
        b0_magnitude,
        b0_phase)

    # Dict with all parameters for connectomist
    algorithm = "DWI-Data-Import-And-QSpace-Sampling"
    parameters_dict = {
        # Parameters are ordered as they appear in connectomist's GUI

        # ---------------------------------------------------------------------
        # Field: "Diffusion weighted-images"
        "fileNameDwi":  ";".join(dwis),  # "DW data"
        "sliceAxis":    AXIS[slice_axis],  # "Slice axis", default "Z-axis"
        "phaseAxis":    AXIS[phase_axis],  # "Phase axis", default "Y-axis"
        "manufacturer": None,

        # Subfield: "Advanced parameters"
        "flipAlongX":           2 if flipX else 0,  # "Flip data along x"
        "flipAlongY":           2 if flipY else 0,
        "flipAlongZ":           2 if flipZ else 0,
        "numberOfDiscarded":    0,  # "#discarded images at beginning"
        "numberOfT2":        None,  # "#T2"
        "numberOfRepetitions":  1,  # "#repetitions"
        # ---------------------------------------------------------------------
        # Field: "Rotation of field of view", default is identity matrix
        "qSpaceTransform_xx": 1.0,
        "qSpaceTransform_xy": 0.0,
        "qSpaceTransform_xz": 0.0,
        "qSpaceTransform_yx": 0.0,
        "qSpaceTransform_yy": 1.0,
        "qSpaceTransform_yz": 0.0,
        "qSpaceTransform_zx": 0.0,
        "qSpaceTransform_zy": 0.0,
        "qSpaceTransform_zz": 1.0,
        # ---------------------------------------------------------------------
        # Field: "Q-space sampling"
        "qSpaceSamplingType": 4,  # default "spherical single-shell custom"
        "qSpaceChoice5BValueFileNames": ";".join(bvals),
        "qSpaceChoice5BValueThreshold": 50,
        "qSpaceChoice5OrientationFileNames": ";".join(bvecs),

        # Apparently Connectomist uses 2 as True, and 0 as False.
        "invertXAxis": 2 if invertX else 0,
        "invertYAxis": 2 if invertY else 0,
        "invertZAxis": 2 if invertZ else 0,

        # In this field but not used/handled parameters
        "qSpaceChoice1MaximumBValue":       1000,  # case Cartesian
        "qSpaceChoice2BValue":              1000,
        "qSpaceChoice3BValue":              1000,
        "qSpaceChoice4BValue":              1000,
        "qSpaceChoice6BValues":               "",
        "qSpaceChoice7BValues":               "",
        "qSpaceChoice8BValues":               "",
        "qSpaceChoice9BValues":               "",
        "qSpaceChoice10BValues":              "",
        "qSpaceChoice11BValues":              "",
        "qSpaceChoice1NumberOfSteps":         11,
        "qSpaceChoice2NumberOfOrientations":   6,
        "qSpaceChoice3NumberOfOrientations":   6,
        "qSpaceChoice4NumberOfOrientations":   6,
        "qSpaceChoice6NumberOfOrientations":   6,
        "qSpaceChoice7NumberOfOrientations":   6,
        "qSpaceChoice8NumberOfOrientations":   6,
        "qSpaceChoice10NumberOfOrientations": "",
        "qSpaceChoice11NumberOfOrientations": "",
        "qSpaceChoice13OrientationFileNames": "",
        # ---------------------------------------------------------------------
        # Field: micro structure config"
        "gradientCharacteristicsFileNames": "",
        # ---------------------------------------------------------------------
        # Field: "Work directory"
        "outputWorkDirectory": outdir,
        # ---------------------------------------------------------------------
        # unknown parameter
        "_subjectName": subject_id,
    }

    # Map the manufacturer name with Connectomist convention
    if manufacturer not in MANUFACTURERS:
        raise ConnectomistBadManufacturerNameError(manufacturer)
    parameters_dict["manufacturer"] = MANUFACTURERS[manufacturer]

    # Read bvals and bvecs
    bvalues, bvectors, nb_shells, nb_nodiff = read_bvals_bvecs(bvals, bvecs)

    # Update Connectomist step description
    parameters_dict["numberOfT2"] = nb_nodiff
    if nb_shells == 1:
        # Spherical single-shell custom
        parameters_dict["qSpaceSamplingType"] = 4
    else:
        warnings.warn(
            "'{0}' shell model(s) not handled yet.".format(nb_shells))
        # Arbitrary shell
        parameters_dict["qSpaceSamplingType"] = 12
        parameters_dict["qSpaceChoice13BValueFileNames"] = ";".join(bvals)
        parameters_dict["qSpaceChoice13BValueThreshold"] = 50.0
        parameters_dict["qSpaceChoice13OrientationFileNames"] = ";".join(bvecs)

    # Call with Connectomist
    process = ConnectomistWrapper(path_connectomist)
    parameter_file = ConnectomistWrapper.create_parameter_file(
        algorithm, parameters_dict, outdir)
    process(algorithm, parameter_file, outdir)

    # When there are multiple volume or when there are multiple t2 (nodif)
    # volumes, Connectomist merges them
    # rewrite bvec, bval file accordingly (remove extra T2 values)
    if nb_nodiff > 1:
        bval = os.path.join(outdir, "dwi.bval")
        dw_indexes = np.where(bvalues >= 100)[0]
        new_bvals = np.concatenate(([0], bvalues[dw_indexes]))
        np.savetxt(bval, new_bvals)
        bvec = os.path.join(outdir, "dwi.bvec")
        new_bvecs = np.concatenate(
            ([[0], [0], [0]], bvectors.T[:, dw_indexes]), axis=1)
        np.savetxt(bvec, new_bvecs)

    return outdir
示例#6
0
def trac_all_longitudinal(outdir,
                          subject_template_id,
                          subject_timepoint_ids,
                          dwis,
                          bvalss,
                          bvecss,
                          bedpostx_dirs,
                          subjects_dir=None,
                          do_eddy=False,
                          do_rotate_bvecs=True,
                          do_bbregister=True,
                          do_register_mni=True,
                          temp_dir=None,
                          fsconfig=DEFAULT_FREESURFER_PATH):
    """

    Parameters
    ----------
    outdir: str
        Root directory where to create the subject's output directories.
        Created if not existing.
    subject_template_id: str
        Identifier of the subject template.
    subject_timepoint_ids: str
        Identifiers of the subject for all the timepoints.
    dwis: list of str
        Paths to Nifti diffusion series. In the order corresponding to
        <subject_timepoint_ids>.
    bvalss: list of str
        Paths to b-values of diffusion series. In the order corresponding to
        <dwis>.
    bvecss: list of str
        Paths to diffusion-sensitized directions of diffusion series. In the
        order corresponding to <dwis>.
    bedpostx_dirs: list of str
        BedpostX output directories. In the order corresponding to <dwis>.
    subjects_dir: str, default None
        Path to the FreeSurfer longitudinal subjects directory. Required if
        the environment variable $SUBJECTS_DIR is not set.
    do_eddy: bool, default False
        Apply FSL eddy-current correction.
    do_rotate_bvecs: bool, default True
        Rotate bvecs to match eddy-current correction.
    do_bbregister: bool, default True
        Register diffusion to T1 using bbregister.
    do_register_mni:
        Register T1 to MNI.
    temp_dir: str, default None
        Set the root temporary directory.
    fsconfig: str, default <pyfreesurfer.DEFAULT_FREESURFER_PATH>
        Path to the FreeSurfer configuration file.

    Returns
    -------
    subject_long_outdirs: list of str
        Path to longitudinal subject's output directories.
    """
    # Check input arguments

    # Check that the user has passed non-empty lists of the same length
    list_args = [subject_timepoint_ids, dwis, bvalss, bvecss, bedpostx_dirs]
    are_all_lists = all(map(lambda x: isinstance(x, list), list_args))
    all_same_size = len(set(map(len, list_args))) == 1
    non_empty = len(subject_timepoint_ids) > 1
    if not (are_all_lists & all_same_size & non_empty):
        raise ValueError("'subject_timepoint_ids', 'dwis', 'bvals', 'bvecs' "
                         "and 'bedpostx_dirs' must be lists of IDs/paths.")

    # FreeSurfer $SUBJECTS_DIR has to be passed or set as an env variable
    subjects_dir = get_or_check_freesurfer_subjects_dir(subjects_dir)

    # Check existence of input files/directories
    input_paths = dwis + bvalss + bvecss + bedpostx_dirs + [fsconfig]
    for path in input_paths:
        if not os.path.exists(path):
            raise ValueError("File or directory does not exist: %s" % path)

    # Create directory for temporary files
    temp_dir = tempfile.mkdtemp(prefix="trac-all_longitudinal_", dir=temp_dir)

    # The bvecs can be stored as a 3 x N or N x 3 matrix (N directions).
    # FreeSurfer requires the 2nd convention (one row per direction).
    # read_bvals_bvecs() always loads the bvecs file as a N x 3 numpy array,
    # save this numpy array in a temporary directory and use it as the
    # bvecs file to be sure to be in the right convention.
    bvecss_Nx3 = []
    for tp_sid, bvals, bvecs in zip(subject_timepoint_ids, bvalss, bvecss):
        _, bvecs_array, _, _ = read_bvals_bvecs(bvals_path=bvals,
                                                bvecs_path=bvecs,
                                                min_bval=200.)
        bvecs_Nx3 = os.path.join(temp_dir, "bvecs_Nx3_%s" % tp_sid)
        numpy.savetxt(bvecs_Nx3, bvecs_array)
        bvecss_Nx3.append(bvecs_Nx3)

    # Create configuration file
    str_subjlist = " ".join(subject_timepoint_ids)
    str_baselist = " ".join([subject_template_id] * len(subject_timepoint_ids))
    str_dwis = " ".join(dwis)
    str_bveclist = " ".join(bvecss_Nx3)
    str_bvallist = " ".join(bvalss)
    config_str = LONG_CONFIG_TEMPLATE.format(subjects_dir=subjects_dir,
                                             subjlist=str_subjlist,
                                             baselist=str_baselist,
                                             dtroot=outdir,
                                             dcmlist=str_dwis,
                                             bveclist=str_bveclist,
                                             bvallist=str_bvallist,
                                             doeddy=do_eddy,
                                             dorotbvecs=do_rotate_bvecs,
                                             doregbbr=do_bbregister,
                                             doregmni=do_register_mni)
    path_config = os.path.join(temp_dir, "trac-all.long.dmrirc")
    with open(path_config, "w") as f:
        f.write(config_str)

    # For each timepoint of subject:
    subject_long_outdirs = []
    for tp_sid, bedpostx_dir in zip(subject_timepoint_ids, bedpostx_dirs):

        # Create <outdir>/<tp sid>.long.<template id> if not existing
        long_sid = "%s.long.%s" % (tp_sid, subject_template_id)
        long_outdir = os.path.join(outdir, long_sid)
        if not os.path.isdir(long_outdir):
            os.makedirs(long_outdir)
        subject_long_outdirs.append(long_outdir)

        # Tracula requires the bedpostX files to be stored in
        # <outdir>/<tp sid>.long.<template id>/dmri.bedpostX
        create_bedpostx_organization(subject_id=long_sid,
                                     subjects_dir=outdir,
                                     bedpostx_dir=bedpostx_dir)

    # Run Tracula preparation
    cmd_prep = ["trac-all", "-prep", "-c", path_config]
    FSWrapper(cmd_prep,
              shfile=fsconfig,
              subjects_dir=subjects_dir,
              add_fsl_env=True)()

    # Tracula pathways tractography
    cmd_path = ["trac-all", "-path", "-c", path_config]
    FSWrapper(cmd_path,
              shfile=fsconfig,
              subjects_dir=subjects_dir,
              add_fsl_env=True)()

    # Clean tmp dir
    shutil.rmtree(temp_dir)

    return subject_long_outdirs
示例#7
0
def trac_all(outdir,
             subject_id,
             dwi,
             bvals,
             bvecs,
             bedpostx_dir,
             subjects_dir=None,
             do_eddy=False,
             do_rotate_bvecs=True,
             do_bbregister=True,
             do_register_mni=True,
             temp_dir=None,
             fsconfig=DEFAULT_FREESURFER_PATH):
    """

    Parameters
    ----------
    outdir: str
        Root directory where to create the subject's output directory.
        Created if not existing.
    subject_id: str
        Identifier of subject.
    dwi: str
        Path to input Nifti diffusion-weighted volumes.
    bvals: str
        Path to b-values of diffusion-weighted volumes.
    bvecs: str
        Path to diffusion-sensitized directions.
    bedpostx_dir: str
        BedpostX output directory.
    subjects_dir: str, default None
        Path to the FreeSurfer subjects directory. Required if the
        environment variable $SUBJECTS_DIR is not set.
    do_eddy: bool, default False
        Apply FSL eddy-current correction.
    do_rotate_bvecs: bool, default True
        Rotate bvecs to match eddy-current correction.
    do_bbregister: bool, default True
        Register diffusion to T1 using bbregister.
    do_register_mni:
        Register T1 to MNI.
    temp_dir: str, default None
        Directory to use to store temporary files. By default OS tmp dir.
    fsconfig: str, default <pyfreesurfer.DEFAULT_FREESURFER_PATH>
        Path to the FreeSurfer configuration file.

    Returns
    -------
    subject_outdir: str
        Path to subject's output directory.
    """
    # FreeSurfer $SUBJECTS_DIR has to be passed or set as an env variable
    subjects_dir = get_or_check_freesurfer_subjects_dir(subjects_dir)

    # Check existence of input files/directories
    for path in [dwi, bvals, bvecs, bedpostx_dir, fsconfig]:
        if not os.path.exists(path):
            raise ValueError("File or directory does not exist: %s" % path)

    # Load bvecs and number of b0 volumes
    _, bvecs_array, _, nb_b0s = read_bvals_bvecs(bvals_path=bvals,
                                                 bvecs_path=bvecs,
                                                 min_bval=200.)

    # Create directory <outdir>/<subject_id>
    subject_outdir = os.path.join(outdir, subject_id)
    if not os.path.isdir(subject_outdir):
        os.makedirs(subject_outdir)

    # Create directory for temporary files
    temp_dir = tempfile.mkdtemp(prefix="trac-all_", dir=temp_dir)

    # The bvecs can be stored as a 3 x N or N x 3 matrix (N directions).
    # FreeSurfer requires the 2nd convention (one row per direction).
    # read_bvals_bvecs() always loads the bvecs file as a N x 3 numpy array,
    # save this numpy array in a temporary directory and use it as the
    # bvecs file to be sure to be in the right convention.
    bvecs_Nx3 = os.path.join(temp_dir, "bvecs_Nx3")
    numpy.savetxt(bvecs_Nx3, bvecs_array)

    # Create configuration file
    config_str = CONFIG_TEMPLATE.format(subjects_dir=subjects_dir,
                                        subjlist=subject_id,
                                        dtroot=outdir,
                                        dcmlist=dwi,
                                        bveclist=bvecs_Nx3,
                                        bvallist=bvals,
                                        doeddy=do_eddy,
                                        dorotbvecs=do_rotate_bvecs,
                                        doregbbr=do_bbregister,
                                        doregmni=do_register_mni)
    path_config = os.path.join(temp_dir, "trac-all.dmrirc")
    with open(path_config, "w") as f:
        f.write(config_str)

    # Run Tracula preparation
    cmd_prep = ["trac-all", "-prep", "-c", path_config]
    FSWrapper(cmd_prep,
              shfile=fsconfig,
              subjects_dir=subjects_dir,
              add_fsl_env=True)()

    # Tracula requires the BedpostX files to be stored in
    # <outdir>/<subject_id>/dmri.bedpostX
    create_bedpostx_organization(subject_id=subject_id,
                                 subjects_dir=outdir,
                                 bedpostx_dir=bedpostx_dir)

    # Tracula pathways tractography
    cmd_path = ["trac-all", "-path", "-c", path_config]
    FSWrapper(cmd_path,
              shfile=fsconfig,
              subjects_dir=subjects_dir,
              add_fsl_env=True)()

    # Clean tmp dir
    shutil.rmtree(temp_dir)

    return subject_outdir
示例#8
0
def data_import_and_qspace_sampling(
        outdir,
        subject_id,
        dwis,
        bvals,
        bvecs,
        manufacturer,
        flipX=False,
        flipY=False,
        flipZ=False,
        invertX=True,
        invertY=False,
        invertZ=False,
        b0_magnitude=None,
        b0_phase=None,
        phase_axis="y",
        slice_axis="z",
        path_connectomist=DEFAULT_CONNECTOMIST_PATH):
    """ Wrapper to Connectomist's 'DWI & Q-space' tab.

    Parameters
    ----------
    outdir: str
        path to Connectomist's output directory.
    subject_id: str
        the subject code in study.
    dwis: list of str
        path to Nifti diffusion-weighted datasets.
    bvals: list of str
        path to .bval files associated to the Nifti.
    bvecs: list of str
        path to .bvec files associated to the Nifti.
    manufacturer: str
        name of the manufacturer (e.g. "Siemens", "GE", "Philips" or "Bruker").
    flipX, flipY, flipZ: bool (optional, default False)
        if True invert the x, y or z-axis of data.
    invertX: bool (optional, default True)
        if True invert x-axis of diffusion model.
    invertY, invertZ: bool (optional, default False)
        if True invert y or z-axis of diffusion model.
    b0_magnitude: str (optional, default None)
        path to the magnitude fieldmap (if fieldmap-based correction of
        susceptibility distortions is to be used).
    b0_phase: str (optional, default None)
        path to phase fieldmap (if fieldmap-based correction of susceptibility
        distortions is to be used).
    phase_axis: str (optional, default 'y')
        the acquistion phase axis 'x', 'y' or 'z'.
    slice_axis: str (optional, default 'z')
        the acquistion slice axis 'x', 'y' or 'z'.
    path_connectomist: str (optional)
        path to the Connectomist executable.

    Returns
    -------
    outdir: str
        path to Connectomist's output directory.
    """
    # Check input parameters
    for axis in (phase_axis, slice_axis):
        if axis not in AXIS:
            raise ValueError("Invalid axis '{0}'.".format(axis))
    if len(set(map(len, (dwis, bvecs, bvals)))) != 1:
        raise ValueError("The DWIs, BVECs and BVALs must have the same number "
                         "of elements.")

    # Create the Connectomist's import data directory
    dwis, bvals, bvecs, b0_magnitude, b0_phase = gather_and_format_input_files(
        outdir, dwis, bvals, bvecs, b0_magnitude, b0_phase)

    # Dict with all parameters for connectomist
    algorithm = "DWI-Data-Import-And-QSpace-Sampling"
    parameters_dict = {
        # Parameters are ordered as they appear in connectomist's GUI

        # ---------------------------------------------------------------------
        # Field: "Diffusion weighted-images"
        "fileNameDwi": ";".join(dwis),  # "DW data"
        "sliceAxis": AXIS[slice_axis],  # "Slice axis", default "Z-axis"
        "phaseAxis": AXIS[phase_axis],  # "Phase axis", default "Y-axis"
        "manufacturer": None,

        # Subfield: "Advanced parameters"
        "flipAlongX": 2 if flipX else 0,  # "Flip data along x"
        "flipAlongY": 2 if flipY else 0,
        "flipAlongZ": 2 if flipZ else 0,
        "numberOfDiscarded": 0,  # "#discarded images at beginning"
        "numberOfT2": None,  # "#T2"
        "numberOfRepetitions": 1,  # "#repetitions"
        # ---------------------------------------------------------------------
        # Field: "Rotation of field of view", default is identity matrix
        "qSpaceTransform_xx": 1.0,
        "qSpaceTransform_xy": 0.0,
        "qSpaceTransform_xz": 0.0,
        "qSpaceTransform_yx": 0.0,
        "qSpaceTransform_yy": 1.0,
        "qSpaceTransform_yz": 0.0,
        "qSpaceTransform_zx": 0.0,
        "qSpaceTransform_zy": 0.0,
        "qSpaceTransform_zz": 1.0,
        # ---------------------------------------------------------------------
        # Field: "Q-space sampling"
        "qSpaceSamplingType": 4,  # default "spherical single-shell custom"
        "qSpaceChoice5BValueFileNames": ";".join(bvals),
        "qSpaceChoice5BValueThreshold": 50,
        "qSpaceChoice5OrientationFileNames": ";".join(bvecs),

        # Apparently Connectomist uses 2 as True, and 0 as False.
        "invertXAxis": 2 if invertX else 0,
        "invertYAxis": 2 if invertY else 0,
        "invertZAxis": 2 if invertZ else 0,

        # In this field but not used/handled parameters
        "qSpaceChoice1MaximumBValue": 1000,  # case Cartesian
        "qSpaceChoice2BValue": 1000,
        "qSpaceChoice3BValue": 1000,
        "qSpaceChoice4BValue": 1000,
        "qSpaceChoice6BValues": "",
        "qSpaceChoice7BValues": "",
        "qSpaceChoice8BValues": "",
        "qSpaceChoice9BValues": "",
        "qSpaceChoice10BValues": "",
        "qSpaceChoice11BValues": "",
        "qSpaceChoice1NumberOfSteps": 11,
        "qSpaceChoice2NumberOfOrientations": 6,
        "qSpaceChoice3NumberOfOrientations": 6,
        "qSpaceChoice4NumberOfOrientations": 6,
        "qSpaceChoice6NumberOfOrientations": 6,
        "qSpaceChoice7NumberOfOrientations": 6,
        "qSpaceChoice8NumberOfOrientations": 6,
        "qSpaceChoice10NumberOfOrientations": "",
        "qSpaceChoice11NumberOfOrientations": "",
        "qSpaceChoice13OrientationFileNames": "",
        # ---------------------------------------------------------------------
        # Field: micro structure config"
        "gradientCharacteristicsFileNames": "",
        # ---------------------------------------------------------------------
        # Field: "Work directory"
        "outputWorkDirectory": outdir,
        # ---------------------------------------------------------------------
        # unknown parameter
        "_subjectName": subject_id,
    }

    # Map the manufacturer name with Connectomist convention
    if manufacturer not in MANUFACTURERS:
        raise ConnectomistBadManufacturerNameError(manufacturer)
    parameters_dict["manufacturer"] = MANUFACTURERS[manufacturer]

    # Read bvals and bvecs
    bvalues, bvectors, nb_shells, nb_nodiff = read_bvals_bvecs(bvals, bvecs)

    # Update Connectomist step description
    parameters_dict["numberOfT2"] = nb_nodiff
    if nb_shells == 1:
        # Spherical single-shell custom
        parameters_dict["qSpaceSamplingType"] = 4
    else:
        warnings.warn(
            "'{0}' shell model(s) not handled yet.".format(nb_shells))
        # Arbitrary shell
        parameters_dict["qSpaceSamplingType"] = 12
        parameters_dict["qSpaceChoice13BValueFileNames"] = ";".join(bvals)
        parameters_dict["qSpaceChoice13BValueThreshold"] = 50.0
        parameters_dict["qSpaceChoice13OrientationFileNames"] = ";".join(bvecs)

    # Call with Connectomist
    process = ConnectomistWrapper(path_connectomist)
    parameter_file = ConnectomistWrapper.create_parameter_file(
        algorithm, parameters_dict, outdir)
    process(algorithm, parameter_file, outdir)

    # When there are multiple volume or when there are multiple t2 (nodif)
    # volumes, Connectomist merges them
    # rewrite bvec, bval file accordingly (remove extra T2 values)
    if nb_nodiff > 1:
        bval = os.path.join(outdir, "dwi.bval")
        dw_indexes = np.where(bvalues >= 100)[0]
        new_bvals = np.concatenate(([0], bvalues[dw_indexes]))
        np.savetxt(bval, new_bvals)
        bvec = os.path.join(outdir, "dwi.bvec")
        new_bvecs = np.concatenate(
            ([[0], [0], [0]], bvectors.T[:, dw_indexes]), axis=1)
        np.savetxt(bvec, new_bvecs)

    return outdir
示例#9
0
def trac_all_longitudinal(outdir, subject_template_id, subject_timepoint_ids,
                          dwis, bvalss, bvecss, bedpostx_dirs,
                          subjects_dir=None, do_eddy=False,
                          do_rotate_bvecs=True, do_bbregister=True,
                          do_register_mni=True, temp_dir=None,
                          fsconfig=DEFAULT_FREESURFER_PATH):
    """

    Parameters
    ----------
    outdir: str
        Root directory where to create the subject's output directories.
        Created if not existing.
    subject_template_id: str
        Identifier of the subject template.
    subject_timepoint_ids: str
        Identifiers of the subject for all the timepoints.
    dwis: list of str
        Paths to Nifti diffusion series. In the order corresponding to
        <subject_timepoint_ids>.
    bvalss: list of str
        Paths to b-values of diffusion series. In the order corresponding to
        <dwis>.
    bvecss: list of str
        Paths to diffusion-sensitized directions of diffusion series. In the
        order corresponding to <dwis>.
    bedpostx_dirs: list of str
        BedpostX output directories. In the order corresponding to <dwis>.
    subjects_dir: str, default None
        Path to the FreeSurfer longitudinal subjects directory. Required if
        the environment variable $SUBJECTS_DIR is not set.
    do_eddy: bool, default False
        Apply FSL eddy-current correction.
    do_rotate_bvecs: bool, default True
        Rotate bvecs to match eddy-current correction.
    do_bbregister: bool, default True
        Register diffusion to T1 using bbregister.
    do_register_mni:
        Register T1 to MNI.
    temp_dir: str, default None
        Set the root temporary directory.
    fsconfig: str, default <pyfreesurfer.DEFAULT_FREESURFER_PATH>
        Path to the FreeSurfer configuration file.

    Returns
    -------
    subject_long_outdirs: list of str
        Path to longitudinal subject's output directories.
    """
    # Check input arguments

    # Check that the user has passed non-empty lists of the same length
    list_args = [subject_timepoint_ids, dwis, bvalss, bvecss, bedpostx_dirs]
    are_all_lists = all(map(lambda x: isinstance(x, list), list_args))
    all_same_size = len(set(map(len, list_args))) == 1
    non_empty = len(subject_timepoint_ids) > 1
    if not (are_all_lists & all_same_size & non_empty):
        raise ValueError("'subject_timepoint_ids', 'dwis', 'bvals', 'bvecs' "
                         "and 'bedpostx_dirs' must be lists of IDs/paths.")

    # FreeSurfer $SUBJECTS_DIR has to be passed or set as an env variable
    subjects_dir = get_or_check_freesurfer_subjects_dir(subjects_dir)

    # Check existence of input files/directories
    input_paths = dwis + bvalss + bvecss + bedpostx_dirs + [fsconfig]
    for path in input_paths:
        if not os.path.exists(path):
            raise ValueError("File or directory does not exist: %s" % path)

    # Create directory for temporary files
    temp_dir = tempfile.mkdtemp(prefix="trac-all_longitudinal_", dir=temp_dir)

    # The bvecs can be stored as a 3 x N or N x 3 matrix (N directions).
    # FreeSurfer requires the 2nd convention (one row per direction).
    # read_bvals_bvecs() always loads the bvecs file as a N x 3 numpy array,
    # save this numpy array in a temporary directory and use it as the
    # bvecs file to be sure to be in the right convention.
    bvecss_Nx3 = []
    for tp_sid, bvals, bvecs in zip(subject_timepoint_ids, bvalss, bvecss):
        _, bvecs_array, _, _ = read_bvals_bvecs(bvals_path=bvals,
                                                bvecs_path=bvecs,
                                                min_bval=200.)
        bvecs_Nx3 = os.path.join(temp_dir, "bvecs_Nx3_%s" % tp_sid)
        numpy.savetxt(bvecs_Nx3, bvecs_array)
        bvecss_Nx3.append(bvecs_Nx3)

    # Create configuration file
    str_subjlist = " ".join(subject_timepoint_ids)
    str_baselist = " ".join([subject_template_id] * len(subject_timepoint_ids))
    str_dwis = " ".join(dwis)
    str_bveclist = " ".join(bvecss_Nx3)
    str_bvallist = " ".join(bvalss)
    config_str = LONG_CONFIG_TEMPLATE.format(subjects_dir=subjects_dir,
                                             subjlist=str_subjlist,
                                             baselist=str_baselist,
                                             dtroot=outdir,
                                             dcmlist=str_dwis,
                                             bveclist=str_bveclist,
                                             bvallist=str_bvallist,
                                             doeddy=do_eddy,
                                             dorotbvecs=do_rotate_bvecs,
                                             doregbbr=do_bbregister,
                                             doregmni=do_register_mni)
    path_config = os.path.join(temp_dir, "trac-all.long.dmrirc")
    with open(path_config, "w") as f:
        f.write(config_str)

    # For each timepoint of subject:
    subject_long_outdirs = []
    for tp_sid, bedpostx_dir in zip(subject_timepoint_ids, bedpostx_dirs):

        # Create <outdir>/<tp sid>.long.<template id> if not existing
        long_sid = "%s.long.%s" % (tp_sid, subject_template_id)
        long_outdir = os.path.join(outdir, long_sid)
        if not os.path.isdir(long_outdir):
            os.makedirs(long_outdir)
        subject_long_outdirs.append(long_outdir)

        # Tracula requires the bedpostX files to be stored in
        # <outdir>/<tp sid>.long.<template id>/dmri.bedpostX
        create_bedpostx_organization(subject_id=long_sid, subjects_dir=outdir,
                                     bedpostx_dir=bedpostx_dir)

    # Run Tracula preparation
    cmd_prep = ["trac-all", "-prep", "-c", path_config]
    FSWrapper(cmd_prep, shfile=fsconfig, subjects_dir=subjects_dir,
              add_fsl_env=True)()

    # Tracula pathways tractography
    cmd_path = ["trac-all", "-path", "-c", path_config]
    FSWrapper(cmd_path, shfile=fsconfig, subjects_dir=subjects_dir,
              add_fsl_env=True)()

    # Clean tmp dir
    shutil.rmtree(temp_dir)

    return subject_long_outdirs
示例#10
0
def trac_all(outdir, subject_id, dwi, bvals, bvecs, bedpostx_dir,
             subjects_dir=None, do_eddy=False, do_rotate_bvecs=True,
             do_bbregister=True, do_register_mni=True, temp_dir=None,
             fsconfig=DEFAULT_FREESURFER_PATH):
    """ Pathway reconstruction.

    Parameters
    ----------
    outdir: str
        Root directory where to create the subject's output directory.
        Created if not existing.
    subject_id: str
        Identifier of subject.
    dwi: str
        Path to input Nifti diffusion-weighted volumes.
    bvals: str
        Path to b-values of diffusion-weighted volumes.
    bvecs: str
        Path to diffusion-sensitized directions.
    bedpostx_dir: str
        BedpostX output directory.
    subjects_dir: str, default None
        Path to the FreeSurfer subjects directory. Required if the
        environment variable $SUBJECTS_DIR is not set.
    do_eddy: bool, default False
        Apply FSL eddy-current correction.
    do_rotate_bvecs: bool, default True
        Rotate bvecs to match eddy-current correction.
    do_bbregister: bool, default True
        Register diffusion to T1 using bbregister.
    do_register_mni:
        Register T1 to MNI.
    temp_dir: str, default None
        Directory to use to store temporary files. By default OS tmp dir.
    fsconfig: str, default <pyfreesurfer.DEFAULT_FREESURFER_PATH>
        Path to the FreeSurfer configuration file.

    Returns
    -------
    subject_outdir: str
        Path to subject's output directory.
    """
    # FreeSurfer $SUBJECTS_DIR has to be passed or set as an env variable
    subjects_dir = get_or_check_freesurfer_subjects_dir(subjects_dir)

    # Check existence of input files/directories
    for path in [dwi, bvals, bvecs, bedpostx_dir, fsconfig]:
        if not os.path.exists(path):
            raise ValueError("File or directory does not exist: %s" % path)

    # Load bvecs and number of b0 volumes
    _, bvecs_array, _, nb_b0s = read_bvals_bvecs(bvals_path=bvals,
                                                 bvecs_path=bvecs,
                                                 min_bval=200.)

    # Create directory <outdir>/<subject_id>
    subject_outdir = os.path.join(outdir, subject_id)
    if not os.path.isdir(subject_outdir):
        os.makedirs(subject_outdir)

    # Create directory for temporary files
    temp_dir = tempfile.mkdtemp(prefix="trac-all_", dir=temp_dir)

    # The bvecs can be stored as a 3 x N or N x 3 matrix (N directions).
    # FreeSurfer requires the 2nd convention (one row per direction).
    # read_bvals_bvecs() always loads the bvecs file as a N x 3 numpy array,
    # save this numpy array in a temporary directory and use it as the
    # bvecs file to be sure to be in the right convention.
    bvecs_Nx3 = os.path.join(temp_dir, "bvecs_Nx3")
    numpy.savetxt(bvecs_Nx3, bvecs_array)

    # Create configuration file
    config_str = CONFIG_TEMPLATE.format(subjects_dir=subjects_dir,
                                        subjlist=subject_id,
                                        dtroot=outdir,
                                        dcmlist=dwi,
                                        bveclist=bvecs_Nx3,
                                        bvallist=bvals,
                                        doeddy=do_eddy,
                                        dorotbvecs=do_rotate_bvecs,
                                        doregbbr=do_bbregister,
                                        doregmni=do_register_mni)
    path_config = os.path.join(temp_dir, "trac-all.dmrirc")
    with open(path_config, "w") as f:
        f.write(config_str)

    # Run Tracula preparation
    cmd_prep = ["trac-all", "-prep", "-c", path_config]
    FSWrapper(cmd_prep, shfile=fsconfig, subjects_dir=subjects_dir,
              add_fsl_env=True)()

    # Tracula requires the BedpostX files to be stored in
    # <outdir>/<subject_id>/dmri.bedpostX
    create_bedpostx_organization(subject_id=subject_id, subjects_dir=outdir,
                                 bedpostx_dir=bedpostx_dir)

    # Tracula pathways tractography
    cmd_path = ["trac-all", "-path", "-c", path_config]
    FSWrapper(cmd_path, shfile=fsconfig, subjects_dir=subjects_dir,
              add_fsl_env=True)()

    # Clean tmp dir
    shutil.rmtree(temp_dir)

    return subject_outdir