예제 #1
0
def qc_connectogram(outdir,
                    tracto_mask,
                    fiber_density,
                    txt_matrix,
                    txt_matrix_normalized,
                    txt_labels,
                    subdir="qc"):
    """
    Create 3 snapshots:
        - fiber density
        - raw connetivity matrix
        - normalized connectivity matrix

    The snap shots are saved in <outdir>/<subdir>/<snap shot>. By default
    <subdir> is "qc". To write in outdir directly, set subdir to anything that
    evaluates to False (empty string or None).
    Directories are automatically created if they don't exist.

    'fiber_density' and 'txt_matrix' are outputs of probtrackx2.
    """
    # If requested use a subdirectory in outdir
    if subdir:
        outdir = os.path.join(outdir, subdir)

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

    nb_slices_in_z = nibabel.load(tracto_mask).get_shape()[2]

    fiber_density_snapshot = os.path.join(outdir, "fiber_density_map.pdf")
    plot_image(tracto_mask,
               overlay_file=fiber_density,
               snap_file=fiber_density_snapshot,
               name="fiber density map",
               overlay_cmap="cold_hot",
               cut_coords=nb_slices_in_z - 2)

    # connectogram snapshot
    connectogram_snapshot = plot_connectogram(outdir,
                                              txt_matrix,
                                              labels=txt_labels,
                                              transform=np.log1p)

    # Normalized by surface target area connectogram snapshot
    connectogram_norm_snapshot = \
        plot_connectogram(outdir,
                          txt_matrix_normalized,
                          labels=txt_labels,
                          transform=np.log1p,
                          prefix="connectogram_normalized")

    return connectogram_snapshot, connectogram_norm_snapshot
예제 #2
0
def qc_connectogram(outdir,
                    tracto_mask,
                    proba_matrix,
                    network_matrix,
                    seed_masks,
                    subdir="qc"):
    """
    Function meant to help quality check (qc) the tractography and the
    connectogram computed by the probtrackx2_probabilist_tractography function.
    It creates snap shots to visualize the connectogram and the fiber density
    in the diffusion space.

    The snap shots are saved in <outdir>/<subdir>/<snap shot>. By default
    <subdir> is "qc". To write in outdir directly, set subdir to anything that
    evaluates to False (empty string or None).
    Directories are automatically created if they don't exist.

    <unit>
        <input name="outdir"         type="Directory"           />
        <input name="tracto_mask"    type="File"                />
        <input name="proba_matrix"   type="File"                />
        <input name="network_matrix" type="File"                />
        <input name="seed_masks"     type="List" content="File" />
        <input name="subdir"         type="Str"                 />

        <output name="qc_dir"        type="Directory"           />
    </unit>
    """
    # If requested use a subdirectory in outdir
    if subdir:
        outdir = os.path.join(outdir, subdir)

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

    nb_slices_in_z = nibabel.load(tracto_mask).get_shape()[2]

    fiber_density_png = os.path.join(outdir, "fiber_density_map.png")
    plot_image(tracto_mask,
               overlay_file = proba_matrix,
               snap_file    = fiber_density_png,
               name         = "fiber density map",
               overlay_cmap = "cold_hot",
               cut_coords   = nb_slices_in_z/2)

    # Second connectogram snap shot as PNG with cortex region labels
    plot_connectogram(outdir, network_matrix, seed_masks, transform=np.log1p)

    # Return something for Capsul
    qc_dir = outdir
    return qc_dir
예제 #3
0
def qc_tracto_masks(outdir,
                    tracto_mask,
                    seed_mask,
                    stop_mask,
                    txt_roi_masks,
                    subdir="qc"):
    """
    Function meant to help quality check (qc) the masks created by the
    create_masks_for_tracto_seeding_*() functions.
    It creates snap shots to visualize the quality of the registration
    of the tractography masks in the diffusion space.

    The snap shots are saved in <outdir>/<subdir>/<snap shot>. By default
    <subdir> is "qc". To write in outdir directly, set subdir to anything that
    evaluates to False (empty string or None).
    Directories are automatically created if they don't exist.
    """
    # If requested use a subdirectory in outdir
    if subdir:
        outdir = os.path.join(outdir, subdir)

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

    nb_slices_in_z = nibabel.load(tracto_mask).get_shape()[2]

    # PDF snapshot
    basename = "tracto_mask_and_stop_mask"
    pdf_snapshot_1 = os.path.join(outdir, "%s.pdf" % basename)
    plot_image(tracto_mask,
               overlay_file=stop_mask,
               snap_file=pdf_snapshot_1,
               name=basename,
               cut_coords=nb_slices_in_z - 2)

    # PDF snapshot
    basename = "tracto_mask_and_seed_mask"
    pdf_snapshot_2 = os.path.join(outdir, "%s.pdf" % basename)
    plot_image(tracto_mask,
               overlay_file=seed_mask,
               snap_file=pdf_snapshot_2,
               name=basename,
               cut_coords=nb_slices_in_z - 2)

    return outdir
예제 #4
0
if not os.path.isfile(b0_file):
    extract_image(
        args.diffusion_file,
        index=b0_index,
        out_file=b0_file)

# Get the qc output directory
if args.graphics:
    qcdir = os.path.join(subjdir, "qc")
    if not os.path.isdir(qcdir):
        os.makedirs(qcdir)

# create a pdf snap of the b0 image
if args.graphics:
    snap_file = os.path.join(qcdir, "nodif.pdf")
    plot_image(b0_file, snap_file=snap_file, name="nodif")

# generate a brain mask on the corrected b0 data
b0_brain_file = os.path.join(subjdir, "nodif_brain")
bet_files = glob.glob(b0_brain_file + "*")
if len(bet_files) == 0:
    (output, mask_file, mesh_file, outline_file,
     inskull_mask_file, inskull_mesh_file,
     outskull_mask_file, outskull_mesh_file, outskin_mask_file,
     outskin_mesh_file, skull_mask_file) = bet2(
        b0_file,
        b0_brain_file,
        m=True,
        f=0.25,
        shfile=args.fslconfig)
else:
예제 #5
0
def qc(t1files, wmfiles, asegfiles, whitefiles, pialfiles, annotfiles,
       actor_ang=[0., 0., 0.], output_directory=None,
       fsconfig="/i2bm/local/freesurfer/SetUpFreeSurfer.sh"):
    """ Compute some quality check plots on the converted FrreSurfer
    outputs.

    The subjecy id in the input files must appear in the -3 position:
        xxx/subject_id/convert/t1.nii.gz

    Steps:

    * t1-images overlays
    * 3d surface segmentation snaps
    * t1-surfaces overlays

    actor_ang: float (optional, default 0)
        the actor rotation in the z direction.

    <unit>
        <input name="t1files" type="List_File" description="The
            t1 subject files."/>
        <input name="wmfiles" type="List_File" description="The
            white matter subject files."/>
        <input name="asegfiles" type="List_File" description="The
            subcortical segmentation subject files."/>
        <input name="output_directory" type="Directory" description="The
            conversion destination folder."/>
        <input name="whitefiles" type="List_File" description="The subject
            cortex surfaces."/>
        <input name="pialfiles" type="List_File" description="The subject pial
            surfaces."/>
        <input name="annotfiles" type="List_File" description="The pial/white
            surface annotations."/>
        <input name="actor_ang" type="List_Float" description="The actor x, y,
            z position (in degrees)."/>
        <input name="fsconfig" type="File" description="The freesurfer
            configuration batch."/>
        <output name="qcfiles" type="List_File" description="The quality check
            snaps."/>
    </unit>
    """
    import clindmri.plot.pvtk as pvtk
    from clindmri.plot.slicer import plot_image

    # Create a t1 subject map
    t1map = {}
    for fname in t1files:
        subject_id = fname.split("/")[-3]
        if subject_id in t1map:
            raise Exception("Can't map two t1 for subject '{0}'"
                            ".".format(subject_id))
        t1map[subject_id] = fname

    # Create the output list that will contain all the qc files
    qcfiles = []

    # Construct the t1-surfaces overlays and the 3d surface segmentation snaps
    ren = pvtk.ren()
    for name, files in [("white", whitefiles), ("pial", pialfiles)]:
        for fname in files:

            # Get the t1 reference image
            subject_id = fname.split("/")[-3]
            t1file = t1map[subject_id]
            t1_image = nibabel.load(t1file)

            # Get the qc output directory
            qcdir = os.path.join(os.path.dirname(fname), "qc")
            qcname = os.path.basename(fname)
            if not os.path.isdir(qcdir):
                os.makedirs(qcdir)

            # Get the triangular mesh
            basename = os.path.basename(fname).replace(
                name, "aparc.annot").replace(".native", "")
            annotfile = os.path.join(os.path.dirname(fname), basename)
            if annotfile not in annotfiles:
                raise ValueError(
                    "Annotation file '{0}' can't be found.".format(annotfile))
            surface = TriSurface.load(fname, annotfile=annotfile)

            # Construct the surfaces binarized volume
            binarizedfile = os.path.join(qcdir, qcname + ".nii.gz")
            overlay = numpy.zeros(t1_image.shape, dtype=numpy.uint)
            indices = numpy.round(surface.vertices).astype(int).T
            indices[0, numpy.where(indices[0] >= t1_image.shape[0])] = 0
            indices[1, numpy.where(indices[1] >= t1_image.shape[1])] = 0
            indices[2, numpy.where(indices[2] >= t1_image.shape[2])] = 0
            overlay[indices.tolist()] = 1
            overlay_image = nibabel.Nifti1Image(overlay, t1_image.get_affine())
            nibabel.save(overlay_image, binarizedfile)
            snap_file = os.path.join(qcdir, qcname + ".png")
            plot_image(t1file, overlay_file=binarizedfile, snap_file=snap_file,
                       name=qcname, overlay_cmap="cold_hot")
            qcfiles.append(snap_file)

            # Create a vtk surface actor of the cortex surface with the
            # associated labels
            ctab = [item["color"] for _, item in surface.metadata.items()]
            actor = pvtk.surface(
                surface.vertices, surface.triangles, surface.labels, ctab)
            actor.RotateX(actor_ang[0])
            actor.RotateY(actor_ang[1])
            actor.RotateZ(actor_ang[2])

            # Create a 3d surface segmentation snap
            pvtk.add(ren, actor)
            snaps = pvtk.record(ren, qcdir, qcname, n_frames=36,
                                az_ang=10, animate=True, delay=50)
            qcfiles.append(snaps[0])
            snaps = pvtk.record(ren, qcdir, qcname + ".3d", n_frames=1)
            qcfiles.append(snaps[0])
            pvtk.clear(ren)

    # Get the FreeSurfer lookup table
    fs_lut_names, fs_lut_colors = parse_fs_lut(os.path.join(
        os.path.dirname(fsconfig), "FreeSurferColorLUT.txt"))
    cmap = []
    nb_values = numpy.asarray(fs_lut_colors.keys()).max()
    cmap = numpy.zeros((nb_values, 4), dtype=numpy.single)
    for key, color in fs_lut_colors.items():
        if key > 0:
            cmap[key - 1, :3] = color
    cmap[:, 3] = 200.
    cmap /= 255.

    # Compute t1-images overlays
    for name, files in [("aseg", asegfiles), ("wm", wmfiles)]:
        for fname in files:

            # Get the t1 reference image
            subject_id = fname.split("/")[-3]
            t1file = t1map[subject_id]
            t1_image = nibabel.load(t1file)

            # Get the qc output directory
            qcdir = os.path.join(os.path.dirname(fname), "qc")
            if not os.path.isdir(qcdir):
                os.makedirs(qcdir)

            # Troncate the color map based on the label max
            array = nibabel.load(fname).get_data()
            order = sorted(set(array.flatten()))
            ccmap = cmap[order[1]: order[-1] + 1]

            # Overlay the current image with the t1 image
            qcname = "t1-{0}".format(name)
            snap_file = os.path.join(qcdir, qcname + ".png")
            plot_image(t1file, overlay_file=fname, snap_file=snap_file,
                       name=qcname, overlay_cmap=ccmap, cut_coords=(0, 0, 0))
            qcfiles.append(snap_file)

    return qcfiles
예제 #6
0
def bet2_nodif_brain(outdir, dwi, bval, subdir="bet2_nodif_brain", qc=True):
    """
    Extract brain from b0 volume, i.e. in DW data for a volume where bvalue=0.

    Parameters
    ----------
    outdir: Str, path to directory where to output.
    dwi:    Str, path to DW data in which at least one volume was acquired
            with bvalue=0.
    bval:   Str, path to .bval file associated to the DW data.
    subdir: Str, if you want the result files to be written in a subdirectory,
            specify the name, by default "bet2_nodif_brain".

    Return
    ------
    nodif_brain:      Str, path to the brain only volume.
    nodif_brain_mask: Str, path to the brain-only binary mask.

    <unit>
        <output name="nodif_brain"      type="File"      />
        <output name="nodif_brain_mask" type="File"      />

        <input name="outdir"            type="Directory" />
        <input name="dwi"               type="File"      />
        <input name="bval"              type="File"      />
        <input name="subdir"            type="Str"       />
        <input name="qc"                type="Bool"      />
    </unit>
    """
    if subdir:
        outdir = os.path.join(outdir, subdir)

    # Create outdir if it does not exist
    if not os.path.isdir(outdir):
        os.makedirs(outdir)

    # Get a/the volume with bvalue=0.
    nodif_volume = extract_nodif_volume(outdir, dwi, bval)

    # Set output path with desired prefix name
    output_prefix = os.path.join(outdir, "nodif_brain")

    # Run FSL bet2
    bet2(nodif_volume, output_prefix, f=0.25, m=True)

    # Output paths
    nodif_brain      = output_prefix + ".nii.gz"
    nodif_brain_mask = output_prefix + "_mask.nii.gz"

    # Check existence of resulting files
    if not (os.path.isfile(nodif_brain) and os.path.isfile(nodif_brain_mask)):
        raise Exception("bet2: output file(s) missing: %s or/and %s ."
                        % (nodif_brain, nodif_brain_mask))

    # If Quality Check, generate a PNG snapshot
    if qc:
        # Snap shot of brain-only contour on T2 image
        brain_contour_png = os.path.join(outdir, "nodif_brain_mask.png")
        plot_image(nodif_volume, contour_file=nodif_brain_mask,
                   snap_file=brain_contour_png, name="nodif_brain_mask")

    return nodif_brain, nodif_brain_mask
예제 #7
0
def qc_tractography_masks(outdir,
                          nodif_brain,
                          tracto_mask,
                          seed_masks,
                          subject_id,
                          cortical_atlas  = "Desikan",
                          fs_subjects_dir = None,
                          subdir          = "qc"):
    """
    Function meant to help quality check (qc) the masks created by the
    create_masks_for_tractography function.
    It creates snap shots to visualize the quality of the registration
    of the tractography mask (white matter) and seed masks in the diffusion space.

    The snap shots are saved in <outdir>/<subdir>/<snap shot>. By default
    <subdir> is "qc". To write in outdir directly, set subdir to anything that
    evaluates to False (empty string or None).
    Directories are automatically created if they don't exist.

    <unit>
        <input name="outdir"          type="Directory"           />
        <input name="nodif_brain"     type="File"                />
        <input name="tracto_mask"     type="File"                />
        <input name="seed_masks"      type="List" content="File" />
        <input name="subject_id"      type="Str"                 />
        <input name="cortical_atlas"  type="Str"                 />
        <input name="fs_subjects_dir" type="Directory"           />
        <input name="subdir"          type="Str"                 />

        <output name="qc_dir"         type="Directory"           />
    </unit>
    """

    if fs_subjects_dir is None:
        if "SUBJECTS_DIR" in os.environ:
            fs_subjects_dir = os.environ["SUBJECTS_DIR"]
        else:
            raise ValueError("Missing <SUBJECTS_DIR>: set the $SUBJECTS_DIR "
                             "environment variable for Freesurfer or pass it "
                             "as an argument.")

    # If requested use a subdirectory in outdir
    if subdir:
        outdir = os.path.join(outdir, subdir)

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

    nb_slices_in_z = nibabel.load(nodif_brain).get_shape()[2]

    # Snap shot for registration and white matter mask checking
    wm_mask_to_dif_png = os.path.join(outdir, "tracto_mask.png")
    plot_image(nodif_brain,
               overlay_file = tracto_mask,
               snap_file    = wm_mask_to_dif_png,
               name         = "White matter mask in diffusion",
               cut_coords   = nb_slices_in_z/2)

    # Gif of white matter mask in diffusion
    animate_image(nodif_brain, overlay_file=tracto_mask, outdir=outdir,
                  name="tracto_mask", cut_coords=nb_slices_in_z/2,
                  clean=True)

    # Return something for Capsul
    qc_dir = outdir
    return qc_dir
예제 #8
0
def qc_dif2anat_registration(outdir,
                             nodif_brain,
                             dif2anat_dat,
                             subject_id,
                             fs_subjects_dir = None,
                             subdir          = "qc"):
    """
    Function meant to help quality check (qc) the registration between
    the diffusion and the anatomy (T1 from Freesurfer recon-all).
    It creates snap shots to help with the visualization:
        - T1 brain registered in diffusion + contour of nodif brain volume.

    The snap shot is saved in <outdir>/<subdir>/"t1_to_diff.pdf". By default
    <subdir> is "qc". To write in outdir directly, set subdir to anything that
    evaluates to False (empty string or None).
    Directories are automatically created if they don't exist.

    <unit>
        <input name="outdir"          type="Directory" />
        <input name="nodif_brain"     type="File"      />
        <input name="dif2anat_dat"    type="File"      />
        <input name="subject_id"      type="Str"       />
        <input name="fs_subjects_dir" type="Directory" />
        <input name="subdir"          type="Str"       />

        <output name="qc_dir"         type="Directory" />
    </unit>
    """

    if fs_subjects_dir is None:
        if "SUBJECTS_DIR" in os.environ:
            fs_subjects_dir = os.environ["SUBJECTS_DIR"]
        else:
            raise ValueError("Missing <SUBJECTS_DIR>: set the $SUBJECTS_DIR "
                             "environment variable for Freesurfer or pass it "
                             "as an argument.")

    # If requested use a subdirectory in outdir
    if subdir:
        outdir = os.path.join(outdir, subdir)

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

    # Paths
    t1        = os.path.join(fs_subjects_dir, subject_id, "mri/brainmask.mgz")
    t1_to_dif = os.path.join(outdir, "t1_to_dif.nii.gz")

    # Project T1 in dif
    cmd = ["mri_vol2vol", "--mov", nodif_brain, "--targ", t1,
           "--inv", "--interp", "nearest", "--o", t1_to_dif,
           "--reg", dif2anat_dat, "--no-save-reg"]
    fsprocess = FSWrapper(cmd)
    fsprocess()  # Run
    if fsprocess.exitcode != 0:
        raise FreeSurferRuntimeError(cmd[0], " ".join(cmd[1:]))

    nb_slices_in_z = nibabel.load(nodif_brain).get_shape()[2]

    # Gif of the T1 in diffusion space with diffusion edges
    animate_image(t1_to_dif, edge_file=nodif_brain, outdir=outdir,
                  name="t1_to_dif", cut_coords=nb_slices_in_z/2, clean=True)

    # First png: T1 registered in diffusion with nodif edges
    t1_with_nodif_edges_png = os.path.join(outdir, "t1_with_nodif_edges.png")
    plot_image(t1_to_dif,
               edge_file  = nodif_brain,
               snap_file  = t1_with_nodif_edges_png,
               name       = "T1 in diffusion + edges of nodif",
               cut_coords = nb_slices_in_z/2)

    # Second png: nodif with edges of T1 registered in diffusion
    nodif_with_t1_edges_png = os.path.join(outdir, "nodif_with_t1_edges.png")
    plot_image(nodif_brain,
               edge_file  = t1_to_dif,
               snap_file  = nodif_with_t1_edges_png,
               name       = "nodif + edges of registered T1",
               cut_coords = nb_slices_in_z/2)

#    # Third png: nodif with WM edges (from T1 segmentation)
#    nodif_with_wm_edges_png = os.path.join(outdir, "nodif_with_wm_edges.png")
#    plot_image(nodif_brain,
#               edge_file  = t1_to_dif,
#               snap_file  = nodif_with_t1_edges_png,
#               name       = "nodif + edges of registered T1",
#               cut_coords = nb_slices_in_z/2)

    # Return something for Capsul
    qc_dir = outdir

    return qc_dir
예제 #9
0
For probabalistic tractography, we need to generate a mask within which we
constrain tractography. We first select the first non-diffusion weighted
volume of the DTI sequence and then use 'bet2' on this image with a fractional
intensity threshold of 0.25 (this is generally a robust threshold to
remove unwanted tissue from a non-diffusion weighted image) and the 'm' option
that creates a binary 'nodif_brain_mask' image.
"""

bvals = numpy.loadtxt(bvals_file).tolist()
b0_index = bvals.index(0)
b0_file = os.path.join(outdir, "nodif.nii.gz")
if not os.path.isfile(b0_file):
    extract_image(diffusion_file, index=b0_index, out_file=b0_file)

snap_file = os.path.join(qcdir, "nodif.pdf")
plot_image(b0_file, snap_file=snap_file, name="nodif")

b0_brain_file = os.path.join(outdir, "nodif_brain")
bet_files = glob.glob(b0_brain_file + "*")
if len(bet_files) == 0:
    (output, mask_file, mesh_file, outline_file, inskull_mask_file,
     inskull_mesh_file, outskull_mask_file, outskull_mesh_file,
     outskin_mask_file, outskin_mesh_file,
     skull_mask_file) = bet2(b0_file, b0_brain_file, m=True, f=0.25)
else:
    mask_file = sorted(bet_files)[0]
    if not os.path.isfile(mask_file):
        raise IOError("FileDoesNotExist: '{0}'.".format(mask_file))

snap_file = os.path.join(qcdir, "bet.pdf")
plot_image(b0_file, contour_file=mask_file, snap_file=snap_file, name="bet")
intensity threshold of 0.25 (this is generally a robust threshold to
remove unwanted tissue from a non-diffusion weighted image) and the 'm' option
that creates a binary 'nodif_brain_mask' image.
"""

bvals = numpy.loadtxt(bvals_file).tolist()
b0_index = bvals.index(0)
b0_file = os.path.join(outdir, "nodif.nii.gz")
if not os.path.isfile(b0_file):
    extract_image(
        diffusion_file,
        index=b0_index,
        out_file=b0_file)

snap_file = os.path.join(qcdir, "nodif.pdf")
plot_image(b0_file, snap_file=snap_file, name="nodif")


b0_brain_file = os.path.join(outdir, "nodif_brain")
bet_files = glob.glob(b0_brain_file + "*")
if len(bet_files) == 0:
    (output, mask_file, mesh_file, outline_file,
     inskull_mask_file, inskull_mesh_file,
     outskull_mask_file, outskull_mesh_file, outskin_mask_file,
     outskin_mesh_file, skull_mask_file) = bet2(
        b0_file,
        b0_brain_file,
        m=True,
        f=0.25)
else:
    mask_file = sorted(bet_files)[0]
def brainsuite_susceptibility_correction(outdir,
                                         dwi,
                                         bval,
                                         bvec,
                                         subject_id,
                                         phase_enc_dir,
                                         subjects_dir=None,
                                         qc_dir=None,
                                         bdp_nthread=4):
    """
    Assuming the beginning of the preprocessing was done with Connectomist
    up to Eddy current and motion correction, we now want to make susceptbility
    distortion correction using Brainsuite.

    Parameters
    ----------
    outdir: str
        Path to directory where to output.
    dwi: str
        Path to the preprocessed diffusion-weighted Nifti.
    bval: str
        Path to the bval file associated to the Nifti.
    bvec: str
        Path to the bvec file associated to the Nifti.
    subject_id: str
        Subject identifier used in Freesurfer.
    phase_enc_dir: str
        In plane phase encoding direction, "y", "y-", "x" or "x-".
    subjects_dir: str, default None
        If the Freesurfer $SUBJECTS_DIR environment variable is not set, or to
        bypass it, pass the path.
    qc_dir: str, default None
        Path to directory where to output snapshots for QC. By default in
        outdir.
    bdp_nthread: int, default 4
        Number of threads for bdp (see bdp.sh --thread flag)
    """

    # Freesurfer 'subjects_dir' has to be passed or set as environment variable
    subjects_dir = get_or_check_freesurfer_subjects_dir(subjects_dir)

    # Check in plane phase encoding direction
    # The sign of the phase encoding direction has no impact in this strategy
    # e.g. for "+y" or "-y" use "y"
    possible_phase_enc_dirs = {"y", "x"}
    if phase_enc_dir not in possible_phase_enc_dirs:
        raise ValueError(
            "Bad argument 'phase_enc_dir': {}, should be in {}.".format(
                phase_enc_dir, possible_phase_enc_dirs))

    # Set and check path to T1 brain-only volume from Freesurfer (mgz format)
    t1_brain_mgz = os.path.join(subjects_dir, subject_id, "mri/brain.mgz")
    if not os.path.isfile(t1_brain_mgz):
        raise Exception("Missing file: {}".format(t1_brain_mgz))

    # Convert Freesurfer T1 to Nifti with reorientation to RAS (to be in the
    # same orientation as the diffusion data)
    t1_brain_RAS_nii = os.path.join(outdir, "t1_brain.nii.gz")
    cmd = [
        "mri_convert", t1_brain_mgz, t1_brain_RAS_nii, "--out_orientation",
        "RAS"
    ]
    run_freesurfer_cmd(cmd, subjects_dir=subjects_dir)

    # Run bfc (bias correction: required by BrainSuite)
    t1_bfc = os.path.join(outdir, "t1_brain.bfc.nii.gz")
    cmd = ["bfc", "-i", t1_brain_RAS_nii, "-o", t1_bfc]
    subprocess.check_call(cmd)

    # Extract brain from the nodif volume with FSL bet2
    nodif_brain = os.path.join(outdir, "nodif_brain.nii.gz")
    bet2(dwi, nodif_brain, f=0.25, m=False)

    # Run bdp.sh: registration + diffusion model
    cmd = [
        "bdp.sh", t1_bfc, "--nii", dwi, "--bval", bval, "--bvec", bvec,
        "--dwi-mask", nodif_brain,
        "--dir=%s" % phase_enc_dir,
        "--threads=%i" % bdp_nthread
    ]
    subprocess.check_call(cmd)

    # Path to files of interest, created by BrainSuite bdp.sh
    dwi_wo_susceptibility = os.path.join(outdir,
                                         "t1_brain.dwi.RAS.correct.nii.gz")

    ###############
    # Quality check: create snapshots to visually assert the registration
    # quality

    if qc_dir is None:
        qc_dir = outdir

    # The snapshots won't contain all slices, half of them
    nb_slices_in_z = nibabel.load(nodif_brain).get_shape()[2]

    # Path to registered T1
    t1_to_dif = os.path.join(outdir, "t1_brain.D_coord.nii.gz")

    # First png: T1 registered in diffusion with nodif edges
    t1_with_nodif_edges_png = os.path.join(qc_dir, "t1_with_nodif_edges.png")
    plot_image(t1_to_dif,
               edge_file=nodif_brain,
               snap_file=t1_with_nodif_edges_png,
               name="T1 in diffusion + edges of nodif",
               cut_coords=nb_slices_in_z - 2)

    # Second png: nodif with edges of T1 registered in diffusion
    nodif_with_t1_edges_png = os.path.join(qc_dir, "nodif_with_t1_edges.png")
    plot_image(nodif_brain,
               edge_file=t1_to_dif,
               snap_file=nodif_with_t1_edges_png,
               name="nodif + edges of registered T1",
               cut_coords=nb_slices_in_z - 2)

    return dwi_wo_susceptibility, bval, bvec
예제 #12
0
bvals = numpy.loadtxt(args.bvals_file).tolist()
b0_index = bvals.index(0)
b0_file = os.path.join(subjdir, "nodif.nii.gz")
if not os.path.isfile(b0_file):
    extract_image(args.diffusion_file, index=b0_index, out_file=b0_file)

# Get the qc output directory
if args.graphics:
    qcdir = os.path.join(subjdir, "qc")
    if not os.path.isdir(qcdir):
        os.makedirs(qcdir)

# create a pdf snap of the b0 image
if args.graphics:
    snap_file = os.path.join(qcdir, "nodif.pdf")
    plot_image(b0_file, snap_file=snap_file, name="nodif")

# generate a brain mask on the corrected b0 data
b0_brain_file = os.path.join(subjdir, "nodif_brain")
bet_files = glob.glob(b0_brain_file + "*")
if len(bet_files) == 0:
    (output, mask_file, mesh_file, outline_file, inskull_mask_file,
     inskull_mesh_file, outskull_mask_file, outskull_mesh_file,
     outskin_mask_file, outskin_mesh_file,
     skull_mask_file) = bet2(b0_file,
                             b0_brain_file,
                             m=True,
                             f=args.thresh,
                             shfile=args.fslconfig)
else:
    mask_file = sorted(bet_files)[0]
def brainsuite_susceptibility_correction(outdir,
                                         dwi,
                                         bval,
                                         bvec,
                                         subject_id,
                                         fs_subjects_dir = None,
                                         qc_dir          = None,
                                         bdp_nthread     = 8):
    """
    Assuming the beginning of the preprocessing was done with Connectomist
    up to Eddy current and motion correction, we now want to make susceptbility
    distortion correction using Brainsuite.

    Parameters
    ----------
    outdir:          Str, path to directory where to output.
    dwi
    bval
    bvec
    subject_id:      Str, subject identifier used in Freesurfer.
    fs_subjects_dir: If the Freesurfer $SUBJECTS_DIR environment variable is
                     not set, or to bypass it, pass the path.
    qc_dir:          Str, path to directory where to output snapshots for QC.
    bdp_nthread:     Int, nb of threads for bdp (see bdp.sh --thread flag)
    """

    # If Freesurfer SUBJECTS_DIR is not passed, it should be set as environment variable
    if fs_subjects_dir is None:
        if "SUBJECTS_DIR" in os.environ:
            fs_subjects_dir = os.environ["SUBJECTS_DIR"]
        else:
            raise ValueError("Missing <SUBJECTS_DIR>: set the $SUBJECTS_DIR "
                             "environment variable for Freesurfer or pass it "
                             "as an argument.")

    # Set and check path to T1 brain-only volume from Freesurfer (mgz format)
    t1_brain_mgz = os.path.join(fs_subjects_dir, subject_id, "mri/brain.mgz")
    if not os.path.isfile(t1_brain_mgz):
        raise Exception("Missing file: {}".format(t1_brain_mgz))

    # Convert Freesurfer T1 to Nifti with reorientation to RAS (to be in the
    # same orientation as the diffusion data)
    t1_brain_RAS_nii = os.path.join(outdir, "t1_brain.nii.gz")
    cmd = ["mri_convert", t1_brain_mgz, t1_brain_RAS_nii,
           "--out_orientation", "RAS"]
    fsprocess = FSWrapper(cmd)
    fsprocess()  # Run
    if fsprocess.exitcode != 0:
        raise FreeSurferRuntimeError(cmd[0], " ".join(cmd[1:]))

    # Run bfc (bias correction: required by BrainSuite)
    t1_bfc = os.path.join(outdir, "t1_brain.bfc.nii.gz")
    cmd    = ["bfc", "-i", t1_brain_RAS_nii, "-o", t1_bfc]
    subprocess.check_call(cmd)

    # Extract brain from the nodif volume with FSL bet2
    nodif_brain = os.path.join(outdir, "nodif_brain.nii.gz")
    bet2(dwi, nodif_brain, f=0.25, m=False)

    # Run bdp.sh: registration + diffusion model
    cmd = ["bdp.sh", t1_bfc, "--nii", dwi, "--bval", bval, "--bvec", bvec,
           "--dwi-mask", nodif_brain, "--threads=%i" % bdp_nthread]
    subprocess.check_call(cmd)

    # Path to files of interest, created by BrainSuite bdp.sh
    dwi_wo_susceptibility = os.path.join(outdir, "t1_brain.dwi.RAS.correct.nii.gz")

    ###############
    # Quality check: create snapshots to visually assert the registration quality

    if qc_dir is None:
        qc_dir = outdir

    # The snapshots won't contain all slices, half of them
    nb_slices_in_z = nibabel.load(nodif_brain).get_shape()[2]

    # Path to registered T1
    t1_to_dif = os.path.join(outdir, "t1_brain.D_coord.nii.gz")

    # First png: T1 registered in diffusion with nodif edges
    t1_with_nodif_edges_png = os.path.join(qc_dir, "t1_with_nodif_edges.png")
    plot_image(t1_to_dif,
               edge_file  = nodif_brain,
               snap_file  = t1_with_nodif_edges_png,
               name       = "T1 in diffusion + edges of nodif",
               cut_coords = nb_slices_in_z/2)

    # Second png: nodif with edges of T1 registered in diffusion
    nodif_with_t1_edges_png = os.path.join(qc_dir, "nodif_with_t1_edges.png")
    plot_image(nodif_brain,
               edge_file  = t1_to_dif,
               snap_file  = nodif_with_t1_edges_png,
               name       = "nodif + edges of registered T1",
               cut_coords = nb_slices_in_z/2)

    return dwi_wo_susceptibility, bval, bvec
예제 #14
0
def qc_dif2anat_registration(outdir,
                             nodif_brain,
                             dif2anat_dat,
                             subject_id,
                             subjects_dir=None,
                             subdir="qc"):
    """
    Function meant to help quality check (qc) the registration between
    the diffusion and the anatomy (T1 from Freesurfer recon-all).
    It creates snap shots:
        - T1 brain registered in diffusion + contour of nodif brain volume.

    The snap shot is saved in <outdir>/<subdir>/"t1_to_diff.png". By default
    <subdir> is "qc". To write in outdir directly, set subdir to anything that
    evaluates to False (empty string or None).
    Directories are automatically created if they don't exist.
    """

    # Freesurfer 'subjects_dir' has to be passed or set as environment variable
    subjects_dir = get_or_check_freesurfer_subjects_dir(subjects_dir)

    # If requested use a subdirectory in outdir
    if subdir:
        outdir = os.path.join(outdir, subdir)

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

    # Paths
    t1 = os.path.join(subjects_dir, subject_id, "mri/brainmask.mgz")
    t1_to_dif = os.path.join(outdir, "t1_to_dif.nii.gz")

    # Project T1 in dif
    cmd = ["mri_vol2vol",
           "--mov",    nodif_brain,
           "--targ",   t1,
           "--inv",
           "--interp", "nearest",
           "--o",      t1_to_dif,
           "--reg",    dif2anat_dat,
           "--no-save-reg"]
    run_freesurfer_cmd(cmd)

    nb_slices_in_z = nibabel.load(nodif_brain).get_shape()[2]

    # PDF snapshots: registered T1 (in diffusion) + nodif edges
    pdf_t1_with_nodif_edges = os.path.join(outdir, "t1_with_nodif_edges.pdf")
    plot_image(t1_to_dif,
               edge_file=nodif_brain,
               snap_file=pdf_t1_with_nodif_edges,
               name="T1 in diffusion + edges of nodif",
               cut_coords=nb_slices_in_z - 2)

    # PDF snapshots: nodif brain + registered T1 edges
    pdf_nodif_with_t1_edges = os.path.join(outdir, "nodif_with_t1_edges.pdf")
    plot_image(nodif_brain,
               edge_file=t1_to_dif,
               snap_file=pdf_nodif_with_t1_edges,
               name="nodif + edges of registered T1",
               cut_coords=nb_slices_in_z - 2)

    return outdir
def brainsuite_susceptibility_correction(outdir,
                                         dwi,
                                         bval,
                                         bvec,
                                         subject_id,
                                         phase_enc_dir,
                                         subjects_dir=None,
                                         qc_dir=None,
                                         bdp_nthread=4):
    """
    Assuming the beginning of the preprocessing was done with Connectomist
    up to Eddy current and motion correction, we now want to make susceptbility
    distortion correction using Brainsuite.

    Parameters
    ----------
    outdir: str
        Path to directory where to output.
    dwi: str
        Path to the preprocessed diffusion-weighted Nifti.
    bval: str
        Path to the bval file associated to the Nifti.
    bvec: str
        Path to the bvec file associated to the Nifti.
    subject_id: str
        Subject identifier used in Freesurfer.
    phase_enc_dir: str
        In plane phase encoding direction, "y", "y-", "x" or "x-".
    subjects_dir: str, default None
        If the Freesurfer $SUBJECTS_DIR environment variable is not set, or to
        bypass it, pass the path.
    qc_dir: str, default None
        Path to directory where to output snapshots for QC. By default in
        outdir.
    bdp_nthread: int, default 4
        Number of threads for bdp (see bdp.sh --thread flag)
    """

    # Freesurfer 'subjects_dir' has to be passed or set as environment variable
    subjects_dir = get_or_check_freesurfer_subjects_dir(subjects_dir)

    # Check in plane phase encoding direction
    # The sign of the phase encoding direction has no impact in this strategy
    # e.g. for "+y" or "-y" use "y"
    possible_phase_enc_dirs = {"y", "x"}
    if phase_enc_dir not in possible_phase_enc_dirs:
        raise ValueError("Bad argument 'phase_enc_dir': {}, should be in {}."
                         .format(phase_enc_dir, possible_phase_enc_dirs))

    # Set and check path to T1 brain-only volume from Freesurfer (mgz format)
    t1_brain_mgz = os.path.join(subjects_dir, subject_id, "mri/brain.mgz")
    if not os.path.isfile(t1_brain_mgz):
        raise Exception("Missing file: {}".format(t1_brain_mgz))

    # Convert Freesurfer T1 to Nifti with reorientation to RAS (to be in the
    # same orientation as the diffusion data)
    t1_brain_RAS_nii = os.path.join(outdir, "t1_brain.nii.gz")
    cmd = ["mri_convert", t1_brain_mgz, t1_brain_RAS_nii,
           "--out_orientation", "RAS"]
    run_freesurfer_cmd(cmd, subjects_dir=subjects_dir)

    # Run bfc (bias correction: required by BrainSuite)
    t1_bfc = os.path.join(outdir, "t1_brain.bfc.nii.gz")
    cmd = ["bfc", "-i", t1_brain_RAS_nii, "-o", t1_bfc]
    subprocess.check_call(cmd)

    # Extract brain from the nodif volume with FSL bet2
    nodif_brain = os.path.join(outdir, "nodif_brain.nii.gz")
    bet2(dwi, nodif_brain, f=0.25, m=False)

    # Run bdp.sh: registration + diffusion model
    cmd = ["bdp.sh", t1_bfc, "--nii", dwi, "--bval", bval, "--bvec", bvec,
           "--dwi-mask", nodif_brain, "--dir=%s" % phase_enc_dir,
           "--threads=%i" % bdp_nthread]
    subprocess.check_call(cmd)

    # Path to files of interest, created by BrainSuite bdp.sh
    dwi_wo_susceptibility = os.path.join(
        outdir, "t1_brain.dwi.RAS.correct.nii.gz")

    ###############
    # Quality check: create snapshots to visually assert the registration
    # quality

    if qc_dir is None:
        qc_dir = outdir

    # The snapshots won't contain all slices, half of them
    nb_slices_in_z = nibabel.load(nodif_brain).get_shape()[2]

    # Path to registered T1
    t1_to_dif = os.path.join(outdir, "t1_brain.D_coord.nii.gz")

    # First png: T1 registered in diffusion with nodif edges
    t1_with_nodif_edges_png = os.path.join(qc_dir, "t1_with_nodif_edges.png")
    plot_image(t1_to_dif,
               edge_file=nodif_brain,
               snap_file=t1_with_nodif_edges_png,
               name="T1 in diffusion + edges of nodif",
               cut_coords=nb_slices_in_z - 2)

    # Second png: nodif with edges of T1 registered in diffusion
    nodif_with_t1_edges_png = os.path.join(qc_dir, "nodif_with_t1_edges.png")
    plot_image(nodif_brain,
               edge_file=t1_to_dif,
               snap_file=nodif_with_t1_edges_png,
               name="nodif + edges of registered T1",
               cut_coords=nb_slices_in_z - 2)

    return dwi_wo_susceptibility, bval, bvec
예제 #16
0
def qc(t1files,
       wmfiles,
       asegfiles,
       whitefiles,
       pialfiles,
       annotfiles,
       actor_ang=[0., 0., 0.],
       output_directory=None,
       fsconfig="/i2bm/local/freesurfer/SetUpFreeSurfer.sh"):
    """ Compute some quality check plots on the converted FrreSurfer
    outputs.

    The subjecy id in the input files must appear in the -3 position:
        xxx/subject_id/convert/t1.nii.gz

    Steps:

    * t1-images overlays
    * 3d surface segmentation snaps
    * t1-surfaces overlays

    actor_ang: float (optional, default 0)
        the actor rotation in the z direction.

    <unit>
        <input name="t1files" type="List_File" description="The
            t1 subject files."/>
        <input name="wmfiles" type="List_File" description="The
            white matter subject files."/>
        <input name="asegfiles" type="List_File" description="The
            subcortical segmentation subject files."/>
        <input name="output_directory" type="Directory" description="The
            conversion destination folder."/>
        <input name="whitefiles" type="List_File" description="The subject
            cortex surfaces."/>
        <input name="pialfiles" type="List_File" description="The subject pial
            surfaces."/>
        <input name="annotfiles" type="List_File" description="The pial/white
            surface annotations."/>
        <input name="actor_ang" type="List_Float" description="The actor x, y,
            z position (in degrees)."/>
        <input name="fsconfig" type="File" description="The freesurfer
            configuration batch."/>
        <output name="qcfiles" type="List_File" description="The quality check
            snaps."/>
    </unit>
    """
    import clindmri.plot.pvtk as pvtk
    from clindmri.plot.slicer import plot_image

    # Create a t1 subject map
    t1map = {}
    for fname in t1files:
        subject_id = fname.split("/")[-3]
        if subject_id in t1map:
            raise Exception("Can't map two t1 for subject '{0}'"
                            ".".format(subject_id))
        t1map[subject_id] = fname

    # Create the output list that will contain all the qc files
    qcfiles = []

    # Construct the t1-surfaces overlays and the 3d surface segmentation snaps
    ren = pvtk.ren()
    for name, files in [("white", whitefiles), ("pial", pialfiles)]:
        for fname in files:

            # Get the t1 reference image
            subject_id = fname.split("/")[-3]
            t1file = t1map[subject_id]
            t1_image = nibabel.load(t1file)

            # Get the qc output directory
            qcdir = os.path.join(os.path.dirname(fname), "qc")
            qcname = os.path.basename(fname)
            if not os.path.isdir(qcdir):
                os.makedirs(qcdir)

            # Get the triangular mesh
            basename = os.path.basename(fname).replace(name,
                                                       "aparc.annot").replace(
                                                           ".native", "")
            annotfile = os.path.join(os.path.dirname(fname), basename)
            if annotfile not in annotfiles:
                raise ValueError(
                    "Annotation file '{0}' can't be found.".format(annotfile))
            surface = TriSurface.load(fname, annotfile=annotfile)

            # Construct the surfaces binarized volume
            binarizedfile = os.path.join(qcdir, qcname + ".nii.gz")
            overlay = numpy.zeros(t1_image.shape, dtype=numpy.uint)
            indices = numpy.round(surface.vertices).astype(int).T
            indices[0, numpy.where(indices[0] >= t1_image.shape[0])] = 0
            indices[1, numpy.where(indices[1] >= t1_image.shape[1])] = 0
            indices[2, numpy.where(indices[2] >= t1_image.shape[2])] = 0
            overlay[indices.tolist()] = 1
            overlay_image = nibabel.Nifti1Image(overlay, t1_image.get_affine())
            nibabel.save(overlay_image, binarizedfile)
            snap_file = os.path.join(qcdir, qcname + ".png")
            plot_image(t1file,
                       overlay_file=binarizedfile,
                       snap_file=snap_file,
                       name=qcname,
                       overlay_cmap="cold_hot")
            qcfiles.append(snap_file)

            # Create a vtk surface actor of the cortex surface with the
            # associated labels
            ctab = [item["color"] for _, item in surface.metadata.items()]
            actor = pvtk.surface(surface.vertices, surface.triangles,
                                 surface.labels, ctab)
            actor.RotateX(actor_ang[0])
            actor.RotateY(actor_ang[1])
            actor.RotateZ(actor_ang[2])

            # Create a 3d surface segmentation snap
            pvtk.add(ren, actor)
            snaps = pvtk.record(ren,
                                qcdir,
                                qcname,
                                n_frames=36,
                                az_ang=10,
                                animate=True,
                                delay=50)
            qcfiles.append(snaps[0])
            snaps = pvtk.record(ren, qcdir, qcname + ".3d", n_frames=1)
            qcfiles.append(snaps[0])
            pvtk.clear(ren)

    # Get the FreeSurfer lookup table
    fs_lut_names, fs_lut_colors = parse_fs_lut(
        os.path.join(os.path.dirname(fsconfig), "FreeSurferColorLUT.txt"))
    cmap = []
    nb_values = numpy.asarray(fs_lut_colors.keys()).max()
    cmap = numpy.zeros((nb_values, 4), dtype=numpy.single)
    for key, color in fs_lut_colors.items():
        if key > 0:
            cmap[key - 1, :3] = color
    cmap[:, 3] = 200.
    cmap /= 255.

    # Compute t1-images overlays
    for name, files in [("aseg", asegfiles), ("wm", wmfiles)]:
        for fname in files:

            # Get the t1 reference image
            subject_id = fname.split("/")[-3]
            t1file = t1map[subject_id]
            t1_image = nibabel.load(t1file)

            # Get the qc output directory
            qcdir = os.path.join(os.path.dirname(fname), "qc")
            if not os.path.isdir(qcdir):
                os.makedirs(qcdir)

            # Troncate the color map based on the label max
            array = nibabel.load(fname).get_data()
            order = sorted(set(array.flatten()))
            ccmap = cmap[order[1]:order[-1] + 1]

            # Overlay the current image with the t1 image
            qcname = "t1-{0}".format(name)
            snap_file = os.path.join(qcdir, qcname + ".png")
            plot_image(t1file,
                       overlay_file=fname,
                       snap_file=snap_file,
                       name=qcname,
                       overlay_cmap=ccmap,
                       cut_coords=(0, 0, 0))
            qcfiles.append(snap_file)

    return qcfiles