Skip to main content

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)

Source distribution for nifti-qc 0.1.0
File Size Uploaded
nifti_qc-0.1.0.tar.gz 12.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nifti-qc 0.1.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page