Skip to main content

Catch silently-broken NIfTI geometry (qform/sform mismatch, bad affines, misaligned inputs) before it ruins your results.

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nifti_qc-0.1.0.tar.gz (12.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nifti_qc-0.1.0-py3-none-any.whl (11.4 kB view details)

Uploaded Python 3

File details

Details for the file nifti_qc-0.1.0.tar.gz.

File metadata

  • Download URL: nifti_qc-0.1.0.tar.gz
  • Upload date:
  • Size: 12.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for nifti_qc-0.1.0.tar.gz
Algorithm Hash digest
SHA256 782f3edea9c9f943a860cbbdd260fdcaaa4aa9895792019ca9211ea9c3b49919
MD5 ccf1b31e88576c64859d1768f1cd95de
BLAKE2b-256 58e8e1524efaad87e1e9322766b0670222563f2b310b97cccbe5436ff63ad3cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for nifti_qc-0.1.0.tar.gz:

Publisher: publish.yml on CedricConday/nifti-qc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nifti_qc-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: nifti_qc-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for nifti_qc-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a348131af842a203b6ff819e8cf22a23c63a8ebd93c219e356bd373cd58b75b4
MD5 e5c219eab1bddc7d96b9c51a134336a5
BLAKE2b-256 daa6d8d00c82b84a2f9dd864234c73d4ac8fcecfde8acaf3e0be2293022262df

See more details on using hashes here.

Provenance

The following attestation bundles were made for nifti_qc-0.1.0-py3-none-any.whl:

Publisher: publish.yml on CedricConday/nifti-qc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page