Loading and saving data#
Larmor reads each file into one of its types: an image into a Volume, a gradient table
into a GradientTable, a tractogram into a Tractogram and a
surface into a Surface. Positions are in world coordinates (RAS+, in mm) whatever the
file’s own convention, and the end of the file name chooses the format. File formats
lists every format, and Conventions explains the coordinates.
The data on this page
The examples on this page run on a small synthetic image, written to the current folder by the code below:
dwi.nii.gz with its gradient files dwi.bval and dwi.bvec, and a mask, mask.nii.gz. It has 12 x 12 x 2
voxels of 2 mm, two b=0 images and 30 directions at each of b=1000 and 2000 s/mm^2. Two bundles cross in its
middle, one along x and one along y; the other voxels hold isotropic diffusion. To use your own data, use your
files instead.
import numpy as np
from larmor.io import save_volume, write_bvals_bvecs
from larmor.sd import AnisotropicRF
from larmor.types import GradientTable, Volume
rng = np.random.default_rng(0)
directions = rng.normal(size=(60, 3))
directions /= np.linalg.norm(directions, axis=1, keepdims=True)
bvals = np.r_[0, 0, [1000] * 30, [2000] * 30]
bvecs = np.r_[np.zeros((2, 3)), directions]
gtab = GradientTable(bvals, bvecs)
fibre = AnisotropicRF([1.7e-3, 0.3e-3, 0.3e-3], mean_kurtosis=0.5)
along_x, along_y = fibre.signal(bvals, gtab.bvecs, np.eye(3)[:2]).T
isotropic = np.exp(-bvals * 0.8e-3)
signal = np.broadcast_to(isotropic, (12, 12, 2, len(bvals))).copy()
signal[:, 4:8] = along_x
signal[4:8, :] = along_y
signal[4:8, 4:8] = (along_x + along_y) / 2
data = 1000 * signal + rng.normal(0, 10, signal.shape)
affine = np.diag([2.0, 2.0, 2.0, 1.0])
save_volume(Volume(data.astype(np.float32), affine), "dwi.nii.gz")
write_bvals_bvecs(gtab, "dwi.bval", "dwi.bvec", ref_path="dwi.nii.gz")
save_volume(Volume(np.ones((12, 12, 2), np.uint8), affine), "mask.nii.gz")
Images#
from larmor.io import load_header, load_volume, save_volume
mask = load_volume("mask.nii.gz")
print(mask.data.shape, mask.voxel_sizes, mask.orientation)
save_volume(mask, "mask.mif")
grid, shape = load_header("dwi.nii.gz")
print(shape)
lar info mask.nii.gz
lar convert mask.nii.gz mask.mif
A Volume holds the stored values (data) and the affine from voxel indices to world coordinates (affine).
as_float() gives the values with the file’s scaling applied.
load_header() reads only the grid and the shape, without the data. NIfTI (.nii, .nii.gz)
and MRtrix (.mif, .mif.gz, .mih) images are read and written. lar convert --to-ras also reorients an image
to its closest RAS orientation, which only permutes and flips the voxel axes.
A diffusion image with its gradients#
load_dwi() reads a diffusion image together with its gradient table. A NIfTI image needs its
bvals and bvecs files; a MIF image can hold the table in its header and then needs none.
from larmor.io import load_dwi, save_volume
dwi, gtab = load_dwi("dwi.nii.gz", "dwi.bval", "dwi.bvec")
save_volume(dwi, "dwi.mif", gradients=gtab)
dwi, gtab = load_dwi("dwi.mif")
print(dwi.data.shape, gtab.shells)
lar convert dwi.nii.gz dwi.mif --bvals dwi.bval --bvecs dwi.bvec
lar info dwi.mif
Each lar command that takes a diffusion image has the options --bvals and --bvecs, which a MIF image with a
table does not need. Gradient tables and shells covers the tables themselves.
Tractograms#
A tractogram holds all the points of its streamlines in one array, in world coordinates, and the offsets where each streamline starts. Three straight streamlines, saved as a TCK file:
import numpy as np
from larmor.io import save_tractogram
from larmor.types import Tractogram
line = np.linspace([2.0, 0.0, 2.0], [22.0, 0.0, 2.0], 11)
points = np.concatenate([line + [0.0, y, 0.0] for y in (8.0, 10.0, 12.0)])
save_tractogram(Tractogram(points, np.array([0, 11, 22, 33])), "tracks.tck")
TCK and VTK files store world coordinates, while a TRK file stores its points in the voxel frame of an image, so writing a TRK needs that image’s grid.
from larmor.io import load_grid, load_tractogram, save_tractogram
tracts = load_tractogram("tracks.tck")
print(tracts.num_streamlines, tracts.num_points)
save_tractogram(tracts, "tracks.trk", reference=load_grid("dwi.nii.gz"))
save_tractogram(tracts, "tracks.vtk")
lar info tracks.tck
lar convert tracks.tck tracks.trk --reference dwi.nii.gz
lar convert tracks.tck tracks.vtk
For large files, load_tractogram(path, max_streamlines=n, seed=0) keeps a random subset of n streamlines, and
iter_tractogram() reads an uncompressed TCK or TRK file batch by batch.
Surfaces#
load_surface() and save_surface() read and write GIfTI and FreeSurfer
surfaces; see Surfaces.
See also#
Gallery: Reading and writing files, and Reading MRIToolkit results from MATLAB files.
API:
larmor.io,larmor.types.Commands:
lar info,lar convert.