brainhops.io.base.mrtrix
The shared MRtrix-reading and MRtrix-writing machinery behind every MRtrix-based image and transformation format.
An MRtrix image is a text header followed by raw voxel data. The header
and the data are either in the same file (.mif, or .mif.gz when the
whole file is gzip-compressed) or in two files (.mih for the header,
and the data file it names, conventionally .dat).
The header is a list of key: value lines, between a first line that
reads mrtrix image and a last line that reads END::
mrtrix image
dim: 64,64,32,7
vox: 2,2,2.5,nan
layout: -0,-1,+2,+3
datatype: Float32LE
transform: 0.996, 0.087, 0, -61.2
transform: -0.087, 0.996, 0, -70.5
transform: 0, 0, 1, -40
scaling: 0,1
dw_scheme: 0,0,1,0
dw_scheme: 0,1,0,1000
file: . 552
END
The conventions below were checked against the MRtrix3 sources
(core/formats/mrtrix_utils.{h,cpp}, core/formats/mrtrix{,_gz}.cpp,
core/file/key_value.cpp, core/stride.h, core/raw.h,
core/header.cpp, core/transform.h, core/datatype.cpp).
- Comments. Everything after a
#on a line is a comment, and is dropped. A value cannot therefore contain a#. - Keys. The compulsory keys (
dim,vox,layout,datatype,transform,scaling) are matched case-insensitively. Any other key is kept as it is spelled. A key that appears on several lines (the three rows oftransform, the rows of adw_scheme, the entries of acommand_history) has one value per line; the other keys are collected as their lines joined by newlines, which is how MRtrix holds them. dim,vox. The size and the voxel size of each axis.voxmay hold fewer entries thandim(but at least three, or as many asdimwhen it has fewer), and may holdnanfor an axis that has no physical size.layout. One signed integer per axis, such as-0,-1,+2. The absolute value is the rank of the axis in the file: the axis of rank 0 changes fastest, the one of rank 1 next, and so on. The sign says whether the voxels along the axis are stored in increasing (+) or decreasing (-) order of their index. Voxel[0, 0, ...]is therefore not the first value in the file when any axis is negative: it issum((size[i] - 1) * |stride[i]|)values in, over the negative axes.layout: +0,+1,+2is a plain Fortran-ordered (x fastest) array.datatype. One ofBit,Int8,UInt8,[U]Int{16,32,64},Float{32,64}andCFloat{32,64}, the multi-byte ones optionally suffixed withLEorBE. Without a suffix the byte order is the native one of the machine.Bitpacks eight voxels per byte, the first one in the most significant bit.transform. Three rows of four numbers, the top of a4x4matrix that maps voxel coordinates multiplied by the voxel sizes (not plain voxel indices) to scanner coordinates, in millimetres, in RAS+ (x to the right, y to the front, z up), like a NIfTI sform. The voxel-to-scanner matrix istransform @ diag(vox[:3], 1). MRtrix writes the three direction columns at unit length; one that is not is normalised and its length moved into the voxel size, which leaves the voxel-to-scanner matrix unchanged.- Missing transform. When
transformis absent (or not finite), MRtrix centres the field of view on the origin: the rotation is the identity, and the translation is-0.5 * (size - 1) * voxalong each of the first three axes. It is not the identity. scaling.offset,scale: a stored valuevmeansoffset + scale * v.file.file: <name> [<offset>]. A name of.means the data follow the header in the same file,offsetbytes from its start. Any other name is the data file, relative to the header's directory, and its offset defaults to zero.
This module holds what an image reader and a transformation reader (an MRtrix warp, for instance) share: the header, the decoding of the voxel data into an array whose axes are the header's axes, and the encoding of such an array back into bytes.
Attributes
MRTRIX_MAGIC
module-attribute
The line every MRtrix header starts with.
Classes
MrtrixHeader
MrtrixHeader(
dim: Sequence[int] = (),
vox: Sequence[float] | None = None,
layout: Sequence[int] | None = None,
datatype: str = "Float32LE",
transform: Any | None = None,
scaling: Sequence[float] | None = None,
file: tuple[str, int] | None = None,
keyval: Mapping[str, str] | None = None,
)
The content of an MRtrix image header.
The keys MRtrix decodes are held decoded: dim, vox, layout,
datatype, transform, scaling and file. Every other key is
kept in keyval, in the order it was read, with the lines of a
repeated key joined by newlines, so the header writes back as it was
read.
The header is plain data: building or changing one never touches a file.
Attributes
vox
instance-attribute
The voxel size of each axis, as stored (possibly fewer entries
than axes, possibly nan).
layout
instance-attribute
The signed one-based symbolic strides (see parse_layout).
datatype
instance-attribute
datatype = str(datatype)
The MRtrix data type, as spelled in the header.
transform
instance-attribute
The (3, 4) stored transform, or None when there is none.
keyval
instance-attribute
keyval = OrderedDict(keyval or {})
Every other key, with its lines joined by newlines.
spacing
property
spacing: tuple[float, ...]
The voxel size of each axis, as MRtrix uses it.
Missing entries are nan. A spatial voxel size that is not
finite is replaced by the mean of the finite spatial ones (or 1
when there is none), which is what MRtrix does.
Methods:
default_transform
The (3, 4) transform MRtrix assumes when the header has none.
The rotation is the identity, and the field of view is centred
on the origin: the translation is -0.5 * (size - 1) * vox along
each of the first three axes (an axis the image does not have
counts as one voxel).
voxel_to_scanner
The (4, 4) matrix from voxel indices to scanner RAS+ mm.
The stored transform maps coordinates scaled by the voxel sizes,
so the matrix is transform @ diag(vox[0], vox[1], vox[2], 1).
An image with fewer than three axes is given unit-size extra
axes, as MRtrix does. A missing or non-finite transform is
replaced by default_transform.
from_lines
classmethod
from_lines(lines: Iterable[str]) -> MrtrixHeader
Parse header lines, from mrtrix image up to END.
Lines after END are ignored, so the decoded text of a whole
file can be handed over.
Raises:
| Type | Description |
|---|---|
ParserContentError
|
If the first line is not |
from_fileobj
classmethod
from_fileobj(file: BinaryIO) -> tuple[MrtrixHeader, int]
Read a header from a binary stream, up to its END line.
The stream is left just after the END line. Returns the header
and the number of bytes it took.
Raises:
| Type | Description |
|---|---|
ParserContentError
|
If the stream does not hold an MRtrix header. |
to_lines
The header's lines, from mrtrix image up to END.
file is the (name, offset) of the data, written in the
file: line; an offset of None is left out. It defaults to the
header's own file, and no file: line is written when there
is none.
embedded
The header of a single-file image, padded up to its data.
The file: . <offset> line points just past the header, at an
offset aligned on align bytes (MRtrix aligns on four). The
offset is part of the header it measures, so it is found by
iteration.
MrtrixParser
magic
MrtrixParser(
_header: MrtrixHeader | None = None,
dataobj: Any | None = None,
)
Bases: DataModelBase, BinaryFileParserWriter
Base class for objects that are encoded by an MRtrix image file.
It reads and writes the container -- the header and the raw voxel
values -- for every MRtrix-based format: an image, and later a warp.
What the values mean is for the concrete format to say, through
_mrtrix_header and _mrtrix_data when writing.
Reading a .mif or a .mih from a local path memory-maps the data,
so nothing but the header is read until the data are indexed. A
.mif.gz, a file object or bytes are read into memory.
Attributes
header
property
writable
header: MrtrixHeader | None
The MRtrix header this object was read from, if any.
EXTENSIONS
class-attribute
EXTENSIONS: tuple[str, ...] = ()
File extensions handled by this parser, e.g. (".nii", ".nii.gz").
Used as a first, cheap dispatch pass. When several parsers match,
the longest matching extension wins, so a parser declaring
".nii.gz" takes precedence over one declaring ".gz".
PREFIXES
class-attribute
PREFIXES: tuple[str, ...] = ()
Filename prefixes required by this parser, e.g. ("y_", "iy_").
An empty tuple means "no constraint". A parser that constrains the prefix is more specific than one that does not, and wins ties.
Declaring EXTENSIONS and PREFIXES separately states the
cross-product implicitly, which is how these conventions actually
work: SPM's four names are {y_, iy_} x {.nii, .nii.gz}.
PRIORITY
class-attribute
PRIORITY: int = 0
Explicit tie-breaker, consulted only when specificity cannot decide.
Higher wins. Leave at 0 unless two parsers genuinely collide.
Methods:
from_file
classmethod
Build the object from an MRtrix file (path or file object).
from_filename
classmethod
from_filename(
filename: FilenameLike, mmap: bool = True, **kwargs
) -> Self
Build the object from the path of a .mif, .mih or .mif.gz.
The data of an uncompressed local file are memory-mapped unless
mmap is false. A .mih header is followed to the data file it
names, relative to the header's directory.
from_fileobj
classmethod
Build the object from an open MRtrix file object, gzipped or not.
The data of a single-file image are read from the stream. A
header that names a separate data file is resolved against the
stream's name, when it has one.
from_bytes
classmethod
Build the object from the bytes of a single-file MRtrix image (gzipped or not).
sniff_fileobj
classmethod
Score how confident the class is that a stream holds an MRtrix image, gzipped or not.
sniff_bytes
classmethod
Score how confident the class is that bytes hold an MRtrix image, gzipped or not.
to_fileobj
to_fileobj(file: IO, **kwargs) -> None
Write a single-file, uncompressed .mif to a stream.
to_filename
to_filename(filename: FilenameLike, **kwargs) -> None
Write to a path, in the variant its extension names.
.mif: header and data in one file;.mif.gz: the same, gzip-compressed;.mih: the header, and the data in a.datfile next to it (named after the header), which the header points to.
to_file
to_file(file: FileLike, **kwargs) -> None
Write to a path (variant chosen by extension) or a stream.
sniff
classmethod
sniff(
file: FileOrContentLike,
error: bool | Type[Exception] = False,
**kwargs,
) -> float
Determine if the given file is of the type that this parser can handle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
FileOrContentLike
|
The file to sniff. |
required |
error
|
bool | type[Exception]
|
If not False, raise an error if the file cannot be sniffed. |
False
|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
float
|
Confidence that the file is of this type, in |
sniff_file
classmethod
Determine if the given file is of the type that this parser can handle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
FileLike
|
The file to sniff. |
required |
error
|
bool | type[Exception]
|
If not False, raise an error if the file cannot be sniffed. |
False
|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
float
|
Confidence that the file is of this type, in |
sniff_filename
classmethod
sniff_filename(
filename: FilenameLike,
error: bool | Type[Exception] = False,
**kwargs,
) -> float
Determine if the given filename is of the type that this parser can handle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
FilenameLike
|
The filename to sniff. |
required |
error
|
bool | type[Exception]
|
If not False, raise an error if the filename cannot be sniffed. |
False
|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
float
|
Confidence that the filename is of this type, in |
sniff_content
classmethod
sniff_content(
content: ContentLike,
error: bool | Type[Exception] = False,
**kwargs,
) -> float
Determine if the given content is of the type that this parser can handle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
content
|
ContentLike
|
The content to sniff. |
required |
error
|
bool | type[Exception]
|
If not False, raise an error if the content cannot be sniffed. |
False
|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
float
|
Confidence that the content is of this type, in |
sniff_text
classmethod
Determine if the given text is of the type that this parser can handle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
The text to sniff. |
required |
error
|
bool | type[Exception]
|
If not False, raise an error if the content cannot be sniffed. |
False
|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
float
|
Confidence that the text is of this type, in |
sniff_lines
classmethod
Determine if the given lines are of the type that this parser can handle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lines
|
Iterable[str]
|
The lines to sniff. |
required |
error
|
bool | type[Exception]
|
If not False, raise an error if the content cannot be sniffed. |
False
|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
float
|
Confidence that the lines is of this type, in |
sniff_line
classmethod
Determine if the given line is of the type that this parser can handle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
line
|
str
|
The line to sniff. |
required |
error
|
bool | type[Exception]
|
If not False, raise an error if the content cannot be sniffed. |
False
|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
float
|
Confidence that the line is of this type, in |
load
classmethod
load(other: FileOrContentLike, **kwargs) -> Self
Build an object from a file (path, file-like object or iterable of lines).
This is the generic front door to the from_* family: it looks
at what it was handed and calls the right one.
A str is always a path, whether or not the file exists, so a
missing file raises FileNotFoundError whichever way its path
was spelled. Text held in memory is read with from_text or
from_content.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
FileOrContentLike
|
Input file, or its content. |
required |
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
obj
|
The parsed object. |
Raises:
| Type | Description |
|---|---|
ParserExistsError
|
If |
from_spec
classmethod
from_spec(spec: SourceSpec, **kwargs) -> Self
Build an object from an unqualified structured source.
from_content
classmethod
from_content(content: ContentLike, **kwargs) -> Self
Build an object from a file content (bytes, str, or iterable of lines).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
content
|
ContentLike
|
The content to parse. |
required |
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
obj
|
The parsed object. |
from_text
classmethod
Build an object from a text representation of a file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
The text to parse. |
required |
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
obj
|
The parsed object. |
from_lines
classmethod
from_line
classmethod
Build an object from a single line of text.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
line
|
str
|
The line to parse. |
required |
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
obj
|
The parsed object. |
save
save(file: FileLike, **kwargs) -> None
Write the object to a file (path or file-like object).
This is the generic front door to the to_* family. It is named
save rather than to because to already means something else
on the data models these parsers are mixed into: Transformation.to
converts an object to another type. A writer's to was shadowed
by it on every writable transformation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
FileLike
|
The file to write to. |
required |
**kwargs
|
Parser-specific options. |
{}
|
to_text
to_text(**kwargs) -> str
Return a text version of the file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
A text version of the file. |
to_lines
Return a text version of the file as an iterable of lines.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
Iterator[str]
|
An iterable of lines representing the object. |
to_line
to_line(**kwargs) -> str
Return a line representing the object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
A line representing the object. |
from_dict
classmethod
Create an instance of the class from a dictionary-like object.
Only keys in the dictionary that match keyword-like fields of
this class, or the keywords its constructor takes without
storing them (its InitVars, such as the matrix= of an
Affine), will be used. Other keys are ignored, but see
from_other,
which refuses them.
Additional positional and/or keyword arguments can be provided, and will take precedence over the values in the dictionary.
A key naming a field that this class fixes (a field that cannot
be passed to its constructor) is checked instead of used: a
dictionary that sets it to anything other than None or the
value of this class is refused with a ValueError.
from_instance
classmethod
Create an instance of the class from an instance of a similar class.
Only attributes of the other instance that match keyword-like
fields of this class will be used. An attribute that is None
is unset, and leaves the default of this class in place.
Additional positional and/or keyword arguments can be provided, and will take precedence over the attributes in the instance.
Unless the other instance is already an instance of this class,
an attribute naming a field that this class fixes (a field that
cannot be passed to its constructor) is checked instead of used:
an instance that sets it to anything other than None or the
value of this class is refused with a ValueError. A
generic Axis whose orientation is right-to-left, for example,
cannot be read as a LeftToRightAxis.
from_other
classmethod
Create an instance of the class from any object that can be interpreted as a dictionary, or an instance of a similar class, or an arguments to be passed to the constructor.
A similar class is this class or one of its parents within the
data model, or another member of a polymorphic family this class
belongs to: calling a polymorphic class such as Axis builds the
subclass its arguments select, so a "generic" axis is usually an
instance of a sibling (a RightToLeftAxis, a TimeAxis) rather
than of a parent. Any other object, including an instance of a
parent that is not a data model (such as a plain object), is
passed to the constructor.
Unlike from_dict,
a dictionary with a key that matches no field of this class is
refused with a TypeError naming the keys, so that a
misspelt key is not silently dropped.