"""5TT and GM-WM interface segmentation nodes.
These wrap the MRtrix3 *Python scripts* ``5ttgen`` and ``5tt2gmwmi`` via
``subprocess`` rather than Nipype's ``Generate5tt`` / ``Generate5tt2gmwmi``
interfaces. Nipype places ``args`` before the positional ``algorithm``
argument, and the resulting ``-force`` flag lands in a position the script
does not honour — so a restart cannot overwrite an existing output. The
Function-node wrappers pre-delete the target and pass ``-force`` after the
positional args, mirroring the tckgen / tcksift2 wrappers.
"""
from pathlib import Path
from typing import Optional
from nipype import Node
from nipype.interfaces.utility import Function
from ..operations import run_5tt2gmwmi_task, run_5ttgen_task, warp_5tt_to_dwi_task
[docs]
def prepare_5tt_node(
out_dir: Path,
algorithm: str = "fsl",
t1_path: Optional[Path] = None,
parc_path: Optional[Path] = None,
name: str = "fivett",
) -> Node:
"""Run ``5ttgen`` to produce the 5-tissue-type segmentation used by ACT.
Two algorithm modes are supported:
* ``"fsl"`` consumes a T1-weighted image (``t1_path``). This is the
historical default; FSL FAST drives the segmentation.
* ``"freesurfer"`` consumes a FreeSurfer-LUT parcellation
(``parc_path``). In this pipeline the parcellation is wired at
runtime by the meta-workflow from the SynthSeg output (which uses
the FreeSurfer LUT and covers brainstem/cerebellum robustly).
Both input fields are always exposed on the resulting node — the
unused one is set to ``""`` so a meta-workflow can override either at
runtime without surprises.
Args:
out_dir: Output directory; ``5tt.mif`` is written here.
algorithm: 5ttgen backend (``"fsl"`` or ``"freesurfer"``).
t1_path: Optional T1 image path (used by ``algorithm="fsl"``).
parc_path: Optional FreeSurfer-LUT parcellation path (used by
``algorithm="freesurfer"``).
name: Nipype node name.
Returns:
Configured Nipype Function Node with output field ``out_file``.
Exposed input fields: ``output_dir``, ``algorithm``, ``t1_path``,
``parc_path``, ``freesurfer_home``.
"""
out_dir.mkdir(parents=True, exist_ok=True)
node = Node(
Function(
input_names=[
"output_dir",
"algorithm",
"t1_path",
"parc_path",
"freesurfer_home",
],
output_names=["out_file"],
function=run_5ttgen_task,
),
name=name,
)
node.inputs.output_dir = str(out_dir)
node.inputs.algorithm = algorithm
node.inputs.t1_path = str(t1_path) if t1_path else ""
node.inputs.parc_path = str(parc_path) if parc_path else ""
# Empty string => task falls back to $FREESURFER_HOME at runtime.
node.inputs.freesurfer_home = ""
return node
[docs]
def prepare_5tt_to_dwi_node(
out_dir: Path,
reference_image: Optional[Path] = None,
name: str = "fivett_to_dwi",
) -> Node:
"""Warp the 5TT from T1 grid into the DWI grid for ACT.
When the meta-workflow connects ``t1_to_dwi_transform`` from
``preprocess.dwi_to_t1_registration.composite_transform``, this node uses
``antsApplyTransforms`` with the inverted transform to bring the 5TT into
the DWI world. When unconnected (standalone runs on HCP-preprocessed data
where T1 and DWI already share a world), the input ``.mif`` is converted
to ``.nii.gz`` and passed through unchanged.
The ``fivett_file`` and ``t1_to_dwi_transform`` inputs are wired by the
workflow. ``reference`` is set statically if ``reference_image`` exists
at build time, and can be overridden by the meta-workflow with the live
eddy brain mask.
Args:
out_dir: Output directory; ``5tt_dwi.nii.gz`` is written here.
reference_image: Optional default reference (DWI grid) used in
standalone runs. Meta-workflows override this with the runtime
eddy brain mask output.
name: Nipype node name.
Returns:
Configured Nipype Function Node with output field ``out_file``.
"""
out_dir.mkdir(parents=True, exist_ok=True)
node = Node(
Function(
input_names=["fivett_file", "reference", "output_dir", "t1_to_dwi_transform"],
output_names=["out_file"],
function=warp_5tt_to_dwi_task,
),
name=name,
)
node.inputs.output_dir = str(out_dir)
node.inputs.t1_to_dwi_transform = ""
if reference_image is not None and Path(reference_image).exists():
node.inputs.reference = str(reference_image)
return node
[docs]
def prepare_gmwmi_node(
out_dir: Path,
name: str = "gmwmi",
) -> Node:
"""Run ``5tt2gmwmi`` to produce the GM-WM interface mask.
The 5TT input is wired in by the workflow, not set statically here.
Args:
out_dir: Output directory; ``gmwmi.mif`` is written here.
name: Nipype node name.
Returns:
Configured Nipype Function Node (``fivett_file`` is left
unconnected; the workflow wires it from the 5tt node's
``out_file``).
"""
out_dir.mkdir(parents=True, exist_ok=True)
node = Node(
Function(
input_names=["fivett_file", "output_dir"],
output_names=["out_file"],
function=run_5tt2gmwmi_task,
),
name=name,
)
node.inputs.output_dir = str(out_dir)
return node