Source code for dipy.workflows.templates
"""Predefined semantic pipeline templates for dipy_auto workflow.
This module provides TOML templates using the new semantic pipeline
specification with [[pipeline]] sections and automatic DAG-based wiring.
"""
import logging
# =============================================================================
# Preprocessing Pipeline - Complete preprocessing chain
# =============================================================================
PREPROCESSING_PIPELINE = """
[General]
name = "preprocessing_pipeline"
description = "Full preprocessing: denoise → Gibbs → motion → bias → reslice → mask"
version = "1.0.0"
author = "DIPY Developers"
[io]
dwi = ""
bvals = ""
bvecs = ""
t1w = ""
bids_folder = ""
dwi_opp_phase = ""
pe_dir = ""
total_readout_time = ""
effective_echo_spacing = ""
echo_time = ""
repetition_time = ""
out_dir = "."
# Same chain, in the same order, as the preprocessing of the full pipeline;
# see the comments there for the constraints that fix this order.
[[pipeline]]
name = "denoise"
cli = "dipy_denoise"
input_files = "${io.dwi}"
bvalues_files = "${io.bvals}"
[[pipeline]]
name = "gibbs"
cli = "dipy_gibbs_ringing"
input_files = "${denoise.out_denoised}"
slice_axis = 2
num_processes = -1
[[pipeline]]
name = "motion_correction"
cli = "dipy_correct_motion"
input_files = "${gibbs.out_unring}"
bvalues_files = "${io.bvals}"
bvectors_files = "${io.bvecs}"
[[pipeline]]
name = "bias_correction"
cli = "dipy_correct_biasfield"
input_files = "${motion_correction.out_moved}"
bval = "${io.bvals}"
bvec = "${motion_correction.out_bvecs}"
method = "bspline"
[[pipeline]]
name = "reslice"
cli = "dipy_reslice"
input_files = "${bias_correction.out_corrected}"
order = "lanczos2"
[[pipeline]]
name = "b0_extraction"
cli = "dipy_extract_b0"
input_files = "${reslice.out_resliced}"
bvalues_files = "${io.bvals}"
b0_threshold = 50
[[pipeline]]
name = "brain_mask"
cli = "dipy_brain_mask"
input_files = "${reslice.out_resliced}"
bvalues_files = ["${io.bvals}"]
median_radius = 2
numpass = 5
finalize_mask = true
save_masked = true
"""
# =============================================================================
# DTI Pipeline - DTI reconstruction
# =============================================================================
DTI_ONLY_PIPELINE = """
[General]
name = "dti_pipeline"
description = "Preprocessing + DTI reconstruction with tensor metrics"
version = "1.0.0"
author = "DIPY Developers"
[io]
dwi = ""
bvals = ""
bvecs = ""
t1w = ""
bids_folder = ""
dwi_opp_phase = ""
pe_dir = ""
total_readout_time = ""
effective_echo_spacing = ""
echo_time = ""
repetition_time = ""
out_dir = "."
[[pipeline]]
name = "denoise"
cli = "dipy_denoise"
input_files = "${io.dwi}"
bvalues_files = "${io.bvals}"
[[pipeline]]
name = "brain_mask"
cli = "dipy_median_otsu"
input_files = "${denoise.out_denoised}"
bvalues_files = "${io.bvals}"
median_radius = 2
numpass = 5
[[pipeline]]
name = "dti_fit"
cli = "dipy_fit_dti"
input_files = "${denoise.out_denoised}"
bvalues_files = "${io.bvals}"
bvectors_files = "${io.bvecs}"
mask_files = "${brain_mask.out_mask}"
fit_method = "WLS"
b0_threshold = 50
"""
# =============================================================================
# Tractography Pipeline - Preprocessing + reconstruction + tracking
# =============================================================================
TRACTOGRAPHY_PIPELINE = """
[General]
name = "tractography_pipeline"
description = "Preprocessing + reconstruction + fiber tracking"
version = "1.0.0"
author = "DIPY Developers"
[io]
dwi = ""
bvals = ""
bvecs = ""
t1w = ""
bids_folder = ""
dwi_opp_phase = ""
pe_dir = ""
total_readout_time = ""
effective_echo_spacing = ""
echo_time = ""
repetition_time = ""
out_dir = "."
# Preprocessing
[[pipeline]]
name = "denoise"
cli = "dipy_denoise"
input_files = "${io.dwi}"
bvalues_files = "${io.bvals}"
[[pipeline]]
name = "brain_mask"
cli = "dipy_median_otsu"
input_files = "${denoise.out_denoised}"
bvalues_files = "${io.bvals}"
median_radius = 2
numpass = 5
# DTI reconstruction for FA-based stopping criterion
[[pipeline]]
name = "dti_fit"
cli = "dipy_fit_dti"
input_files = "${denoise.out_denoised}"
bvalues_files = "${io.bvals}"
bvectors_files = "${io.bvecs}"
mask_files = "${brain_mask.out_mask}"
fit_method = "WLS"
b0_threshold = 50
extract_pam_values = true
# CSD reconstruction for fiber orientation
[[pipeline]]
name = "csd_fit"
cli = "dipy_fit_csd"
input_files = "${denoise.out_denoised}"
bvalues_files = "${io.bvals}"
bvectors_files = "${io.bvecs}"
mask_files = "${brain_mask.out_mask}"
b0_threshold = 50
extract_pam_values = true
# Fiber tracking using CSD peaks
[[pipeline]]
name = "tracking"
cli = "dipy_track"
pam_files = "${csd_fit.out_pam}"
stopping_files = "${dti_fit.out_fa}"
seeding_files = "${brain_mask.out_mask}"
seed_density = 2
step_size = 0.5
"""
# =============================================================================
# Full Pipeline - Complete analysis
# =============================================================================
FULL_PIPELINE = """
[General]
name = "full_pipeline"
description = "Full pipeline: preprocessing with motion correction + T1 registration + reconstruction + tracking + SLR + bundles"
version = "1.0.0"
author = "DIPY Developers"
[io]
dwi = ""
bvals = ""
bvecs = ""
t1w = ""
bids_folder = ""
dwi_opp_phase = ""
pe_dir = ""
total_readout_time = ""
effective_echo_spacing = ""
echo_time = ""
repetition_time = ""
out_dir = "."
atlas_tractogram = ""
bundle_atlas_dir = ""
# Full preprocessing chain.
#
# Stage order is constrained, in decreasing strictness:
# 1. Denoising and Gibbs unringing both exploit properties of the data as it
# came off the scanner, so every stage that interpolates (reslice, motion
# and eddy correction, registration) has to run after them.
# 2. Denoising comes first. Gibbs unringing selects its subvoxel shift by
# minimising local total variation, and noise corrupts that choice. The
# ordering is a hard requirement for MP-PCA, whose noise-level estimate
# assumes spatially white noise that unringing would correlate.
# 3. Gibbs unringing is FFT-based and needs the full field of view. Handing
# it a skull-stripped volume puts a hard edge at the mask boundary, which
# is itself a source of ringing.
# 4. The brain mask is thresholded on intensity, so it is computed last: on
# denoised, unrung, bias-corrected data, and on the final grid.
# 5. Motion correction rotates the b-vectors; every later stage reads the
# rotated ones.
[[pipeline]]
name = "denoise"
cli = "dipy_denoise"
input_files = "${io.dwi}"
bvalues_files = "${io.bvals}"
[[pipeline]]
name = "gibbs"
cli = "dipy_gibbs_ringing"
input_files = "${denoise.out_denoised}"
slice_axis = 2
num_processes = -1
[[pipeline]]
name = "motion_correction"
cli = "dipy_correct_motion"
input_files = "${gibbs.out_unring}"
bvalues_files = "${io.bvals}"
bvectors_files = "${io.bvecs}"
[[pipeline]]
name = "bias_correction"
cli = "dipy_correct_biasfield"
input_files = "${motion_correction.out_moved}"
bval = "${io.bvals}"
bvec = "${motion_correction.out_bvecs}"
method = "bspline"
# Resampling. Everything below this point works on the resliced grid.
[[pipeline]]
name = "reslice"
cli = "dipy_reslice"
input_files = "${bias_correction.out_corrected}"
order = "lanczos2"
[[pipeline]]
name = "b0_extraction"
cli = "dipy_extract_b0"
input_files = "${reslice.out_resliced}"
bvalues_files = "${io.bvals}"
b0_threshold = 50
[[pipeline]]
name = "brain_mask"
cli = "dipy_brain_mask"
input_files = "${reslice.out_resliced}"
bvalues_files = ["${io.bvals}"]
median_radius = 2
numpass = 5
finalize_mask = true
save_masked = true
# T1 to diffusion space, skipped when no T1 image is given.
[[pipeline]]
name = "t1_registration"
cli = "dipy_align_affine"
static_image_files = "${b0_extraction.out_b0}"
moving_image_files = "${io.t1w}"
transform = "rigid"
out_moved = "t1w_dwispace.nii.gz"
out_affine = "t1w_to_dwi_affine.txt"
optional = true
# Multiple reconstructions
[[pipeline]]
name = "dti_fit"
cli = "dipy_fit_dti"
input_files = "${reslice.out_resliced}"
bvalues_files = "${io.bvals}"
bvectors_files = "${motion_correction.out_bvecs}"
mask_files = "${brain_mask.out_mask}"
fit_method = "WLS"
extract_pam_values = true
out_dir = "${io.out_dir}/dti"
[[pipeline]]
name = "force_fit"
cli = "dipy_fit_force"
input_files = "${reslice.out_resliced}"
bvalues_files = "${io.bvals}"
bvectors_files = "${motion_correction.out_bvecs}"
mask_files = "${brain_mask.out_mask}"
compute_kurtosis = true
engine = "ray"
out_dir = "${io.out_dir}/force"
# Tractography
[[pipeline]]
name = "tracking"
cli = "dipy_track"
pam_files = "${force_fit.out_pam}"
stopping_files = "${force_fit.out_fa}"
seeding_files = "${brain_mask.out_mask}"
seed_density = 2
# Registration (SLR)
[[pipeline]]
name = "register"
cli = "dipy_slr"
moving_files = "${tracking.out_tractogram}"
static_files = "${io.atlas_tractogram}"
bbox_valid_check = false
# Bundle segmentation (RecoBundles)
[[pipeline]]
name = "segment_bundles"
cli = "dipy_recobundles"
streamline_files = "${register.out_moved}"
model_bundle_files = "${io.bundle_atlas_dir}/*.trk"
mix_names = true
out_dir = "${io.out_dir}/rec_bundles"
# Label bundles
[[pipeline]]
name = "label_bundles"
cli = "dipy_labelsbundles"
streamline_files = "${tracking.out_tractogram}"
labels_files = "${segment_bundles.out_dir}/*_labels.npy"
mix_names = true
out_dir = "${io.out_dir}/org_bundles"
# Buan profiles
[[pipeline]]
name = "buan_profiles"
cli = "dipy_buan_profiles"
model_bundle_folder = "${io.bundle_atlas_dir}"
subject_folder = "${io.out_dir}"
metric_folder= "${dti_fit.out_dir}"
orig_bundle_folder= "${label_bundles.out_dir}"
out_dir = "${io.out_dir}/buan_profiles"
"""
# =============================================================================
# Pipeline Dictionary and Helper Functions
# =============================================================================
PREDEFINED_PIPELINES = {
"preprocessing": {
"description": "Full preprocessing: denoise → Gibbs → motion → bias → reslice → mask",
"config": PREPROCESSING_PIPELINE,
},
"dti_only": {
"description": "Preprocessing + DTI reconstruction with tensor metrics",
"config": DTI_ONLY_PIPELINE,
},
"tractography": {
"description": "Preprocessing + DTI + CSD + fiber tracking",
"config": TRACTOGRAPHY_PIPELINE,
},
"full": {
"description": (
"Full pipeline: preprocessing with motion correction + T1 registration"
" + reconstruction + tracking + SLR + bundles"
),
"config": FULL_PIPELINE,
},
}
[docs]
def get_predefined_pipeline(*, pipeline_name):
"""Get predefined pipeline configuration by name.
Parameters
----------
pipeline_name : str
Name of the predefined pipeline.
Returns
-------
str
TOML configuration string with [[pipeline]] sections.
Raises
------
KeyError
If pipeline_name is not found.
"""
if pipeline_name not in PREDEFINED_PIPELINES:
available = ", ".join(list_predefined_pipelines())
raise KeyError(
f"Pipeline '{pipeline_name}' not found. Available pipelines: {available}"
)
return PREDEFINED_PIPELINES[pipeline_name]["config"]
[docs]
def get_pipeline_description(*, pipeline_name):
"""Get description for a predefined pipeline.
Parameters
----------
pipeline_name : str
Name of the predefined pipeline.
Returns
-------
str
Description of the pipeline.
Raises
------
KeyError
If pipeline_name is not found.
"""
if pipeline_name not in PREDEFINED_PIPELINES:
available = ", ".join(list_predefined_pipelines())
raise KeyError(
f"Pipeline '{pipeline_name}' not found. Available pipelines: {available}"
)
return PREDEFINED_PIPELINES[pipeline_name]["description"]
[docs]
def list_predefined_pipelines(*, log_level=None):
"""List all available predefined pipeline names.
Parameters
----------
log_level : int, optional
Logging level. If not DEBUG (10), only returns "full" and allows
"interactive" mode. If DEBUG or None, returns all pipelines.
Returns
-------
list
List of pipeline names.
"""
all_pipelines = list(PREDEFINED_PIPELINES.keys())
# If log_level is provided and it's not DEBUG, filter to show only "full"
# Note: "interactive" is handled separately as it's not a predefined pipeline
if log_level is not None and log_level != logging.DEBUG:
return ["full"]
return all_pipelines
[docs]
def list_pipelines_with_descriptions(*, log_level=None):
"""List all predefined pipelines with descriptions.
Parameters
----------
log_level : int, optional
Logging level. If not DEBUG (10), only shows "full" pipeline.
If DEBUG or None, shows all pipelines.
Returns
-------
str
Formatted list of pipelines with descriptions.
"""
lines = ["Available Semantic Pipelines (using [[pipeline]] sections):", "=" * 70]
for name in list_predefined_pipelines(log_level=log_level):
desc = PREDEFINED_PIPELINES[name]["description"]
lines.append(f" {name:<15} - {desc}")
lines.append("=" * 70)
lines.append(
"\nEach pipeline uses semantic stage names with automatic DAG-based wiring."
)
lines.append("Stages are executed in topological order based on dependencies.")
return "\n".join(lines)