HCP Workflow#

ProbTrackX2-based tractography workflow for HCP-structured datasets.

The package re-exports the HCP workflow entry points, while the canonical API documentation lives on the concrete submodules below.

thesis.workflows.hcp.workflow#

HCP ProbTrackX2 workflow orchestration.

thesis.workflows.hcp.workflow.build_workflow(*, t1, mask, bedpostx, tract_dir, config, context)[source]#

Build the HCP ProbTrackX2 tractography workflow.

Hemisphere modes via config.tractography.hemisphere: "left"/"right" (single hemisphere via fixed input), "both" (merged ROIs, default), "both-separately" (two hemispheres via Nipype iterables).

Parameters:
Return type:

Workflow

Configuration Helpers#

HCP path resolution and preparation.

class thesis.workflows.hcp.config.paths.HCPPaths[source]#

Bases: TypedDict

Resolved filesystem paths for the HCP workflow.

input_dir: Path#
diffusion_dir: Path#
bedpostx_dir: Path#
samples_base_name: Path#
thsamples: list[Path]#
phsamples: list[Path]#
fsamples: list[Path]#
mask_path: Path#
t1_path: Path#
thesis.workflows.hcp.config.paths.prepare_hcp_paths(config, context)[source]#

Resolve HCP directory paths from config and context.

Parameters:
Return type:

HCPPaths

thesis.workflows.hcp.config.paths.prepare_seed_path(config, input_dir)[source]#

Resolve seed ROI path from config.

Defaults to <diffusion_dir>/seed_source.nii.gz with input_dir/cwd fallback when tractography.seed_roi is not configured.

Parameters:
Return type:

Path

HCP tractography parameter extraction.

class thesis.workflows.hcp.config.params.TractographyParams[source]#

Bases: TypedDict

Runtime tractography parameters for the ProbTrackX2 node.

n_samples: int#
n_steps: int#
step_length: float#
curvature_threshold: float#
force_dir: bool#
opd: bool#
loop_check: bool#
dist_thresh: float#
fibst: int#
rand_fib: int#
mod_euler: bool#
mem_gb_gpu: float#
mem_gb_cpu: float#
thesis.workflows.hcp.config.params.prepare_tractography_params(config)[source]#

Extract tractography parameters from config with sensible defaults.

Parameters:

config (PipelineConfig) – PipelineConfig with tractography section

Returns:

n_samples, n_steps, step_length, curvature_threshold, force_dir, opd, loop_check, dist_thresh, fibst, rand_fib, mod_euler, mem_gb_gpu, mem_gb_cpu

Return type:

TractographyParams

HCP configuration value resolution.

thesis.workflows.hcp.config.values.resolve_hcp_value(config, key, default=None)[source]#

Get HCP config value from PipelineConfig (supports extra dict fields).

PipelineConfig uses extra=”allow” so protocol-specific keys like “hcp” may be available as an attribute (dict) or not present.

Parameters:
  • config (PipelineConfig) – PipelineConfig object

  • key (str) – Configuration key to retrieve

  • default (Optional[str]) – Default value if key not found

Return type:

str

Returns:

Configuration value as string

Nodes#

SynthSeg ROI extraction, validation, and resampling node builders.

thesis.workflows.hcp.nodes.roi.prepare_synthseg_roi_extractor(config, input_dir, context, out_dir)[source]#

Create and configure a SynthSeg ROI extractor node.

Reads config.tractography.synthseg_roi_labels. The node is structurally identical to the atlas roi_extractor but is named synthseg_roi_extractor and its roi_file input is intentionally left unset — it must be connected from outside (typically from a SynthSeg workflow node via workflow.connect).

If synthseg_roi_labels.roi_file is explicitly provided in config, it is set directly so the node can also be used in a standalone HCP workflow without a live SynthSeg connection.

Parameters:
  • config (PipelineConfig) – PipelineConfig

  • input_dir (Path) – Base input directory

  • context (ProcessingContext) – ProcessingContext with data_dir attribute

  • out_dir (Path) – Output directory for extracted ROIs

Return type:

Optional[Node]

Returns:

Configured Nipype Node, or None if synthseg_roi_labels is absent or has no waypoint_labels.

thesis.workflows.hcp.nodes.roi.prepare_synthseg_seg_resampler(t1_image, out_dir)[source]#

Create a node that resamples the SynthSeg segmentation to the T1w voxel grid.

By resampling the integer label map before label extraction, every binary mask produced by synthseg_roi_extractor (seed, waypoint, stop, avoid, target) is already at the correct resolution and world-coordinate frame. This removes the need for a separate per-mask resampling step and ensures SynthSeg-derived masks never pass through the ANTs transformer.

Uses nearest-neighbour interpolation to preserve integer label values. The input_image input is left unset here — it must be supplied either by connecting the live SynthSeg output (meta-workflow) or by setting node.inputs.input_image to a pre-computed segmentation file.

Parameters:
  • t1_image (str) – Absolute path to the T1w image (defines the target voxel grid, same image used as SynthSeg input).

  • out_dir (Path) – Output directory; resampled file goes in <out_dir>/synthseg_resampled.

Return type:

Node

Returns:

Configured Nipype Node wrapping resample_label_map_task().

thesis.workflows.hcp.nodes.roi.prepare_roi_validator(reference_image, name='roi_validator', min_voxels=10, singularity_threshold=1e-06, volume_ratio_min=0.5, volume_ratio_max=1.5)[source]#

Create a validation node that checks ROI masks before tractography.

Transparent passthrough: accepts the five ROI paths, validates each mask (voxel count + affine singularity + centroid plausibility), and re-emits the same paths unchanged for the next downstream node.

Parameters:
  • reference_image (str) – Absolute path to a reference image used for centroid plausibility checks (e.g. the subject-space brain mask for atlas ROIs or the T1w image for SynthSeg ROIs).

  • name (str) – Nipype node name (default "roi_validator"; use "synthseg_roi_validator" for the SynthSeg source).

  • min_voxels (int) – Minimum non-zero voxel count per warped mask.

  • singularity_threshold (float) – Minimum absolute determinant of affine 3x3 block.

  • volume_ratio_min (float) – Lower bound for warped-vs-original voxel ratio.

  • volume_ratio_max (float) – Upper bound for warped-vs-original voxel ratio.

Return type:

Node

Returns:

Configured Nipype Node wrapping validate_warped_rois_passthrough_task().

thesis.workflows.hcp.nodes.roi.prepare_waypoint_avoid_overlap_verifier(name='waypoint_avoid_verifier', max_overlap_fraction=0.9)[source]#

Pre-ProbTrackX2 guard that fails if any waypoint sits inside the avoid mask.

The five ROI inputs are left unset here — they must be wired from the upstream DWI resampler. Pure passthrough on success.

Parameters:
  • name (str) – Nipype node name.

  • max_overlap_fraction (float) – Maximum allowed waypoint-vs-avoid overlap before raising. Default 0.9 (90%) — targets the catastrophic wrong-hemisphere case (~100% overlap) without tripping on anatomically inherent overlap (e.g. peduncle near brainstem boundary often shows ~30-40%).

Return type:

Node

Returns:

Configured Nipype Node wrapping verify_waypoint_avoid_overlap_task().

thesis.workflows.hcp.nodes.roi.prepare_roi_dwi_resampler(dwi_mask_path, out_dir, name='roi_dwi_resampler')[source]#

Create a node that brings ROI masks from T1 world into the DWI grid.

The node has two modes determined at runtime by whether its t1_to_dwi_transform input is connected:

  • When connected to preprocess.dwi_to_t1_registration.composite_transform (i.e. meta-workflows that include a preprocess stage), the masks are warped with antsApplyTransforms using the inverted DWI→T1 transform. This bridges the world-coordinate gap between the atlas-warped masks (T1 world) and the DWI grid; without it, ProbTrackX2 silently produces anatomically-displaced tracks and tckgen+ACT rejects every streamline.

  • When unconnected (standalone runs on HCP-preprocessed data where T1 and DWI already share a world), the legacy nilearn.image.resample_to_img regrid is used.

The five ROI inputs and the t1_to_dwi_transform input are left unset here and must be connected from upstream nodes via workflow.connect.

Parameters:
  • dwi_mask_path (str) – Absolute path to the DWI brain mask (e.g. nodif_brain_mask.nii.gz) that defines the target voxel grid.

  • out_dir (Path) – Output directory; resampled files go in <out_dir>/rois_dwi_resampled.

  • name (str) – Nipype node name (default "roi_dwi_resampler").

Return type:

Node

Returns:

Configured Nipype Node wrapping resample_rois_to_dwi_task().

MapNode/JoinNode factories for the atlas ROI pipeline.

These replace the per-source prepare_roi_extractor/prepare_roi_transformer/ prepare_roi_validator/prepare_roi_merger factory pattern used by the legacy HCP workflow. The new design uses Nipype’s native MapNode to iterate over atlas sources and a JoinNode to merge results — eliminating the per-source Python loop and the binary merger reduce tree.

thesis.workflows.hcp.nodes.roi_mapnode.prepare_atlas_extract_mapnode(config, context, out_dir, name='atlas_extract')[source]#

Build a MapNode that runs extract_rois_task once per atlas source.

Returns None if the config declares no atlas sources.

Parameters:
Return type:

Optional[MapNode]

thesis.workflows.hcp.nodes.roi_mapnode.prepare_atlas_transform_mapnode(config, context, out_dir, name='atlas_transform')[source]#

Build a MapNode running transform_rois_task per atlas source.

Returns None if no atlas sources have a transform configured (in which case the upstream extract MapNode connects directly to the validate MapNode). Sources whose transform list is empty still appear as iterations, with empty warp_field causing the task to no-op-passthrough.

Parameters:
Return type:

Optional[MapNode]

thesis.workflows.hcp.nodes.roi_mapnode.prepare_atlas_validate_mapnode(thresholds, reference_image, out_dir, name='atlas_validate')[source]#

Build a MapNode that validates each per-source ROI bundle.

Parameters:
  • thresholds (ValidationThresholds)

  • reference_image (str)

  • out_dir (Path)

  • name (str)

Return type:

MapNode

thesis.workflows.hcp.nodes.roi_mapnode.prepare_atlas_merge_joinnode(joinsource_name, out_dir, name='atlas_merge')[source]#

Build a Node that collects per-source ROI bundles into one bundle.

Connected from an upstream MapNode (atlas_validate) whose per-source outputs are aggregated into lists by the MapNode itself; the regular Node receives those lists directly via Nipype’s standard connection semantics.

A JoinNode was used here originally, but under outer iterables on hemisphere (--hemisphere both-separately) Nipype’s join machinery collected zero iterations from the MapNode joinsource and merge_rois_task was invoked with empty lists for every field — producing all-empty outputs and a downstream probtrackx2.seed='' crash. joinsource_name is kept in the signature for backward compatibility with callers and is unused.

Parameters:
Return type:

Node

thesis.workflows.hcp.nodes.roi_mapnode.prepare_final_merger(out_dir, name='final_merger')[source]#

Build a binary 2-input merger combining atlas and SynthSeg bundles.

Parameters:
Return type:

Node

Tractography node builders.

thesis.workflows.hcp.nodes.tractography.prepare_outdir_router(base_out_dir, name='outdir_router')[source]#

Return a Function node that scopes a tractography out_dir per hemisphere.

Backend-neutral: used by both the HCP (ProbTrackX2) and MRtrix3 workflows. Under --hemisphere both-separately the workflow sets inputs.iterables = ("hemisphere", ["left", "right"]); connecting this node between the hemisphere iterable and each output-writing node’s out_dir/output_dir input ensures every iteration writes into <base>/<hemisphere>/ instead of colliding on the shared parent directory. For hemisphere == "both" the base directory is returned unchanged (flat layout).

Parameters:
  • base_out_dir (Path) – Absolute path to the un-scoped tractography directory.

  • name (str) – Nipype node name.

Return type:

Node

Returns:

Configured Function node with input hemisphere and output out_dir.

thesis.workflows.hcp.nodes.tractography.prepare_probtrackx2_outdir_router(base_out_dir, name='probtrackx2_outdir')[source]#

Backward-compatible alias of prepare_outdir_router() for the HCP workflow.

Kept so existing HCP wiring and tests that reference the probtrackx2_outdir node name continue to work unchanged.

Parameters:
Return type:

Node

thesis.workflows.hcp.nodes.tractography.prepare_probtrackx2_node(tract_params, hcp_paths, out_dir, use_gpu=False, gpu_runtime_env=None, gpu_slot_cost=1, name='probtrackx2')[source]#

Create and configure FSL ProbTrackX2 node.

When use_gpu=True the function attempts to use the GPU-accelerated binary (probtrackx2_gpu11.0 or probtrackx2_gpu). If neither binary is found on $FSLDIR/bin or $PATH, it automatically falls back to the standard CPU probtrackx2 and logs a warning.

Parameters:
  • tract_params (TractographyParams) – Dictionary with tractography parameters.

  • hcp_paths (HCPPaths) – Dictionary with HCP paths.

  • out_dir (str) – Output directory.

  • use_gpu (bool) – Prefer GPU-accelerated binary when available.

  • gpu_runtime_env (Optional[Dict[str, str]]) – When use_gpu is true, environment variables merged into THIS node’s command environ only. Nipype passes that env to the probtrackx2 subprocess (the Neurodesk probtrackx2_gpu wrapper), whose own singularity exec --cleanenv then forwards the SINGULARITYENV_/APPTAINERENV_-prefixed keys into the FSL container. Scoped to this node alone — the ANTs/SynthSeg nodes never see it. Ignored on CPU runs and when None.

  • gpu_slot_cost (int) – How many of the scheduler’s n_gpu_procs GPU slots this GPU node reserves (MultiProc charges min(node.n_procs, n_gpu_procs) slots per GPU node). Pass n_gpu_procs so probtrackx2 claims ALL slots and runs exclusively on the card: it is fast but balloons its buffer to fill GPU memory, so it must not co-locate. Light GPU nodes (e.g. SynthSeg) keep the default cost of 1 and pack up to n_gpu_procs concurrently around it. Only applied on GPU runs.

  • name (str) – Nipype node name (must be unique within a workflow).

Return type:

Node

Returns:

Configured Nipype Node for ProbTrackX2.

thesis.workflows.hcp.nodes.tractography.prepare_probtrackx2_params_writer(out_dir, n_samples, name='probtrackx2_params_writer')[source]#

Write tractography_params.json (backend, n_samples) for stats.

The stats collector needs n_samples to compute the per-sample Normalized Connection Strength. We persist a tiny JSON so the collector doesn’t have to re-read the YAML config.

Parameters:
  • out_dir (Path) – Per-hemisphere tractography output directory.

  • n_samples (int) – --nsamples passed to probtrackx2.

  • name (str) – Nipype node name.

Return type:

Node

Returns:

Configured Function node with output params_file.

thesis.workflows.hcp.nodes.tractography.prepare_streamline_warper(config, context, out_dir, name='streamline_warper')[source]#

Create and configure a streamline warping node if transform config exists.

Reads config.transforms.patient_to_template and config.transforms.template_reference_image. Both must be non-empty strings for the node to be created.

Parameters:
  • config (PipelineConfig) – PipelineConfig

  • context (ProcessingContext) – ProcessingContext with patient_id and data_dir attributes

  • out_dir (Path) – Output directory for warped files

  • name (str) – Nipype node name (must be unique within a workflow).

Return type:

Optional[Node]

Returns:

Configured Nipype Node wrapping inverse_warp_streamlines_task(), or None if the required transform paths are not configured.

Verifiers#

Preflight verifiers for the HCP ProbTrackX2 workflow.

Each verifier is a pure function returning a list of error strings (empty when the check passes). verify_requirements aggregates them in the order they were defined in the legacy workflow.py so error messages remain stable.

thesis.workflows.hcp.verifiers.verify_requirements(config, context)[source]#

Cross-cutting preflight checks for the HCP ProbTrackX2 workflow.

Note: T1, brain mask, and BedpostX sample existence are also validated declaratively by @requires on build_workflow(); the composite verifier may surface near-duplicate messages on a missing file. The explicit verifier checks below carry the more informative, search-path aware messages and stay for that reason.

Parameters:
Return type:

list[str]

Operations#

ROI extraction from label maps.

NOTE: The main function extract_rois_task runs inside a Nipype Function node (potentially in a separate process), so the thesis logger is not available at runtime. sys.stderr.write() is used for progress messages instead.

thesis.workflows.hcp.operations.extraction.extract_rois_task(roi_file, label_file, waypoint_labels, output_dir, hemisphere='both')[source]#

Extract ROI masks from a label map and assemble workflow-ready outputs.

Parameters:
  • roi_file (str) – Input label-map image path.

  • label_file (str) – Optional CSV or whitespace-delimited label mapping file.

  • waypoint_labels (dict) – ROI extraction specification keyed by ROI name. May contain hemisphere-specific keys (left_label_name etc.) that this task resolves at runtime via hemisphere.

  • output_dir (str) – Output directory for generated masks and waypoint list files.

  • hemisphere (str) – "left", "right", or "both" (default). When waypoint_labels entries declare hemisphere-specific labels, this selects which side(s) to extract; entries without hemisphere-specific fields pass through unchanged.

Return type:

tuple[str, str, str, str, str]

Returns:

Tuple of (seed, waypoints_file, stop_mask, avoid_mask, target_mask).

Raises:

ROI transformation using ANTs.

NOTE: Runs inside a Nipype Function node; the thesis logger is not available at runtime. print() may be used for progress output instead.

The low-level ANTs call and sform/qform fix are provided by the shared thesis.workflows.transforms.operations module. This module retains its original public interface for backward compatibility with the HCP workflow.

thesis.workflows.hcp.operations.transformation.transform_rois_task(seed, waypoints_file, stop_mask, avoid_mask, target_mask, warp_field, reference_image, output_dir, invert_transform_flags=None, hemisphere='both', _ordering_signal='')[source]#

Apply template-to-patient space transform to ROIs using ANTs ApplyTransforms.

Returns transformed ROI paths in the same structure as extract_rois_task.

Parameters:
  • seed (str) – Path to seed ROI mask (or empty string)

  • waypoints_file (str) – Text file containing paths to waypoint masks

  • stop_mask (str) – Path to stop mask (or empty string)

  • avoid_mask (str) – Path to avoid mask (or empty string)

  • target_mask (str) – Path to target/destination mask (or empty string)

  • warp_field (str | list[str]) – Path or ordered list of transform paths for template-to-patient transformation

  • reference_image (str) – Reference image in patient space for resampling

  • output_dir (str) – Output directory for transformed ROIs

  • invert_transform_flags (list[bool] | None) – Optional per-transform inversion flags.

  • hemisphere (str) – "left", "right", or "both" (default). When the HCP workflow runs under --hemisphere both-separately, two iterations of this task race for the same output paths. Scoping the output dir per hemisphere (<output_dir>/<hemisphere>/) prevents the collision. "both" keeps the legacy flat layout for single-hemisphere runs.

  • _ordering_signal (str) – Unused ordering-only input. Connecting an upstream node (e.g. registration completion, via the hcp roi_transform_gate contract field) to this MapNode input creates a Nipype dependency edge so the ROI warp waits until the template->patient transforms exist on disk. The value itself is ignored.

Return type:

tuple

Returns:

Tuple of (transformed_seed, transformed_waypoints_file, transformed_stop,

transformed_avoid, transformed_target)

ROI merging from multiple atlas sources.

NOTE: Both tasks run inside Nipype Function nodes, which serialize each function’s source in isolation. A task therefore cannot reference module-level helpers or sibling tasks at runtime — every helper must be defined inside the function body and every import must be local. The two tasks consequently share shape (out-dir scoping, mask union, waypoint concat) but each carries its own nested copies; the only behavioural difference is the seed rule (binary merge picks the left seed; the list merge unions all seeds).

thesis.workflows.hcp.operations.merging.binary_merge_rois_task(left_seed='', left_waypoints_file='', left_stop='', left_avoid='', left_target='', right_seed='', right_waypoints_file='', right_stop='', right_avoid='', right_target='', output_dir='', hemisphere='both')[source]#

Combine two ROI bundles into one (legacy binary-merge interface).

Used by the MRtrix3 workflow’s binary-tree merger and by the HCP workflow’s final_merger that combines atlas-joined output with the SynthSeg branch. Semantics match the historical contract:

  • Seed: pick-first (left wins over right).

  • Waypoints: concatenate both files’ lines.

  • Stop / avoid / target: union via fslmaths when both are present, else passthrough whichever exists.

Parameters:
  • hemisphere (str) – "left", "right", or "both" (default). Scopes the output dir to <output_dir>/<hemisphere>/ for the first two so concurrent left/right iterations don’t race on the same merged-output paths.

  • left_seed (str)

  • left_waypoints_file (str)

  • left_stop (str)

  • left_avoid (str)

  • left_target (str)

  • right_seed (str)

  • right_waypoints_file (str)

  • right_stop (str)

  • right_avoid (str)

  • right_target (str)

  • output_dir (str)

Return type:

tuple

Returns:

Tuple (seed, waypoints_file, stop_mask, avoid_mask, target_mask).

thesis.workflows.hcp.operations.merging.merge_rois_task(seeds, waypoints_files, stop_masks, avoid_masks, target_masks, output_dir, hemisphere='both')[source]#

Merge N ROI bundles (one per atlas source) into one canonical bundle.

Designed to be the join task for an upstream MapNode. Each list argument is a per-iteration value (one entry per atlas source), and may contain empty strings for sources that didn’t produce that role.

Parameters:
  • seeds (list) – Per-source seed mask paths.

  • waypoints_files (list) – Per-source waypoint list-file paths.

  • stop_masks (list) – Per-source stop mask paths.

  • avoid_masks (list) – Per-source avoid mask paths.

  • target_masks (list) – Per-source target mask paths.

  • output_dir (str) – Directory for merged outputs.

  • hemisphere (str) – "left", "right", or "both" (default). Scopes <output_dir>/<hemisphere>/ for left/right so parallel hemisphere iterations under --hemisphere both-separately don’t race on identically-named merged outputs.

Return type:

tuple

Returns:

Tuple (seed, waypoints_file, stop_mask, avoid_mask, target_mask).

Mask resampling utilities for aligning ROIs to diffusion space.

NOTE: Runs inside a Nipype Function node; the thesis logger is not available at runtime. sys.stderr.write() may be used for progress output instead.

thesis.workflows.hcp.operations.resampling.resample_label_map_task(input_image, reference, output_dir)[source]#

Resample a label map to match a reference image’s voxel grid.

Used to bring the SynthSeg segmentation into the T1w voxel grid before label extraction, so every extracted binary mask (seed, waypoint, stop, avoid, target) is already at the correct resolution — no per-mask resampling step is needed afterwards.

Uses nearest-neighbour interpolation to preserve integer label values. copy_header=True copies the reference image’s full NIfTI header (including sform/qform codes) to the output.

Parameters:
  • input_image (str) – Path to the SynthSeg label map NIfTI file, or "" if absent.

  • reference (str) – Path to a reference NIfTI image that defines the target voxel grid (e.g. the T1w image used as SynthSeg input).

  • output_dir (str) – Directory for the resampled output file.

Return type:

str

Returns:

Path to the resampled label map, or the original input_image path unchanged when the input image is absent.

thesis.workflows.hcp.operations.resampling.resample_rois_to_dwi_task(seed, waypoints_file, stop_mask, avoid_mask, target_mask, reference, output_dir, t1_to_dwi_transform='', hemisphere='both')[source]#

Resample all ROI masks to a DWI reference grid before tractography.

ProbTrackX2 / tckgen require that every mask (seed, waypoints, stop, avoid, target) shares the voxel grid AND world coordinates of the diffusion data. When the ROI masks come out of the atlas warp in T1 world but the DWI was acquired in a different physical orientation (typical for non-HCP-preprocessed datasets), a pure grid-only regrid leaves the data at the wrong DWI voxels and tractography produces 0 streamlines under ACT or anatomically-displaced streamlines under ProbTrackX2.

Two modes:

  • Transform mode (t1_to_dwi_transform non-empty and pointing at a valid file): the path is the ANTs composite transform produced by preprocess.dwi_to_t1_registration (direction: DWI→T1). Each mask is warped via antsApplyTransforms -t [transform,1] (inverted to give T1→DWI) with NearestNeighbor interpolation, landing on the DWI grid at the correct anatomical positions.

  • Pass-through mode (t1_to_dwi_transform empty): falls back to nilearn.image.resample_to_img with nearest-neighbour interpolation. This is correct when the input ROIs and reference already share a world (e.g. HCP-preprocessed data where T1 and DWI have both been ACPC-aligned upstream).

NOTE: Runs inside a Nipype Function node; use print() for logging.

Parameters:
  • seed (str) – Path to seed ROI mask, or "" if absent.

  • waypoints_file (str) – Path to a text file listing waypoint mask paths, one per line, or "" if absent.

  • stop_mask (str) – Path to stop mask, or "" if absent.

  • avoid_mask (str) – Path to avoid mask, or "" if absent.

  • target_mask (str) – Path to target mask, or "" if absent.

  • reference (str) – Path to the DWI reference image (e.g. nodif_brain_mask.nii.gz) that defines the target voxel grid.

  • output_dir (str) – Directory where resampled masks are written.

  • t1_to_dwi_transform (str) – Optional path to an ANTs composite transform (DWI→T1 direction). When supplied, antsApplyTransforms with the inverse transform is used; when empty, nilearn regrid only.

  • hemisphere (str) – "left", "right", or "both" (default). Scopes <output_dir>/<hemisphere>/ for left/right so parallel hemisphere iterations under --hemisphere both-separately don’t race on identically-named resampled mask paths.

Return type:

tuple

Returns:

Tuple of (seed, waypoints_file, stop_mask, avoid_mask, target_mask) paths pointing to the resampled files. Absent inputs (empty string or non-existent path) are returned unchanged.

Streamline (tractography output) warping to template space.

thesis.workflows.hcp.operations.streamline_warping.inverse_warp_streamlines_task(fdt_paths, warp_field, reference_image, output_dir, _ordering_signal=None)[source]#

Warp ProbTrackX2 outputs from subject diffusion space to template space.

Uses ANTs ApplyTransforms with the patient→template forward warp field directly (invert_transform_flags=[False]) and Linear interpolation. Handles fdt_paths.nii.gz, seeds_to_*.nii.gz, and waytotal.nii.gz.

All imports are performed inside the function body so that Nipype can serialise and execute it as a standalone Function node.

Parameters:
  • fdt_paths (str) – Path to the directory containing ProbTrackX2 outputs.

  • warp_field (str) – Path to patient→template warp field.

  • reference_image (str) – Template-space reference image (defines output grid).

  • output_dir (str) – Output directory for warped files.

  • _ordering_signal (object) – Ordering-only input (ignored). Wired from probtrackx2.fdt_paths so this node runs only after ProbTrackX2 has written its outputs into fdt_paths; the directory itself comes from the outdir router, not from this value.

Return type:

list

Returns:

List of paths to the warped output files.

ROI warp validation checks.

NOTE: Runs inside a Nipype Function node; the thesis logger is not available at runtime. warnings.warn() is used for soft failures and PipelineError is raised for hard failures.

thesis.workflows.hcp.operations.validation.validate_warped_rois_task(roi_paths, reference_image, original_roi_paths=None, min_voxels=10, singularity_threshold=1e-06, volume_ratio_min=0.5, volume_ratio_max=1.5)[source]#

Validate warped ROI masks for voxel count, centroid, and volume consistency.

Performs four checks per non-empty ROI path:

  • (a) Voxel count — raises PipelineError if the mask contains fewer than min_voxels non-zero voxels (hard failure, halts pipeline).

  • (a2) Affine singularity — raises PipelineError if the absolute determinant of the affine’s 3×3 rotation/scale block is below singularity_threshold. A near-zero determinant causes probtrackx2 to crash with inv(): matrix is singular (hard failure, halts pipeline).

  • (b) Centroid plausibility — emits a UserWarning if the mask centroid falls outside the subject brain mask (soft failure, continues).

  • (c) Volume consistency — emits a UserWarning if the warped mask volume ratio (warped / original) falls outside [volume_ratio_min, volume_ratio_max] (soft failure, continues). Only performed when original_roi_paths is provided.

Empty-string entries in roi_paths are silently skipped (absent masks).

Parameters:
  • roi_paths (List[str]) – Paths to warped ROI masks in subject space. Empty strings are treated as absent and skipped.

  • reference_image (str) – Path to the subject-space brain mask used for centroid plausibility check (check b).

  • original_roi_paths (Optional[List[str]]) – Corresponding pre-warp ROI paths used for volume comparison (check c). Must have the same length as roi_paths when provided. None or empty-string entries skip the check for that mask.

  • min_voxels (int) – Minimum number of non-zero voxels required in each warped mask. Fewer voxels triggers the hard failure (check a).

  • singularity_threshold (float) – Minimum absolute determinant of the affine 3×3 rotation/scale block. Below this the mask is rejected as degenerate (check a2).

  • volume_ratio_min (float) – Lower bound for warped-vs-original voxel count ratio. Below this a warning is emitted (check c).

  • volume_ratio_max (float) – Upper bound for warped-vs-original voxel count ratio. Above this a warning is emitted (check c).

Return type:

List[str]

Returns:

roi_paths unchanged (passthrough for Nipype graph chaining).

Raises:

PipelineError – If any warped mask is empty (fewer than min_voxels non-zero voxels) or has a degenerate (near-singular) affine matrix.

thesis.workflows.hcp.operations.validation.validate_warped_rois_passthrough_task(seed, waypoints_file, stop_mask, avoid_mask, target_mask, reference_image, min_voxels=10, singularity_threshold=1e-06, volume_ratio_min=0.5, volume_ratio_max=1.5)[source]#

Validate all warped ROI masks and pass them through unchanged.

Nipype-compatible wrapper around validate_warped_rois_task(). Collects individual NIfTI mask paths and expands the waypoints text file into its constituent paths, then delegates validation. Returns all five inputs unchanged so this node can be inserted transparently between the transformer and any downstream node.

Runs inside a Nipype Function node — all imports are local; warnings.warn is used for soft failures (no module-level logger).

Parameters:
  • seed (str) – Path to the transformed seed mask, or empty string if absent.

  • waypoints_file (str) – Path to text file listing transformed waypoint NIfTI paths, or empty string if absent.

  • stop_mask (str) – Path to transformed stop mask, or empty string.

  • avoid_mask (str) – Path to transformed avoid mask, or empty string.

  • target_mask (str) – Path to transformed target mask, or empty string.

  • reference_image (str) – Subject-space brain mask for centroid plausibility check.

  • min_voxels (int) – Minimum non-zero voxel count per warped mask.

  • singularity_threshold (float) – Minimum absolute determinant of affine 3x3 block.

  • volume_ratio_min (float) – Lower bound for warped-vs-original voxel ratio.

  • volume_ratio_max (float) – Upper bound for warped-vs-original voxel ratio.

Return type:

tuple

Returns:

5-tuple (seed, waypoints_file, stop_mask, avoid_mask, target_mask) unchanged.

Raises:

PipelineError – If any transformed mask has fewer than min_voxels non-zero voxels or has a degenerate affine matrix.

thesis.workflows.hcp.operations.validation.verify_waypoint_avoid_overlap_task(seed, waypoints_file, stop_mask, avoid_mask, target_mask, max_overlap_fraction=0.9)[source]#

Fail-fast guard against waypoint masks landing inside the avoid mask.

ProbTrackX2 must satisfy two mutually exclusive constraints when a waypoint overlaps the avoid mask: streamlines must pass through the waypoint AND must not enter avoid. If a waypoint is wholly contained in avoid, every streamline is killed at step 0 and the run silently produces waytotal=0.

This check runs after DWI-grid resampling — the last point before the masks are handed to probtrackx2 — and raises PipelineError when any waypoint has more than max_overlap_fraction of its voxels inside avoid. Pure passthrough on success.

Parameters:
  • seed (str) – Path to seed mask (unused, for signature compatibility).

  • waypoints_file (str) – Text file listing waypoint mask paths (one per line), or empty string if absent.

  • stop_mask (str) – Path to stop mask (unused, for signature compatibility).

  • avoid_mask (str) – Path to avoid mask, or empty string. When absent, the check is a no-op.

  • target_mask (str) – Path to target mask (unused, for signature compatibility).

  • max_overlap_fraction (float) – Maximum allowed fraction of waypoint voxels inside the avoid mask before failing. Default 0.9 (90%). The threshold is deliberately high — anatomically valid waypoints often clip the avoid mask near boundaries (e.g. the cerebral peduncle near the brainstem can show ~30-40% overlap on healthy subjects). The bug this guard targets is the catastrophic case (e.g. an IC waypoint warped into the wrong hemisphere where the entire mask lands inside the contralateral-hemisphere avoid), which produces ~100% overlap.

Return type:

tuple

Returns:

5-tuple of the five inputs unchanged.

Raises:

PipelineError – If any waypoint has more than max_overlap_fraction of its voxels inside the avoid mask.