SORAT (Segmentation Orchestration and Reproducible Analysis Toolkit) is a modular Nextflow pipeline for cardiac MRI segmentation that integrates multiple deep learning models. It enables reproducible orchestration, side-by-side comparison of segmentation results, and comprehensive evaluation metrics.
| Model | Description | Reference |
|---|---|---|
| CineMA | Convolutional Vision Transformer for cardiac MRI | CineMA Paper |
| nnFormer | 3D medical image segmentation transformer | Zhou et al., 2021 |
| VSA-3L | MONAI Ventricular Short Axis 3-Label model | MONAI Model Zoo |
| Atrial nnUNet | nnUNetv2 2D atrial segmentation model (Dataset001_LGE) | nnU-Net |
SAX models (cinema, nnformer, vsa3l) produce ACDC-compatible labels:
- 0: Background
- 1: Right Ventricle (RV)
- 2: Myocardium (MYO)
- 3: Left Ventricle (LV)
Atrial model (atrial_nnunet) produces atrial labels:
- 0: Background
- 1: Wall
- 2: Right Atrium (RA)
- 3: Left Atrium (LA)
curl -s https://get.nextflow.io | bashpython3 bin/sorat_setup.pyThis writes local overrides to .sorat/user.config (gitignored).
You can run SORAT directly with nextflow run main.nf, or use the helper runner:
./bin/sorat_run.sh [pipeline options]The helper runner uses main.nf from the repo root and reminds you to run setup if no local user config is present.
Using Default Inputs (Model-Dependent):
# SAX-only runs default to generated samplesheet from --sax_data_root
./bin/sorat_run.sh -profile local
# With SLURM + Apptainer on HPC
./bin/sorat_run.sh -profile slurm
# Atrial-only runs default when atrial root data is configured
export SORAT_ATRIAL_DATA_ROOT=/path/to/nnUNet_raw/Dataset001_LGE
./bin/sorat_run.sh --models atrial_nnunet -profile slurmNotes:
- If
--modelsincludes both SAX and atrial models,--inputis required. --models allis SAX-only.
Using Custom Data:
# Run locally (Apptainer/Singularity)
./bin/sorat_run.sh \
--input samplesheet.csv \
--outdir results \
--models all \
-profile local
# Run on SLURM (HPC)
./bin/sorat_run.sh \
--input samplesheet.csv \
--outdir results \
--models all \
-profile slurm
# Run specific models only
./bin/sorat_run.sh \
--input samplesheet.csv \
--outdir results \
--models cinema,nnformer \
-profile local# Pull SORAT images from GitHub Container Registry
export SORAT_GHCR_NAMESPACE="your-org"
apptainer pull sorat-cinema.sif docker://ghcr.io/${SORAT_GHCR_NAMESPACE}/sorat-cinema:latest
apptainer pull sorat-nnformer.sif docker://ghcr.io/${SORAT_GHCR_NAMESPACE}/sorat-nnformer:latest
apptainer pull sorat-vsa3l.sif docker://ghcr.io/${SORAT_GHCR_NAMESPACE}/sorat-vsa3l:latest
apptainer pull sorat-atrial-nnunet.sif docker://ghcr.io/${SORAT_GHCR_NAMESPACE}/sorat-atrial-nnunet:latestFor interactive use, open the Jupyter notebook:
jupyter notebook notebooks/run_sorat_pipeline.ipynbThe notebook provides:
- Visual data exploration
- Interactive model configuration
- Result visualization and comparison
- Direct model execution (without Nextflow)
Create a CSV file with the following columns:
| Column | Required | Description |
|---|---|---|
patient_id |
Yes | Unique patient identifier |
image |
Yes | Path to 4D cardiac MRI NIfTI file |
ground_truth |
No | Path to ground truth segmentation |
info_cfg |
No | Optional metadata config with ED/ES frame indices. When absent, the pipeline segments all temporal frames of the cardiac cycle. |
patient_id,image,ground_truth,info_cfg
patient101,/data/patient101/patient101_4d.nii.gz,/data/patient101,/data/patient101/Info.cfg
patient102,/data/patient102/patient102_4d.nii.gz,/data/patient102,/data/patient102/Info.cfg
patient103,/data/patient103/patient103_4d.nii.gz,,Use these templates when your directory structure does not match ACDC or MBAS conventions.
patient_id,image,ground_truth,info_cfg
case001,/data/custom/case001/image.nii.gz,,
case002,/data/custom/case002/image.nii.gz,,Use this when you only need inference. You can point image to any valid NIfTI path.
patient_id,image,ground_truth,info_cfg
case001,/data/custom/case001/image_4d.nii.gz,/data/custom/case001/label_dir,/data/custom/case001/Info.cfg
case002,/data/custom/case002/image_4d.nii.gz,/data/custom/case002/label_dir,Guidance:
- Always provide stable
patient_idvalues; they are used in outputs and comparisons. - Set
ground_truthwhen you want metrics/reporting; leave empty for inference-only runs. info_cfgis optional and mainly useful for CineMA ED/ES frame metadata.- When `info_cfg` is absent, the pipeline uses `frames_mode=auto` and segments all frames. Override with `--frames_mode ed_es` to force 2-frame mode.
- Keep column names unchanged even if your on-disk folder names are different.
The Info.cfg file specifies ED and ES frame indices:
ED: 0
ES: 12
Group: NOR
Height: 175
Weight: 70
| Parameter | Description |
|---|---|
--input |
Optional override for input samplesheet CSV (required for mixed SAX+atrial runs) |
| Parameter | Default | Description |
|---|---|---|
--outdir |
./results |
Output directory |
--models |
all |
Models to run: cinema, nnformer, vsa3l, atrial_nnunet, or all |
--default_inputs.sax |
null |
Preferred explicit default samplesheet for SAX-only runs (or SORAT_SAX_SAMPLESHEET) |
--sax_data_root |
null |
Preferred SAX data root used to auto-generate defaults (ACDC-style layout) (or SORAT_SAX_DATA_ROOT) |
--sax_data_split |
testing |
SAX split folder used with --sax_data_root when auto-generating defaults |
--default_inputs.atrial |
null |
Preferred explicit default samplesheet for atrial-only runs (or SORAT_ATRIAL_SAMPLESHEET) |
--atrial_nnunet.dataset_root |
null |
Preferred atrial data root for auto-generated defaults. Supports nnUNet layout (imagesTr/,labelsTr/) and MBAS-style layout (MBAS_###/MBAS_###_gt.nii.gz,MBAS_###_label.nii.gz) (or SORAT_ATRIAL_DATA_ROOT) |
--slurm_account |
null |
SLURM allocation/account (required when -profile slurm, can use SORAT_SLURM_ACCOUNT) |
--singularity_cache_dir |
$HOME/.singularity_cache |
Per-user Singularity cache location (can use SORAT_SINGULARITY_CACHEDIR) |
--compare |
true |
Generate comparison report |
--inference_only |
false |
Run inference only (skip metrics + report generation) |
--evaluation.label_schema |
architecture_default |
Evaluation contract; use atrial_binary_union when a binary biatrial reference is compared with atrial labels 1--3 collapsed to foreground |
--debug |
false |
Generate debug analytics report (execution/runtime/GPU/scalability/success + scientific utility metrics) |
--postprocess.enabled |
false |
Enable optional LV-intensity postprocessing (LV dark regions -> MYO) |
--postprocess.use_for_metrics |
true |
If postprocess enabled, compute metrics on corrected segmentations |
--postprocess.visualize |
true |
Generate before/after/delta postprocess visualizations |
--visualization.enabled |
true |
Generate ED/ES previews for each model output. If GT exists, previews include prediction-vs-GT overlays, per-structure DSC, and difference maps |
--feature_extraction.enabled |
false |
Enable interpretable feature extraction (volumes, wall thickness, radiomics) in the main workflow |
--feature_extraction.mask_source |
prompt |
Feature mask source: predictions, postprocess, or prompt |
--feature_extraction.virtualenv_path |
null |
Path to a pre-built virtualenv containing pyradiomics for feature extraction jobs |
--feature_extraction.require_virtualenv |
false |
If true, fail fast unless --feature_extraction.virtualenv_path is provided and valid |
--feature_extraction.samplesheet |
null |
Optional samplesheet override for -entry FEATURES_ONLY |
--feature_extraction.results_dir |
null |
Existing results directory to read segmentations from in -entry FEATURES_ONLY |
--slurm_max_forks |
30 |
Maximum concurrent task submissions in slurm profile |
--slurm_queue_size |
64 |
Max tasks queued/submitted to executor at once |
--slurm_submit_rate |
50/1min |
Submission throttling rate to reduce scheduler pressure |
--slurm_poll_interval |
30 sec |
Job-status polling interval |
--slurm_queue_stat_interval |
60 sec |
Queue-stat refresh interval |
--preprocess_cache_enabled |
true |
Reuse model-specific preprocessing outputs across reruns |
--preprocess_cache_dir |
${projectDir}/.cache/preprocess |
Cache root for reusable preprocessing artifacts |
--frames_mode |
'auto' |
Frame selection: auto (ED/ES when info_cfg present, else all frames), ed_es (force 2 frames), all (always all frames) |
--max_frames |
null |
Cap to N frames in all-frames mode by subsampling (e.g. 10) |
Note: --models all currently runs SAX models (cinema, nnformer, vsa3l) and does not automatically include atrial_nnunet.
When --input is omitted, SORAT resolves input automatically:
- SAX-only runs (
cinema,nnformer,vsa3l, orall) use--default_inputs.saxif set; otherwise SORAT generates a SAX default samplesheet from--sax_data_rootand--sax_data_split. - Atrial-only runs (
atrial_nnunet) use--default_inputs.atrialif set; otherwise SORAT auto-generates an atrial default samplesheet from--atrial_nnunet.dataset_root. - For MBAS-style atrial roots, SORAT automatically stages nnUNet-style paths (
imagesTr/*_0000.nii.gz,labelsTr/*.nii.gz) in.cache/generated_inputs/and uses those paths in the generated samplesheet. - Mixed SAX+atrial runs fail fast and require explicit
--input.
Use these options when needed:
--inference_only true: skip metrics/report generation.--evaluation.label_schema atrial_binary_union: evaluate the union of atrial prediction labels 1--3 against a binary foreground reference, such as AIM-ASD's biatrial mask.--debug true: generate debug analytics outputs.--preprocess_cache_enabled true|false: enable or disable preprocessing cache reuse.--postprocess.enabled true: enable optional LV -> MYO postprocessing.--feature_extraction.enabled true: emit per-mask feature CSV files.
For the locally available AIM-ASD subset, use the LGE image and binary biatrial reference samplesheet with the atrial model:
nextflow run main.nf \
--input data/aim_asd_samplesheet.csv \
--models atrial_nnunet \
--evaluation.label_schema atrial_binary_union \
--outdir results/AIM_ASDUse --inference_only true for prediction and preview generation without
metrics or comparison-report outputs.
For SLURM tuning, the main controls are --slurm_max_forks, --slurm_queue_size, --slurm_submit_rate, --slurm_poll_interval, and --slurm_queue_stat_interval.
nextflow run main.nf \
--input samplesheet.csv \
--models all \
--inference_only true \
--debug false \
-profile slurmUse -entry POSTPROCESS_ONLY to run postprocessing and delta visualizations from a prior segmentation run.
nextflow run main.nf \
-entry POSTPROCESS_ONLY \
-profile slurm \
--slurm_account <your-slurm-account> \
--models all \
--input /path/to/acdc_testing_samplesheet.csv \
--postprocess.results_dir /path/to/results \
--outdir /path/to/results_postprocess_onlyUse -entry FEATURES_ONLY to read existing segmentation masks from a previous run and produce feature CSVs without rerunning preprocess/segmentation.
Important path split:
--feature_extraction.results_dirpoints to an existing completed SORAT results directory that already has segmentation files.--outdiris where this new isolated run writes its outputs (features/,pipeline_info/, etc.).
nextflow run main.nf \
-entry FEATURES_ONLY \
-profile slurm \
--slurm_account <your-slurm-account> \
--models all \
--input /path/to/acdc_testing_samplesheet.csv \
--feature_extraction.enabled true \
--feature_extraction.mask_source predictions \
--feature_extraction.results_dir /path/to/results \
--outdir /path/to/results_features_onlynextflow run main.nf \
-entry FEATURES_ONLY \
-profile slurm \
--slurm_account <your-slurm-account> \
--models all \
--input /path/to/acdc_testing_samplesheet.csv \
--feature_extraction.enabled true \
--feature_extraction.mask_source postprocess \
--feature_extraction.results_dir /path/to/results \
--outdir /path/to/results_features_only_ppNote: On SLURM/non-interactive runs, set --feature_extraction.mask_source explicitly to avoid prompt fallback behavior.
Sockeye offline recommendation (one-time setup on login node):
module purge
module load CVMFS_CC apptainer/1.3.4
# Use the same Python ABI as the runtime container.
apptainer exec /path/to/SORAT/containers/sorat-cinema.sif \
python -m venv /path/to/venvs/sorat-features-container
apptainer exec /path/to/SORAT/containers/sorat-cinema.sif \
/path/to/venvs/sorat-features-container/bin/python -m pip install --upgrade pip
apptainer exec /path/to/SORAT/containers/sorat-cinema.sif \
/path/to/venvs/sorat-features-container/bin/python -m pip install pyradiomicsThen run with that virtualenv:
nextflow run main.nf \
-entry FEATURES_ONLY \
-profile slurm \
--slurm_account <your-slurm-account> \
--models all \
--input /path/to/acdc_testing_samplesheet.csv \
--feature_extraction.enabled true \
--feature_extraction.mask_source predictions \
--feature_extraction.virtualenv_path /path/to/venvs/sorat-features-container \
--feature_extraction.require_virtualenv true \
--feature_extraction.results_dir /path/to/results \
--outdir /path/to/results_features_onlyImportant compatibility note:
EXTRACT_FEATURESkeeps the container Python interpreter and only adds venvsite-packagesviaPYTHONPATH.- Do not rely on
source <venv>/bin/activateinside container tasks. - Your virtualenv must have
site-packagesfor the same Python major.minor as the container runtime.
results/
├── cinema/
│ ├── preprocessed/ # Preprocessed data
│ └── segmentations/ # Model predictions
├── nnformer/
│ ├── preprocessed/
│ └── segmentations/
├── vsa3l/
│ ├── preprocessed/
│ └── segmentations/
├── atrial_nnunet/
│ ├── preprocessed/
│ └── segmentations/
├── metrics/
│ ├── cinema/ # Per-model metrics
│ ├── nnformer/
│ ├── vsa3l/
│ └── atrial_nnunet/
├── comparison/
│ ├── aggregated_metrics.csv # All metrics combined
│ ├── model_comparison.csv # Model summary statistics
│ ├── per_patient_summary.csv # Per-patient comparison
│ ├── comparison_report.html # Interactive HTML report
│ └── figures/ # Visualization plots
├── previews/
│ ├── <model_tag>/*_ED_preview.png or *_frameNN_preview.png
│ └── <model_tag>/*_ES_preview.png or *_frameNN_preview.png
├── features/
│ └── *_features.csv
└── pipeline_info/
├── execution_timeline.html
├── execution_report.html
└── pipeline_dag.svg
└── debug/
├── text/
│ ├── debug_report.md
│ ├── debug_metrics_summary.json
│ ├── task_profile.csv
│ ├── gpu_profile.csv
│ └── metrics_snapshot.csv
└── figures/
├── runtime_by_process.png
├── gpu_walltime_by_process.png
└── overall_dice_by_model.png
| Profile | Description |
|---|---|
local |
Local execution with Apptainer/Singularity |
slurm |
SLURM execution with Apptainer |
test |
Quick test with minimal data |
# Local run
./bin/sorat_run.sh -profile local --input samplesheet.csv
# SLURM run
./bin/sorat_run.sh -profile slurm --input samplesheet.csvUse one execution profile at a time.
-
Out of Memory
# Increase memory allocation nextflow run main.nf --max_memory 64.GB --input samplesheet.csv -
GPU Not Detected
# Ensure NVIDIA runtime is configured docker run --gpus all nvidia/cuda:11.8-base nvidia-smi -
Path Issues
- Use absolute paths in samplesheet
- Ensure all input files are accessible
- Check execution logs:
results/pipeline_info/ - View work directory:
.nextflow/andwork/ - Enable verbose logging:
nextflow run main.nf -with-trace
If you use SORAT in your research, please cite:
@software{sorat2024,
title = {SORAT: Segmentation Orchestration and Reproducible Analysis Toolkit},
year = {2024},
url = {https://github.com/your-org/SORAT}
}Also cite the individual models you use:
- nnFormer: https://arxiv.org/abs/2109.03201
- MONAI VSA-3L (LV quantification reference): https://doi.org/10.1007/978-3-030-12029-0_40
- CineMA: https://arxiv.org/abs/2506.00679
This project is licensed under the MIT License - see the LICENSE file for details.