AutoAIF¶
AutoAIF finds the arterial input function in brain DCE-MRI automatically, with no manual ROI drawing. It is a 3D U-Net in Keras/TensorFlow, trained on multi-site brain DCE-MRI cohorts, and it ships with pretrained weights — no training is required.
Given a 4D DCE series it predicts a vascular function curve and the 3D mask the curve was measured from.
Where this fits¶
AutoAIF covers step 3. The full DCEasy pipeline:
Citation¶
If AutoAIF contributes to published work, please cite:
Saca, L., et al. Automatic detection of arterial input function for brain DCE-MRI in multi-site cohorts. Magnetic Resonance in Medicine, 94(6), 2732–2744 (2025). PMID: 40808286
When to use it¶
Use AutoAIF when you have brain DCE-MRI and you want the AIF chosen the same way every time. See the arterial input function for the background.
Consider alternatives when:
| Situation | Use instead |
|---|---|
| Not brain — the model was trained on brain only | AIFArtist |
| You want a human in the loop, or a multi-rater reference to validate against | AIFArtist |
| You are already running the whole pipeline in one go | DCEPrep, which calls AutoAIF for you |
Input images should be gzipped NIfTI (.nii.gz) in radiological orientation. Dimensions do
not need to match the training data — everything is resampled to 256×256×32×32 internally and
the outputs are resampled back — but the training set spanned roughly 208×256×40×50 to
320×320×14×64, and inputs far outside that range are worth resampling yourself first.
Requirements¶
- Python 3.9+
- TensorFlow 2.12+ / Keras 2.12+
- CUDA GPU recommended but not required
requirements.txt is the GPU install
It includes cupy, tensorrt and tensorrt_cu12, which are CUDA-only. On a CPU-only or
Apple Silicon machine, install the remaining dependencies directly instead:
pip install matplotlib nibabel numba numpy pandas Pillow \
pingouin plotly psutil scipy tensorboard tensorflow
Inference then runs on CPU, more slowly but with identical results.
Install¶
git clone https://github.com/petmri/AutoAIF.git && cd AutoAIF
python3 -m venv tf && source tf/bin/activate
pip install -r requirements.txt
Then fetch the pretrained weights. They are posted on github, download manually or run:
# ~470 MB
curl -L -o model_weight_huber1.h5 \
https://github.com/petmri/AutoAIF/releases/latest/download/model_weight_huber1.h5
Worked single example¶
One subject, from a preprocessed DCE series to an AIF mask and figures:
source tf/bin/activate
python main_vif.py --mode inference \
--input_path /data/derivatives/DCEPrep/sub-01/sub-01_desc-hmc_DCE.nii.gz \
--model_weight_path model_weight_huber1.h5 \
--save_output_path /data/derivatives/AutoAIF/sub-01/ \
--save_image 1
That writes four files into --save_output_path, named after the input:
| File | What it is |
|---|---|
sub-01_desc-hmc_DCE_float_mask.nii |
The raw per-voxel probability map, resampled back to the input geometry |
sub-01_desc-hmc_DCE_mask.nii |
The AIF ROI proper — the highest-probability voxels, binarized |
sub-01_desc-hmc_DCE_curve.svg |
The vascular function, normalized to its own baseline |
sub-01_desc-hmc_DCE_mask.svg |
The ROI overlaid on the image, at the mask's center-of-mass slice |
Drop --save_image 1 and you get the two NIfTIs only, which is what you want in a batch run.
Check the overlay before you trust the curve
_mask.svg is the fastest sanity check in the pipeline. The ROI should land in a major
artery/vein. If it does not, it should be manually corrected.
A cohort example¶
A simple shell or python loop is an easy way to process lots of images. Pass one image per call and loop in the shell:
source tf/bin/activate
for img in /data/derivatives/DCEPrep/sub-*/sub-*_desc-hmc_DCE.nii.gz; do
sub=$(basename "$img" | cut -d_ -f1)
python main_vif.py --mode inference \
--input_path "$img" \
--model_weight_path model_weight_huber1.h5 \
--save_output_path "/data/derivatives/AutoAIF/$sub/"
done
The weights load once per invocation, which dominates runtime on short series. For a large cohort it is worth batching inside a single Python process instead.
Fine-tuning on your own data¶
The shipped weights were trained on multi-site brain DCE-MRI, and they generalize across scanners better than a single-site model would. Retraining is still worth it if your sequence, field strength or contrast protocol sits well outside that range — and the same entry point trains a model from scratch.
Training dataset layout¶
For training organize the data by site. Each site needs an images/ folder and a masks/ folder, with one
mask per image under the same filename:
dataset
├── site1
│ ├── images
│ │ └── id_x.nii.gz
│ └── masks
│ └── id_x.nii.gz
├── site2
│ ├── images
│ │ └── id_x.nii.gz
│ └── masks
│ └── id_x.nii.gz
└── site3
├── images
│ └── id_x.nii.gz
└── masks
└── id_x.nii.gz
Every folder directly under dataset is treated as a site, except those whose names begin
with test or TF — which is what keeps the generated TFRecords directory from being
picked up as one.
Two constraints that will not announce themselves
All NIfTI files must be 32-bit.
Filenames must start with a subject ID followed by an underscore. The split is taken on
everything before the first _, so sub-01_ses-1_DCE.nii.gz and sub-01_ses-2_DCE.nii.gz
are correctly recognized as one subject and land in the same split. Filenames without an
underscore make every image its own subject, which quietly leaks sessions across the
train/test boundary artifically inflating accuracy.
Running it¶
source tf/bin/activate
python main_vif.py --mode training \
--dataset_path /path/to/dataset/ \
--save_checkpoint_path /path/to/checkpoints/ \
--epochs 200 \
--batch_size 1
--epochs 200 and --batch_size 1 are the defaults and can be omitted. --model_name selects
the architecture — best (the shipped one), attn or modified_attn. python main_vif.py -h
lists everything.
The split is 80/10/10 train/validation/test per site, on a fixed seed. It is written to
train_set.txt, val_set.txt and test_set.txt in the checkpoint directory on the first run
and reused verbatim if those files are already there — so a resumed or repeated run keeps the
same test set, and forcing a fresh split means deleting them. TFRecords are likewise only
generated when they are not already present.
Training also writes log.txt, history.npy and a history.png loss curve into the
checkpoint directory, and TensorBoard logs under logs/fit/.
Hyperparameter search¶
--mode hp_tuning sweeps convolution kernel sizes — the first/last layers and the body
independently — and logs each trial to TensorBoard through the HParams plugin:
python main_vif.py --mode hp_tuning \
--dataset_path /path/to/dataset/ \
--save_checkpoint_path /path/to/tuning/
Each trial gets its own subdirectory named after its parameters, and a trial whose directory already exists is skipped — so an interrupted sweep resumes where it stopped rather than starting over. It is a full grid, so budget accordingly.