Detect, differentiate, and delineate radiation necrosis on post-contrast MRI.
rn-d3 is a registry-driven command-line toolkit for radiation necrosis (RN)
MRI workflows. It supports two connected use cases:
- Run RN segmentation inference with configured nnU-Net, Spark3D, and STU-Net model backends.
- Compare segmentation methods on matched image/mask registries using DSC, surface Dice, and lesion-detection metrics.
RN-D3 separates the workflow into two explicit inputs: registry JSON files
describe the image/mask cohort, and YAML model configs describe how each
checkpoint is run. Images, masks, and weights can live in local folders such as
data/ and weights/, or any other path referenced by those config files.
The associated paper is RN-D3: Detection, differentiation, and delineation of radiation necrosis on post-contrast MRI.
git clone https://github.com/pgsalome/rn-d3.git
cd rn-d3
python -m venv .venv
source .venv/bin/activate
pip install -e .Build a registry from image and mask lists:
rnd3 registry build \
--images image_paths.txt \
--masks mask_paths.txt \
--out registries/external.jsonCreate paired left-right and anterior-posterior augmentations:
rnd3 augment \
--registry registries/external.json \
--out-dir data/augmented \
--out-registry registries/external_augmented.jsonCompare existing prediction folders:
rnd3 compare \
--registry registries/external.json \
--pred-dir nnunet=predictions/nnunet \
--pred-dir spark3d=predictions/spark3d \
--pred-dir stunet=predictions/stunet \
--out-dir results/model_comparison \
--surface-mm 2.0Run one configured model on one image:
cp configs/models.local.example.yaml configs/models.local.yaml
rnd3 predict \
--model spark3d \
--image data/images/case001_t1ce.nii.gz \
--out outputs/spark3d_case001_mask.nii.gz \
--models-config configs/models.local.yaml- Study context
- How it works
- Registry format
- Data augmentation
- Compare segmentation models
- One-command inference
- Understand the output
- Data and weights
- Testing
- Citation
- License
Radiation necrosis is an important complication of radiation therapy and is challenging to assess radiographically because lesions are prone to inconsistent delineation and may require intracranial surgery for diagnosis. The RN-D3 study evaluated how established deep learning segmentation models can be retrained on an institutional RN cohort, tested for external generalization, and integrated into an end-to-end workflow for RN detection, differentiation, and delineation.
Models were trained on 52 patients with RN after proton beam radiation therapy at Massachusetts General Hospital between 2004 and 2016. External validation used 29 patients with 39 RN lesions from the MOLAB brain metastasis dataset, collected across five Spanish institutions between 2005 and 2021 after photon-based radiation therapy.
Six segmentation architectures were evaluated: nnU-Net, STU-Net, TRIAD PlainConv, Spark3D, SwinUNETR, and TRIAD SwinB. Performance was assessed with Dice similarity coefficient (DSC), surface Dice at 2 mm, and lesion detection rate. The best segmentation components were combined with an RN-versus-non-RN classifier using deep encoder features and shape radiomics.
Key external findings from the study were:
- Spark3D and STU-Net had the highest external DSC: Spark3D 0.69 [IQR 0.50-0.84] and STU-Net 0.65 [0.29-0.88].
- nnU-Net followed with external DSC 0.61 [0.39-0.83].
- For lesions >=1 mL, Spark3D and STU-Net detected 100% of lesions, with median DSC 0.77 and 0.73.
- For lesions <1 mL, Spark3D and STU-Net detected 78% and 67%, with median DSC 0.60 and 0.58.
- Small-lesion detection remained the main translational bottleneck.
- RN-D3 achieved high RN-versus-non-RN differentiation performance on external brain-metastasis lesions, with end-to-end sensitivity and specificity reported in the associated Frontiers article.
- Define a registry with one row per lesion or case, including image, mask, patient, lesion, and split metadata.
- Optionally create paired deterministic augmentations from the registry.
- Configure model backends for nnU-Net, Spark3D, STU-Net, or another compatible
segmentation runner. A backend only needs to accept
{image}and write a binary NIfTI mask to{out}. - Run prediction over one image or an entire registry.
- Compare models on the same cases. The comparison step computes per-case DSC, surface Dice, overlap-based detection, foreground volume counts, and summary statistics.
- Inspect the per-case CSV when debugging failures. Missing predictions are treated as empty masks, so missed lesions are visible in the output rather than silently dropped.
A registry is a JSON file with one row per lesion or case:
{
"cases": [
{
"case_id": "case001_lesion1",
"patient_id": "case001",
"lesion_id": "lesion1",
"image": "data/images/case001_t1ce.nii.gz",
"mask": "data/masks/case001_lesion1_mask.nii.gz",
"split": "external"
}
]
}Required fields for comparison are case_id, image, and mask.
patient_id is strongly recommended so uncertainty analyses can be grouped at
the patient level.
Build a registry from two file lists:
rnd3 registry build \
--images image_paths.txt \
--masks mask_paths.txt \
--out registries/external.jsonOr from shell-expanded file paths:
rnd3 registry build \
--images data/images/*.nii.gz \
--masks data/masks/*.nii.gz \
--out registries/external.jsonValidate a registry:
rnd3 registry validate registries/external.jsonrnd3 augment creates deterministic paired image/mask augmentations from a
registry. The current operations are:
flip-lr: flip the image and mask along voxel axis 0.flip-ap: flip the image and mask along voxel axis 1.
Run both operations, which is the default:
rnd3 augment \
--registry registries/train.json \
--out-dir data/augmented \
--out-registry registries/train_augmented.jsonRun only one operation:
rnd3 augment \
--registry registries/train.json \
--out-dir data/augmented_lr \
--out-registry registries/train_augmented_lr.json \
--operations flip-lrInclude the original rows alongside augmented rows:
rnd3 augment \
--registry registries/train.json \
--out-dir data/augmented \
--out-registry registries/train_augmented_with_originals.json \
--include-originalThe augmented registry keeps the same patient_id as the source row. This is
intentional: augmented versions of the same patient must stay in the same fold
for patient-level cross-validation.
If predictions already exist, compare prediction masks against registry masks:
rnd3 compare \
--registry registries/external.json \
--pred-dir nnunet=predictions/nnunet \
--pred-dir spark3d=predictions/spark3d \
--pred-dir stunet=predictions/stunet \
--out-dir results/model_comparison \
--surface-mm 2.0The comparison looks for prediction filenames in each model directory using common names:
{case_id}.nii.gz{case_id}_pred.nii.gz{patient_id}.nii.gz{patient_id}_pred.nii.gz- the input image basename
Copy the portable config template and edit it to point to your installed model runners and checkpoint files:
cp configs/models.local.example.yaml configs/models.local.yamlThe recommended portable backend is command, where each model entry is a shell
command that receives {image}, {out}, and {work_dir} placeholders:
models:
spark3d:
backend: command
command: >
spark3d-predict
--image {image}
--out {out}
--checkpoint ${RND3_WEIGHTS_DIR}/spark3d/best_model.pthThe config loader expands environment variables, so paths such as
${RND3_WEIGHTS_DIR}/spark3d/best_model.pth are portable across machines.
Run one model on one image:
rnd3 predict \
--model spark3d \
--image data/images/case001_t1ce.nii.gz \
--out outputs/spark3d_case001_mask.nii.gz \
--models-config configs/models.local.yamlRun all configured models on one image:
rnd3 predict-all \
--image data/images/case001_t1ce.nii.gz \
--out-dir outputs/predictions \
--models-config configs/models.local.yamlRun all configured models over a registry:
rnd3 predict-registry \
--registry registries/external.json \
--out-dir outputs/predictions \
--models-config configs/models.local.yaml \
--models nnunet,spark3d,stunetRun prediction and comparison together:
rnd3 benchmark \
--registry registries/external.json \
--models-config configs/models.local.yaml \
--models nnunet,spark3d,stunet \
--out-dir results/benchmark_external \
--surface-mm 2.0This writes predictions under results/benchmark_external/predictions/<model>/
and metrics under results/benchmark_external/metrics/.
The package also includes an nnunetv2 backend for standard nnU-Net v2
installations. Use the command backend for custom Spark3D or STU-Net runners
that write binary NIfTI masks to {out}.
per_case_metrics.csv contains one row per case and model:
| Column | Meaning |
|---|---|
case_id |
Registry case identifier. |
patient_id |
Patient/group identifier from the registry. |
lesion_id |
Optional lesion identifier. |
model |
Model name from --pred-dir or config. |
prediction |
Prediction mask path, blank if missing. |
missing_prediction |
Whether the prediction was absent and evaluated as an empty mask. |
dsc |
Dice similarity coefficient. |
surface_dice |
Surface Dice at the requested tolerance. |
detected |
Overlap-based lesion detection flag. |
gt_voxels |
Reference-mask foreground voxels. |
pred_voxels |
Prediction foreground voxels. |
summary_metrics.csv aggregates each model across the registry and reports mean,
median, IQR, detection rate, and missing-prediction count. Use the per-case file
for audit/debugging and the summary file for model-level reporting.
Place cohort files and checkpoints wherever appropriate on your machine, then reference them from a registry and a model config. A common local layout is:
data/
images/
masks/
weights/
nnunet/
spark3d/
stunet/
registries/
outputs/
results/
The example data/, weights/, outputs/, and results/ folders are ignored
by Git so local datasets, checkpoints, predictions, and metrics stay outside the
source history.
Install development dependencies and run the test suite:
pip install -e '.[dev]'
pytestThe tests cover:
- Registry read/write/validation.
- Paired registry augmentation for image/mask flips.
- DSC, surface Dice, and lesion-detection metrics.
- Environment-variable expansion in model configs.
- A CLI smoke test that creates synthetic NIfTI files, builds a registry, runs
rnd3 compare, and verifies the generated CSV outputs.
If you use RN-D3, please cite the associated paper:
@article{salome2026rnd3,
title = {RN-D3: Detection, differentiation, and delineation of radiation necrosis on post-contrast MRI},
author = {Salome, Patrick and Cogno, Nicolò and Lee, Hoyeon and Buti, Gregory and Shah, Keyur Devendra and Ehret, Felix and Wisdom, Amy Jordan and Shih, Helen A. and Paganetti, Harald and Chamseddine, Ibrahim},
journal = {Frontiers in Artificial Intelligence},
volume = {9},
year = {2026},
doi = {10.3389/frai.2026.1891883},
url = {https://www.frontiersin.org/journals/artificial-intelligence/articles/10.3389/frai.2026.1891883/full}
}A machine-readable citation is also provided in CITATION.cff.
This software is source-available under the PolyForm Noncommercial License 1.0.0.
Noncommercial use, modification, and redistribution are permitted subject to the license terms. Commercial use requires a separate written agreement.
This is not an OSI-approved open-source license.
Patrick Salome
This repository is research software. It is not a cleared medical device and is not intended for clinical decision-making without independent validation and appropriate regulatory review.