Source code for thesis.workflows.mrtrix3.nodes.segmentation

"""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