bifti
A Python package for the BIfTI phantom format: parse/serialize the phantom JSON, load a phantom into plain NumPy arrays, and fetch phantoms from the public registry.
from bifti import NumpyPhantom
phantom = NumpyPhantom.load("subj42-3T.json")
tissue = phantom.tissues["gm"] # a NumpyTissue: density, T1, T2, ... as np.ndarray
print(tissue.shape, tissue.T1.mean())
phantom.config is the parsed BiftiPhantom (the raw JSON structure: units,
system, tissue definitions) — see phantom.py for the
full data model, and loader.py for how each property
resolves to an array.
API at a glance
Everything below is importable from the top-level bifti package (from bifti import ...):
| Name | What it is |
|---|---|
NumpyPhantom.load(path, *, max_substeps=8) |
Load a phantom JSON + its NIfTIs into NumpyPhantom(config, tissues). max_substeps caps the quadrature samples per axis when an oblique reslice_to is resampled; raise it when the ObliqueResampleWarning says the result is under-sampled. |
NumpyTissue |
One tissue as NumPy arrays: density, T1, T2, T2dash, ADC, dB0, B1_tx, B1_rx, plus .shape/.affine. |
NumpyPhantom.save(dir, json_name="phantom.json", *, constant_threshold=None) |
Save a loaded phantom as a self-contained folder (float64 / complex128 NIfTIs named after the JSON + JSON, no reslice_to, no func; near-constant maps become constants with constant_threshold); see Saving. Returns the JSON path. |
BiftiPhantom.load(path) / .save(path) |
Parse/serialize just the JSON side (no NIfTI I/O) — BiftiPhantom(units, system, tissues, reslice_to, schema). |
load_catalog() |
Fetch and parse the public catalog.json — the discovery list, mapping a label to an immutable registry name. |
load_registry() |
Fetch and parse the public registry.json — the immutable archive of every collection. |
load_registry_phantom(collection, name) |
Download one phantom's JSON + NIfTIs from Zenodo into a local cache; returns the JSON path. collection is an immutable registry name. |
NumpyPhantom.load is what you want for simulation/analysis (arrays); use
BiftiPhantom directly only if you're generating or editing phantom JSONs
without touching pixel data (e.g. examples/make_numerical_brain_cropped_bifti.py).
Installation
From PyPI:
# using uv (recommended):
uv add bifti
# or using pip:
pip install bifti
Or the latest unreleased version from git:
uv add --git https://github.com/mrx-org/bifti-phantoms --subdirectory python/bifti bifti
pip install "git+https://github.com/mrx-org/bifti-phantoms.git#subdirectory=python/bifti"
Examples
examples/ has a small, self-contained demo that loads a BIfTI
phantom and plots every tissue's data — a starting point for seeing the
package in action.
git clone https://github.com/mrx-org/bifti-phantoms
uv add --path bifti-phantoms/python/bifti bifti
| File | Purpose |
|---|---|
examples/demo.py |
Load a phantom and plot one figure per tissue. |
examples/nifti_to_koma.py |
Convert a phantom to a KomaMRI .phantom HDF5 file. |
examples/mrzero_sim.py |
Simulate a Pulseq sequence on a phantom with MR-zero. |
examples/make_numerical_brain_cropped_bifti.py |
Port the legacy MRzero test phantom to BIfTI. |
examples/data/ |
Phantom JSONs + the NIfTIs they reference. |
examples/data/generate.py |
Regenerates the example data in examples/data/ (reproducible). |
Requirements
The core package only needs bifti's own dependencies (numpy, nibabel,
requests). Resampling uses torch when it is importable — on CPU as well as
CUDA — and NumPy otherwise; install it with the torch extra
(pip install "bifti[torch]") or force a backend with
BIFTI_RESAMPLE_BACKEND=numpy|torch. The examples pull in extra,
examples-only dependencies
(matplotlib, h5py, MRzeroCore, torch) declared as a
dependency group
rather than real package dependencies:
uv sync --group examples
Running
From this python/bifti directory:
# list the registry, pick one, download + plot
uv run --group examples examples/demo.py
# ...or plot a local phantom JSON directly
uv run --group examples examples/demo.py examples/data/shapes.json
uv run --group examples examples/demo.py examples/data/shapes_resliced.json
uv run --group examples examples/demo.py examples/data/shapes_downsampled.json
With no argument, demo.py fetches the live catalog.json,
resolves each label to its registry.json collection,
prints their phantoms as a numbered list, and downloads the one you pick (its
JSON and every NIfTI it references) into examples/cache/ via bifti.registry
before plotting. Passing a local JSON path skips the catalog and plots that
file directly (the example data is committed in examples/data/).
demo.py saves one PNG per tissue into examples/figures/ and, on a GUI
backend, also shows them. Each figure tiles the tissue's maps — density,
T1, T2, T2', ADC, dB0, and every B1+/B1- channel — at the volume's
middle slice. Spatially uniform properties show as a flat field; properties
left at their default (e.g. T1 = inf) are blanked out.
The example data
The NIfTIs in examples/data/ are committed, but they are all produced by
examples/data/generate.py — re-run it
(uv run --group examples examples/data/generate.py) to regenerate them. It
derives everything from a fixed seed and simple meshgrid functions (smooth
bumps, low-order polynomials, a little noise), so re-running reproduces
byte-identical files. The phantoms together exercise the whole format:
subj42-3T.json(hand-written) — a brain-like single slice. Usesreslice_to, so all maps are resampled from their native 64×64 grid onto a 100×100 grid on load. Exercises: scalar properties, a shareddB0map, an 8-channelB1+, and a transformed reference (fat.dB0 = "x - 420").shapes.json(generated) — a small 40×32×4 phantom with noreslice_to, so it loads on its native grid. Exercises: multiple tissues sharing one grid, a polynomialdB0map, a 2-channelB1+, afuncmapping ("x * 0.5 + 10"), property defaults, and a true 3D volume.shapes_resliced.json(generated) — the same NIfTIs asshapes.jsonbut with areslice_toonto a finer grid (40×32×4 → 60×48×4), so loading genuinely resamples the 3D volumes.shapes_downsampled.json(generated) — the same NIfTIs again, resliced onto a coarser grid (40×32×4 → 16×12×3) whose FOV deliberately overhangs the source. Exercises the case plain interpolation gets wrong: output voxels that average many source voxels, and output voxels that straddle the edge of the source FOV.
Note: loading a tissue with a
funcmapping prints a warning — the loader evaluatesfuncwithevalfor brevity, so only load phantoms you trust (see the note inbifti/loader.py).
Known limitations
- Complex-valued NIfTI data isn't handled. A complex
B1+/B1-map gets cast withnp.asarray(..., dtype=np.float64)inloader.py, which silently discards the imaginary part (numpy emits aComplexWarning, easy to miss). The Rust crate fails loudly instead in this case. - Only the default
unitsare accepted.
See also
- ../../README.md — repo overview, format spec, and the registry.
- ../../rust/bifti/ — the Rust crate, including how it differs from this package.
Metadata
Release files for bifti 0.3.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 | Provenance | Actions |
|---|---|---|---|---|
| bifti-0.3.0.tar.gz | 28.8 kB | With provenance | Details for bifti-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| bifti-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for bifti-0.3.0-py3-none-any.whl |
Total release size: 60.1 kB
Release files / bifti-0.3.0.tar.gz
| Download URL | bifti-0.3.0.tar.gz |
|---|---|
| Size | 28.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fa9521cd639fe9ee02325bd7085b70cbae48e76425b81ac801b430539d08258f
|
|
BLAKE2b-256 checksum How to use checksums |
a4197102ee07ce187e9729a8cc5b37d97a4250376c05b79a13afba864d78423d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 28, 2026.
Transparency logRelease files / bifti-0.3.0-py3-none-any.whl
| Download URL | bifti-0.3.0-py3-none-any.whl |
|---|---|
| Size | 31.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3b25cf55a5e35c177e7b3538a32e1bfe795c4b4d6829864338f47f88c10425bd
|
|
BLAKE2b-256 checksum How to use checksums |
5a7ddbaf0fb2433eefa948286e74a8468164490da2b953496915fa791d425d47
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 28, 2026.
Transparency logSecurity for bifti 0.3.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for bifti-0.3.0.tar.gz
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 Oct 28, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.