Skip to main content

tomio

I/O utilities for medical images: image volumes (thin wrapper over SimpleITK), interpolation, label files (ITK-SNAP, FreeSurfer, 3D Slicer) and config files (JSON, YAML, TOML).

Installation

pip install tomio
pip install tomio[interp]   # optional: scipy interpolation backend
pip install tomio[nibabel]  # optional: conversion to/from nibabel images

Requires Python >= 3.10. Reading TOML files on Python 3.10 requires tomli.

Quick start

import tomio as io

vol = io.read_volume("vol.nii.gz")  # numpy array with spacing, origin, transform, metadata
io.write_volume("vol.mha", vol)

vol2 = io.resample(vol, spacing=(1, 1, 1))

cfg = io.read_config("config.yml")
labels = io.read_labels("labels.txt")

Modules: io.volume, io.interp, io.labelfile, io.configfile.

Conventions

  • Arrays are indexed (x, y, z, ...), in ITK index order (not the (z, y, x) order of sitk.GetArrayFromImage).
  • World coordinates are LPS (ITK, DICOM). Conversion to nibabel (RAS) is done by to_nibabel/from_nibabel.
  • vol.transform is a tuple of axis directions: transform[i] is the world direction of array axis i, i.e. column i of the ITK direction matrix.
  • Orientation codes use the ITK "from" convention by default (identity: RAI); the "to" convention (identity: LPS) is available with convention="to".

Volumes

import numpy as np
import tomio as io

# read / write (formats: .nii, .nii.gz, .mha, .mhd, .nrrd...)
vol = io.read_volume("vol.nii.gz")
io.write_volume("vol2.mha", vol)

# options
vol = io.read_volume("vol.nii.gz", dtype=np.float32)  # cast
vol = io.read_volume("complex.mha", as_complex=True)  # complex volume (written by io.write_volume)
io.write_volume("vol2.nii.gz", vol, compression_level=9)
header = io.volume.read_header("vol.nii.gz")  # shape, spacing, origin, transform, metadata (no pixel data)

# Volume is a numpy ndarray
isinstance(vol, np.ndarray)  # True
vol.A  # plain np.ndarray

# header
vol.spacing  # (sx, sy, sz)
vol.origin  # world coordinates of voxel (0, 0, 0)
vol.transform  # axis directions
vol.metadata  # dict[str, str], written to the file
vol.header  # dict of spacing, origin, transform and metadata
vol.affine  # 4x4 affine transform (indices -> LPS coordinates)

# world coordinates <-> indices
coords = vol.get_coords([1, 2, 3])
indices = vol.get_indices(coords)

# create volumes
vol2 = io.Volume(np.zeros((10, 20, 30)), spacing=(1, 1, 2), origin=(0, 0, 0))
vol2 = io.asvolume(array, ref=vol)  # copy the header of `vol`
vol2 = io.tovolume(values, mask=mask, ref=vol)  # fill the voxels of a mask

Volumes with more than 3 dimensions (e.g. time series (x, y, z, t)) and complex volumes can be read and written; the geometry applies to the first 3 axes.

Geometry

geom = vol.geometry  # io.Geometry: shape, spacing, origin, transform (immutable)
geom.affine, geom.extent, geom.center, geom.voxel_volume
geom.same_as(other_vol, tol=1e-4)
vol2 = io.Volume(array, geometry=geom)

Slicing and numpy operations

The geometry follows numpy operations when it makes sense:

vol[2:].origin  # world coordinates of the first voxel of the slice
vol[::2].spacing  # spacing x 2 in the first axis
vol[::-1]  # axis direction flipped (also np.flip)
vol.T, np.moveaxis(vol, 0, -1)  # spacing and directions permuted
vol + 1, np.sqrt(vol)  # same geometry

# no geometry: plain np.ndarray
vol[0], vol[mask], vol.sum(axis=0), vol.mean()
# time series: geometry kept
vol4d[..., 0], vol4d.mean(axis=3)

Orientation

vol.anatomical_orientation  # e.g. "RAI" (ITK "from" convention)
vol.get_orientation(convention="to")  # e.g. "LPS"
vol2 = vol.reorient("RAS", convention="to")  # flip/permute the axes

Interoperability

image = vol.to_sitk()  # SimpleITK Image
vol = io.Volume.from_sitk(image)
nifti = vol.to_nibabel()  # nibabel Nifti1Image (requires nibabel)
vol = io.Volume.from_nibabel(nifti)

Interpolation

import numpy as np
import tomio as io

vol2 = io.resample(vol, like=ref)  # geometry of another volume (or a Geometry)
vol2 = io.resample(vol, spacing=(1, 1, 1))  # new spacing, same extent
vol2 = io.resample(vol, geometry={"spacing": (1, 1, 1), "shape": (64, 64, 64)})
vol2 = io.resample(vol, like=ref, method="linear", fill=np.nan)  # value outside `vol`

# label maps and masks
labels2 = io.resample(labelmap, like=ref, method="labels")  # label by label (smooth boundaries)
roi2 = io.resample(roi, like=ref, method="roi")  # no label lost when downsampling
  • Methods: nearest, linear, spline3 (and splineN), labels, roi. Default: nearest for integer and bool data (label maps), spline3 otherwise. The output has the dtype of the input.
  • Backends: SimpleITK (default, multithreaded) or scipy (backend="scipy", requires tomio[interp]). SimpleITK considers voxels inside the source up to half a voxel beyond the border voxel centers.
  • Lower-level functions: io.interp.interpolate, interpolate_like, interpolate_to, interpolate_roi, interpolate_labels.

Label files

import tomio as io

labels = io.read_labels("labels.txt")  # ITK-SNAP label description file
labels = io.read_labels("FreeSurferColorLUT.txt", format="freesurfer")
labels = io.read_labels("labels.ctbl", format="slicer")  # 3D Slicer color table
io.write_labels("labels.txt", labels)  # or labels.save("labels.txt"), ITK-SNAP format

# Labels: mapping {index: description}, sorted by index
labels = io.labelfile.Labels({1: "white matter", 2: "grey matter"})  # automatic colors
labels = io.labelfile.Labels({1: {"LABEL": "white matter", "RGBA": (255, 255, 255, 1.0)}})
labels = io.labelfile.Labels(["background", "white matter"])  # indices 0, 1

labels[1]  # description
labels[3] = "csf"  # add or rename (keeps the color)
del labels[3]
labels.getindex("grey matter")  # index of a description
labels.indices, labels.descriptions  # lists, sorted by index (can be assigned)
labels.colors  # {index: (R, G, B, A)}, RGB in 0..255, alpha in 0..1 (can be assigned)

# label maps
labels = io.labelfile.Labels.from_volume(labelmap)  # one label per value
rgba = labels.lut[labelmap]  # RGBA image (uint8)
labels2, mapping = labels.reindex()  # indices 1..N
labelmap2 = io.labelfile.apply_mapping(labelmap, mapping)

Labels are validated: non-negative integer indices, unique descriptions without " or line breaks (allow_duplicates=True to allow duplicates), RGB in 0..255, alpha in 0..1. Label files are UTF-8.

Config files

import tomio as io

cfg = io.read_config("config.yml")  # .json, .yaml, .yml, .toml -> dict
io.write_config("config.yml", cfg)  # .json, .yaml, .yml
  • The format is set by the file extension (no extension: .json). TOML files are read-only.
  • Values are written as standard types: numpy arrays as lists, numpy scalars as numbers, complex numbers as strings "3 + 4i" (read back as complex numbers).
  • YAML files are loaded safely (no Python-specific tags).

Command line

tomio convert vol.mha vol.nii.gz                        # convert (format from the extensions)
tomio convert vol.mha vol.nii.gz --dtype float32 --compression-level 9
tomio resample vol.nii.gz out.nii.gz --spacing 1 1 1    # new spacing
tomio resample labels.nii.gz out.nii.gz --like ref.nii.gz --method labels
tomio info vol.nii.gz --metadata                        # header: shape, pixel type, geometry

Existing output files are not overwritten unless --force is given. See tomio COMMAND --help.

Metadata

Release files for tomio 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tomio 0.1.1
File Size Uploaded Provenance Actions
tomio-0.1.1.tar.gz 46.5 kB With provenance Details for tomio-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for tomio 0.1.1
File Interpreter ABI Platform Actions
tomio-0.1.1-py3-none-any.whl Python 3 none any Details for tomio-0.1.1-py3-none-any.whl

Total release size: 76.5 kB

Release files / tomio-0.1.1.tar.gz

Download URL tomio-0.1.1.tar.gz
Size 46.5 kB
Tags Source
SHA-256 checksum
How to use checksums
4bb4a92faa055caba1163770f213e3f656f48a26b6bb186a561dbb377537805f
BLAKE2b-256 checksum
How to use checksums
adb22f5d66ca74b936f93296bbd7651a6ecaf7f0682a2da5eb6e9c34f58ee7a1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 12, 2026.

Transparency log

Release files / tomio-0.1.1-py3-none-any.whl

Download URL tomio-0.1.1-py3-none-any.whl
Size 30.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
60619330418f80286c205da7cd555ac8cc9d296bebf48125d9a0298ee9b653d2
BLAKE2b-256 checksum
How to use checksums
77f19864a826db3b393c8583639eddd423a9a399e1f0c2337f5338c404a763fd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 12, 2026.

Transparency log

Security for tomio 0.1.1

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.

Select from 2 files

Attestation for tomio-0.1.1.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 12, 2026.

Transparency log

A 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.

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