QC Visualisation Workflow#
The thesis.workflows.qc package provides a self-registered QC workflow
that generates ROI overlay figures, track density maps, and tractography
statistics. Invoked via thesis run -w qc -p <patient_id>.
thesis.workflows.qc#
QC visualisation and statistics workflow.
Provides headless figure generation for ROI overlay inspection, track
density map review, and tractography statistics collection. Registered
as the qc workflow, invoked via thesis run -w qc -p <patient_id>.
Example
>>> from thesis.workflows.qc import (
... generate_roi_overlays,
... generate_track_density_figures,
... collect_tractography_stats,
... )
- thesis.workflows.qc.generate_roi_overlays(roi_paths, background_image, output_dir, display_mode='ortho')[source]
Generate QC overlay PNGs for each ROI mask on a background image.
Produces one PNG per ROI mask plus a combined figure showing all ROIs in different colours. Runs headlessly (
Aggbackend).- Parameters:
roi_paths (
Mapping[str,Union[str,Path]]) – Mapping of ROI name to NIfTI mask path.background_image (
Union[str,Path]) – T1w (or FA) image used as the anatomical background.output_dir (
Union[str,Path]) – Directory to write PNGs into. Created if it does not exist.display_mode (
str) – nilearndisplay_modeparameter ("ortho","x","y","z").
- Return type:
- Returns:
List of
Pathobjects for every generated PNG.- Raises:
ImportError – If nibabel or nilearn is not installed.
FileNotFoundError – If the background image or any ROI mask does not exist.
ValueError – If
roi_pathsis empty.
Example
>>> paths = generate_roi_overlays( ... roi_paths={"seed": "seed.nii.gz"}, ... background_image="T1w.nii.gz", ... output_dir="qc/roi_overlays", ... )
- thesis.workflows.qc.generate_track_density_figures(fdt_paths, template_image, output_dir, thresholds=None, display_mode='ortho', prefix='track_density')[source]
Generate QC figures overlaying track density on a template image.
For every requested percentile threshold the track density map is thresholded at that percentile of non-zero voxels and overlaid on the template background. One PNG is produced per threshold.
- Parameters:
fdt_paths (
Union[str,Path]) – Path tofdt_paths.nii.gz(track density map).template_image (
Union[str,Path]) – Path to the background image (MNI template for template-space maps, T1w for subject-space maps).output_dir (
Union[str,Path]) – Directory to write PNGs into. Created if it does not exist.thresholds (
Optional[List[float]]) – Percentile thresholds (default[50, 90, 99]). Each value must be in the range (0, 100).display_mode (
str) – nilearndisplay_modeparameter.prefix (
str) – Filename prefix for the output PNGs.
- Return type:
- Returns:
List of
Pathobjects for every generated PNG.- Raises:
ImportError – If nibabel or nilearn is not installed.
FileNotFoundError – If fdt_paths or template_image does not exist.
ValueError – If thresholds contains values outside (0, 100).
Example
>>> figures = generate_track_density_figures( ... "fdt_paths.nii.gz", ... "MNI152_T1_2mm.nii.gz", ... "qc/normtracks", ... )
- thesis.workflows.qc.collect_tractography_stats(patient_output, patient_id=None, tractography_relpath='tractography/probtrackx2', tract_similarity_subdir='tract_similarity')[source]
Collect tractography statistics for a single subject.
Reads output files from a tractography run (ProbTrackX2 or MRtrix3) and returns a structured dictionary of statistics.
- Parameters:
patient_output (
Union[str,Path]) – Patient-level output directory (e.g.outputs/114823/).patient_id (
Optional[str]) – Optional patient identifier (included in the returned dict for convenience).tractography_relpath (
str) – Relative path underpatient_outputwhere the tractography run lives. Defaults to"tractography/probtrackx2". Use"tractography/mrtrix3"for the MRtrix3 backend.tract_similarity_subdir (
str) – Relative path underpatient_outputwhere thetract_similarity/tract_similarity_hcp_looworkflow wrotemetrics.json. Read opportunistically and attached underresult["tract_similarity"]when present.
- Returns:
patient_id— subject identifier (or"unknown").waytotal— total streamlines reaching all waypoints.subject_space— fdt_paths statistics in subject space.template_space— fdt_paths statistics in template space (empty dict if warped_streamlines not present).roi_voxel_counts— non-zero voxel count per ROI mask.tract_similarity— overlap / correlation / distance_mm / distribution / voxel_counts / thresholds blocks copied from<tract_similarity_subdir>/metrics.json(only present when that file exists).
- Return type:
- Raises:
FileNotFoundError – If patient_output does not exist.
Example
>>> stats = collect_tractography_stats("outputs/114823") >>> stats["waytotal"] 42351
- thesis.workflows.qc.collect_batch_stats(output_base, patient_ids=None, tractography_relpath='tractography/probtrackx2', tract_similarity_subdir='tract_similarity')[source]
Collect tractography statistics for multiple subjects.
If patient_ids is
None, discovers subject directories automatically by looking for subdirectories of output_base that contain<tractography_relpath>/.- Parameters:
output_base (
Union[str,Path]) – Parent directory containing per-subject output directories (e.g.outputs/).patient_ids (
Optional[List[str]]) – Explicit list of subject IDs. IfNone, auto-discovers from output_base.tractography_relpath (
str) – Relative path under each patient directory where the tractography run lives. Defaults to"tractography/probtrackx2". Use"tractography/mrtrix3"for the MRtrix3 backend.tract_similarity_subdir (
str) – Relative path under each patient directory wheretract_similarity/tract_similarity_hcp_loowrotemetrics.json. When present, that file’s content is attached undersubject["tract_similarity"].
- Return type:
- Returns:
List of per-subject statistics dictionaries (same format as
collect_tractography_stats()).
Example
>>> all_stats = collect_batch_stats("outputs/") >>> for s in all_stats: ... print(s["patient_id"], s["waytotal"])
- thesis.workflows.qc.format_stats_table(stats_list, include_roi_counts=False)[source]
Format tractography statistics as a human-readable text table.
- Parameters:
- Return type:
- Returns:
Formatted multi-line string suitable for terminal output.
Example
>>> print(format_stats_table(all_stats)) Patient Waytotal Nonzero Vox Mean Dens Max Dens Vol Frac ------- -------- ----------- --------- -------- -------- 114823 42351 12834 3.30 287 0.0128
- thesis.workflows.qc.run_post_workflow_qc(cfg, ctx_obj)[source]
Generate QC overlays after workflow completion if configured.
This is a best-effort step — QC failures are logged as warnings but do not cause the pipeline run to fail.
Intended to be called by the CLI after a successful
thesis runwhenqc.generate_overlaysisTrue.- Parameters:
cfg (
PipelineConfig) – Loaded pipeline configuration.ctx_obj (
ProcessingContext) – Processing context for the patient run.
- Return type:
- thesis.workflows.qc.generate_connectivity_map_figures(tractography_dir, background_image, output_dir, thresholds=None, space='subject')[source]
Generate overlay figures for
seeds_to_<target>.nii.gzmaps.- Parameters:
tractography_dir (
Union[str,Path]) – ProbTrackX2 output directory.background_image (
Union[str,Path]) – Anatomical background for the overlay.output_dir (
Union[str,Path]) – Directory to write PNGs into.thresholds (
Optional[List[float]]) – Percentile thresholds (default[50, 90]).space (
str) – Label for filenames ("subject"or"template").
- Return type:
- Returns:
List of generated PNG paths.
- thesis.workflows.qc.collect_connectivity_map_stats(tractography_dir)[source]
Collect density statistics for each
seeds_to_<target>map.
- thesis.workflows.qc.parse_synthseg_qc_csv(patient_output, threshold=0.6)[source]
Parse a SynthSeg
_qc.csvfile and flag low-quality subjects.
- thesis.workflows.qc.parse_synthseg_volumes_csv(patient_output)[source]
Parse SynthSeg volumes CSV and flag anatomically unreasonable values.
- thesis.workflows.qc.generate_synthseg_overlay(patient_output, background_image, output_dir)[source]
Generate a colour-coded SynthSeg label overlay on T1w.
- thesis.workflows.qc.generate_roi_transform_comparison(tractography_dir, background_image, output_dir)[source]
Compare original and transformed ROI masks.
Generates overlay figures and computes volume change and centroid shift for each ROI that has both an original and transformed version.
- Parameters:
- Return type:
- Returns:
Tuple of (list of PNG paths, list of comparison dicts).
- thesis.workflows.qc.validate_waypoints_file(tractography_dir, reference_image=None)[source]
Validate waypoints text files.
Checks that every path listed in waypoints files exists and (if a reference image is provided) has compatible dimensions.
- Parameters:
- Return type:
- Returns:
Dict with
files_checked,total_paths,missing,dimension_mismatches,valid.
- thesis.workflows.qc.generate_brain_mask_overlay(brain_mask, background_image, output_dir)[source]
Generate an overlay of the brain mask on an anatomical image.
- thesis.workflows.qc.compute_jacobian_stats(warp_field, output_dir=None)[source]
Compute Jacobian determinant statistics from a warp field.
- Parameters:
- Return type:
- Returns:
Dict with
negative_voxels,negative_fraction,min_jacobian,max_jacobian,mean_jacobian. Empty dict if warp field cannot be loaded.
- thesis.workflows.qc.generate_bedpostx_overlay(bedpostx_dir, background_image, output_dir)[source]
Generate an overlay of f1 (primary fibre fraction) and report stats.
- Parameters:
- Return type:
- Returns:
Tuple of (list of PNG paths, stats dict with mean_f1, etc.).
- thesis.workflows.qc.generate_waytotal_overlay(tractography_dir, template_image, output_dir)[source]
Generate overlay for template-space
waytotal.nii.gz.
- thesis.workflows.qc.detect_batch_outliers(stats_list, sd_threshold=2.0)[source]
Flag subjects whose metrics are outliers relative to the batch.
thesis.workflows.qc.workflow#
QC visualisation and statistics workflow.
Produces ROI overlay PNGs, track density figures, and tractography statistics for a patient whose HCP workflow has already completed.
Usage:
thesis run -w qc -p 114823 -c default
- thesis.workflows.qc.workflow.verify_requirements(config, context)[source]#
Verify the patient has tractography output from a prior backend run.
- Parameters:
config (
PipelineConfig)context (
ProcessingContext)
- Return type:
- thesis.workflows.qc.workflow.build_workflow(*, qc_dir, config, context)[source]#
Build a single-Function-node QC workflow.
The Function node deserialises the config, builds a fresh context, and delegates to
run_qc_for_patientwhich has its own path-discovery fallbacks for T1 and tractography outputs.- Parameters:
qc_dir (
Path)config (
PipelineConfig)context (
ProcessingContext)
- Return type:
Workflow
thesis.workflows.qc.operations#
QC workflow operations and helpers.
Contains the post-workflow QC hook, ROI discovery logic, and background
image resolution that support both the thesis run -w qc workflow
and the automatic post-HCP QC generation.
- thesis.workflows.qc.operations.discover_roi_files(tractography_dir)[source]#
Discover ROI NIfTI masks from patient tractography output.
Searches
rois_merged/,rois_transformed/, androis/(in that order), returning the first non-empty set found. Descends one level into per-atlas-source subdirectories (e.g.rois/main/) so naming-convention runs withatlas_sources[].name != "atlas"are picked up.
- thesis.workflows.qc.operations.resolve_background_image(config, context)[source]#
Resolve the T1w background image for a patient.
Uses the same resolution logic as the HCP workflow (
resolve_t1_path) so paths that work forthesis runalso work for QC.- Parameters:
config (
PipelineConfig) – Loaded pipeline config.context (
ProcessingContext) – Processing context (carries patient_id, input_dir, etc.).
- Return type:
- Returns:
Path to the T1w image, or
Noneif it cannot be resolved.
- thesis.workflows.qc.operations.run_post_workflow_qc(cfg, ctx_obj)[source]#
Generate QC overlays after workflow completion if configured.
This is a best-effort step — QC failures are logged as warnings but do not cause the pipeline run to fail.
Intended to be called by the CLI after a successful
thesis runwhenqc.generate_overlaysisTrue.- Parameters:
cfg (
PipelineConfig) – Loaded pipeline configuration.ctx_obj (
ProcessingContext) – Processing context for the patient run.
- Return type:
- thesis.workflows.qc.operations.run_qc_for_patient(config, context, background=None, track_density=True, stats=True)[source]#
Run full QC suite for a single patient.
This is the core logic used by both the
qcworkflow’s Nipype Function node and any future CLI invocation.- Parameters:
config (
PipelineConfig) – Loaded pipeline configuration.context (
ProcessingContext) – Processing context.background (
Optional[Path]) – Override background image. IfNone, resolved from config.track_density (
bool) – Generate track density overlay figures.stats (
bool) – Print tractography statistics.
- Return type:
- Returns:
List of output file paths (PNGs) generated.
thesis.workflows.qc.roi_overlay#
Generate QC overlay figures for ROI masks on anatomical backgrounds.
All plotting is performed with the Agg backend so the module works
in headless environments (e.g. compute nodes without an X server).
Example
>>> from thesis.workflows.qc.roi_overlay import generate_roi_overlays
>>> paths = generate_roi_overlays(
... roi_paths={"seed": Path("seed.nii.gz"), "waypoint": Path("wp.nii.gz")},
... background_image=Path("T1w.nii.gz"),
... output_dir=Path("qc/roi_overlays"),
... )
- thesis.workflows.qc.roi_overlay.generate_roi_overlays(roi_paths, background_image, output_dir, display_mode='ortho')[source]#
Generate QC overlay PNGs for each ROI mask on a background image.
Produces one PNG per ROI mask plus a combined figure showing all ROIs in different colours. Runs headlessly (
Aggbackend).- Parameters:
roi_paths (
Mapping[str,Union[str,Path]]) – Mapping of ROI name to NIfTI mask path.background_image (
Union[str,Path]) – T1w (or FA) image used as the anatomical background.output_dir (
Union[str,Path]) – Directory to write PNGs into. Created if it does not exist.display_mode (
str) – nilearndisplay_modeparameter ("ortho","x","y","z").
- Return type:
- Returns:
List of
Pathobjects for every generated PNG.- Raises:
ImportError – If nibabel or nilearn is not installed.
FileNotFoundError – If the background image or any ROI mask does not exist.
ValueError – If
roi_pathsis empty.
Example
>>> paths = generate_roi_overlays( ... roi_paths={"seed": "seed.nii.gz"}, ... background_image="T1w.nii.gz", ... output_dir="qc/roi_overlays", ... )
thesis.workflows.qc.track_density#
Generate QC figures for track density maps on template backgrounds.
Overlays fdt_paths.nii.gz on a template (or subject) image at
configurable percentile thresholds. All plotting uses the Agg
backend so the module works in headless environments.
Example
>>> from thesis.workflows.qc.track_density import generate_track_density_figures
>>> paths = generate_track_density_figures(
... fdt_paths=Path("fdt_paths.nii.gz"),
... template_image=Path("MNI152_T1_2mm.nii.gz"),
... output_dir=Path("qc/normtracks"),
... thresholds=[50.0, 90.0, 99.0],
... )
- thesis.workflows.qc.track_density.generate_track_density_figures(fdt_paths, template_image, output_dir, thresholds=None, display_mode='ortho', prefix='track_density')[source]#
Generate QC figures overlaying track density on a template image.
For every requested percentile threshold the track density map is thresholded at that percentile of non-zero voxels and overlaid on the template background. One PNG is produced per threshold.
- Parameters:
fdt_paths (
Union[str,Path]) – Path tofdt_paths.nii.gz(track density map).template_image (
Union[str,Path]) – Path to the background image (MNI template for template-space maps, T1w for subject-space maps).output_dir (
Union[str,Path]) – Directory to write PNGs into. Created if it does not exist.thresholds (
Optional[List[float]]) – Percentile thresholds (default[50, 90, 99]). Each value must be in the range (0, 100).display_mode (
str) – nilearndisplay_modeparameter.prefix (
str) – Filename prefix for the output PNGs.
- Return type:
- Returns:
List of
Pathobjects for every generated PNG.- Raises:
ImportError – If nibabel or nilearn is not installed.
FileNotFoundError – If fdt_paths or template_image does not exist.
ValueError – If thresholds contains values outside (0, 100).
Example
>>> figures = generate_track_density_figures( ... "fdt_paths.nii.gz", ... "MNI152_T1_2mm.nii.gz", ... "qc/normtracks", ... )
thesis.workflows.qc.statistics#
Collect tractography statistics from per-patient output directories.
Reads waytotal, fdt_paths.nii.gz, and ROI mask files to produce
a structured summary of tractography results for one or more subjects.
Works with both ProbTrackX2 (tractography/probtrackx2, the default)
and MRtrix3 (tractography/mrtrix3) layouts via the
tractography_relpath parameter.
For cross-method comparison the collector also emits a unified Normalized Connection Strength (NCS) per subject and per hemisphere:
ProbTrackX2:
NCS = target_total_streamlines / (n_samples × |seed_voxels|)— per-sample arrival probability in[0, 1].MRtrix3:
NCS = mu × target_total_streamlines— Fibre Bundle Capacity (units of fibre cross-sectional area). The target sum is already SIFT2-weighted (the per-target TDI uses-tck_weights_in), so multiplying bymufromsift2_mu.txtgives the FBC value.
The two values are different physical quantities; cross-method comparison should use Spearman rank / z-scored profiles, not absolute equality.
Example
>>> from thesis.workflows.qc.statistics import collect_tractography_stats
>>> stats = collect_tractography_stats(Path("outputs/114823"))
>>> print(stats["waytotal"])
42351
- thesis.workflows.qc.statistics.collect_tractography_stats(patient_output, patient_id=None, tractography_relpath='tractography/probtrackx2', tract_similarity_subdir='tract_similarity')[source]#
Collect tractography statistics for a single subject.
Reads output files from a tractography run (ProbTrackX2 or MRtrix3) and returns a structured dictionary of statistics.
- Parameters:
patient_output (
Union[str,Path]) – Patient-level output directory (e.g.outputs/114823/).patient_id (
Optional[str]) – Optional patient identifier (included in the returned dict for convenience).tractography_relpath (
str) – Relative path underpatient_outputwhere the tractography run lives. Defaults to"tractography/probtrackx2". Use"tractography/mrtrix3"for the MRtrix3 backend.tract_similarity_subdir (
str) – Relative path underpatient_outputwhere thetract_similarity/tract_similarity_hcp_looworkflow wrotemetrics.json. Read opportunistically and attached underresult["tract_similarity"]when present.
- Returns:
patient_id— subject identifier (or"unknown").waytotal— total streamlines reaching all waypoints.subject_space— fdt_paths statistics in subject space.template_space— fdt_paths statistics in template space (empty dict if warped_streamlines not present).roi_voxel_counts— non-zero voxel count per ROI mask.tract_similarity— overlap / correlation / distance_mm / distribution / voxel_counts / thresholds blocks copied from<tract_similarity_subdir>/metrics.json(only present when that file exists).
- Return type:
- Raises:
FileNotFoundError – If patient_output does not exist.
Example
>>> stats = collect_tractography_stats("outputs/114823") >>> stats["waytotal"] 42351
- thesis.workflows.qc.statistics.collect_batch_stats(output_base, patient_ids=None, tractography_relpath='tractography/probtrackx2', tract_similarity_subdir='tract_similarity')[source]#
Collect tractography statistics for multiple subjects.
If patient_ids is
None, discovers subject directories automatically by looking for subdirectories of output_base that contain<tractography_relpath>/.- Parameters:
output_base (
Union[str,Path]) – Parent directory containing per-subject output directories (e.g.outputs/).patient_ids (
Optional[List[str]]) – Explicit list of subject IDs. IfNone, auto-discovers from output_base.tractography_relpath (
str) – Relative path under each patient directory where the tractography run lives. Defaults to"tractography/probtrackx2". Use"tractography/mrtrix3"for the MRtrix3 backend.tract_similarity_subdir (
str) – Relative path under each patient directory wheretract_similarity/tract_similarity_hcp_loowrotemetrics.json. When present, that file’s content is attached undersubject["tract_similarity"].
- Return type:
- Returns:
List of per-subject statistics dictionaries (same format as
collect_tractography_stats()).
Example
>>> all_stats = collect_batch_stats("outputs/") >>> for s in all_stats: ... print(s["patient_id"], s["waytotal"])
- thesis.workflows.qc.statistics.format_stats_table(stats_list, include_roi_counts=False)[source]#
Format tractography statistics as a human-readable text table.
- Parameters:
stats_list (
List[Dict[str,Any]]) – List of per-subject statistics dictionaries (fromcollect_tractography_stats()orcollect_batch_stats()).include_roi_counts (
bool) – IfTrue, append per-ROI voxel counts below the main table.
- Return type:
- Returns:
Formatted multi-line string suitable for terminal output.
Example
>>> print(format_stats_table(all_stats)) Patient Waytotal Nonzero Vox Mean Dens Max Dens Vol Frac ------- -------- ----------- --------- -------- -------- 114823 42351 12834 3.30 287 0.0128
thesis.workflows.qc.checks#
Extended QC checks for pipeline outputs.
Covers per-target connectivity maps, SynthSeg quality, ROI transform comparison, waypoints validation, brain mask overlay, warp field Jacobian analysis, BedpostX fibre quality, and cross-subject outlier detection.
All plotting uses the Agg backend for headless rendering.
- thesis.workflows.qc.checks.generate_connectivity_map_figures(tractography_dir, background_image, output_dir, thresholds=None, space='subject')[source]#
Generate overlay figures for
seeds_to_<target>.nii.gzmaps.- Parameters:
tractography_dir (
Union[str,Path]) – ProbTrackX2 output directory.background_image (
Union[str,Path]) – Anatomical background for the overlay.output_dir (
Union[str,Path]) – Directory to write PNGs into.thresholds (
Optional[List[float]]) – Percentile thresholds (default[50, 90]).space (
str) – Label for filenames ("subject"or"template").
- Return type:
- Returns:
List of generated PNG paths.
- thesis.workflows.qc.checks.collect_connectivity_map_stats(tractography_dir)[source]#
Collect density statistics for each
seeds_to_<target>map.
- thesis.workflows.qc.checks.parse_synthseg_qc_csv(patient_output, threshold=0.6)[source]#
Parse a SynthSeg
_qc.csvfile and flag low-quality subjects.
- thesis.workflows.qc.checks.parse_synthseg_volumes_csv(patient_output)[source]#
Parse SynthSeg volumes CSV and flag anatomically unreasonable values.
- thesis.workflows.qc.checks.generate_synthseg_overlay(patient_output, background_image, output_dir)[source]#
Generate a colour-coded SynthSeg label overlay on T1w.
- thesis.workflows.qc.checks.generate_roi_transform_comparison(tractography_dir, background_image, output_dir)[source]#
Compare original and transformed ROI masks.
Generates overlay figures and computes volume change and centroid shift for each ROI that has both an original and transformed version.
- Parameters:
- Return type:
- Returns:
Tuple of (list of PNG paths, list of comparison dicts).
- thesis.workflows.qc.checks.validate_waypoints_file(tractography_dir, reference_image=None)[source]#
Validate waypoints text files.
Checks that every path listed in waypoints files exists and (if a reference image is provided) has compatible dimensions.
- Parameters:
- Return type:
- Returns:
Dict with
files_checked,total_paths,missing,dimension_mismatches,valid.
- thesis.workflows.qc.checks.generate_brain_mask_overlay(brain_mask, background_image, output_dir)[source]#
Generate an overlay of the brain mask on an anatomical image.
- thesis.workflows.qc.checks.compute_jacobian_stats(warp_field, output_dir=None)[source]#
Compute Jacobian determinant statistics from a warp field.
- Parameters:
- Return type:
- Returns:
Dict with
negative_voxels,negative_fraction,min_jacobian,max_jacobian,mean_jacobian. Empty dict if warp field cannot be loaded.
- thesis.workflows.qc.checks.generate_bedpostx_overlay(bedpostx_dir, background_image, output_dir)[source]#
Generate an overlay of f1 (primary fibre fraction) and report stats.
- Parameters:
- Return type:
- Returns:
Tuple of (list of PNG paths, stats dict with mean_f1, etc.).
- thesis.workflows.qc.checks.generate_waytotal_overlay(tractography_dir, template_image, output_dir)[source]#
Generate overlay for template-space
waytotal.nii.gz.