Example #1
0
def deface(input_files,
           outdir,
           reference_file=None,
           verbose=0,
           fs_config=DEFAULT_FREESURFER_PATH):
    """ Deface MRI head images using the FreeSurfer 'mri_deface' command.


    Parameters
    ----------
    input_files: list of str
        Input MRI head images to be defaced.
    outdir: str
        The output folder.
    reference_file: str (optional, default None)
        The image that must be used as reference if more than one image
        have been supplied as input.
    verbose: int (optional, default 0)
        The verbosity level.
    fs_config: str (optional, default DEFAULT_FREESURFER_PATH)
        The FreeSurfer configuration file.

    Returns
    -------
    deface_files: list of str
        The defaced input MRI head images.
    snap_files: list of str
        The corresponding snaps that can be used to check the defacing result.
    """
    # Check input parameters
    outdir = os.path.abspath(outdir)
    if len(input_files) == 0:
        raise ValueError("You must specify at least one image.")
    elif len(input_files) > 1:
        raise ValueError("Mutliple input files not yet supported.")
        # if reference_file is None or reference_file not in input_files:
        #     raise ValueError("If more than one image is specified, you must "
        #                      "also specify one of them as reference. The "
        #                      "reference image will be used for spatial "
        #                      "co-registration with an atlas, and others "
        #                      "will use the reference facial mask.")

    # Define the command
    deface_file = os.path.join(outdir, os.path.basename(input_files[0]))
    wrapper = FSWrapper([], shfile=fs_config)
    face_file = os.path.join(wrapper.environment["FREESURFER_HOME"], "average",
                             "face.gca")
    skull_file = os.path.join(wrapper.environment["FREESURFER_HOME"],
                              "average", "talairach_mixed_with_skull.gca")
    cmd = ["mri_deface", input_files[0], skull_file, face_file, deface_file]

    # Call defacing
    wrapper = FSWrapper(cmd, shfile=fs_config)
    wrapper()

    return [deface_file], None
Example #2
0
def mri_binarize(inputfile,
                 outputfile,
                 match=None,
                 wm=False,
                 fsconfig=DEFAULT_FREESURFER_PATH):
    """ Binarize a FreeSurfer label map.

    Binding over the FreeSurfer's 'mri_binarize' command.

    Parameters
    ----------
    inputfile: str (mandatory)
        input volume.
    outputfile: str (mandatory)
        output volume.
    match: list on int (optional)
        match labels instead of threshold.
    wm: bool (optional)
        set match vals to 2 and 41 (aseg for cerebral WM).
    fsconfig: str (optional)
        The freesurfer configuration batch.
    """
    # Call FreeSurfer
    cmd = ["mri_binarize", "--i", inputfile, "--o", outputfile]
    if match is not None:
        cmd.append("--match")
        cmd.extend(match)
    if wm:
        cmd.append("--wm")
    recon = FSWrapper(cmd, shfile=fsconfig)
    recon()
Example #3
0
def asegstats2table(fsdir, outdir, fsconfig=DEFAULT_FREESURFER_PATH):
    """ Generate text/ascii tables of freesurfer parcellation stats data
    'aseg.stats'.

    This can then be easily imported into a spreadsheet and/or stats program.

    Binding over the FreeSurfer's 'asegstats2table' command.

    Parameters
    ----------
    fsdir: str (mandatory)
        The freesurfer working directory with all the subjects.
    outdir: str (mandatory)
        The statistical destination folder.
    fsconfig: str (optional)
        The freesurfer configuration batch.

    Return
    ------
    statfiles: list of str
        The freesurfer summary stats.
    """
    # Check input parameters
    for path in (fsdir, outdir):
        if not os.path.isdir(path):
            raise ValueError("'{0}' is not a valid directory.".format(path))

    # Parameter that will contain the output stats
    statfiles = []

    # Fist find all the subjects with a stat dir
    statdirs = glob.glob(os.path.join(fsdir, "*", "stats"))
    subjects = [item.lstrip(os.sep).split("/")[-2] for item in statdirs]

    # Save the FreeSurfer current working directory and set the new one
    fscwd = None
    if "SUBJECTS_DIR" in os.environ:
        fscwd = os.environ["SUBJECTS_DIR"]
    os.environ["SUBJECTS_DIR"] = fsdir

    # Create the output stat directory
    fsoutdir = os.path.join(outdir, "stats")
    if not os.path.isdir(fsoutdir):
        os.mkdir(fsoutdir)

    # Call freesurfer
    statfile = os.path.join(fsoutdir, "aseg_stats_volume.csv")
    statfiles.append(statfile)
    cmd = ["asegstats2table", "--subjects"] + subjects + [
        "--meas", "volume", "--tablefile", statfile, "--delimiter", "comma"
    ]
    recon = FSWrapper(cmd, shfile=fsconfig)
    recon()

    # Restore the FreeSurfer working directory
    if fscwd is not None:
        os.environ["SUBJECTS_DIR"] = fscwd

    return statfiles
Example #4
0
def mri_surf2surf(hemi,
                  input_surface_file,
                  output_surface_file,
                  ico_order,
                  fsdir,
                  sid,
                  fsconfig=DEFAULT_FREESURFER_PATH):
    """ Resample surface vertices.

    Binding over the FreeSurfer's 'mri_surf2surf' command.

    Parameters
    ----------
    hemi: str (mandatory)
        hemisphere ('lh' or 'rh').
    input_surface_file: str (mandatory)
        input surface path.
    output_surface_file: str (mandatory)
        output surface path.
    ico_order: int (mandatory)
        icosahedron order in [0, 7] that will be used to generate the cortical
        surface texture at a specific tessalation (the corresponding cortical
        surface can be resampled using the
        'clindmri.segmentation.freesurfer.resample_cortical_surface' function).
    fsdir: str (mandatory)
        FreeSurfer subjects directory 'SUBJECTS_DIR'.
    sid: str (mandatory)
        FreeSurfer subject identifier.
    fsconfig: str (optional)
        The FreeSurfer '.sh' config file.
    """
    # Check input parameters
    for path in (input_surface_file, ):
        if not os.path.isfile(path):
            raise ValueError("'{0}' is not a valid input file.".format(path))
    for path in (fsdir, ):
        if not os.path.isdir(path):
            raise ValueError("'{0}' is not a valid directory.".format(path))
    if hemi not in ["lh", "rh"]:
        raise ValueError("'{0}' is not a valid hemisphere value which must be "
                         "in ['lh', 'rh']".format(hemi))
    if ico_order < 0 or ico_order > 7:
        raise ValueError("'Ico order '{0}' is not in 0-7 "
                         "range.".format(ico_order))

    # Define FreeSurfer command
    cmd = [
        "mri_surf2surf", "--hemi", hemi, "--srcsurfval", input_surface_file,
        "--srcsubject", sid, "--trgsubject", "ico", "--trgicoorder",
        str(ico_order), "--trgsurfval", output_surface_file, "--sd", fsdir,
        "--trg_type", "mgz"
    ]

    # Execute the FreeSurfer command
    recon = FSWrapper(cmd, shfile=fsconfig)
    recon()
Example #5
0
def midgray_surface(hemi,
                    outdir,
                    fsdir,
                    sid,
                    fsconfig=DEFAULT_FREESURFER_PATH):
    """ Create a mid-thickness gray surface.

    Binding over the FreeSurfer's 'mris_expand' command.

    Parameters
    ----------
    hemi: str (mandatory)
        hemisphere ('lh' or 'rh').
    outdir: str (mandatory)
        the destination folder.
    fsdir: str (mandatory)
        FreeSurfer subjects directory 'SUBJECTS_DIR'.
    sid: str (mandatory)
        FreeSurfer subject identifier.
    fsconfig: str (optional)
        The FreeSurfer '.sh' config file.

    Returns
    -------
    midgray_file: str
        the mid-thickness gray surface.
    """
    # Check input parameters
    white_file = os.path.join(fsdir, sid, "surf", "{0}.white".format(hemi))
    for path in (white_file, ):
        if not os.path.isfile(path):
            raise ValueError("'{0}' is not a valid input file.".format(path))
    for path in (fsdir, ):
        if not os.path.isdir(path):
            raise ValueError("'{0}' is not a valid directory.".format(path))
    if hemi not in ["lh", "rh"]:
        raise ValueError("'{0}' is not a valid hemisphere value which must be "
                         "in ['lh', 'rh']".format(hemi))

    # Define FreeSurfer command
    midgray_file = os.path.join(outdir, "{0}.graymid".format(hemi))
    cmd = ["mris_expand", "-thickness", white_file, "0.5", midgray_file]

    # Execute the FreeSurfer command
    recon = FSWrapper(cmd, shfile=fsconfig)
    recon()

    # Create a symlink to the 'surf' FreeSurfer subject folder
    surf_file = os.path.join(fsdir, sid, "surf", "{0}.graymid".format(hemi))
    if not os.path.islink(surf_file):
        os.symlink(midgray_file, surf_file)

    return midgray_file
Example #6
0
def tkregister_translation(mgzfile, fsconfig=DEFAULT_FREESURFER_PATH):
    """ Get the tkregister translation.

    FreeSurfer use a special origin for the Right-Anterior-Superior
    (anatomical coordinates) space. To get the standard, freesurfer scanner
    space in RAS coordinates we can use the 'mri_info --vox2ras aseg.mgz' or
    'mri_info --vox2ras-trk aseg.mgz' commands respectively.

    Binding over the FreeSurfer's 'mri_info' command.

    Parameters
    ----------
    mgzfile: str (mandatory)
        a FreeSurfer '.mgz' file.
    fsconfig: str (mandatory)
        the freesurfer configuration file.

    Returns
    -------
    translation: array
        the translation matrix between the ras and ras-tkregister spaces.
    """
    # Check the input parameter
    if not os.path.isfile(mgzfile):
        raise ValueError("'{0}' is not a valid '.mgz' file.".format(mgzfile))

    # Get the affine matrices corresponding to the the ras or ras-tkregister
    # spaces
    affines = {}
    for tkregister in [True, False]:

        # Execute the FreeSurfer command
        command = ["mri_info", "--vox2ras", mgzfile]
        if tkregister:
            command[1] = "--vox2ras-tkr"
        process = FSWrapper(command, shfile=fsconfig)
        process()

        # Get the affine matrix displayed in the stdout
        affine = process.stdout.splitlines()
        affine = ",".join([line.strip() for line in affine])
        affine = re.sub(r"  *", ",", affine)
        affine = numpy.fromstring(affine, dtype=float, sep=",").reshape(4, 4)
        affines[tkregister] = affine

    # Compute the translation
    translation = numpy.eye(4)
    translation += (affines[False] - affines[True])

    return translation
Example #7
0
def run_freesurfer_cmd(cmd, subjects_dir=None, fsl_init="/etc/fsl/5.0/fsl.sh"):
    """
    To avoid repeating the code to run Freesurfer and check exitcode
    everywhere.
    Step:
        - add $SUBJECTS_DIR to the environment if requested
        - add FSL's environment if requested (some Freesurfer commands require
          FSL)
        - run the Freesurfer cmd
        - check exit code

    Parameters
    ----------
    cmd: list of str
        the command to run (subprocess like).
    subjects_dir: str, default None.
        To set the $SUBJECTS_DIR environment variable.
    add_fsl_env:  bool, default False
        To activate the FSL environment, required for commands like bbregister.
    fsl_init: str
        Path to the Bash script setting the FSL environment, if needed.
    """
    fs_process = FSWrapper(cmd)

    # Add FSL and current env to Freesurfer environment
    fsl_env = FSLWrapper([], env=os.environ, shfile=fsl_init).environment
    complete_env = concat_environment(fsl_env, fs_process.environment)
    fs_process.environment = complete_env

    if subjects_dir is not None:
        fs_process.environment["SUBJECTS_DIR"] = subjects_dir

    fs_process()  # Run
    if fs_process.exitcode != 0:
        raise FreeSurferRuntimeError(cmd[0], " ".join(cmd[1:]))

    return fs_process
Example #8
0
def mri_binarize(inputfile,
                 outputfile,
                 match=None,
                 wm=False,
                 ventricles=False,
                 inv=False,
                 fsconfig=DEFAULT_FREESURFER_PATH):
    """ Binarize a FreeSurfer label map.

    Binding over the FreeSurfer's 'mri_binarize' command.

    Parameters
    ----------
    inputfile: str (mandatory)
        input volume.
    outputfile: str (mandatory)
        output volume.
    match: list on int (optional)
        match labels instead of threshold.
    wm: bool (optional)
        set match vals to 2 and 41 (aseg for cerebral WM).
    inv: bool (optional)
        inverse the result.
    fsconfig: str (optional)
        The freesurfer configuration batch.
    """
    # Check input parameters
    for path in (inputfile, ):
        if not os.path.isfile(path):
            raise ValueError("'{0}' is not a valid file.".format(path))

    # Call FreeSurfer
    cmd = ["mri_binarize", "--i", inputfile, "--o", outputfile]
    if match is not None:
        cmd.append("--match")
        cmd.extend(match)
    if wm:
        cmd.append("--wm")
    if ventricles:
        cmd.append("--ventricles")
    if inv:
        cmd.append("--inv")
    recon = FSWrapper(cmd, shfile=fsconfig)
    recon()
Example #9
0
    def test_normal_execution(self, mock_path, mock_open, mock_warn):
        """ Test the normal behaviour of the function.
        """
        # Set the mocked functions returned values
        mock_path.isfile.side_effect = [True]
        mock_context_manager = mock.Mock()
        mock_open.return_value = mock_context_manager
        mock_file = mock.Mock()
        mock_file.read.return_value = (
            "freesurfer-Linux-centos4_x86_64-stable-pub-v5.2.0")
        mock_enter = mock.Mock()
        mock_enter.return_value = mock_file
        mock_exit = mock.Mock()
        setattr(mock_context_manager, "__enter__", mock_enter)
        setattr(mock_context_manager, "__exit__", mock_exit)

        # Test execution
        os.environ["FREESURFER_HOME"] = "/my/path/mock_fshome"
        process = FSWrapper(**self.kwargs)
        self.assertEqual(len(mock_warn.call_args_list), 1)
Example #10
0
    def test_noreleaseerror_raise(self, mock_path, mock_open, mock_error,
                                  mock_env):
        """ No FreeSurfer release found -> raise ValueError.
        """
        # Set the mocked functions returned values
        mock_path.isfile.side_effect = [True]
        mock_context_manager = mock.Mock()
        mock_open.return_value = mock_context_manager
        mock_file = mock.Mock()
        mock_file.read.return_value = "WRONG"
        mock_enter = mock.Mock()
        mock_enter.return_value = mock_file
        mock_exit = mock.Mock()
        setattr(mock_context_manager, "__enter__", mock_enter)
        setattr(mock_context_manager, "__exit__", mock_exit)
        mock_env.return_value = {"FREESURFER_HOME": "/my/path/mock_fshome"}

        # Test execution
        process = FSWrapper(**self.kwargs)
        self.assertEqual(process.environment, mock_env.return_value)
        self.assertEqual(len(mock_error.call_args_list), 1)
Example #11
0
def recon_all_longitudinal(outdir,
                           subject_id,
                           subjects_dirs,
                           timepoints=None,
                           fsconfig=DEFAULT_FREESURFER_PATH):
    """
    Assuming you have run recon-all for all timepoints of a given subject,
    and that the results are stored in one SUBJECTS_DIR per timepoint, this
    function will:
    - create a template for the subject and process it with recon-all
    - rerun recon-all for all timepoints of the subject using the template

    Parameters
    ----------
    outdir: str
        Directory where to output. Created if not already existing.
    subject_id: str
        Identifier of subject, used for all timepoints.
    subjects_dirs: list of str
        The FreeSurfer SUBJECTS_DIRs of timepoints.
    timepoints: list of str, default None
        The timepoint names in the same order as the SUBJECTS_DIRs.
        Used to create the subject longitudinal IDs.
        By default timepoints are "1", "2"...
    fsconfig: str, default <pyfreesurfer.DEFAULT_FREESURFER_PATH>
        The FreeSurfer configuration batch.

    Return
    ------
    subject_template_id: str
        ID of the subject template.
    subject_long_ids: list of str
        Longitudinal IDs of the subject for all the timepoints.
    """
    # Check existence of FreeSurfer subject directories
    for subjects_dir in subjects_dirs:
        subject_dir = os.path.join(subjects_dir, subject_id)
        if not os.path.isdir(subject_dir):
            raise ValueError("Directory does not exist: %s" % subject_dir)

    # If 'timepoints' not passed, used defaults, else check validity
    if timepoints is None:
        timepoints = [str(n) for n in range(1, len(subjects_dirs) + 1)]
    elif len(timepoints) != len(subjects_dirs):
        raise ValueError("There should be as many timepoints as subjects_dirs")

    # If <outdir> does not exist, create it
    if not os.path.isdir(outdir):
        os.mkdir(outdir)

    # FreeSurfer requires a unique SUBJECTS_DIR will all the timepoints to
    # compute the template: create symbolic links in <outdir> to all timepoints
    subject_tp_ids = []  # To accumulate all the timepoint IDs
    for tp, subjects_dir in zip(timepoints, subjects_dirs):
        subject_tp_id = "%s_%s" % (subject_id, tp)  # subject timepoint ID
        src_path = os.path.join(subjects_dir, subject_id)
        dst_path = os.path.join(outdir, subject_tp_id)
        os.symlink(src_path, dst_path)
        subject_tp_ids.append(subject_tp_id)

    # STEP 1 - create and process template
    subject_template_id = "%s_template_%s" % (subject_id, "_".join(timepoints))
    cmd = ["recon-all", "-base", subject_template_id]
    for subj_tp_id in subject_tp_ids:
        cmd += ["-tp", subj_tp_id]
    cmd += ["-all"]
    FSWrapper(cmd, shfile=fsconfig, subjects_dir=outdir)()

    # STEP 2 - rerun recon-all for all timepoints using the template
    subject_long_ids = []
    for subj_tp_id in subject_tp_ids:
        cmd = ["recon-all", "-long", subj_tp_id, subject_template_id, "-all"]
        FSWrapper(cmd, shfile=fsconfig, subjects_dir=outdir)()
        subject_long_ids += ["%s.long.%s" % (subj_tp_id, subject_template_id)]

    return subject_template_id, subject_long_ids
Example #12
0
def mkstat_sess(
        sessid,
        outdir,
        fsdir,
        analysis_names,
        svres=False,
        svres_unwhitened=False,
        run_wise=False,
        max_threads=False,
        overwrite=False,
        fsconfig=DEFAULT_FREESURFER_PATH,
        verbose=0):
    """ First-Level GLM Analysis

    Binding around the FreeSurfer's 'selxavg3-sess ' command.

    Requires matlab or octave.

    Parameters
    ----------
    sessid: str (mandatory)
        the session id file or string.
    outdir: str (mandatory)
        the folder where the organized FreeSurfer data are.
    fsdir: str (mandatory)
        the FreeSurfer working home directory.
    analysis_names: list of str (mandatory)
        the configured model names.
    svres: bool (optional, default False)
        save residuals (usually not needed).
    svres_unwhitened: bool (optional, default False)
        save unwhitened residuals (usually not needed).
    run_wise: bool (optional, default False)
        analyze each run separately.
    max_threads: bool (optional, default False)
        use all CPUs.
    overwrite: bool (optional, default False)
         delete analysis if session of already analyzed.
    fsconfig: str (optional, default DEFAULT_FREESURFER_PATH)
        the FreeSurfer configuration batch.
    verbose: int (optional, default 0)
        the verbosity level.
    """
    # Change directory to project directory
    pwd = os.getcwd()
    os.chdir(outdir)

    # Call FreeSurfer fMRI preproc
    cmd = ["selxavg3-sess", "-d", outdir, "-debug", "-no-preproc"]
    if os.path.isfile(sessid):
        cmd += ["-sf", sessid]
    else:
        cmd += ["-s", sessid]
    for value, name in ((svres, "-svres"),
                        (svres_unwhitened, "-svres-unwhitened"),
                        (run_wise, "-run-wise"),
                        (overwrite, "-overwrite"),
                        (max_threads, "-max-threads")):
        if value:
            cmd.append(name)
    log_dir = os.path.join(outdir, "logs")
    if not os.path.isdir(log_dir):
        os.mkdir(log_dir)
    cmd += ["-log", os.path.join(log_dir, "selxavg3-sess.log")]
    cmd += ["-analysis", ""]
    for name in analysis_names:
        if verbose > 0:
            print("[info] Performing analysis '{0}'...".format(name))
        cmd[-1] = name
        wrap = FSWrapper(cmd, shfile=fsconfig, env=os.environ,
                         subjects_dir=fsdir)
        wrap()

    # Restore working directory
    os.chdir(pwd)
Example #13
0
def mkpreproc_sess(
        sessid,
        outdir,
        fsdir,
        fsd="bold",
        perrun=True,
        persession=False,
        fwhm=5.,
        update=True,
        force=False,
        sliceorder=None,
        surface="lhrh",
        mni3052mm=True,
        mni3051mm=False,
        nomc=False,
        nostc=False,
        nosmooth=False,
        nomask=False,
        noreg=False,
        noinorm=False,
        fsconfig=DEFAULT_FREESURFER_PATH,
        fslconfig=DEFAULT_FSL_PATH,
        verbose=0):
    """ Performs all the FreeSurfer fMRI preprocessing steps.

    Binding around the FreeSurfer's 'preproc-sess' command.

    Processing stages:

    * motion correction (MC)
    * slice-timing correction (STC)
    * smoothing
    * intensity normalization (INorm)
    * brain mask creation

    Note: MC and INorm require matlab but none of the other stages does.

    Parameters
    ----------
    sessid: str (mandatory)
        the session id file or string.
    outdir: str (mandatory)
        the folder where the organized FreeSurfer data are.
    fsdir: str (mandatory)
        the FreeSurfer working home directory.
    fsd: str (optional, default 'bold')
        the folder name that contains the 'f.nii.gz' functional volume.
    perrun: bool (optional, default True)
        motion cor and reg to middle TP of each run.
    persession: bool (optional, default False)
         motion cor and reg to 1st TP of 1st run.
    fwhm: float (optional, default 5)
        FWHM : smoothing level (mm)
    update: bool (optional, default True)
        only run a stage if input is newer than output.
    force: bool (optional, default False)
        force reprocessing of all stages (turns off -update).
    sliceorder: list of int (optional, default None)
        turn on slice timing correction with the given slice order.
    surface: str (optional, default 'lhrh')
        'self' or 'fsaverage' followed by hemi 'lh', 'rh' or 'lhrh'.
    mni3052mm: bool (optional, default True)
        sample raw data to mni305 at 2mm.
    mni3051mm: bool (optional, default False)
        sample raw data to mni305 at 1mm.
    nomc: bool (optional, default False)
        don't do motion correction.
    nostc: bool (optional, default False)
        don't do slice-timing correction.
    nosmooth: bool (optional, default False)
        don't do smoothing.
    nomask: bool (optional, default False)
        don't make brain mask.
    noreg: bool (optional, default False)
        don't do registration.
    noinorm: bool (optional, default False)
        don't do inorm.
    fsconfig: str (optional, default DEFAULT_FREESURFER_PATH)
        the FreeSurfer configuration batch.
    fslconfig: str (optional, default DEFAULT_FSL_PATH)
        the FSL configuration batch.
    verbose: int (optional, default 0)
        the verbosity level.

    Returns
    -------
    lh_fsaverage: list of str
        left hemisphere of fsaverage.
    rh_fsaverage: list of str
        right hemisphere of fsaverage.
    sub_fsaverage: list of str
        volume of fsaverage (MNI305 space) - for subcortical analyses.
    bbr_sum: str
        a file containing functional-anatomical cross-modal registration
        QC score. The QC value will be between 0 and 1, with 0 being
        perfect and 1 being terrible. Generally, anything over 0.8 indicates
        that something is probably wrong. View registrations using the
        following command tkregister-sess -s sess02 -fsd bold -per-run.
    mc_plots: list of str
        a list of motion correction plot for each session.
    """
    # Change current directory to deal with FreeeSurfer logs
    pwd = os.getcwd()
    os.chdir(outdir)

    # Check input parameters
    func_files = glob.glob(os.path.join(outdir, "*", fsd, "*", "f.nii.gz"))
    if verbose > 0:
        print("[info] Found {0} fMRI file(s) to be processed.".format(
            len(func_files)))
    if surface not in ("rh", "lh", "lhrh"):
        raise ValueError("Unknown '{0}' preproc-sess surface.".format(surface))

    # Call FreeSurfer fMRI preproc
    cmd = ["preproc-sess", "-d", outdir, "-fsd", fsd, "-fwhm",
           str(fwhm), "-surface", "fsaverage", surface]
    if os.path.isfile(sessid):
        cmd += ["-sf", sessid]
    else:
        cmd += ["-s", sessid]
    if sliceorder is not None:
        cmd.extend(sliceorder)  # -stc
    for value, name in ((perrun, "-per-run"), (persession, "-per-session"),
                        (update, "-update"), (force, "-force"),
                        (mni3052mm, "-mni305-2mm"), (mni3051mm, "-mni305-1mm"),
                        (nomc, "-nomc"), (nostc, "-nostc"),
                        (nosmooth, "-nosmooth"), (nomask, "-nomask"),
                        (noreg, "-noreg"), (noinorm, "-noinorm")):
        if value:
            cmd.append(name)
    fsl_env = environment(fslconfig)
    wrap = FSWrapper(cmd, shfile=fsconfig, env=fsl_env, subjects_dir=fsdir)
    wrap()

    # QC
    # > Motion Correction plot: gives the vector motion at each time point for
    # each run. Note that it is always positive because this is a magnitude.
    # It is also 0 at the middle time point because the middle time point is
    # used as the reference.
    cmd = ["plot-twf-sess", "-d", outdir, "-fsd", fsd, "-mc"]
    if os.path.isfile(sessid):
        cmd += ["-sf", sessid]
    else:
        cmd += ["-s", sessid]
    wrap = FSWrapper(cmd, shfile=fsconfig, subjects_dir=fsdir)
    wrap()

    # > Functional-Anatomical Cross-modal Registration: a summary of
    # registration quality. The QC value will be between 0 and 1, with 0 being
    # perfect and 1 being terrible. Generally, anything over 0.8 indicates
    # that something is probably wrong. View registrations using the following
    # command tkregister-sess -s sess02 -fsd bold -per-run.
    cmd = ["tkregister-sess", "-d", outdir, "-fsd", fsd, "-per-run",
           "-bbr-sum"]
    if os.path.isfile(sessid):
        cmd += ["-sf", sessid]
    else:
        cmd += ["-s", sessid]
    wrap = FSWrapper(cmd, shfile=fsconfig, subjects_dir=fsdir)
    wrap()
    bbr_sum = os.path.join(outdir, "bbr_sum.txt")
    with open(bbr_sum, "wt") as open_file:
        open_file.write(wrap.stdout)

    # Restore working directory
    os.chdir(pwd)

    # Move FreeSurfer logs
    fs_log_dir = os.path.join(outdir, "log")
    log_dir = os.path.join(outdir, "logs")
    if not os.path.isdir(log_dir):
        os.mkdir(log_dir)
    if os.path.isdir(fs_log_dir):
        for basename in os.listdir(fs_log_dir):
            shutil.move(os.path.join(fs_log_dir, basename),
                        os.path.join(log_dir, basename))
        shutil.rmtree(fs_log_dir)

    # Outputs
    lh_fsaverage = sorted(glob.glob(os.path.join(
        outdir, "*", fsd, "*", "f*.fsaverage.lh.nii.gz")))
    rh_fsaverage = sorted(glob.glob(os.path.join(
        outdir, "*", fsd, "*", "f*.fsaverage.rh.nii.gz")))
    sub_fsaverage = sorted(glob.glob(os.path.join(
        outdir, "*", fsd, "*", "f*.mni305.*mm.nii.gz")))
    mc_plots = glob.glob(os.path.join(
        outdir, "*", fsd, "*.mcdat.png"))

    return lh_fsaverage, rh_fsaverage, sub_fsaverage, bbr_sum, mc_plots
Example #14
0
def mkmodel_sess(
        outdir,
        tr,
        contrasts_file,
        funcstem=None,
        fsd="bold",
        perrun=True,
        persession=False,
        fwhm=5.,
        mni3052mm=True,
        mni3051mm=False,
        blocked_design=True,
        retinototy_design=False,
        abblocked_design=False,
        spmhrf=0,
        fslhrf=None,
        gammafit=None,
        ngammaderiv=None,
        fir=None,
        nconditions=None,
        refeventdur=None,
        polyfit=2,
        mcextreg=True,
        nuisreg=None,
        nskip=4,
        fsconfig=DEFAULT_FREESURFER_PATH,
        verbose=0):
    """ Configure First Level GLM Analysis for event-related and blocked
    design.

    Not yet implemented possibilities are abblocked and retinotopy design.

    Binding around the FreeSurfer's 'mkanalysis-sess ' and 'mkcontrast-sess'
    commands.

    Requires matlab or octave

    Parameters
    ----------
    outdir: str (mandatory)
        the folder where the organized FreeSurfer data are.
    tr: float (mandatory)
        TR value in seconds.
    contrasts_file: str (mandatory)
        a JSON file that contains a list of contrasts with each contrast
        being a tuple of the form: ('name', 'stat', [condition list],
        [condition list ids], [weight list]).
    funcstem: str (optional, default None)
        override default basename, need to specify extension.
        The FWHM is no relevant when you have specified a funcstem
    fsd: str (optional, default 'bold')
        the folder name that contains the 'f.nii.gz' functional volume.
    perrun: bool (optional, default True)
        motion cor and reg to middle TP of each run.
    persession: bool (optional, default False)
         motion cor and reg to 1st TP of 1st run.
    fwhm: float (optional, default 5)
        FWHM : smoothing level (mm)
    mni3052mm: bool (optional, default True)
        sample raw data to mni305 at 2mm.
    mni3051mm: bool (optional, default False)
        sample raw data to mni305 at 1mm.
    spmhrf: int (optional, default 0)
        assume SPM HRF with nderiv derivatives.
    fslhrf: int (optional, default None)
        assume FSL HRF with nderiv derivatives
    gammafit: 2-uplet (optional, default None)
        assume IRF is a gamma function (gfDelta gfTau) -> (2.25 1.25).
    ngammaderiv: int (optional, default None)
        number of derivatives to gamma function.
    fir: 2-uplet (optional, default None)
        event prestimulus and total time window (sec) for FIR designs
    nconditions: int (optional, default None)
        number of conditions (excluding fixation).
    refeventdur: int (optional, default None)
        duration (sec) of reference event for scaling.
    polyfit: int (optional, default 2)
        fit trend with polynomial of order N: 0 mean offset, 1 temporal trend,
        2 quadratic trend.
    mcextreg: bool (optional, default True)
        use motion parameters as external nuissance regressors.
    nuisreg: 2-uplet (optional, default None)
        external nuisance regressor file and number of regressors to include
        (extreg n).
    nskip: int (optional, default 4)
        skip the first N time points in each run
    fsconfig: str (optional, default DEFAULT_FREESURFER_PATH)
        the FreeSurfer configuration batch.
    verbose: int (optional, default 0)
        the verbosity level.

    Returns
    -------
    analysis_names: list of str
        the configured model names.
    """
    # Change current directory to deal with FreeeSurfer logs
    pwd = os.getcwd()
    os.chdir(outdir)

    # Check input parameters
    for path in [contrasts_file]:
        if not os.path.isfile(path):
            raise ValueError("'{0}' is not a valid file.".format(path))
    if retinototy_design or abblocked_design:
        raise NotImplementedError("Selected design not yet supported.")

    # Load contrast description file
    with open(contrasts_file, "rt") as open_file:
        contrasts = json.load(open_file)

    # Call FreeSurfer fMRI firsl level GLM analysis: a different analysis is
    # needed for each space lh, rh, and mni305.
    analysis_names = []
    for space in ["lh", "rh", "mni305"]:

        # Analysis Name - name used to reference this collection of
        # parameters.
        analysis_name = "odd.even.sm{0}.{1}".format(fwhm, space)
        analysis_names.append(analysis_name)
        if verbose > 0:
            print("[info] Performing analysis '{0}'...".format(analysis_name))

        # Set preproc options
        cmd = ["mkanalysis-sess", "-fsd", fsd, "-paradigm", "odd.even.par",
               "-analysis", analysis_name, "-TR", str(tr), "-force"]
        if funcstem is None:
            cmd += ["-fwhm", str(fwhm)]
        else:
            cmd += ["-funcstem", funcstem]
        if space in ["lh", "rh"]:
            cmd += ["-surface", "fsaverage", space]
        else:
            cmd += ["-mni305"]
        for value, name in ((perrun, ["-per-run"]),
                            (persession, ["-per-session"]),
                            (mni3052mm, ["-mni305", "2"]),
                            (mni3051mm, ["-mni305", "1"])):
            if value:
                cmd.extend(name)

        # Set design options
        cmd += ["-event-related"]
        for value, name in ((spmhrf, "-spmhrf"),
                            (fslhrf, "-fslhrf"),
                            (ngammaderiv, "-ngammaderiv"),
                            (refeventdur, "-refeventdur"),
                            (nconditions, "-nconditions")):
            if value is not None:
                cmd.extend([name, str(value)])
        for value, name in ((gammafit, "-gammafit"),
                            (fir, "-fir")):
            if value is not None:
                cmd.extend([name] + [str(e) for e in value])

        # Set noise, drift, and temporal filtering options
        cmd += ["-polyfit", str(polyfit), "-nskip", str(nskip)]
        if mcextreg:
            cmd += ["-mcextreg"]
        if nuisreg is not None:
            cmd += ["-nuisreg", nuisreg[0], str(nuisreg[1])]

        # Create the analysis configuration file
        wrap = FSWrapper(cmd, shfile=fsconfig)
        wrap()

        # Create the contrasts
        for contrast_name, _, _, conditions, weights in contrasts:
            cmd = ["mkcontrast-sess", "-debug", "-analysis", analysis_name,
                   "-contrast", contrast_name, "-ncond", str(nconditions),
                   "-wcond"]
            wcond = numpy.zeros((nconditions,))
            wcond[conditions] = weights
            cmd += [str(e) for e in wcond]
            wrap = FSWrapper(cmd, shfile=fsconfig, env=os.environ)
            wrap()

    # Restore working directory
    os.chdir(pwd)

    # Move FreeSurfer logs
    fs_log_dir = os.path.join(outdir, "log")
    log_dir = os.path.join(outdir, "logs")
    if not os.path.isdir(log_dir):
        os.mkdir(log_dir)
    if os.path.isdir(fs_log_dir):
        for basename in os.listdir(fs_log_dir):
            shutil.move(os.path.join(fs_log_dir, basename),
                        os.path.join(log_dir, basename))
        shutil.rmtree(fs_log_dir)

    return analysis_names
Example #15
0
def trac_all(outdir,
             subjects_dir=None,
             temp_dir=None,
             fsconfig=DEFAULT_FREESURFER_PATH,
             fslconfig=DEFAULT_FSL_PATH):
    """ Anisotropy and diffusivity along the trajectory of a pathway.

    Parameters
    ----------
    outdir: str
        Root directory where to create the subject's output directory.
        Created if not existing.
    subjects_dir: str, default None
        Path to the FreeSurfer subjects directory. Required if the
        environment variable $SUBJECTS_DIR is not set.
    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
    -------
    statdir: str
        The directory containing the FreeSurfer summary files.
    outlierfile: str
        A file that contains the subjects flagged as outliers.
    """
    # FreeSurfer $SUBJECTS_DIR has to be passed or set as an env variable
    subjects_dir = get_or_check_freesurfer_subjects_dir(subjects_dir)

    # Find all the subjects with a pathway stat file
    statdirs = glob.glob(os.path.join(subjects_dir, "*", "dpath"))
    subjects = set([path.split(os.sep)[-2] for path in statdirs])
    subjects = " ".join(subjects)

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

    # Create configuration file
    config_str = CONFIG_TEMPLATE.format(subjects_dir=subjects_dir,
                                        subjlist=subjects,
                                        dtroot=subjects_dir)
    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", "-stat", "-c", path_config]
    FSWrapper(cmd_prep,
              shfile=fsconfig,
              subjects_dir=subjects_dir,
              add_fsl_env=True,
              fsl_sh=fslconfig)()

    # Move results to destination folder
    statdir = os.path.join(subjects_dir, "stats")
    shutil.move(statdir, outdir)

    # Clean tmp dir
    shutil.rmtree(temp_dir)

    # Detect outliers
    statdir = os.path.join(outdir, "stats")
    outlierfile = os.path.join(outdir, "outliers.json")
    logfiles = glob.glob(os.path.join(statdir, "*.log"))
    outliers = set()
    regex = r"^Found outlier path: .*"
    for path in logfiles:
        with open(path, "rt") as open_file:
            for match in re.findall(regex,
                                    open_file.read(),
                                    flags=re.MULTILINE):
                outliers.add(match.replace("Found outlier path: ", ""))
    with open(outlierfile, "wt") as open_file:
        json.dump(list(outliers), open_file, indent=4)

    return statdir, outlierfile
Example #16
0
def mri_vol2surf(hemi,
                 volume_file,
                 out_texture_file,
                 ico_order,
                 dat_file,
                 fsdir,
                 sid,
                 surface_name="white",
                 fsconfig=DEFAULT_FREESURFER_PATH):
    """ Assigns values from a volume to each surface vertices.

    Wrapper around the FreeSurfer 'mri_vol2surf' command to create the
    described texture.

    Parameters
    ----------
    hemi: str (mandatory)
        hemisphere ('lh' or 'rh').
    volume_file: str (mandatory)
        input volume path.
    out_texture_file: str (mandatory)
        output texture file.
    ico_order: int (mandatory)
        icosahedron order in [0, 7] that will be used to generate the cortical
        surface texture at a specific tessalation (the corresponding cortical
        surface can be resampled using the
        'pyfreesurfer.utils.surftools.resample_cortical_surface' function).
    dat_file: str (mandatory)
        structural to FreeSurfer space affine '.dat' transformation matrix
        file as computed by 'tkregister2'.
    fsdir: str (mandatory)
        FreeSurfer subjects directory 'SUBJECTS_DIR'.
    sid: str (mandatory)
        FreeSurfer subject identifier.
    surface_name: str (optional, default 'white')
        The surface we  want to resample ('white' or 'pial').
    fsconfig: str (optional)
        The FreeSurfer '.sh' config file.
    """
    # Check input parameters
    for path in (volume_file, dat_file):
        if not os.path.isfile(path):
            raise ValueError("'{0}' is not a valid input file.".format(path))
    for path in (fsdir, ):
        if not os.path.isdir(path):
            raise ValueError("'{0}' is not a valid directory.".format(path))
    if hemi not in ["lh", "rh"]:
        raise ValueError("'{0}' is not a valid hemisphere value which must be "
                         "in ['lh', 'rh']".format(hemi))
    if surface_name not in ["white", "pial"]:
        raise ValueError("'{0}' is not a valid surface value which must be in "
                         "['white', 'pial']".format(surface_name))
    if ico_order < 0 or ico_order > 7:
        raise ValueError("'Ico order '{0}' is not in 0-7 "
                         "range.".format(ico_order))

    # Construct the FreeSurfer vol2surf command
    cmd = [
        "mri_vol2surf", "--src", volume_file, "--out", out_texture_file,
        "--srcreg", dat_file, "--hemi", hemi, "--trgsubject", "ico",
        "--icoorder", "{0}".format(ico_order), "--surf", surface_name, "--sd",
        fsdir, "--srcsubject", sid, "--noreshape", "--out_type", "mgz"
    ]

    # Execute the FreeSurfer command
    recon = FSWrapper(cmd, shfile=fsconfig)
    recon()
Example #17
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
Example #18
0
def recon_all_custom_wm_mask(subject_id,
                             wm_mask,
                             keep_orig=True,
                             subjects_dir=None,
                             temp_dir=None,
                             fsconfig=DEFAULT_FREESURFER_PATH):
    """
    Assuming you have run recon-all (at least upto wm.mgz creation), this
    function allows to rerun recon-all using a custom white matter mask. The
    mask has to be in the subject's FreeSurfer space (1mm iso + aligned with
    brain.mgz) with values in [0; 1] (i.e. probability of being white matter).

    Parameters
    ----------
    subject_id: str
        Identifier of subject.
    wm_mask: str
        Path to the custom white matter mask. It has to be in the subject's
        FreeSurfer space (1mm iso + aligned with brain.mgz) with values in
        [0; 1] (i.e. probability of being white matter).
        For example, tt can be the 'brain_pve_2.nii.gz" white matter
        probability map created by FSL Fast.
    keep_orig: bool, default True
        Save original 'wm.seg.mgz' as 'wm.seg.orig.mgz' instead of overwriting
        it.
    subjects_dir: str, default None
        Path to the FreeSurfer subjects directory. Required if the environment
        variable $SUBJECTS_DIR is not set.
    temp_dir: str, default None
        Directory to use to store temporary files. By default OS tmp dir.
    fsconfig: str, default <pyfreesurfer.DEFAULT_FREESURFER_PATH>
        The FreeSurfer configuration batch.
    """
    # 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 the subject's directory
    subject_dir = os.path.join(subjects_dir, subject_id)
    if not os.path.isdir(subject_dir):
        ValueError("Directory does not exist: %s" % subject_dir)

    # Create temporary directory to store intermediate files
    temp_dir = tempfile.mkdtemp(prefix="recon_all_custom_wm_mask_",
                                dir=temp_dir)

    # Change input mask range of values: [0-1] to [0-110]
    wm_mask_0_110 = os.path.join(temp_dir, "wm_mask_0_110.nii.gz")
    cmd_1 = ["mris_calc", "-o", wm_mask_0_110, wm_mask, "mul", "110"]
    FSWrapper(cmd_1, shfile=fsconfig)()

    # If requested save original wm.seg.mgz as wm.seg.orig.mgz
    wm_seg_mgz = os.path.join(subject_dir, "mri", "wm.seg.mgz")
    if keep_orig:
        save_as = os.path.join(subject_dir, "mri", "wm.seg.orig.mgz")
        shutil.move(wm_seg_mgz, save_as)

    # Write the new wm.seg.mgz, FreeSurfer requires MRI_UCHAR type
    cmd_2 = ["mri_convert", wm_mask_0_110, wm_seg_mgz, "-odt", "uchar"]
    FSWrapper(cmd_2, shfile=fsconfig)()

    # Clean tmp dir
    shutil.rmtree(temp_dir)

    # Rerun recon-all
    cmd_3 = ["recon-all", "-autorecon2-wm", "-autorecon3", "-s", subject_id]
    FSWrapper(cmd_3, shfile=fsconfig, subjects_dir=subjects_dir)()

    return subject_dir
Example #19
0
def mri_convert(fsdir,
                regex,
                outdir,
                destdirname="convert",
                reslice=True,
                interpolation="interpolate",
                fsconfig=DEFAULT_FREESURFER_PATH):
    """ Export FreeSurfer '.mgz' image in Nifti format.

    Convert in native space: the destination image is resliced like the
    'rawavg.mgz' file if the reslice option is set. The converted file will
    then have a '.native' suffix.

    Binding over the FreeSurfer's 'mri_convert' command.

    Parameters
    ----------
    fsdir: str (mandatory)
        The FreeSurfer home directory with all the subjects.
    regex: str (mandatory)
        A regular expression used to locate the files to be converted from the
        'fsdir' directory.
    outdir: str (mandatory)
        The conversion destination folder.
    destdirname: str (optional, default 'convert')
        The name of the folder where each subject converted volumes will be
        saved. If None, don't create a sub folder.
    reslice: bool (optional default False)
        If True reslice the input images like the raw image.
    interpolation: str (optional default interpolate)
        The interpolation method: interpolate|weighted|nearest|cubic.
    fsconfig str (optional)
        The FreeSurfer configuration batch.

    Returns
    -------
    niftifiles: list of str
        The converted nifti files.
    """
    # Check the input parameters
    for path in (fsdir, outdir):
        if not os.path.isdir(path):
            raise ValueError("'{0}' is not a valid directory.".format(path))

    # Check the interpolation method
    if interpolation not in ["interpolate", "weighted", "nearest", "cubic"]:
        raise ValueError(
            "'{0}' is not a valid interpolation method.".format(interpolation))

    # Get the images to convert from the regex
    inputs = glob.glob(os.path.join(fsdir, regex))

    # Convert each input file
    niftifiles = []
    for input_file in inputs:

        # Create the output directory
        subject = input_file.replace(fsdir, "")
        subject = subject.lstrip(os.sep).split(os.sep)[0]
        if destdirname is not None:
            subjoutdir = os.path.join(outdir, subject, destdirname)
        else:
            subjoutdir = outdir
        if not os.path.isdir(subjoutdir):
            os.makedirs(subjoutdir)

        # Create the FreeSurfer command
        basename = os.path.basename(input_file).replace(".mgz", "")
        cmd = ["mri_convert", "--resample_type", interpolation]
        # "--out_orientation", "RAS"]
        if reslice:
            reference_file = os.path.join(fsdir, subject, "mri", "rawavg.mgz")
            if not os.path.isfile(reference_file):
                raise ValueError("'{0}' does not exists, can't reslice image "
                                 "'{1}'.".format(reference_file, input_file))
            cmd += ["--reslice_like", reference_file]
            basename = basename + ".native"
        converted_file = os.path.join(subjoutdir, basename + ".nii.gz")
        niftifiles.append(converted_file)
        cmd += [input_file, converted_file]

        # Execute the FreeSurfer command
        recon = FSWrapper(cmd, shfile=fsconfig)
        recon()

    return niftifiles
Example #20
0
def freesurfer_bbregister_t1todif(outdir,
                                  subject_id,
                                  nodif_brain,
                                  subjects_dir=None,
                                  fs_sh=DEFAULT_FREESURFER_PATH,
                                  fsl_sh=DEFAULT_FSL_PATH):
    """ Compute DWI to T1 transformation and project the T1 to the diffusion
    space without resampling.

    Parameters
    ----------
    outdir: str
        Directory where to output.
    subject_id: str
        Subject id used with FreeSurfer 'recon-all' command.
    nodif_brain: str
        Path to the preprocessed brain-only DWI volume.
    subjects_dir: str or None, default None
        Path to the FreeSurfer subjects directory. Required if the FreeSurfer
        environment variable (i.e. $SUBJECTS_DIR) is not set.
    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
    -------
    t1_brain_to_dif: str
        The anatomical image in the diffusion space (without resampling).
    dif2anat_dat, dif2anat_mat: str
        The DWI to T1 transformation in FreeSurfer or FSL space respectivelly.
    """
    # -------------------------------------------------------------------------
    # STEP 0 - Check arguments

    # FreeSurfer subjects_dir
    subjects_dir = get_or_check_freesurfer_subjects_dir(subjects_dir)

    # Check input paths
    paths_to_check = [nodif_brain, fs_sh, fsl_sh]
    for p in paths_to_check:
        if not os.path.exists(p):
            raise ValueError("File or directory does not exist: %s" % p)

    # -------------------------------------------------------------------------
    # STEP 1 - Compute T1 <-> DWI rigid transformation

    # Register diffusion to T1
    dif2anat_dat = os.path.join(outdir, "dif2anat.dat")
    dif2anat_mat = os.path.join(outdir, "dif2anat.mat")
    cmd_1a = [
        "bbregister", "--s", subject_id, "--mov", nodif_brain, "--reg",
        dif2anat_dat, "--fslmat", dif2anat_mat, "--dti", "--init-fsl"
    ]
    FSWrapper(cmd_1a,
              subjects_dir=subjects_dir,
              shfile=fs_sh,
              add_fsl_env=True,
              fsl_sh=fsl_sh)()

    # Align FreeSurfer T1 brain to diffusion without downsampling
    fs_t1_brain = os.path.join(subjects_dir, subject_id, "mri", "brain.mgz")
    t1_brain_to_dif = os.path.join(outdir, "fs_t1_brain_to_dif.nii.gz")
    cmd_1b = [
        "mri_vol2vol", "--mov", nodif_brain, "--targ", fs_t1_brain, "--inv",
        "--no-resample", "--o", t1_brain_to_dif, "--reg", dif2anat_dat,
        "--no-save-reg"
    ]
    FSWrapper(cmd_1b, shfile=fs_sh)()

    return t1_brain_to_dif, dif2anat_dat, dif2anat_mat
Example #21
0
def aparcstats2table(fsdir, outdir, fsconfig=DEFAULT_FREESURFER_PATH):
    """ Generate text/ascii tables of freesurfer parcellation stats data
    '?h.aparc.stats'.

    This can then be easily imported into a spreadsheet and/or stats program.

    Binding over the FreeSurfer's 'aparcstats2table' command.

    Parameters
    ----------
    fsdir: (mandatory)
        The freesurfer working directory with all the subjects.
    outdir: str (mandatory)
        The statistical destination folder.
    fsconfig: str (optional)
        The freesurfer configuration batch.

    Return
    ------
    statfiles: list of str
        The freesurfer summary stats.
    """
    # Check input parameters
    for path in (fsdir, outdir):
        if not os.path.isdir(path):
            raise ValueError("'{0}' is not a valid directory.".format(path))

    # Parameter that will contain the output stats
    statfiles = []

    # Fist find all the subjects with a stat dir
    statdirs = glob.glob(os.path.join(fsdir, "*", "stats"))
    subjects = [item.lstrip(os.sep).split("/")[-2] for item in statdirs]

    # Save the FreeSurfer current working directory and set the new one
    fscwd = None
    if "SUBJECTS_DIR" in os.environ:
        fscwd = os.environ["SUBJECTS_DIR"]
    os.environ["SUBJECTS_DIR"] = fsdir

    # Create the output stat directory
    fsoutdir = os.path.join(outdir, "stats")
    if not os.path.isdir(fsoutdir):
        os.mkdir(fsoutdir)

    # Call freesurfer
    for hemi in ["lh", "rh"]:
        for meas in [
                "area", "volume", "thickness", "thicknessstd", "meancurv",
                "gauscurv", "foldind", "curvind"
        ]:

            statfile = os.path.join(
                fsoutdir, "aparc_stats_{0}_{1}.csv".format(hemi, meas))
            statfiles.append(statfile)
            cmd = ["aparcstats2table", "--subjects"] + subjects + [
                "--hemi", hemi, "--meas", meas, "--tablefile", statfile,
                "--delimiter", "comma", "--parcid-only"
            ]

            recon = FSWrapper(cmd, shfile=fsconfig)
            recon()
            if recon.exitcode != 0:
                raise FreeSurferRuntimeError(recon.cmd[0],
                                             " ".join(recon.cmd[1:]),
                                             recon.stderr + recon.stdout)

    # Restore the FreeSurfer working directory
    if fscwd is not None:
        os.environ["SUBJECTS_DIR"] = fscwd

    return statfiles
Example #22
0
def resample_cortical_surface(fsdir,
                              regex,
                              outdir,
                              destdirname="convert",
                              orders=[4, 5, 6, 7],
                              surface_name="white",
                              fsconfig=DEFAULT_FREESURFER_PATH):
    """ Resamples one cortical surface onto an icosahedron.

    Resample the white or pial FreeSurfer cotical surface using the
    'mri_surf2surf' command. Map also the associated annotation file.

    Can resample at different icosahedron order which specifies the size of the
    icosahedron according to the following table:
    Order  Number of Vertices
    0              12
    1              42
    2             162
    3             642
    4            2562
    5           10242
    6           40962
    7          163842

    Binding over the FreeSurfer's 'mri_surf2surf' command.

    Parameters
    ----------
    fsdir: str (mandatory)
        The freesurfer home directory with all the subjects.
    regex: str (mandatory)
        A regular expression used to locate the surface files to be converted
        from the 'fsdir' directory.
    outdir: str (optional, default None)
        The destination folder.
    destdirname: str (optional, default 'convert')
        The name of the folder where each subject resample cortical surface
        will be saved.
    orders: list of int
        The icosahedron orders.
    surface_name: str (optional, default 'white')
        The surface we want to resample ('white' or 'pial').
    fsconfig: str (optional)
        The freesurfer configuration batch.

    Returns
    -------
    resamplefiles: list of str
        The resample surfaces.
    annotfiles: list of str
        The resample annotations.
    """
    # Check input parameters
    for path in (fsdir, outdir):
        if not os.path.isdir(path):
            raise ValueError("'{0}' is not a valid directory.".format(path))
    if surface_name not in ["white", "pial", "graymid"]:
        raise ValueError("'{0}' is not a valid surface value which must be in "
                         "['white', 'pial']".format(surface_name))
    norders = numpy.asarray(orders)
    if norders.min() < 0 or norders.max() > 7:
        raise ValueError("'At least one value in {0} is not in 0-7 "
                         "range.".format(orders))

    # Get all the subjects with the specified surface
    surfaces = glob.glob(os.path.join(fsdir, regex))

    # Go through all the subjects with the desired surface
    resamplefiles = []
    annotfiles = []
    for surf in surfaces:

        # Get some information based on the surface path
        subject_id = surf.split("/")[-3]
        hemi = os.path.basename(surf).split(".")[0]
        convertdir = os.path.join(outdir, subject_id, destdirname)
        if not os.path.isdir(convertdir):
            os.makedirs(convertdir)

        # Go through all specified orders
        for level in orders:

            # Construct the FS surface map command
            convertfile = os.path.join(
                convertdir, "{0}.{1}.{2}".format(hemi, surface_name, level))
            resamplefiles.append(convertfile)
            recon = FSWrapper([], shfile=fsconfig)
            if version.parse(recon.version or "") >= version.parse("6.0.0"):
                origfile = os.path.join(fsdir, subject_id, "mri", "orig.mgz")
                cmd = [
                    "mri_surf2surf", "--sval-xyz", surface_name,
                    "--srcsubject", subject_id, "--trgsubject", "ico",
                    "--trgicoorder",
                    str(level), "--tval", convertfile, "--tval-xyz", origfile,
                    "--hemi", hemi, "--sd", fsdir
                ]
            else:
                cmd = [
                    "mri_surf2surf", "--sval-xyz", surface_name,
                    "--srcsubject", subject_id, "--trgsubject", "ico",
                    "--trgicoorder",
                    str(level), "--tval", convertfile, "--tval-xyz", "--hemi",
                    hemi, "--sd", fsdir
                ]

            # Execute the FS command
            recon.cmd = cmd
            recon()

            # Construct the FS label map command
            annotfile = os.path.join(convertdir,
                                     "{0}.aparc.annot.{1}".format(hemi, level))
            annotfiles.append(annotfile)
            if not os.path.isfile(annotfile):
                svalannot = os.path.join(fsdir, subject_id, "label",
                                         "{0}.aparc.annot".format(hemi))
                cmd = [
                    "mri_surf2surf", "--srcsubject", subject_id,
                    "--trgsubject", "ico", "--trgicoorder",
                    str(level), "--hemi", hemi, "--sval-annot", svalannot,
                    "--tval", annotfile, "--sd", fsdir
                ]

                # Execute the FS command
                recon = FSWrapper(cmd, shfile=fsconfig)
                recon()

    # Remove duplicate annotation files
    annotfiles = list(set(annotfiles))

    return sorted(resamplefiles), sorted(annotfiles)
Example #23
0
def probtrackx2_connectome_complete(outdir,
                                    subject_id,
                                    lh_surf,
                                    rh_surf,
                                    nodif_brain,
                                    nodif_brain_mask,
                                    bedpostx_dir,
                                    nsamples,
                                    nsteps,
                                    steplength,
                                    subjects_dir=None,
                                    loopcheck=True,
                                    cthr=0.2,
                                    fibthresh=0.01,
                                    distthresh=0.0,
                                    sampvox=0.0,
                                    fs_sh=DEFAULT_FREESURFER_PATH,
                                    fsl_sh=DEFAULT_FSL_PATH):
    """ Compute the connectome of a given tesellation, like the FreeSurfer,
    using ProbTrackx2.

    Requirements:
        - brain masks for the preprocessed DWI: nodif_brain and
          nodif_brain_mask.
        - FreeSurfer: result of recon-all on the T1.
        - FSL Bedpostx: computed for the preprocessed DWI.

    Connectome construction strategy:
        - Pathways are constructed from 'constitutive points' and not from
          endpoints. A pathway is the result of 2 samples propagating in
          opposite directions from a seed point. It is done using the
          --omatrix3 option of Probtrackx2.
        - The seed mask is the mask of WM voxels that are neighbors
          (12-connexity) of nodes.
        - The stop mask is the inverse of white matter, i.e. a sample stops
          propagating as soon as it leaves the white matter.

    Note:

    --randfib refers to initialization of streamlines only (i.e. the very first
    step) and only affects voxels with more than one fiber reconstructed:
    randfib==0, only sample from the strongest fiber
    randfib==1, randomly sample from all fibers regardless of strength that are
    above --fibthresh
    randfib==2, sample fibers stronger than --fibthresh in proportion to their
    strength (in my opinion, this is the best choice)
    randfib==3, sample all fibers randomly regardless of whether or not they
    are above --fibthresh.

    Parameters
    ----------
    outdir: str
        Directory where to output.
    subject_id: str
        Subject id used with FreeSurfer 'recon-all' command.
    lh_surf: str
        The left hemisphere surface.
    rh_surf: str
        The left hemisphere surface.
    nodif_brain: str
        Path to the preprocessed brain-only DWI volume.
    nodif_brain_mask: str
        Path to the brain binary mask.
    bedpostx_dir: str
        Bedpostx output directory.
    nsamples: int
        Number of samples per voxel to initiate in the seed mask.
    nsteps: int
        Maximum number of steps for a given sample.
    steplength: int
        Step size in mm.
    subjects_dir: str or None, default None
        Path to the FreeSurfer subjects directory. Required if the FreeSurfer
        environment variable (i.e. $SUBJECTS_DIR) is not set.
    cthr: float, optional
        Probtrackx2 option.
    fibthresh, distthresh, sampvox: float, optional
        Probtrackx2 options.
    loopcheck: bool, optional
        Probtrackx2 option.
    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
    ------
    coords: str
        The connectome coordinates.
    weights: str
        The connectome weights.
    """
    # -------------------------------------------------------------------------
    # STEP 0 - Check arguments

    # FreeSurfer subjects_dir
    subjects_dir = get_or_check_freesurfer_subjects_dir(subjects_dir)

    # Check input paths
    paths_to_check = [
        nodif_brain, nodif_brain_mask, bedpostx_dir, fs_sh, fsl_sh
    ]
    for p in paths_to_check:
        if not os.path.exists(p):
            raise ValueError("File or directory does not exist: %s" % p)

    # Create <outdir> if not existing
    if not os.path.isdir(outdir):
        os.makedirs(outdir)

    # -------------------------------------------------------------------------
    # STEP 1 - Compute T1 <-> DWI rigid transformation

    # FreeSurfer T1 to Nifti
    fs_t1_brain = os.path.join(subjects_dir, subject_id, "mri", "brain.mgz")
    t1_brain = os.path.join(outdir, "t1_brain.nii.gz")
    cmd_1a = ["mri_convert", fs_t1_brain, t1_brain]
    FSWrapper(cmd_1a, shfile=fs_sh)()

    # Register diffusion to T1
    dif2anat_dat = os.path.join(outdir, "dif2anat.dat")
    dif2anat_mat = os.path.join(outdir, "dif2anat.mat")
    nodif_brain_reg = os.path.join(outdir, "nodif_brain_to_t1.nii.gz")
    cmd_1b = [
        "bbregister", "--s", subject_id, "--mov", nodif_brain, "--reg",
        dif2anat_dat, "--fslmat", dif2anat_mat, "--dti", "--init-fsl", "--o",
        nodif_brain_reg
    ]
    FSWrapper(cmd_1b,
              subjects_dir=subjects_dir,
              shfile=fs_sh,
              add_fsl_env=True,
              fsl_sh=fsl_sh)()

    # Invert dif2anat transform
    m = numpy.loadtxt(dif2anat_mat)
    m_inv = numpy.linalg.inv(m)
    anat2dif_mat = os.path.join(outdir, "anat2dif.mat")
    numpy.savetxt(anat2dif_mat, m_inv)

    # -------------------------------------------------------------------------
    # STEP 2 - Create the masks for Probtrackx2

    # White matter mask
    aparc_aseg = os.path.join(subjects_dir, subject_id, "mri",
                              "aparc+aseg.mgz")
    wm_mask = os.path.join(outdir, "wm_mask.nii.gz")
    mri_binarize(inputfile=aparc_aseg,
                 outputfile=wm_mask,
                 match=None,
                 wm=True,
                 inv=False,
                 fsconfig=fs_sh)

    # Stop mask is inverse of white matter mask
    stop_mask = os.path.join(outdir, "inv_wm_mask.nii.gz")
    mri_binarize(inputfile=aparc_aseg,
                 outputfile=stop_mask,
                 match=None,
                 wm=True,
                 inv=True,
                 fsconfig=fs_sh)

    # Create seed mask
    seed_mask = wm_mask

    # Create target masks: the white surface
    white_surf = os.path.join(outdir, "white.asc")
    cmd_2a = ["mris_convert", "--combinesurfs", lh_surf, rh_surf, white_surf]
    FSWrapper(cmd_2a, subjects_dir=subjects_dir, shfile=fs_sh)()

    # -------------------------------------------------------------------------
    # STEP 7 - Run Probtrackx2
    probtrackx2(dir=outdir,
                forcedir=True,
                seedref=t1_brain,
                xfm=anat2dif_mat,
                invxfm=dif2anat_mat,
                samples=os.path.join(bedpostx_dir, "merged"),
                mask=nodif_brain_mask,
                seed=seed_mask,
                omatrix3=True,
                target3=white_surf,
                stop=stop_mask,
                nsamples=nsamples,
                nsteps=nsteps,
                steplength=steplength,
                loopcheck=loopcheck,
                cthr=cthr,
                fibthresh=fibthresh,
                distthresh=distthresh,
                sampvox=sampvox,
                pd=True,
                randfib=2,
                shfile=fsl_sh)
    coords = os.path.join(outdir, "coords_for_fdt_matrix3")
    weights = os.path.join(outdir, "fdt_matrix3.dot")

    return coords, weights
Example #24
0
def interhemi_surfreg(hemi,
                      outdir,
                      fsdir,
                      sid,
                      template_file=DEFAULT_TEMPLATE_SYM_PATH,
                      destname="surfreg",
                      fsconfig=DEFAULT_FREESURFER_PATH):
    """ Surface-based interhemispheric registration by aplying an existing
    atlas, the 'fsaverage_sym'.

    Reference
    Greve, Douglas N., Lise Van der Haegen, Qing Cai, Steven Stufflebeam,
    Mert R. Sabuncu, Bruce Fischl, and Marc Bysbaert.
    "A surface-based analysis of language lateralization and cortical
    asymmetry." (2013). Journal of Cognitive Neuroscience 25.9: 1477-1492.

    Parameters
    ----------
    hemi: str (mandatory)
        hemisphere ('lh' or 'rh').
    outdir: str (mandatory)
        the destination folder.
    fsdir: str (mandatory)
        FreeSurfer subjects directory 'SUBJECTS_DIR'.
    sid: str (mandatory)
        FreeSurfer subject identifier.
    template_file: str (optional, default DEFAULT_TEMPLATE_SYM_PATH)
        path to the 'fsaverage_sym' template.
    destname: str (optional, default 'destname')
        the name of the folder where the results will be stored.
    fsconfig: str (optional, default DEFAULT_FREESURFER_PATH)
        The FreeSurfer '.sh' config file.

    Returns
    -------
    xhemidir: str
        the symetrized hemispheres.
    spherefile: str
        the registration file to the template.
    """
    # Check input parameters
    if hemi not in ["lh", "rh"]:
        raise ValueError("'{0}' is not a valid hemisphere value which must be "
                         "in ['lh', 'rh']".format(hemi))
    subjfsdir = os.path.join(fsdir, sid)
    for path in (subjfsdir, outdir):
        if not os.path.isdir(path):
            raise ValueError("'{0}' is not a valid directory.".format(path))

    # Symlink input data in destination foler
    wdir = os.path.join(outdir, destname)
    symlinks = []
    dest_template_file = os.path.join(outdir, "fsaverage_sym")
    if not os.path.islink(dest_template_file):
        os.symlink(template_file, dest_template_file)
    symlinks.append(dest_template_file)
    if os.path.isdir(wdir):
        shutil.rmtree(wdir)
    os.mkdir(wdir)
    for basename in os.listdir(subjfsdir):
        if basename == "scripts":
            continue
        path = os.path.join(subjfsdir, basename)
        destpath = os.path.join(wdir, basename)
        symlinks.append(destpath)
        os.symlink(path, destpath)

    # Create the commands
    os.environ["SUBJECTS_DIR"] = outdir
    cmds = []
    sym_template_file = os.path.join(
        wdir, "surf", "{0}.fsaverage_sym.sphere.reg".format(hemi))
    if os.path.isfile(sym_template_file):
        os.remove(sym_template_file)
    cmds += [[
        "surfreg", "--s", destname, "--t", "fsaverage_sym",
        "--{0}".format(hemi)
    ], ["xhemireg", "--s", destname],
             [
                 "surfreg", "--s", destname, "--t", "fsaverage_sym",
                 "--{0}".format(hemi), "--xhemi"
             ]]

    # Execute the FS commands
    for cmd in cmds:
        recon = FSWrapper(cmd, shfile=fsconfig)
        recon()

    # Remove symliks
    for path in symlinks:
        os.unlink(path)

    # Get outputs
    xhemidir = os.path.join(wdir, "xhemi")
    spherefile = os.path.join(subjfsdir, "surf",
                              "{0}.fsaverage_sym.sphere.reg".format(hemi))

    return xhemidir, spherefile
Example #25
0
def mitk_gibbs_tractogram(outdir,
                          subject_id,
                          dwi,
                          bvals,
                          bvecs,
                          nodif_brain=None,
                          nodif_brain_mask=None,
                          subjects_dir=None,
                          sh_order=4,
                          reg_factor=0.006,
                          nb_iterations=5e8,
                          particle_length=0,
                          particle_width=0,
                          particle_weight=0,
                          start_temperature=0.1,
                          end_temperature=0.001,
                          inex_energy_balance=0,
                          min_fiber_length=20,
                          curvature_threshold=45,
                          tempdir=None,
                          fs_sh=DEFAULT_FREESURFER_PATH,
                          fsl_sh=DEFAULT_FSL_PATH):
    """
    Wrapper to the MITK global tractography tool (Gibbs Tracking).

    Parameters
    ----------
    outdir: str
        Directory where to output.
    subject_id: str
        Subject id used with FreeSurfer 'recon-all' command.
    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.
    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'.
    subjects_dir: str or None, default None
        Path to the FreeSurfer subjects directory. Required if the FreeSurfer
        environment variable (i.e. $SUBJECTS_DIR) is not set.
    sh_order: int, default 4
        Qball reconstruction spherical harmonics order.
    reg_factor: float, default
        Qball reconstruction regularization factor..
    nb_iterations: int, default 5E8
        Gibbs tracking number of iterations.
    particle_length: float, default 0
         Gibbs tracking particle length, selected automatically if 0.
    particle_width: float, default 0
        Gibbs tracking particle width, selected automatically if 0.
    particle_weight: float, default 0
        Gibbs tracking particle weight, selected automatically if 0.
    start_temperature: float, default 0.1
        Gibbs tracking start temperature.
    end_temperature: float, default 0.001
        Gibbs tracking end temperature.
    inex_energy_balance: float, default 0
        Gibbs tracking weighting between in/ext energies.
    min_fiber_length: int, default 20
        Minimum fiber length in mm. Fibers that are shorter are discarded.
    curvature_threshold: int, default 45
        Maximum fiber curvature in degrees.
    tempdir: str
        Path to the directory where temporary directories should be written.
        It should be a partition with 5+ GB available.
    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
    -------
    mitk_tractogram: str
        The computed global tractogram in VTK format.
    """
    # -------------------------------------------------------------------------
    # STEP 0 - Check arguments

    # FreeSurfer subjects_dir
    subjects_dir = get_or_check_freesurfer_subjects_dir(subjects_dir)

    # Check input paths
    paths_to_check = [dwi, bvals, bvecs, nodif_brain_mask, fs_sh, fsl_sh]
    for p in [nodif_brain, nodif_brain_mask]:
        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)

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

    # -------------------------------------------------------------------------
    # STEP 1 - Compute DWI to T1 transformation and project the T1
    #          to the diffusion space without resampling.

    # 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_1a = ["dwi2mask", dwi, nodif_brain_mask, "-fslgrad", bvecs, bvals]
        subprocess.check_call(cmd_1a)

    # 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,
                                       b0s=b0s,
                                       mean_b0=mean_b0,
                                       bvals=bvals,
                                       bvecs=bvecs,
                                       nb_threads=1)
        # 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)()

    # Register nodif_brain to FreeSurfer T1
    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 2 - Apply brain mask to DWI before Qball reconstruction
    dwi_brain = os.path.join(outdir, "dwi_brain.nii.gz")
    cmd_4 = ["fslmaths", dwi, "-mas", nodif_brain_mask, dwi_brain]
    FSLWrapper(cmd_4, shfile=fsl_sh)()

    # MITK requires the Nifti to have an .fslgz extension and the bvals/bvecs
    # to have the same name with .bvals/.bvecs extension
    dwi_brain_fslgz = os.path.join(outdir, "dwi_brain.fslgz")
    shutil.copyfile(dwi_brain, dwi_brain_fslgz)
    shutil.copyfile(bvals, "%s.bvals" % dwi_brain_fslgz)
    shutil.copyfile(bvecs, "%s.bvecs" % dwi_brain_fslgz)

    # -------------------------------------------------------------------------
    # STEP 3 - Qball reconstruction
    qball_coefs = os.path.join(outdir, "sphericalHarmonics_CSA_Qball.qbi")
    cmd_5 = [
        "MitkQballReconstruction.sh", "-i", dwi_brain_fslgz, "-o", qball_coefs,
        "-sh",
        "%i" % sh_order, "-r",
        "%f" % reg_factor, "-csa", "--mrtrix"
    ]
    # TODO: create MITK wrapper with LD_LIBRARY_PATH and QT_PLUGIN_PATH
    subprocess.check_call(cmd_5)

    # -------------------------------------------------------------------------
    # STEP 4 - Create white matter probability map with FSL Fast

    # Create directory for temporary files
    fast_tempdir = tempfile.mkdtemp(prefix="FSL_fast_", dir=tempdir)
    base_outpath = os.path.join(fast_tempdir, "brain")
    cmd_6 = ["fast", "-o", base_outpath, t1_brain_to_dif]
    FSLWrapper(cmd_6, shfile=fsl_sh)()

    # Save the white matter probability map
    wm_prob_map = os.path.join(outdir, "wm_prob_map.nii.gz")
    shutil.copyfile(base_outpath + "_pve_2.nii.gz", wm_prob_map)

    # Clean temporary directory
    shutil.rmtree(fast_tempdir)

    # -------------------------------------------------------------------------
    # STEP 5 - Gibbs tracking (global tractography)

    # Create XML parameter file
    root = ElementTree.Element("global_tracking_parameter_file")
    root.set("version", "1.0")
    attributes = {
        "iterations": "%i" % nb_iterations,
        "particle_length": "%f" % particle_length,
        "particle_width": "%f" % particle_width,
        "particle_weight": "%f" % particle_weight,
        "temp_start": "%f" % start_temperature,
        "temp_end": "%f" % end_temperature,
        "inexbalance": "%f" % inex_energy_balance,
        "fiber_length": "%i" % min_fiber_length,
        "curvature_threshold": "%i" % curvature_threshold
    }
    ElementTree.SubElement(root, "parameter_set", attrib=attributes)
    tree = ElementTree.ElementTree(element=root)
    path_xml = os.path.join(outdir, "parameters.gtp")
    tree.write(path_xml)

    # Run tractography
    mitk_tractogram = os.path.join(outdir, "fibers.fib")
    cmd_7 = [
        "MitkGibbsTracking.sh", "-i", qball_coefs, "-p", path_xml, "-m",
        wm_prob_map, "-o", mitk_tractogram, "-s", "MRtrix"
    ]
    subprocess.check_call(cmd_7)

    return mitk_tractogram
Example #26
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
Example #27
0
def tractstats2table(fsdir, outdir, fsconfig=DEFAULT_FREESURFER_PATH):
    """ Generate text/ascii tables of FreeSurfer tracula pathways anisotropy
    and diffusivity summary from 'dpath/<hemi>.<path>/pathstats.overall.txt'
    and 'dpath/<hemi>.<path>/pathstats.byvoxel.txt'.

    This can then be easily imported into a spreadsheet and/or stats program.

    Binding over the FreeSurfer's 'tractstats2table' command.

    Parameters
    ----------
    fsdir: (mandatory)
        The freesurfer working directory with all the subjects.
    outdir: str (mandatory)
        The destination folder.
    fsconfig: str (optional)
        The FreeSurfer configuration batch.

    Return
    ------
    statfiles: list of str
        The FreeSurfer summary files.
    """
    # Check input parameters
    for path in (fsdir, outdir):
        if not os.path.isdir(path):
            raise ValueError("'{0}' is not a valid directory.".format(path))

    # Parameter that will contain the output group statistics
    statfiles = []

    # Fist find all the subjects with a pathway stat file
    pathstatfiles = glob.glob(
        os.path.join(fsdir, "*", "dpath", "*", "pathstats.overall.txt"))

    # Split these files by pathway names
    pathwayfiles = {}
    for path in pathstatfiles:
        pathwayname = path.split(os.sep)[-2]
        if pathwayname not in pathwayfiles:
            pathwayfiles[pathwayname] = []
        pathwayfiles[pathwayname].append(path)

    # Save the FreeSurfer current working directory and set the new one
    fscwd = None
    if "SUBJECTS_DIR" in os.environ:
        fscwd = os.environ["SUBJECTS_DIR"]
    os.environ["SUBJECTS_DIR"] = fsdir

    # Create the output stat directory
    fsoutdir = os.path.join(outdir, "overall_stats")
    if not os.path.isdir(fsoutdir):
        os.mkdir(fsoutdir)

    # Call freesurfer
    for name, files in pathwayfiles.items():

        statfile = os.path.join(fsoutdir, "{0}.csv".format(name))
        statfiles.append(statfile)
        cmd = ["tractstats2table", "--inputs"
               ] + files + ["--overall", "--tablefile", statfile]

        recon = FSWrapper(cmd, shfile=fsconfig)
        recon()

    # Restore the FreeSurfer working directory
    if fscwd is not None:
        os.environ["SUBJECTS_DIR"] = fscwd

    return statfiles
Example #28
0
def conformed_to_native_space(fsdir,
                              regex,
                              outdir,
                              fsconfig=DEFAULT_FREESURFER_PATH):
    """ Return the conformed to native transformation files.

    Create a registration matrix between the conformed space (orig.mgz) and
    the native anatomical (rawavg.mgz).

    Binding over the FreeSurfer's 'tkregister2' command.

    Parameters
    ----------
    fsdir: str (mandatory)
        The FreeSurfer working directory with all the subjects.
    regex: str
        A regular expression used to locate the mri files to be converted
        from the 'fsdir' directory.
    outdir: str
        The destination folder.
    fsconfig: str (optional)
        The FreeSurfer configuration batch.

    Returns
    -------
    trffiles: list of str
        The conformed to native transformation files.
    """
    # Check the input parameters
    for path in (fsdir, outdir):
        if not os.path.isdir(path):
            raise ValueError("'{0}' is not a valid directory.".format(path))

    # Get all the subjects with a 'mri' directory
    mridirs = glob.glob(os.path.join(fsdir, regex))

    # Go through all the subjects with the desired folder
    trffiles = []
    for path_mri in mridirs:

        # Get some information based on the folder path
        subject_id = path_mri.rstrip(os.path.sep).split(os.path.sep)[-2]
        convertdir = os.path.join(outdir, subject_id, "convert")
        if not os.path.isdir(convertdir):
            os.makedirs(convertdir)

        # Check that the two images of interest are present
        rawfile = os.path.join(path_mri, "rawavg.mgz")
        origfile = os.path.join(path_mri, "orig.mgz")
        for path in (rawfile, origfile):
            if not os.path.isfile(path):
                raise ValueError("In folder '{0}' can't find file "
                                 "'{1}'.".format(path_mri, path))

        # Construct the FreeSurfer command
        trffile = os.path.join(convertdir, "register.native.dat")
        trffiles.append(trffile)
        cmd = [
            "tkregister2", "--mov", rawfile, "--targ", origfile, "--reg",
            trffile, "--noedit", "--regheader"
        ]

        # Execute the FreeSurfer command
        recon = FSWrapper(cmd, shfile=fsconfig)
        recon()

    return trffiles
Example #29
0
def recon_all(fsdir,
              anatfile,
              sid,
              reconstruction_stage="all",
              resume=False,
              t2file=None,
              flairfile=None,
              fsconfig=DEFAULT_FREESURFER_PATH):
    """ Performs all the FreeSurfer cortical reconstruction steps.

    Binding around the FreeSurfer's 'recon-all' command.

    Processing stages:

    * Motion Correction and Conform
    * NU (Non-Uniform intensity normalization)
    * Talairach transform computation
    * Intensity Normalization 1
    * Skull Strip
    * EM Register (linear volumetric registration)
    * CA Intensity Normalization
    * CA Non-linear Volumetric Registration
    * Remove Neck
    * LTA with Skull
    * CA Label (Volumetric Labeling, ie Aseg) and Statistics
    * Intensity Normalization 2 (start here for control points)
    * White matter segmentation
    * Edit WM With ASeg
    * Fill (start here for wm edits)
    * Tessellation (begins per-hemisphere operations)
    * Smooth1
    * Inflate1
    * QSphere
    * Automatic Topology Fixer
    * Final Surfs (start here for brain edits for pial surf)
    * Smooth2
    * Inflate2
    * Spherical Mapping
    * Spherical Registration
    * Spherical Registration, Contralateral hemisphere
    * Map average curvature to subject
    * Cortical Parcellation - Desikan_Killiany and Christophe (Labeling)
    * Cortical Parcellation Statistics
    * Cortical Ribbon Mask
    * Cortical Parcellation mapping to Aseg

    Parameters
    ----------
    fsdir: str (mandatory)
        The FreeSurfer working directory with all the subjects.
    anatfile: str (mandatory)
        The input anatomical image to be segmented with FreeSurfer.
    sid: str (mandatory)
        The current subject identifier.
    reconstruction_stage: str (optional, default 'all')
        The FreeSurfer reconstruction stage that will be launched.
    resume: bool (optional, default False)
        If true, try to resume the recon-all. This option is also usefull if
        custom segmentation is used in recon-all.
    t2file: str (optional, default None)
        Specify the path to a T2 image that will be used to improve the pial
        surfaces.
    flairfile: str (optional, default None)
        Specify the path to a FLAIR image that will be used to improve the pial
        surfaces.
    fsconfig: str (optional)
        The FreeSurfer configuration batch.
    Returns
    -------
    subjfsdir: str
        Path to the resulting FreeSurfer segmentation.
    """
    # Check input parameters
    if not os.path.isdir(fsdir):
        raise ValueError("'{0}' FreeSurfer home directory does not "
                         "exists.".format(fsdir))
    if reconstruction_stage not in ("all", "autorecon1", "autorecon2",
                                    "autorecon2-cp", "autorecon2-wm",
                                    "autorecon2-pial", "autorecon3"):
        raise ValueError("Unsupported '{0}' recon-all reconstruction "
                         "stage.".format(reconstruction_stage))

    # Call FreeSurfer segmentation
    cmd = [
        "recon-all", "-{0}".format(reconstruction_stage), "-subjid", sid, "-i",
        anatfile, "-sd", fsdir
    ]
    if t2file is not None:
        cmd.extend(["-T2", t2file, "-T2pial"])
    if flairfile is not None:
        cmd.extend(["-FLAIR", t2file, "-FLAIRpial"])
    if resume:
        cmd[1] = "-make all"
    recon = FSWrapper(cmd, shfile=fsconfig)
    recon()
    subjfsdir = os.path.join(fsdir, sid)

    return subjfsdir
Example #30
0
def resample_cortical_surface(
        fsdir,
        regex,
        outdir,
        destdirname="convert",
        orders=[4, 5, 6, 7],
        surface_name="white",
        fsconfig=DEFAULT_FREESURFER_PATH):
    """ Resamples one cortical surface onto an icosahedron.

    Resample the white or pial FreeSurfer cotical surface using the
    'mri_surf2surf' command. Map also the associated annotation file.

    Can resample at different icosahedron order which specifies the size of the
    icosahedron according to the following table:
    Order  Number of Vertices
    0              12
    1              42
    2             162
    3             642
    4            2562
    5           10242
    6           40962
    7          163842

    Binding over the FreeSurfer's 'mri_surf2surf' command.

    Parameters
    ----------
    fsdir: str (mandatory)
        The freesurfer home directory with all the subjects.
    regex: str (mandatory)
        A regular expression used to locate the surface files to be converted
        from the 'fsdir' directory.
    outdir: str (optional, default None)
        The destination folder.
    destdirname: str (optional, default 'convert')
        The name of the folder where each subject resample cortical surface
        will be saved.
    orders: list of int
        The icosahedron orders.
    surface_name: str (optional, default 'white')
        The surface we want to resample ('white' or 'pial').
    fsconfig: str (optional)
        The freesurfer configuration batch.

    Returns
    -------
    resamplefiles: list of str
        The resample surfaces.
    annotfiles: list of str
        The resample annotations.
    """
    # Check input parameters
    for path in (fsdir, outdir):
        if not os.path.isdir(path):
            raise ValueError("'{0}' is not a valid directory.".format(path))
    if surface_name not in ["white", "pial", "graymid"]:
        raise ValueError("'{0}' is not a valid surface value which must be in "
                         "['white', 'pial']".format(surface_name))
    norders = numpy.asarray(orders)
    if norders.min() < 0 or norders.max() > 7:
        raise ValueError("'At least one value in {0} is not in 0-7 "
                         "range.".format(orders))

    # Get all the subjects with the specified surface
    surfaces = glob.glob(os.path.join(fsdir, regex))

    # Go through all the subjects with the desired surface
    resamplefiles = []
    annotfiles = []
    for surf in surfaces:

        # Get some information based on the surface path
        subject_id = surf.split("/")[-3]
        hemi = os.path.basename(surf).split(".")[0]
        convertdir = os.path.join(outdir, subject_id, destdirname)
        if not os.path.isdir(convertdir):
            os.makedirs(convertdir)

        # Go through all specified orders
        for level in orders:

            # Construct the FS surface map command
            convertfile = os.path.join(convertdir, "{0}.{1}.{2}".format(
                hemi, surface_name, level))
            resamplefiles.append(convertfile)
            recon = FSWrapper([], shfile=fsconfig)
            if version.parse(recon.version or "") >= version.parse("6.0.0"):
                origfile = os.path.join(fsdir, subject_id, "mri", "orig.mgz")
                cmd = ["mri_surf2surf", "--sval-xyz", surface_name,
                       "--srcsubject", subject_id, "--trgsubject", "ico",
                       "--trgicoorder", str(level), "--tval", convertfile,
                       "--tval-xyz",  origfile, "--hemi", hemi, "--sd", fsdir]
            else:
                cmd = ["mri_surf2surf", "--sval-xyz", surface_name,
                       "--srcsubject", subject_id, "--trgsubject", "ico",
                       "--trgicoorder", str(level), "--tval", convertfile,
                       "--tval-xyz", "--hemi", hemi, "--sd", fsdir]

            # Execute the FS command
            recon.cmd = cmd
            recon()

            # Construct the FS label map command
            annotfile = os.path.join(convertdir, "{0}.aparc.annot.{1}".format(
                hemi, level))
            annotfiles.append(annotfile)
            if not os.path.isfile(annotfile):
                svalannot = os.path.join(fsdir, subject_id, "label",
                                         "{0}.aparc.annot".format(hemi))
                cmd = ["mri_surf2surf", "--srcsubject", subject_id,
                       "--trgsubject", "ico", "--trgicoorder", str(level),
                       "--hemi", hemi, "--sval-annot", svalannot,
                       "--tval", annotfile, "--sd", fsdir]

                # Execute the FS command
                recon = FSWrapper(cmd, shfile=fsconfig)
                recon()

    # Remove duplicate annotation files
    annotfiles = list(set(annotfiles))

    return sorted(resamplefiles), sorted(annotfiles)
Example #31
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