Skip to content
This repository was archived by the owner on Aug 13, 2026. It is now read-only.

Latest commit

 

History

31 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ASTRA: Atomic Surface Transformations for Radiotherapy Quality Assurance

EMBC 2023 Student paper competition, 2nd place Python License

This repository accompanies our paper, which took second place in the EMBC 2023 student paper competition:

"ASTRA: Atomic Surface Transformations for Radiotherapy Quality Assurance" Amith Kamath, Robert Poel, Jonas Willmann, Ekin Ermiş, Nicolaus Andratschke, Mauricio Reyes. 45th IEEE Engineering in Medicine and Biology Conference (EMBC), 2023.

Data, model weights and the pre-computed sensitivity maps are archived outside this repository and shared on request — see Configuring where the data lives.

See a short video description of this work here:

🔗 Project Website


Overview

Radiotherapy planning for glioblastoma starts with manual contours of the tumour target and the surrounding organs at risk. These are reviewed without information about the dosimetric consequence of any given correction, so review effort is spread uniformly over a surface where the consequences are not uniform.

ASTRA estimates that consequence point by point. A ball of radius 3 voxels is added to one point of one organ's surface — an atomic surface transformation, modelling local over-segmentation at roughly the scale of inter-observer disagreement — the dose is predicted again, and the mean absolute change over the brain is recorded at that point. Repeating this over the surface gives a sensitivity map: a per-point estimate of how much a local contour change would alter the predicted dose.

The dose predictor is the cascaded 3D U-Net from deepdosesens, used unmodified; only the input contours change between predictions. The cost is one forward pass per surface point — roughly a thousand dose estimates per organ, about 3 000 per patient, and 30 589 across the experiment reported here. This is tractable only because the predictor runs in seconds where a treatment planning system takes hours.


Demonstration videos

docs/videos/ holds one clip per experiment, rendered natively in Python — see README-videos.md for the layout and the appearance choices.

Video Content
transformation_walk_DLDP_081.mp4 The method: 50 consecutive transformations on the brainstem, one at a time, with the map accumulating as the walk proceeds
sensitivity_DLDP_090.mp4 Contours alone beside the sensitivity map, orbiting. A complex target shape
sensitivity_DLDP_084.mp4 The same, for a target close to the hippocampi and brainstem
sensitivity_DLDP_082.mp4 The same, for the smallest target of the test set, distant from the organs at risk
sensitivity_DLDP_088.mp4 The same, for a larger target
radius_sweep_DLDP_081.mp4 Figure 4: the same brainstem at transformation radius 3, 5 and 7

Each frame puts a near-transparent brain behind the structures for context, the target volume in red, and the organs at risk either uncoloured or coloured by the sensitivity map in Gy, with a colour bar. The four sensitivity clips are the four cases of the paper's Table I, which the reproduction script identifies — the paper labels them (a) to (d) and never names them.

Previously MATLAB. The paper's figures came from MATLAB's Medical Imaging Toolbox, driven by live scripts whose scene state lived in fifteen binary .mat files, so they could not be regenerated without a licence and those files. Everything is PyVista now: same reading of the scene, same camera orbit, and the appearance choices are code in astra/visualization/scene.py. The original scripts are kept under astra/visualization/matlab/ for provenance.

Rebuild them with:

scripts/fetch_artifacts.sh
scripts/make_videos.sh

Results, as reproduced from the archived artifacts

scripts/reproduce.sh recomputes every published number from the archived sensitivity maps and prints it beside the paper's value. 66 of 68 reproduce.

Quantity Paper Reproduced
Table I, mean absolute dose difference per organ (20 values) all match to ≤ 0.0001 Gy
Figure 3, the four colour-bar maxima 0.168, 0.609, 0.772, 0.622 0.1689, 0.6094, 0.7726, 0.6220
Table II, organ sizes (13 values) all match to ≤ 0.01 voxels
Table II, correlations (26 values) 24 match; the right optic nerve differs
Figure 4, radius 3 vs 5 / 5 vs 7 0.949, 0.939 0.9497, 0.9398
Transformations, total / per subject > 20 000 / > 2 000 30 589 / 3 059

The archived maps are checked against the archived weights, not only restated: python -m astra.analyze.verify_inference re-runs transformations through the network and agrees with the stored map to 2.3 × 10⁻⁶ Gy on a 0–70 Gy scale.

Two things the paper does not say, which the artifacts settle:

  • Table I's four cases are DLDP_082, DLDP_088, DLDP_090 and DLDP_084, in that order. Matching the 20 values identifies them uniquely, and Figure 3's colour-bar maxima confirm the same order independently.
  • Table II's size column was computed over seven cases and its correlations over ten. The sizes reproduce exactly from DLDP_081–087 and not from any ten-case set; the correlations reproduce from DLDP_081–090 and not from any seven-case set.

The right-optic-nerve row of Table II could not be reproduced: published −0.15 and +0.05, recomputed +0.25 and −0.11, both signs reversed, and no subset of cases or method of averaging comes closer than 0.23. results/embc_verification.csv records every claim individually, and the project page carries an errata with the corrected values.


Getting started

Requirements

  • Python 3.10+
  • PyTorch (CUDA, MPS or CPU) — only for the inference check and for training
  • SimpleITK, NumPy, SciPy, scikit-image, pandas, PyVista
  • ffmpeg, for the videos
git clone https://github.com/amithjkamath/astra.git
cd astra
uv venv .venv
source .venv/bin/activate
uv pip install -r pyproject.toml

Configuring where the data lives

No paths are hardcoded. Copy .env.example to .env and point it at your copy of the artifacts (or set the same variable in the environment):

ASTRA_EMBC_ARCHIVE=/path/to/2023-02-EMBC/artifacts
#ASTRA_DATA=/mnt/big-disk/astra/data
#ASTRA_CHECKPOINTS=/mnt/big-disk/astra/checkpoints

Everything falls back to directories inside the repository, so the defaults work once scripts/fetch_artifacts.sh has unpacked the archive. Check what is in effect with:

python -m astra.config

Only code and the demonstration videos are committed here. Planning CTs, contours, reference plans, sensitivity maps and model weights live in the artifact archive and are shared on request; the archive carries its own manifest describing the layout. Fetch from it with:

WHAT=core scripts/fetch_artifacts.sh   # cohort, radius-3 maps, weights (~960 MB)
scripts/fetch_artifacts.sh             # adds radius 5 and 7, and the transformation walk

The sensitivity maps are archived, so nothing needs a GPU. Reproducing every table and rendering every video reads the maps; only verify_inference runs the network.

Reading a sensitivity map

An archived Perturbed_<organ>.nii.gz is sparse: at each sampled surface point it holds the absolute dose difference summed over the brain, and zero everywhere else. The paper reports the mean over the brain, so the conversion is one division:

from astra.analyze.maps import brain_voxels, read
from astra.config import sensitivity_path

sensitivity = read(sensitivity_path("DLDP_082", "Perturbed_Hippocampus_L.nii.gz"))
gy = sensitivity / brain_voxels("DLDP_082")     # mean absolute dose difference, Gy
points = gy[gy > 0]
print(points.mean(), points.max())              # 0.1041, 0.1689 -- Table I (a), Figure 3 (a)

Read as a dose it is meaningless, and nothing in the file says so — so astra/analyze/maps.py does the reading for you and every script goes through it.

Computing a sensitivity map

Only needed to verify the method or to run it on a new case; the paper's maps are archived.

from astra.config import case_path, checkpoint_path
from astra.inference import DosePredictor
from astra.sensitivity import sensitivity_map

predictor = DosePredictor(checkpoint_path("dose-predictor", "weights.pt"))
smap, baseline, points = sensitivity_map(predictor, case_path("DLDP_081"), "Pituitary")

One forward pass per surface point, about 25 s each on Apple silicon, so a whole organ is hours. points= takes a subset, which is how verify_inference checks a handful cheaply.

Training the dose predictor

The model and its training belong to deepdosesens; the code is here so the repository is self-contained.

uv pip install -r pyproject.toml --extra train
python astra/train.py

Repository layout

Path Contents
astra/config.py every path the project uses, from env vars or .env
astra/sensitivity.py atomic surface transformations, and the map they produce
astra/inference.py DosePredictor — load once, predict many
astra/analyze/maps.py reading archived maps and deriving the paper's quantities
astra/analyze/reproduce_embc_tables.py every published number, recomputed and checked
astra/analyze/verify_inference.py the archived maps against the archived weights
astra/visualization/ the PyVista scene, the three video builders, and the superseded MATLAB scripts
astra/model/, astra/data/, astra/train/ the cascaded 3D U-Net, its data pipeline and its trainer
scripts/ fetch_artifacts.sh, reproduce.sh, make_videos.sh
docs/ the project website, served at amithjkamath.github.io/astra
examples/ notebooks and scripts from the original analysis

If this is useful in your research, please consider citing:

@inproceedings{kamath2023astra,
title={ASTRA: Atomic Surface Transformations for Radiotherapy quality Assurance},
author={Kamath, Amith and Poel, Robert and Willmann, Jonas and Ermis, Ekin and Andratschke, Nicolaus and Reyes, Mauricio},
booktitle={45th IEEE Engineering in Medicine and Biology Conference (EMBC)},
year={2023}
}

Credits

Major props to the code and organization in https://github.com/LSL000UD/RTDosePrediction, which is what this model is based on (looks like this repo is not maintained/available anymore!)

About

ASTRA: estimating, point by point, how much a local organ-at-risk contour change alters the planned radiotherapy dose (EMBC 2023)

Resources

Stars

2 stars

Watchers

1 watching

Forks

Used by

Contributors

Languages