Skip to content

Reproducible Research with RTpipeline

A guide to ensuring methodological rigor and reproducibility in radiotherapy data science

This document provides guidance for researchers—particularly PhD students and postdocs—on how to leverage RTpipeline's features to produce reproducible, publication-ready analyses.


Why Reproducibility Matters

In radiotherapy research, reproducibility failures often stem from:

  1. Undocumented preprocessing - DVH interpolation methods, dose grid resolutions, and structure definitions are not specified
  2. Environment drift - Package versions change between analysis runs
  3. Configuration ambiguity - Key parameters (e.g., radiomics bin width) are buried in code
  4. Data provenance gaps - No clear link between raw DICOM and final features

RTpipeline addresses these by encoding all preprocessing decisions in version-controlled configuration files and providing complete audit trails.


The Reproducibility Checklist

Use this checklist before submitting a manuscript or thesis:

Essential Elements

  • [ ] RTpipeline version recorded (git commit hash or release tag)
  • [ ] Complete config.yaml saved with results
  • [ ] Conda environment exported (conda env export > environment.yml)
  • [ ] QC reports reviewed and saved
  • [ ] Structure mapping dictionary documented
  • [ ] Random seeds fixed (where applicable)

For Radiomics Studies

  • [ ] PyRadiomics parameters documented (radiomics_params.yaml)
  • [ ] IBSI feature definitions referenced
  • [ ] Robustness analysis (ICC, CoV) completed
  • [ ] Perturbation chain parameters recorded

For Multi-Center Studies

  • [ ] Shared configuration bundle created and versioned
  • [ ] All centers record the same released version or immutable commit
  • [ ] Structure mapping harmonized across sites
  • [ ] QC criteria applied consistently

Configuration as Protocol

The Key Principle

Your config.yaml IS your preprocessing protocol. It should be:

  • Version-controlled (git)
  • Published as supplementary material
  • Citable (via DOI if possible)

Example Configuration Header

# RTpipeline Configuration for Prostate Toxicity Study
# Protocol Version: 1.2
# Date: 2025-01-15
# DOI: 10.5281/zenodo.XXXXXXX
#
# This configuration defines the complete preprocessing pipeline
# for the PROTECT-01 multi-center prostate radiotherapy study.
#
# Modifications from v1.1:
# - Updated ICC threshold from 0.75 to 0.90 per reviewer feedback
# - Added V75Gy metric for rectal dose-response analysis

version: "1.2"
study_id: "PROTECT-01"

Key Configuration Sections to Document

# Structure harmonization
structure_mapping:
  RECTUM:
    patterns: ["Rectum*", "RECT*", "rectum*"]
    required: true
    # Documentation: Maps all local rectum names to canonical RECTUM

# DVH computation parameters
dvh:
  interpolation: "linear"  # Method for DVH curve interpolation
  dose_units: "Gy"         # Absolute dose units
  volume_type: "relative"  # Relative (%) or absolute (cc)
  metrics:
    - Dmean
    - Dmax
    - D2cc
    - V70Gy

# Radiomics extraction
radiomics:
  binWidth: 25              # Fixed bin width in HU; report this project choice
  resampling_mm: [1, 1, 3]  # Isotropic x/y, 3mm z
  interpolator: "sitkBSpline"

# Robustness assessment
radiomics_robustness:
  enabled: true
  thresholds:
    icc:
      robust: 0.90      # Features with ICC >= 0.90 classified as robust
      acceptable: 0.75
    cov:
      robust_pct: 10.0  # Features with CoV <= 10% classified as robust

Environment Reproducibility

Dual Environment Architecture

RTpipeline uses two conda environments to resolve dependency conflicts:

Environment Purpose Key Dependencies
rtpipeline Main processing NumPy 2.x, TotalSegmentator, SimpleITK
rtpipeline-radiomics Radiomics extraction NumPy 1.x, PyRadiomics, opencv-python-headless==4.8.1.78, rt-utils==1.2.7 (--no-deps)

RTSTRUCT rasterization

Radiomics and robustness convert RTSTRUCT contours to CT-grid masks through rt-utils/cv2.fillPoly. Reproducible runs require one OpenCV provider: opencv-python-headless==4.8.1.78 in the isolated NumPy 1.26 radiomics runtime. Install rt-utils==1.2.7 separately with python -m pip install --no-deps "rt-utils==1.2.7" after creating either radiomics YAML environment. NumPy, pydicom and dataclasses (declared by rt-utils) are explicit dependencies. Docker and scripts/install_local.sh perform this step. The Apptainer definition converts the Docker image published by this repository’s release workflow. Its mandatory %post environment check fails the build if the radiomics interpreter or pinned dependencies are missing or incompatible. Do not install RTpipeline with dependency resolution in this helper environment: use the source through PYTHONPATH or install the package with --no-deps.

RT-utils declares the GUI opencv-python distribution even though it uses only cv2. Therefore ordinary pip check reports that intentionally absent provider. Run PYTHONPATH=. python scripts/check_radiomics_environment.py in the radiomics environment; it validates the active rasterizer and permits only that one known metadata mismatch. Other dependency errors remain fatal. Third-party metadata is retained unchanged. The local installer removes competing provider wheels before reinstalling headless, because their installed files overlap.

Before radiomics RTSTRUCT mask preparation, the runtime verifies rt-utils 1.2.7, OpenCV 4.8.1, the sole pinned provider and the exact 23-pixel reference-triangle mask with vertices (8,9), (8,13), (14,5). OpenCV 4.11.0 fills 21 pixels on this example. A mismatch raises an error; there is no bypass. Launch radiomics and robustness in the radiomics runtime. CT calls from NumPy 2 re-execute the whole course driver in the isolated radiomics interpreter, resolved through RTPIPELINE_RADIOMICS_ENV_PREFIX or the existing environment discovery. A missing interpreter fails before course processing. NIfTI mask loading does not fill polygons.

The main NumPy 2 runtime pins opencv-python==5.0.0.93, matching the production main runtime. Main stages record imported OpenCV version, provider distribution name and version, module path, and probe errors in their existing provenance or completion records. They do not impose the radiomics 4.8.1 guard. Its RTSTRUCT utilities fill masks for custom structures, ROI repair, QC, review images and DVH crop labels. Its RTSTRUCT writers use OpenCV contour tracing. These paths retain their existing algorithms and do not establish equivalence to the radiomics mask convention.

Python environment launches set PYTHONNOUSERSITE=1, while retaining the existing PYTHONPATH and multiprocessing socket hook. Radiomics tables and completion receipts record rasterizer_backend, rasterizer_opencv_version, rasterizer_opencv_path, rasterizer_rt_utils_version, rasterizer_providers, and rasterizer_probe_error. Robustness tables and source-disposition sidecars also record these identities. The environment fingerprint includes the imported cv2 version, provider metadata, rt-utils version and reference mask; the absolute module path is recorded separately. Polygon rows are reusable only with the recorded single headless pin and rt-utils 1.2.7. Missing or different identities invalidate resume, checkpoints, publication, completion receipts, partial refresh, and cohort aggregation. Historical polygon outputs without the pinned rasterizer identity are not republished by this release. NIfTI and DICOM-SEG rows use distinct no-polygon-fill backend labels and do not require the polygon pin for reuse. These records describe the runtime used for the measurement; they do not establish clinical accuracy.

Custom structures are derived only in the main runtime. Its locked builder publishes RS_custom.dcm and records OpenCV 5.0.0 in rs_custom_meta.json. Radiomics and robustness consume that shared polygon artifact only when the code, configuration, source identities and main-runtime builder record are current. They never rebuild it. Missing, stale or legacy custom structures fail closed when required for extraction. The workflow orders radiomics after DVH, which builds the custom structures; robustness follows radiomics. The shared polygons are filled with OpenCV 4.8.1 for radiomics measurement. Derivation and final measurement rasterization therefore have separate, explicit runtime identities.

Exporting Environments

# Export main environment
conda activate rtpipeline
conda env export --no-builds > rtpipeline-environment.yml

# Export radiomics environment
conda activate rtpipeline-radiomics
conda env export --no-builds > rtpipeline-radiomics-environment.yml

Docker for Complete Reproducibility

For maximum reproducibility, use the Docker image with a specific version tag and record its digest.

# Use a specific version, not :latest
docker pull kstawiski/rtpipeline:3.0.0

# Record the image digest
docker inspect --format='{{.RepoDigests}}' kstawiski/rtpipeline:3.0.0

Document in your methods:

"All preprocessing was performed using RTpipeline Docker image kstawiski/rtpipeline:3.0.0 (SHA256: abc123...)."

Also record whether radiomics and robustness were executed through the default dual-environment setup (rtpipeline + rtpipeline-radiomics) or a custom local installation. When structures come from RTsegmentator, keep each course's Segmentation_CustomModels/rtseg_<model>/manifest.json and report the RTsegmentator version, task names and model references it records.


Methods Section Templates

Template 1: DVH Extraction

**Dose-Volume Histogram Analysis**

DVH metrics were extracted using RTpipeline (version 3.0.0; DOI: [release DOI]) [1].
DICOM RTSTRUCT, RTDOSE, and RTPLAN files were exported from [TPS name/version]
and processed using the following standardized protocol:

1. **Structure Harmonization:** Contour names were mapped to canonical nomenclature
   using a predefined dictionary (Supplementary Table S1). Structures named
   [list variations] were mapped to the canonical label [STRUCTURE_NAME].

2. **DVH Computation:** Cumulative DVH curves were computed using [interpolation
   method] interpolation with a dose resolution of [X] Gy. The following metrics
   were derived:
   - Mean dose (D_mean)
   - Maximum dose (D_max)
   - Dose to hottest 2cc (D_2cc)
   - Volume receiving ≥X Gy (V_XGy)

3. **Quality Control:** Cases with [QC criteria] were flagged and reviewed.
   [N] cases were excluded due to [reasons].

The complete preprocessing configuration is available as Supplementary File S2.

[1] RTpipeline software citation (see repository `CITATION.cff`).

Template 2: Radiomics with Robustness Assessment

**Radiomic Feature Extraction and Stability Assessment**

CT radiomic features were extracted using RTpipeline (version 3.0.0; DOI: [release DOI]) [1] with
PyRadiomics (version 3.0.1) [2] following Image Biomarker Standardisation
Initiative (IBSI) recommendations [3].

**Preprocessing:**
- Images were resampled to [X × X × X mm] isotropic voxels using
  [interpolation method]
- Intensity discretization used a fixed bin width of [X] HU
- [CT cropping method, if applicable]

**Feature Extraction:**
A total of [N] features were extracted from [structure(s)], comprising:
- Shape features (N = X)
- First-order statistics (N = X)
- Texture features: GLCM (N = X), GLRLM (N = X), GLSZM (N = X), GLDM (N = X)
- [Filtered features if applicable]

**Robustness Assessment:**
Feature stability was assessed using an RTpipeline-adapted NTCV chain inspired
by Zwanenburg et al. [4], not an exact reimplementation. For each ROI, [N]
perturbations were required:
- Gaussian noise injection (σ = 0, 10, 20 HU)
- Rigid translations (up to +/-4 mm)
- Reproducible random physical-space contour offsets (2 realizations)
- Distance-ranked volume adaptation to the closest voxel counts representing ±15%

Intraclass correlation coefficients ICC(3,1) were computed using Pingouin [5].
CoV was computed separately within each patient/course/ROI/source across
perturbations, then summarized by its cohort median; it was not computed from
pooled between-patient values.
Features were classified as:
- **Robust:** ICC ≥ 0.90 and CoV ≤ 10%
- **Acceptable:** ICC ≥ 0.75 and CoV ≤ 20%
- **Poor:** ICC < 0.75 or CoV > 20%

[N] features ([X]%) met robustness criteria and were retained for modeling.

**References:**
[1] RTpipeline software citation (see repository `CITATION.cff`).
[2] van Griethuysen JJM, et al. Cancer Res. 2017;77(21):e104-e107.
[3] Zwanenburg A, et al. Radiology. 2020;295(2):328-338.
[4] Zwanenburg A, et al. Sci Rep. 2019;9:614.
[5] Vallat R. JOSS. 2018;3(31):1026.

Template 3: Multi-Center Harmonization

**Multi-Center Data Harmonization**

Data from [N] centers were processed using RTpipeline ([released version and tag, or immutable post-tag commit/container digest]) [1]. Each center processed local DICOM exports using shared configs and versioned artifacts (Supplementary File S3). These inputs make consistent local processing operationally possible, but do not prove identical processing unless a bound contract verifies it.

**Harmonization Protocol:**
1. **Structure Mapping:** A consortium-wide structure dictionary was defined
   (Supplementary Table S1) mapping site-specific nomenclature to [N] canonical
   structure labels.

2. **Preprocessing Standardization:**
   - CT resampling: [X × X × X mm]
   - Dose grid interpolation: [method]
   - Field-of-view cropping: [region]-based using TotalSegmentator landmarks

3. **Quality Control:**
   - All centers declared the same QC criteria; bound packet checks and site receipts verified the criteria actually used (Supplementary Table S2)
   - [N] cases across all sites were excluded due to [reasons]

4. **Feature Extraction:**
   - DVH metrics: [list]
   - Radiomics features: [list or reference to table]

**Data Governance:**
Raw DICOM data remained at each institution. RTpipeline's current packet workflow supports distributed aggregate radiomics reliability analysis. The packets contain cohort-level reliability statistics, not outcomes or model updates. They do not themselves establish federated model training, secure aggregation, differential privacy, privacy guarantees, or outcome federation. External federated learning requires a separately implemented downstream system.

The RTpipeline configuration bundle is available at [repository or archive URL].

[1] RTpipeline software citation (see repository `CITATION.cff`).

Template 4: Structures from RTsegmentator

**Automatic Segmentation**

[Structures] were segmented on the planning CT with [model name] ([model reference])
[2], run through RTsegmentator version [X.Y.Z] [3] (task `[task name]`). RTsegmentator
maps each prediction onto the acquired CT grid and releases an RTSTRUCT only after
checking its image references, physical bounds and a contour-to-mask round trip
(Dice ≥ 0.95). RTpipeline (version 3.0.0) [1] imported each result only after
verifying every packaged file against the result manifest's SHA-256 checksums and
confirming that the RTSTRUCT referenced the course's planning CT series. Automatic
contours were [reviewed by / not edited by] [role] before analysis. [N] courses had
no structure found by the model and were [handled as].

[1] RTpipeline software citation (see repository `CITATION.cff`).
[2] Publication of the segmentation model.
[3] RTsegmentator software citation (see its `CITATION.cff`).

The values for the bracketed fields are recorded per course in Segmentation_CustomModels/rtseg_<model>/manifest.json (RTsegmentator version, task, model reference, licence and limitations) and in metadata/rtsegmentator_imports.json (job ID and outcome for every model, including models that found nothing).


Provenance Tracking

RTpipeline Output Structure

Each processed course includes provenance metadata:

Output/{PatientID}/{CourseID}/
├── metadata/
│   ├── case_metadata.xlsx       # DICOM tag extraction
│   ├── processing_log.txt       # Complete processing log
│   ├── config_snapshot.yaml     # Config used for this case
│   └── cropping_metadata.json   # CT cropping boundaries
├── qc_reports/
│   └── qc_summary.json          # Quality control flags
└── ...

Linking Outputs to Inputs

import pandas as pd

# Load metadata with DICOM provenance
metadata = pd.read_excel("Output/Patient001/Course001/metadata/case_metadata.xlsx")

# Key provenance fields
print("Source DICOM UIDs:")
print(f"  CT Series: {metadata['ct_series_uid'].iloc[0]}")
print(f"  RTSTRUCT: {metadata['rtstruct_sop_uid'].iloc[0]}")
print(f"  RTDOSE: {metadata['dose_sop_uid'].iloc[0]}")
print(f"  RTPLAN: {metadata['plan_sop_uid'].iloc[0]}")

Reproducibility Validation

Regression Testing

When updating RTpipeline versions or configurations:

# Run on reference dataset
rtpipeline run --config reference_config.yaml --input test_data/ --output test_output_v2/

# Compare outputs
python -c "
import pandas as pd
import numpy as np

v1 = pd.read_excel('test_output_v1/_RESULTS/dvh_metrics.xlsx')
v2 = pd.read_excel('test_output_v2/_RESULTS/dvh_metrics.xlsx')

# Numerical columns
numeric_cols = v1.select_dtypes(include=[np.number]).columns

# Compare
diff = (v1[numeric_cols] - v2[numeric_cols]).abs()
max_diff = diff.max().max()
print(f'Maximum absolute difference: {max_diff}')

if max_diff > 1e-6:
    print('WARNING: Outputs differ!')
    print(diff[diff > 1e-6].dropna(how='all'))
"

Hash Verification

For critical analyses, compute checksums:

import hashlib
import pandas as pd

def compute_df_hash(filepath):
    """Compute SHA256 hash of a DataFrame."""
    df = pd.read_excel(filepath)
    return hashlib.sha256(
        pd.util.hash_pandas_object(df).values
    ).hexdigest()

# Record hashes
hashes = {
    "dvh_metrics": compute_df_hash("_RESULTS/dvh_metrics.xlsx"),
    "radiomics_ct": compute_df_hash("_RESULTS/radiomics_ct.xlsx")
}

# Save for reproducibility verification
import json
with open("output_hashes.json", "w") as f:
    json.dump(hashes, f, indent=2)

Publication Checklist

Before submitting your manuscript:

Data Availability Statement

**Data Availability**

The RTpipeline configuration files used in this study are available at
[DOI/GitHub URL]. Due to [privacy/IRB/GDPR] restrictions, the raw DICOM
data cannot be shared. Aggregated results tables are provided as
Supplementary Data.

Code Availability Statement

**Code Availability**

All data preprocessing was performed using RTpipeline version 3.0.0
(https://github.com/kstawiski/rtpipeline; archived release DOI: [release DOI]).
The complete configuration bundle, including structure mapping dictionaries and
radiomics parameters, is available at [repository or archive URL]. Analysis
scripts for statistical modeling are available at [repository URL]. Report the
git commit or container digest as well when the bytes you ran differ from the
release tag.

Supplementary Materials to Include

  1. config.yaml - Complete preprocessing configuration
  2. structure_mapping.yaml - Structure name harmonization dictionary
  3. radiomics_params.yaml - PyRadiomics extraction settings
  4. environment.yml - Conda environment specification
  5. qc_criteria.md - Quality control inclusion/exclusion criteria

Best Practices Summary

Area Recommendation
Version Control Git-track all configuration files
Environment Use Docker or export conda environments
Configuration Document all parameters in config files, not code
Provenance Save config snapshots with each analysis
QC Review and document all exclusions
Methods Use templates with specific parameter values
Sharing Publish config bundles with DOI

Further Reading

  • IBSI Standards - Image Biomarker Standardization Initiative
  • TRIPOD - Reporting guidelines for prediction models
  • CLAIM - Checklist for AI in medical imaging
  • Zwanenburg A, et al. (2019) "Assessing robustness of radiomic features by image perturbation." Sci Rep 9:614