Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 110 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@

# PhantomKit

**PhantomKit** is a Python toolkit for automated quality assurance (QA) of medical imaging scanners using physical phantoms. It provides pydra-based workflows that register phantom scans to a reference template, extract per-vial signal statistics across multiple contrast types, and generate publication-quality plots — supporting both MRI and PET phantom protocols.
**PhantomKit** is a Python toolkit for automated quality assurance (QA) of MRI scanners using physical phantoms. It provides a high-level processing engine and pydra-based workflows that register phantom scans to a reference template, extract per-vial signal statistics across multiple contrast types, and generate publication-quality plots — with full support for DWI preprocessing and diffusion metric (ADC, FA) extraction.

## Features

- **Template-based registration** — iterative ANTs SyN registration with automatic orientation search across a rotation library
- **Vial metric extraction** — per-vial mean, median, std, min and max across all contrast images, written to CSV
- **Plotting** — scatter plots of vial intensity and parametric map plots (T1/IR, T2/TE) with mrview ROI overlays
- **Protocol support** — extensible `protocols` sub-package for phantom- and project-specific workflow configurations
- **End-to-end pipeline** — single command processes a raw DICOM session directory through DWI preprocessing, phantom QC in DWI space, and native contrast QC
- **Automatic series classification** — DWI, reverse phase-encode, T1, IR, and TE series are detected and paired automatically from folder names and DICOM sidecar metadata; no manual configuration required
- **DWI preprocessing** — FSL `dwifslpreproc` with automatic phase-encoding correction mode selection (`rpe_none`, `rpe_pair`, `rpe_all`), optional denoising/Gibbs correction, tensor fitting, and T1-to-DWI co-registration via FLIRT
- **Template-based registration** — iterative ANTs rigid registration with automatic orientation search across a rotation library; vial masks propagated to subject space via inverse transform
- **Vial metric extraction** — per-vial mean, median, std, min and max across all contrast images (T1, IR, TE, ADC, FA), written to CSV
- **Plotting** — ADC/FA scatter plots with SPIRIT reference values, T1/IR and T2/TE parametric map plots with mrview ROI overlays and Monte Carlo 95% CI bands
- **Checkpoint-based resumption** — re-running the pipeline skips stages whose outputs already exist
- **Parallel batch processing** — pydra-native splitting and combining for multi-session datasets

## Installation
Expand All @@ -23,65 +26,136 @@
python -m pip install phantomkit
```

### External dependencies

The pipeline requires FSL, MRtrix3, ANTs, and dcm2niix to be available on `PATH`:

- [FSL](https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/) — `dwifslpreproc`, `flirt`, `convert_xfm`
- [MRtrix3](https://www.mrtrix.org/) — `mrconvert`, `dwi2tensor`, `tensor2metric`, `dwidenoise`, `mrdegibbs`, `mrstats`, `mrview`
- [ANTs](http://stnava.github.io/ANTs/) — `antsRegistrationSyN.sh`, `antsApplyTransforms`
- [dcm2niix](https://github.com/rordenlab/dcm2niix) — DICOM to NIfTI conversion

## Basic usage

### End-to-end pipeline

Point the pipeline at a session directory containing DICOM subdirectories:

```bash
phantomkit pipeline \
--input-dir /data/session01 \
--output-dir /results/session01 \
--phantom SPIRIT
```

Optional flags:

```
--denoise-degibbs Apply dwidenoise + mrdegibbs before preprocessing
--gradcheck Run dwigradcheck to verify gradient orientations
--nocleanup Keep intermediate tmp/ directories after completion
--readout-time Override TotalReadoutTime (seconds) for dwifslpreproc
--eddy-options Override FSL eddy options string
--dry-run Print the planned workflow without executing
```

Output structure:

```
/results/session01/
<DWI_series>/
DWI_preproc_biascorr.mif.gz
ADC.nii.gz
FA.nii.gz
T1_in_DWI_space.nii.gz
metrics/ ← per-vial CSVs and QA plots
vial_segmentations/
native_contrasts_staging/
metrics/ ← per-vial CSVs and parametric map plots
vial_segmentations/
images_template_space/
```

### Python API

```python
from phantomkit.phantom_processor import PhantomProcessor

processor = PhantomProcessor(
template_dir="/templates/SPIRIT",
output_base_dir="/results",
rotation_library_file="/templates/rotations.txt",
)
results = processor.process_session("/data/session01/T1.nii.gz")
```

Or via the pydra workflow API:

```python
from phantomkit.protocols.gsp_spirit import GspSpiritAnalysis
from phantomkit.analyses.vial_signal import VialSignalAnalysis

wf = GspSpiritAnalysis(
input_image="/data/session01/t1_mprage.nii.gz",
template_dir="/templates/gsp_spirit",
rotation_library_file="/templates/gsp_spirit/rotations.txt",
wf = VialSignalAnalysis(
input_image="/data/session01/T1.nii.gz",
template_dir="/templates/SPIRIT",
rotation_library_file="/templates/rotations.txt",
)
outputs = wf(cache_root="/data/cache-root")
outputs = wf(cache_root="/data/pydra-cache")
```

Or via the command line:
### CLI — run a protocol directly

```bash
# Single session
phantom-process run gsp-spirit /data/session01/t1_mprage.nii.gz \
--template-dir /templates/gsp_spirit \
--rotation-library-file /templates/gsp_spirit/rotations.txt \
--output-base-dir /results

# Batch — process every matching image found under /data/
phantom-process run gsp-spirit /data/ \
--template-dir /templates/gsp_spirit \
--rotation-library-file /templates/gsp_spirit/rotations.txt \
--output-base-dir /results \
--pattern "*t1*mprage*.nii.gz"
phantomkit run vial-signal /data/session01/T1.nii.gz \
--template-dir /templates/SPIRIT \
--rotation-library-file /templates/rotations.txt \
--output-base-dir /results

# Batch mode — process every matching image under /data/
phantomkit run vial-signal /data/ \
--template-dir /templates/SPIRIT \
--rotation-library-file /templates/rotations.txt \
--output-base-dir /results \
--pattern "*T1*.nii.gz"

# List available protocols
phantom-process list
phantomkit list
```

### Plotting

Generate QA plots from existing CSV metric files:

```bash
# Vial intensity scatter plot for one contrast
phantom-process plot vial-intensity \
/results/session01/metrics/session01_t1_mprage_mean_matrix.csv scatter \
--std_csv /results/session01/metrics/session01_t1_mprage_std_matrix.csv \
--output /results/session01/metrics/session01_t1_PLOTmeanstd.png
# Generic scatter plot
phantomkit plot vial-intensity \
/results/session01/metrics/session01_T1_mean_matrix.csv scatter \
--std-csv /results/session01/metrics/session01_T1_std_matrix.csv \
--output /results/session01/metrics/session01_T1_PLOTmeanstd.png

# ADC scatter plot with SPIRIT reference values
phantomkit plot vial-intensity \
/results/session01/metrics/session01_ADC_mean_matrix.csv scatter \
--std-csv /results/session01/metrics/session01_ADC_std_matrix.csv \
--phantom SPIRIT \
--template-dir /templates \
--output /results/session01/metrics/session01_ADC_PLOTmeanstd.png

# T1 inversion-recovery parametric map plot
phantom-process plot maps-ir \
/results/session01/images_template_space/ir_*.nii.gz \
--metric_dir /results/session01/metrics \
phantomkit plot maps-ir \
/results/session01/images_template_space/se_ir_*.nii.gz \
--metric-dir /results/session01/metrics \
--output /results/session01/metrics/session01_T1map_plot.png

# T2 spin-echo parametric map plot
phantom-process plot maps-te \
/results/session01/images_template_space/te_*.nii.gz \
--metric_dir /results/session01/metrics \
phantomkit plot maps-te \
/results/session01/images_template_space/t2_se_*.nii.gz \
--metric-dir /results/session01/metrics \
--output /results/session01/metrics/session01_T2map_plot.png
```

See the [CLI documentation](https://australian-imaging-service.github.io/phantomkit/cli.html) for the full option reference.

## License

Copyright 2026 Australian Imaging Service. Released under the [Apache License 2.0](LICENSE).
Copyright 2026 Australian Imaging Service. Released under the [Apache License 2.0](LICENSE).
70 changes: 67 additions & 3 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
@@ -1,19 +1,83 @@
API
===

This page documents the public Python API of PhantomKit. The package is
organised into five sub-packages:

- :mod:`phantomkit.analyses` — top-level pydra workflows (user-facing entry
points)
- :mod:`phantomkit.phantom_processor` — imperative processing engine
(:class:`~phantomkit.phantom_processor.PhantomProcessor`)
- :mod:`phantomkit.registration` — ANTs registration tasks and workflows
- :mod:`phantomkit.metrics` — vial mask transform and metric extraction
workflows
- :mod:`phantomkit.plotting` — QA plot generation functions and pydra tasks


Analyses
--------
---------

The ``analyses`` sub-package contains the pydra workflow classes that serve as
the primary programmatic entry points.

Vial signal analysis
~~~~~~~~~~~~~~~~~~~~

.. autoclass:: phantomkit.analyses.vial_signal.VialSignalAnalysis

.. autoclass:: phantomkit.protocols.diffusion_metrics.DiffusionMetricsAnalysis
.. autoclass:: phantomkit.analyses.vial_signal.VialSignalAnalysisBatch

Diffusion metrics analysis
~~~~~~~~~~~~~~~~~~~~~~~~~~

.. autoclass:: phantomkit.analyses.diffusion_metrics.DiffusionMetricsAnalysis

.. autoclass:: phantomkit.analyses.diffusion_metrics.DiffusionMetricsAnalysisBatch


Phantom processor
------------------

:class:`~phantomkit.phantom_processor.PhantomProcessor` is the imperative
counterpart to :class:`~phantomkit.analyses.vial_signal.VialSignalAnalysis`.
It is used internally by the pipeline orchestrator (Stages 2 and 3) and can
also be used directly for scripted single-session processing.

.. autoclass:: phantomkit.phantom_processor.PhantomProcessor
:members: process_session


Registration
------------

.. autoclass:: phantomkit.registration.IterativeRegistration

.. autoclass:: phantomkit.registration.CheckRegistration

.. autoclass:: phantomkit.registration.SaveTemplateInScannerSpace

.. autoclass:: phantomkit.registration.RegistrationSynN


Metrics
-------

.. autoclass:: phantomkit.metrics.TransformVialsToSubjectSpace

.. autoclass:: phantomkit.metrics.ExtractMetricsFromContrasts

.. autoclass:: phantomkit.metrics.TransformContrastsToTemplateSpace


Plotting
--------

.. autofunction:: phantomkit.plotting.vial_intensity.plot_vial_intensity

.. autofunction:: phantomkit.plotting.maps_ir.plot_vial_ir_means_std

.. autofunction:: phantomkit.plotting.maps_te.plot_vial_te_means_std

.. autofunction:: phantomkit.plotting.vial_intensity.plot_vial_intensity
.. autoclass:: phantomkit.plotting.visualization.GeneratePlots

.. autoclass:: phantomkit.plotting.visualization.BuildRoiOverlay
Loading
Loading