Skip to main content

PyPI made-with-python

pvraw: read raw Bruker ParaVision MRI data

pvraw reads a Bruker Biospin preclinical MRI study — a PvDataset, supplied either as a study directory or as a .zip/.PvDatasets archive — and converts it to NIfTI or to a BIDS dataset, with image orientation and metadata preserved.

It has three parts:

  • a command-line tool for inspecting and converting studies, including batch conversion of a whole corpus into a BIDS tree;
  • a high-level Python API that hands you nibabel objects with the affine already correct, so there is no conversion step;
  • a low-level Python API for reading Bruker parameter and binary files as plain Python types.

New to pvraw? Start with Tutorial: your first conversion, which takes a public sample study to a validated BIDS dataset end to end.

Report issues at CoBrALab/pvraw.


Installation

Requires Python >= 3.11. Everything here uses uv, which manages the interpreter and the dependencies for you — there is no separate virtualenv to create or activate.

Released versions are on PyPI — install from there unless you need an unreleased change.

As a command-line tool. Installs pvraw onto your PATH, isolated from your other environments:

uv tool install pvraw
uv tool upgrade pvraw      # later, to update

As a dependency of your own project, for the Python API:

uv add pvraw               # or: pip install pvraw

From git, to run a change that is not released yet:

uv tool install git+https://github.com/CoBrALab/pvraw.git
uv add git+https://github.com/CoBrALab/pvraw.git

From source, for development:

git clone https://github.com/CoBrALab/pvraw.git
cd pvraw
uv sync                       # runtime deps, editable install
uv sync --extra dev           # also pytest, ruff and bids-validator

Each release is listed with its changes in CHANGELOG.md and on the releases page.

One command-line tool is installed: pvraw (inspection/conversion).

The examples below are written as uv run pvraw ..., which works from a source checkout. If you installed with uv tool install, drop the uv run prefix.


Command-line usage

Inspect a dataset — pvraw info

Print study/subject info and a table of scans, reconstructions, dimensions and resolutions — including each reconstruction's frame groups, whether it is a ParaVision-derived map, a diffusion summary, the BIDS datatype/suffix the conversion rules predict for it, and any warnings raised while reading it.

uv run pvraw info <input>           # <input> = study dir or .zip
uv run pvraw info --json <input>    # the same summary as JSON on stdout

--json is made for scripting: warnings go to stderr so stdout always parses, an unreadable input exits 1 with nothing on stdout, and a scan that cannot be read stays in the document as {"scan_id": N, "error": "..."} rather than disappearing. Numbers stay numbers, with units in the key names (tr_ms, fov_mm, ...):

uv run pvraw info --json <input> | jq '.scans[] | select(.recos[].bids.datatype == "dwi") | .scan_id'

Convert one study — pvraw tonii

Convert a single study to NIfTI. Without -s every scan/reconstruction is converted.

uv run pvraw tonii <input>                       # convert all scans
uv run pvraw tonii <input> -s 2 -r 1 -o out      # only ScanID 2, RecoID 1
uv run pvraw tonii <input> -s 2,3,7 -r 1,2       # several scans, several recos

Each file is named <output>-<ScanID>-<RecoID>-<ProtocolName>.nii.gz, so -o sets the prefix rather than the whole filename — one scan can write several files (a multi-echo or multi-slicepack reconstruction writes one per echo or pack).

Option Description Default
-o, --output <name> Output filename (without extension) / prefix <SubjID>_<StudyID>, or the input folder name when the dataset has no subject file
-s, --scanid <id[,id...]> Convert only the listed scans every scan
-r, --recoid <id[,id...]> Reconstruction ids; only read together with -s, since without it every reconstruction is converted 1
-b, --bids Also write a JSON sidecar of BIDS-recommended metadata off
-t, --subjecttype <T> Override subject type (Biped, Quadruped, Phantom, Other, OtherAnimal) the recorded subject type
-p, --position <P> The position the animal was actually in, <BodyPart>_<Side> (e.g. Foot_Supine). The recorded position is what ParaVision was told and is not trusted Head_Prone (prone, head first)
--ignore-rescale Write raw stored values, with no intensity scaling in the header. --ignore-slope and --ignore-offset are aliases: slope and offset cannot be suppressed independently off (the header carries the scaling)
--ignore-localizer Skip localizer/tripilot scans on

Every on/off option also has a --no- form — --no-bids, --no-ignore-localizer, --no-ignore-rescale — so a default that is on can be turned off from the command line.

Non-image scans (spectroscopy, etc.) and unclassifiable scans are skipped with a clear message rather than producing invalid output. Diffusion scans also emit FSL-style .bval/.bvec.

Batch convert — pvraw tonii_all

Convert every study under a parent directory into a simple sub-<id>/ses-<id>/<datatype>/ tree (anat/func/dwi/etc).

uv run pvraw tonii_all <parent_dir> -o <output_dir>

Accepts the same -b/-t/-p/--ignore-* options as tonii, with the same defaults. Without -o the tree is written to Data in the current directory.

Convert to BIDS — pvraw bids_helper + bids_convert

Produce a spec-compliant BIDS dataset in two steps:

# 1. Generate an editable datasheet (+ JSON metadata template with -j)
uv run pvraw bids_helper <parent_dir> bids_map -j

# 2. Review/fill bids_map.csv (subject, session, datatype, suffix, task, acq, run, ...),
#    then convert using the datasheet and metadata template
uv run pvraw bids_convert <parent_dir> bids_map.csv -j bids_map.json -o <bids_output>

bids_helper options:

Option Description Default
-f, --format <fmt> Datasheet format, csv or tsv; ignored when output already ends in .csv/.tsv csv
-j, --json Also write the JSON metadata template off
--subj Swap subject and study IDs off
--sess Swap session and study IDs off

--subj and --sess have no short form on purpose: -s and -t mean --scanid and --subjecttype elsewhere.

The prefilled SessID is ParaVision's session number -- the second-last _ field of the study directory name, the only place ParaVision writes it (ADR 0003). SUBJECT_study_nr is the study number inside the session (under a project, the study-template slot), so a visit that ran several study templates is one session. A name without that suffix (PV5) falls back to SUBJECT_study_nr.

bids_convert options:

Option Description Default
-j, --json <template> Metadata template to merge into the sidecars none — the sidecars carry only what the converter derives
-o, --output <dir> Output directory Data
-t, -p, --ignore-* The same overrides as tonii as in tonii

bids_convert converts exactly what the datasheet lists, so it has no --ignore-localizer: bids_helper already leaves localizers out.

Step 2 is where the work is: what to put in the datasheet, what to do about scans marked etc, how to name a task, how to link a fieldmap to the images it corrects, and what the validator warnings mean are all covered in How to convert Bruker studies to BIDS.

The output is validator-clean: correct filenames and entity ordering, JSON sidecars, dataset_description.json, participants.tsv/.json, <subject>_sessions.tsv, <subject>_scans.tsv with acquisition times, README and CHANGES. anat, func, dwi, fmap and perf (ASL, with its aslcontext.tsv) are emitted, and fieldmaps are linked to the images they correct via B0FieldIdentifier and IntendedFor.

BIDSVersion is taken from the BIDS schema the converter validates against, so the two can never disagree. Validate with the official bids-validator.

Nothing is silently dropped. A ParaVision-computed stack with no single BIDS suffix (a DTI tensor reconstruction) goes to derivatives/pvraw/, and a scan that cannot be classified goes to sourcedata/ — both outside the validated tree, so the data is kept without costing an error.


Python API

Changed in 1.0. All Bruker file reading is delegated to brukerapi (see docs/adr/0002-delegate-bruker-reading-to-brukerapi.md). study.pvobj is now a brukerapi folder rather than a PvDataset, the scan/reco listings and subject fields moved onto the loader itself, parameter files are brukerapi JCAMPDX objects, and get_dataobj returns an array with one named axis per Frame Group -- which is also the shape pvraw info prints, where it used to print a collapsed matrix size.

Requires brukerapi>=0.4.5, which supplies the voxel-to-patient affine (affine_of_package), the slice-package division and the slice spacing (slice_distance), and which returns JCAMP-DX string values without their <...> delimiters. Earlier releases either lack those or place volumes wrongly.

import pvraw

study = pvraw.load('path/to/study_or_archive.zip')   # == BrukerLoader(path)

study.is_pvdataset          # True if a valid PvDataset
study.num_scans             # number of scans
study.info()                # print the same summary as `pvraw info`
study.info_dict()           # the summary as a JSON-serialisable dict
                            # (what `pvraw info --json` dumps)

study.avail_scan_id         # e.g. [1, 2, 3, ...]
study.avail_reco_id         # {scan_id: [reco_id, ...]}
study.subj_id, study.study_id, study.session_id
study.subject_name, study.operator, study.user_account   # SUBJECT_name_string, the registered operator, ##OWNER

study.pvobj                 # the brukerapi folder the data is read through

Images (high-level)

# nibabel object, orientation + affine preserved
nii = study.get_niftiobj(scan_id=2, reco_id=1)

# write to disk (.nii.gz); save_as is an alias of save_nifti
study.save_nifti(2, 1, 'output_name', dir='.')
study.save_as(2, 1, 'output_name')

# raw ndarray and 4x4 affine
data   = study.get_dataobj(2, 1)
affine = study.get_affine(2, 1)

# one name per axis of that array: ('spatial', 'spatial', 'slice', 'echo', ...)
study.get_axis_labels(2, 1)
study.get_frame_groups(2, 1)   # [('echo', 6), ('slice', 5)]

The array carries one axis per ParaVision Frame Group, named. Multi-slice-package or multi-echo scans return a list of images; save_nifti writes them as name-01.nii.gz, name-02.nii.gz, ....

Parameters (low-level)

from pvraw.lib.utils import get_value

method = study.get_method(2)            # method file  (a brukerapi JCAMPDX)
acqp   = study.get_acqp(2)              # acqp file
visu   = study.get_visu_pars(2, 1)      # visu_pars (per reconstruction)

get_value(method, 'Method')             # any parameter by key
get_value(acqp, 'ACQ_size')
get_value(visu, 'NotThere', default=0)  # absence is version-dependent; default it

get_value resolves the JCAMP-DX representation -- it unwraps <...> string literals, returns a struct array as a list of rows, and returns default for an absent key (which brukerapi's own get_value raises on).

Diffusion

bvals, bvecs = study.get_bdata(scan_id)     # FSL-style arrays
study.save_bdata(scan_id, 'dwi', dir='.')   # writes dwi.bval / dwi.bvec

Overrides

study.override_subjtype('Quadruped')        # fix mis-set subject type
study.override_position('Foot_Supine')       # the animal really lay like this (default assumes Head_Prone)

Development

From a source checkout, after uv sync --extra dev:

uv run pytest                        # everything; sample data is fetched from the network
uv run pytest -m "not data"          # offline unit tests only, no downloads
uv run pytest tests/08_orientation_test.py    # a single file

uv run ruff check .                  # must be clean
uv run ruff check . --fix            # safe fixes only -- then re-run the tests

Data-dependent tests download public sample studies (Zenodo, GitHub) and cache them under $PVRAW_TEST_DATA_DIR. Set it to keep the cache between runs:

PVRAW_TEST_DATA_DIR=~/.cache/pvraw-test-data uv run pytest -m data

Two sweep tools compare a whole corpus before and after a change, which is how geometry and BIDS regressions get caught:

uv run python tools/sweep_nifti.py <corpus_dir>              # affines, data hashes, headers
uv run python tools/sweep_nifti.py <corpus_dir> --compare=<earlier.json>
uv run python tools/sweep_bids.py                            # convert + validate every study

CI installs from uv.lock with uv sync --locked, so every tool version is pinned and a new ruff or pytest release cannot break an unrelated change. --locked also fails if pyproject.toml was edited without re-running uv lock.


License

GNU General Public License v3.0.

History and credits

pvraw began life as BrkRaw, and specifically as a hard fork of its 0.3.x/0.4 line. Upstream has since moved to a rewritten 0.5+ architecture; this project is developed independently of it. If you want the 0.5+ architecture, go upstream — this is not a drop-in replacement for it.

Little of the original reading code survives. All Bruker file reading — directory and archive traversal, JCAMP-DX parsing, byte→array assembly and the voxel-to-patient affine — is now delegated to brukerapi (see docs/adr/0002-delegate-bruker-reading-to-brukerapi.md). What remains here is how the subject was framed, the NIfTI headers, and BIDS.

Naming. 1.0.0 is the first release under the pvraw name, and the first tagged release of this project at all — earlier work was installable only from git, under the name brkraw-legacy, and signed its output that way: NIfTI files carry brkraw-legacy in the header descrip field, BIDS dataset_description.json names BrkRaw-legacy under GeneratedBy, and derived reconstructions were written to derivatives/brkraw-legacy/. From 1.0.0 all three say pvraw. If you hold a dataset converted before that, this is why its provenance strings differ.

The original BrkRaw authors, whose work this is built on:

  • SungHo Lee (shlee@unc.edu) — main developer
  • Woomi Ban (banwoomi@unc.edu) — tested and refined the module structure
  • Jaiden Dumas — documentation and user-community content
  • Gabriel A. Devenyi — refinement of module functionality and troubleshooting
  • Yen-Yu Ian Shih (shihy@neurology.unc.edu) — technical and academic advisory, and funding

Also acknowledged by the original project: Chris Rorden and Sebastiano Ferraris, whose dcm2niix and bruker2nifti inspired it; and Mikael Naveau, who published bruker2nifti_qa, benchmark data still used for validation (see DATASETS.md).

Download files

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

Source Distribution

pvraw-1.1.1.tar.gz (259.6 kB view details)

Uploaded Source

Built Distribution

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

pvraw-1.1.1-py3-none-any.whl (124.8 kB view details)

Uploaded Python 3

File details

Details for the file pvraw-1.1.1.tar.gz.

File metadata

  • Download URL: pvraw-1.1.1.tar.gz
  • Upload date:
  • Size: 259.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pvraw-1.1.1.tar.gz
Algorithm Hash digest
SHA256 09d2d79a46449e515de936f0acb5145776836a281015a0836171b40e023f5291
MD5 6c324d60d3c3ecad2c9073a7689b967f
BLAKE2b-256 fefaaa96652376643256ccdeb99bde22f378439fae232afa3e9dbc956f51ed3a

See more details on using hashes here.

Provenance

The following attestation bundles were made for pvraw-1.1.1.tar.gz:

Publisher: release.yml on CoBrALab/pvraw

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

File details

Details for the file pvraw-1.1.1-py3-none-any.whl.

File metadata

  • Download URL: pvraw-1.1.1-py3-none-any.whl
  • Upload date:
  • Size: 124.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pvraw-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 88bda3bdc4d5af9dbda213d02910444c3146f1e9c978351c9023932395a30c6b
MD5 77b95305da687c27613efb1040534133
BLAKE2b-256 7bfb7ffbcd370e70451f506a60326bb15f44346b8d348540d1d5de7d48cd0774

See more details on using hashes here.

Provenance

The following attestation bundles were made for pvraw-1.1.1-py3-none-any.whl:

Publisher: release.yml on CoBrALab/pvraw

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

Release history Release notifications | RSS feed

This release

1.1.1 This release

2 files

1.1.0

2 files

Supported by

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