nifti-qc
Catch silently-broken NIfTI geometry before it ruins your results.
A NIfTI file stores its world→voxel mapping twice — the quaternion
qform and the affine sform. Different tools trust different ones:
nibabel and most Python tooling follow the
sform; some C/registration tools (e.g. greedy) read the qform. When an
earlier step updates only one of them — SPM co-registration is the classic
culprit — the two disagree and your image is silently mislocated in world
space. No error, no crash: just a segmentation, overlay, or registration
that lands in the wrong place.
nifti-qc is a tiny, dependency-light QC gate that catches that trap and a
handful of related geometry/data problems, so you find them in seconds instead
of in a downstream result three steps later.
This tool grew out of a real fix for exactly this bug in the MS-lesion segmentation pipeline LST-AI (CompImg/LST-AI#45), where a qform/sform mismatch mislocated lesion masks in FLAIR space.
Install
pip install nifti-qc # once published
# or from source:
pip install -e .
Requires only nibabel and numpy.
Use it on the command line
# QC a single file
nifti-qc scan t1.nii.gz
# QC each file AND check they share a grid / world space (e.g. T1 + FLAIR)
nifti-qc scan t1.nii.gz flair.nii.gz
# machine-readable, for CI / pipelines
nifti-qc scan *.nii.gz --json
Example output:
sub-01_flair.nii.gz [RAS]
ERROR qform_sform_mismatch: qform and sform disagree: up to 10.000 mm
translation and 0.000 deg rotation apart. Tools that read the qform
(e.g. greedy) and tools that read the sform (e.g. nibabel) will place
this image differently.
PROBLEMS: 1 error(s), 0 warning(s) across 1 file(s)
The command exits non-zero when any error-severity problem is found, so it drops straight into a CI step or a pre-processing script as a gate:
nifti-qc scan "$T1" "$FLAIR" || { echo "fix your inputs first"; exit 1; }
Use it in CI (GitHub Action)
A composite Action ships with the repo, so a workflow can gate on NIfTI geometry with no setup — the job fails if any file has an error-severity problem:
- uses: CedricConday/nifti-qc@main
with:
paths: data/*.nii.gz
# args: "--no-align" # optional flags for `nifti-qc scan`
Use it as a library
import nifti_qc
report = nifti_qc.scan(["t1.nii.gz", "flair.nii.gz"])
if not report.ok:
for fr in report.files:
for f in fr.findings:
print(fr.path, f.code, f.severity, f.message)
for f in report.alignment:
print("align", f.code, f.message)
Every check is also importable on its own (check_affine, check_geometry,
check_data, check_alignment) and returns plain Finding dataclasses.
What it checks
| Check | Severity | What it means |
|---|---|---|
qform_sform_mismatch |
error | qform and sform disagree — the silent-mislocation trap |
no_valid_affine |
error | both codes are 0; world orientation undefined |
qform_unset / sform_unset |
warn | only one transform is set; some tools will misread |
nonpositive_voxel_size |
error | a voxel dimension is ≤ 0 |
extreme_anisotropy |
warn | very non-isotropic voxels (default ≥ 5:1) |
non_orthonormal_direction |
warn | affine encodes shear — usually a corrupt header |
non_finite_data |
error | NaN/Inf voxels |
empty_volume |
warn | volume is all zeros |
grid_shape_mismatch |
warn | inputs live on different voxel grids (resampling needed) |
voxel_size_mismatch |
warn | inputs have different voxel sizes |
world_space_mismatch |
info | inputs are not co-registered in world space |
Each finding carries a machine-readable detail dict (e.g. the exact
translation in mm and rotation in degrees) for programmatic use.
Why trust the numbers
Every check has a unit test that builds a synthetic NIfTI designed to trigger it and asserts on the reported magnitude — the qform/sform mismatch test constructs a 10 mm offset and asserts the tool reports 10 mm. Run them:
pip install -e ".[test]"
pytest -q
Scope
nifti-qc reports problems; it does not fix them. To repair a qform/sform
mismatch you decide which transform is authoritative and harmonize to it — see
the LST-AI fix linked above for one approach. Keeping detection and repair
separate is deliberate: the right fix depends on which upstream tool you trust.
License
MIT © Cedric Conday
Release files for nifti-qc 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nifti_qc-0.1.0.tar.gz | 12.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nifti_qc-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.0 kB
Release files / nifti_qc-0.1.0.tar.gz
| Download URL | nifti_qc-0.1.0.tar.gz |
|---|---|
| Size | 12.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
782f3edea9c9f943a860cbbdd260fdcaaa4aa9895792019ca9211ea9c3b49919
|
|
BLAKE2b-256 checksum How to use checksums |
58e8e1524efaad87e1e9322766b0670222563f2b310b97cccbe5436ff63ad3cb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 2, 2026.
Transparency logRelease files / nifti_qc-0.1.0-py3-none-any.whl
| Download URL | nifti_qc-0.1.0-py3-none-any.whl |
|---|---|
| Size | 11.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a348131af842a203b6ff819e8cf22a23c63a8ebd93c219e356bd373cd58b75b4
|
|
BLAKE2b-256 checksum How to use checksums |
daa6d8d00c82b84a2f9dd864234c73d4ac8fcecfde8acaf3e0be2293022262df
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 2, 2026.
Transparency log