brainhops.io.transformations.itk.mat
ITK can save linear transformations in a binary MATLAB file
(itk::MatlabTransformIO). This is the format ANTs uses for every linear
transform it writes: antsRegistration saves <prefix>0GenericAffine.mat,
and also Rigid.mat, Affine.mat, Similarity.mat, Translation.mat
and DerivedInitialMovingTranslation.mat.
The file holds the same transform blocks as the text-based TFM format -- a transform class, its parameters and its fixed parameters -- in a MATLAB v4 container instead of text.
A 3-D affine, as scipy.io.loadmat sees it
Not to be confused with FSL FLIRT
FSL FLIRT also saves its affines as .mat files, but as plain text.
The two are told apart by content, not by name: this reader claims
only files that start with a MATLAB v4 header naming an ITK transform.
Approximate specification
1. Variables
A MATLAB v4 file is a flat sequence of variables, each a header, a name and the values. ITK writes every block of the transform as two variables, in this order:
- The parameters, a column vector named after the transform class,
{ClassName}_{Precision}_{InputDim}_{OutputDim}-- for exampleAffineTransform_double_3_3. Older ANTs releases name affinesMatrixOffsetTransformBase_double_3_3, which has the same parameters. - The fixed parameters, a column vector named
fixed-- typically the center of rotation.
ITK's reader takes the variables in pairs, and the second of each pair is
the fixed parameters whatever its name; both must be column vectors. A
file with several blocks repeats the pair, so the same name may appear
more than once. A chain written from a CompositeTransform starts with a
pair for the composite itself, which only points to the blocks after it.
ITK applies the blocks of a composite last to first, so the reader lists
them in reverse file order (see "Composite files" below).
2. Variable header
Each variable starts with five 32-bit integers:
| Field | Meaning |
|---|---|
type |
M*1000 + O*100 + P*10 + T (see below) |
mrows |
number of rows (the length of the vector) |
ncols |
number of columns (1) |
imagf |
1 if the values are complex (0) |
namlen |
length of the name, including its terminating NUL |
followed by the NUL-terminated name and the mrows * ncols values in
column-major order. The digits of type are:
M: byte order,0for little-endian and1for big-endian. ITK (throughvnl_matlab_write) writes in the native order of the machine, and the header integers are in that same order.O:0. MATLAB reserves this digit; VNL sets it to1for a matrix it writes row by row, which reads the same for a vector.P: precision,0fordoubleand1forfloat. The parameters of afloattransform arefloat, but fixed parameters are alwaysdouble.T: matrix type,0for a full numeric matrix.
The layout is that of itk::MatlabTransformIOTemplate::Read and Write
(Modules/IO/TransformMatlab/src/itkMatlabTransformIO.cxx), which go
through VNL's vnl_matlab_write and vnl_matlab_readhdr
(vnl/vnl_matlab_write.cxx, vnl/vnl_matlab_read.cxx).
Implicit Geometrical Specifications
As in every ITK format, the parameters map points of the fixed space to
points of the moving space, in LPS world coordinates, and a matrix is
stored row-major. For an AffineTransform with matrix A, translation
t (the last D parameters) and center c (the fixed parameters), a
point x of the fixed space maps to the point
of the moving space: the transform pulls the moving image onto the
fixed grid. That is the chain an
[ItkAffineBase][brainhops.io.transformations.itk.ItkAffineBase] block
holds -- recenter, linear, uncenter, translation -- as an
immutable sequence: to change a block, build a new one rather than edit
its chain in place.
Writing
MatTransform
writes a file as itk::MatlabTransformIO does: the parameters, then
the fixed parameters, as two column vectors, little-endian and double
by default (save(..., byteorder=">", precision="float") changes
either). scipy.io.loadmat reads what it writes.
- A block read from an ITK file is written back unchanged -- its class, its parameters and its center -- so a file read and saved again is the same file, byte for byte.
- Any other affine (or a transformation that converts to one, such as a
Translation) is written as anAffineTransformwhose center is the origin:fixedis zero, and the translation is the last column of the matrix. A brainhops affine has no center, and withc = 0ITK reads back exactlyy = A x + t. Its endpoints must be ITK's space (LPS millimetres) or unspecified; an affine between RAS spaces is refused rather than silently reinterpreted.
from brainhops.datamodel.transformations import Affine
from brainhops.io.transformations.itk.mat import MatTransform
MatTransform([Affine(matrix)]).save("out0GenericAffine.mat")
Only one block is written, as ANTs writes one transform per .mat file.
A chain is refused: compose it first (.compute()).
ANTs conventions
ANTs reads and writes its transforms through ITK, so the conventions above are those of ANTs: LPS millimetres, and fixed to moving.
- Warps.
<prefix><n>Warp.nii.gzand<prefix><n>InverseWarp.nii.gzare ITK NIfTI displacement fields, read bybrainhops.io.transformations.itk.nifti. -
Transform lists.
antsApplyTransforms -t T1 -t T2 ... -t Tndescribes the image transform as a stack, "the last one listed is applied first" -- to the moving image. To the points of the fixed space, which is how the transforms are evaluated, they apply in the order listed:T1first. A brainhopsSequencelists its transformations in the order they are applied to points, so it is the ANTs list in the same order:-t out1Warp.nii.gz -t out0GenericAffine.matisSequence([warp, affine]);-t [out0GenericAffine.mat,1] -t out1InverseWarp.nii.gzisSequence([~affine, inverse_warp]),
with
warp = io.load("out1Warp.nii.gz", hint="ants"),inverse_warp = io.load("out1InverseWarp.nii.gz", hint="ants")andaffine = io.load("out0GenericAffine.mat")(a warp needs the hint: its header alone does not say it holds LPS vectors). The first maps the fixed space to the moving space (it resamples the moving image onto the fixed grid), the second maps the moving space back to the fixed one. - Inversion.[file.mat,1](useInverse) inverts a linear transform: it isio.load("file.mat").inverse()(or~). ANTs does not invert a warp this way; it writes the inverse warp to its own file. - Composite files. Inside one ITK file holding aCompositeTransform(<prefix>Composite.h5), ITK lists the blocks the other way round from-t: the file holds the header, then the transform queue front to back, andCompositeTransform::TransformPointapplies the queue back to front -- a file[Composite, T0, T1]mapsxtoT0(T1(x)). Every ITK reader here (.h5,.tfmand.mat) therefore lists the blocks of a composite in reverse file order, which is the order they apply in: that file reads asSequence([T1, T0]), andantsApplyTransforms -t <prefix>Composite.h5is the same as-t T1 -t T0. A file that holds several blocks but noCompositeTransformheader is a list of separate transforms, which ITK does not compose. The reader loads one of them: the first, as SimpleITK'sReadTransformdoes, with a warning that the file holds several, or the one atposition=(MatTransform.from_file(path, position=1)). A composite file holds a single transform, the composite, at position 0. The.matwriter writes a single block, as ANTs does, and refuses chains.
Classes
MatTransformParser
Bases: Magic, BinaryFileParserWriter
Parses an ITK binary MATLAB (.mat) transform file into a chain
of transform blocks, and writes one back.
Each block is itself a brainhops transformation, so the parsed blocks
are stored straight into the transformations of the sequence that
this parser is mixed into.
Attributes
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:
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_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_file
classmethod
Build an object from a file (path or file-like object).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
FileLike
|
The file to parse. |
required |
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
obj
|
The parsed object. |
from_filename
classmethod
from_filename(filename: FilenameLike, **kwargs) -> Self
Build an object from a filename.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
FilenameLike
|
The filename to parse. |
required |
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
obj
|
The parsed object. |
from_fileobj
classmethod
Build an object from a file-like object.
The default implementation reads the whole stream and hands its
content to from_content (hence to from_bytes for binary
streams). Parsers that only need part of the stream (e.g., a
header) should override this method; from_bytes then falls back
to it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
IO
|
A file object open for reading. |
required |
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
obj
|
The parsed object. |
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_file
to_file(file: FileLike, **kwargs) -> None
Write the object to a file (path or file-like object).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
FileLike
|
The file to write to. |
required |
**kwargs
|
Parser-specific options. |
{}
|
to_fileobj
to_fileobj(file: IO, **kwargs) -> None
Write the object to a file-like object open for writing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
IO
|
A file object open for writing. |
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. |
sniff_fileobj
classmethod
Score how confident the parser is that an open binary file object is an ITK MATLAB transform file.
Only the first variable header is needed, so only enough bytes to hold it are read.
sniff_lines
classmethod
Text is never an ITK MATLAB transform file.
sniff_bytes
classmethod
Score how confident the parser is that bytes are an ITK MATLAB transform file.
The file must start with a MATLAB v4 variable header, and that variable must be named after an ITK transform class, which is how ITK names the parameters of every block it writes.
from_bytes
classmethod
Build the transform chain from the bytes of an ITK MATLAB transform file.
ITK writes each block as two column vectors: its parameters,
named after its transform class, then its fixed parameters, named
fixed. Like ITK's own reader, this one reads the variables in
pairs, takes the second of each pair as the fixed parameters
whatever its name, and refuses anything but column vectors.
position selects which top-level transform of the file to
read: the composite, if the file starts with a
CompositeTransform header, else one of its blocks. By default,
the first one, with a warning if the file holds several.
to_filename
to_filename(filename: FilenameLike, **kwargs) -> None
Write the transformation to a file.
The content is built before the file is opened, so a transformation that the format cannot hold is refused without creating or truncating the file.
to_bytes
The content of the ITK MATLAB file that encodes this
transformation, as itk::MatlabTransformIO writes it.
The transformation must be a single block, which is what ANTs
writes to a .mat file. It is written as two column vectors: its
parameters, named {Class}_{Precision}_{D}_{D}, then its fixed
parameters, named fixed.
- An ITK block (one that was read from an ITK file, or built as
an [
ItkStruct][brainhops.io.transformations.itk._common.ItkStruct]) is written as it is: its class, its parameters and its fixed parameters -- and so its center. - Any other transformation that converts to an
Affineis written as anAffineTransformwhose center (fixed) is the origin. The translation is then the last column of the matrix, and ITK reads back exactly that matrix. Itsinputandoutputmust be ITK's space --LPSmmin 3-D -- or left unspecified, in which case they are taken to be ITK's space.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
byteorder
|
('<', '>', '=')
|
The byte order of the headers and the values. ITK writes in
the native order of the machine, which is little-endian on
every common one; |
"<"
|
precision
|
(double, float)
|
The precision of the parameters, which also goes into the
class name. By default, that of the block, and |
"double"
|
Raises:
| Type | Description |
|---|---|
UnrepresentableTransformationError
|
If the transformation is not a single block, or not an ITK block or an affine between ITK's spaces. |
MatTransform
MatTransform(
_transformations: Sequence[Transformation]
| None = None,
)
Bases: MatTransformParser, ItkTransform, WritableFileBasedTransformation
A transformation stored in an ITK binary MATLAB (.mat) file.
This is the file that ANTs writes for every linear transform:
<prefix>0GenericAffine.mat, but also Rigid.mat, Affine.mat,
Similarity.mat, Translation.mat and
DerivedInitialMovingTranslation.mat.
FSL FLIRT also writes .mat files, but as text. The two are told
apart by content: this reader claims only files that start with a
MATLAB v4 header naming an ITK transform.
What is written
save writes a single block, as ANTs does (see
to_bytes).
A block read from an ITK file is written back as it was read,
center included. An affine is written as an AffineTransform
centered on the origin (fixed is zero), since a brainhops
affine has no center:
MatTransform([affine]).save("out0GenericAffine.mat"). ITK and
ANTs read back the same matrix, since y = A (x - c) + c + t
is y = A x + t when c = 0.
Attributes
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:
sniff
classmethod
sniff(
file: FileOrContentLike,
error: bool | Type[Exception] = False,
**kwargs,
) -> type | None
On a dispatcher, identify which registered format would read
file. On a concrete format, score how confident it is that
file, in any supported form, is its own.
sniff_file
classmethod
On a dispatcher, identify which registered format would read the file (path or file-like object). On a concrete format, score how confident it is that the file is its own.
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_fileobj
classmethod
Score how confident the parser is that an open binary file object is an ITK MATLAB transform file.
Only the first variable header is needed, so only enough bytes to hold it are read.
sniff_content
classmethod
sniff_content(
content: ContentLike,
error: bool | Type[Exception] = False,
**kwargs,
) -> type | None
On a dispatcher, identify which registered format would read the content (text or bytes). On a concrete format, score how confident it is that the content is its own.
sniff_bytes
classmethod
Score how confident the parser is that bytes are an ITK MATLAB transform file.
The file must start with a MATLAB v4 variable header, and that variable must be named after an ITK transform class, which is how ITK names the parameters of every block it writes.
sniff_text
classmethod
On a dispatcher, identify which registered format would read the text. On a concrete format, score how confident it is that the text is its own.
sniff_lines
classmethod
Text is never an ITK MATLAB transform file.
sniff_line
classmethod
On a dispatcher, identify which registered format would read the line. On a concrete format, score how confident it is that the line is its own.
load
classmethod
load(other: FileOrContentLike, **kwargs) -> Self
On a dispatcher, pick the best-matching registered format and
build an instance of it from other. On a concrete format,
build an instance of this class from other, in any supported
form.
from_spec
classmethod
from_spec(spec: SourceSpec, **kwargs) -> Self
Load a structured source specification through this dispatcher.
from_file
classmethod
On a dispatcher, pick the best-matching registered format and build an instance of it from the file (path or file-like object). On a concrete format, build an instance of this class from the file.
from_filename
classmethod
from_filename(filename: FilenameLike, **kwargs) -> Self
Build an object from a filename.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
FilenameLike
|
The filename to parse. |
required |
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
obj
|
The parsed object. |
from_fileobj
classmethod
On a dispatcher, pick the best-matching registered format and build an instance of it from the open file object. On a concrete format, build an instance of this class from the file object.
from_content
classmethod
from_content(content: ContentLike, **kwargs) -> Self
On a dispatcher, pick the best-matching registered format and build an instance of it from the content (text or bytes). On a concrete format, build an instance of this class from the content.
from_bytes
classmethod
Build the transform chain from the bytes of an ITK MATLAB transform file.
ITK writes each block as two column vectors: its parameters,
named after its transform class, then its fixed parameters, named
fixed. Like ITK's own reader, this one reads the variables in
pairs, takes the second of each pair as the fixed parameters
whatever its name, and refuses anything but column vectors.
position selects which top-level transform of the file to
read: the composite, if the file starts with a
CompositeTransform header, else one of its blocks. By default,
the first one, with a warning if the file holds several.
from_text
classmethod
On a dispatcher, pick the best-matching registered format and build an instance of it from the text. On a concrete format, build an instance of this class from the text.
from_lines
classmethod
On a dispatcher, pick the best-matching registered format and build an instance of it from the lines. On a concrete format, build an instance of this class from the lines.
from_line
classmethod
On a dispatcher, pick the best-matching registered format and build an instance of it from the line. On a concrete format, build an instance of this class from the line.
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_file
to_file(file: FileLike, **kwargs) -> None
Write the object to a file (path or file-like object).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
FileLike
|
The file to write to. |
required |
**kwargs
|
Parser-specific options. |
{}
|
to_filename
to_filename(filename: FilenameLike, **kwargs) -> None
Write the transformation to a file.
The content is built before the file is opened, so a transformation that the format cannot hold is refused without creating or truncating the file.
to_fileobj
to_fileobj(file: IO, **kwargs) -> None
Write the object to a file-like object open for writing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
IO
|
A file object open for writing. |
required |
**kwargs
|
Parser-specific options. |
{}
|
to_bytes
The content of the ITK MATLAB file that encodes this
transformation, as itk::MatlabTransformIO writes it.
The transformation must be a single block, which is what ANTs
writes to a .mat file. It is written as two column vectors: its
parameters, named {Class}_{Precision}_{D}_{D}, then its fixed
parameters, named fixed.
- An ITK block (one that was read from an ITK file, or built as
an [
ItkStruct][brainhops.io.transformations.itk._common.ItkStruct]) is written as it is: its class, its parameters and its fixed parameters -- and so its center. - Any other transformation that converts to an
Affineis written as anAffineTransformwhose center (fixed) is the origin. The translation is then the last column of the matrix, and ITK reads back exactly that matrix. Itsinputandoutputmust be ITK's space --LPSmmin 3-D -- or left unspecified, in which case they are taken to be ITK's space.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
byteorder
|
('<', '>', '=')
|
The byte order of the headers and the values. ITK writes in
the native order of the machine, which is little-endian on
every common one; |
"<"
|
precision
|
(double, float)
|
The precision of the parameters, which also goes into the
class name. By default, that of the block, and |
"double"
|
Raises:
| Type | Description |
|---|---|
UnrepresentableTransformationError
|
If the transformation is not a single block, or not an ITK block or an affine between ITK's spaces. |
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 from an instance of a similar class.
The data model copies the fields both classes share, by name.
A field that a file format declares for its own use -- such as
the nibabel image and header of the NIfTI and MGH formats
-- is only copied from an object of that same format: from any
other object, a field of the same name holds something else
(a NIfTI image is no MGH image), so this class's default is
kept instead. Saving a NIfTI image to MGH, or the converse,
therefore converts the data model only, and the format-specific
state is rebuilt by the writer.
from_other
classmethod
Create an instance from a file, or from anything the data model reads.
A path (str or os.PathLike), an open file, bytes or a
structured source (SourceSpec)
is read with load: on a dispatcher such as FileBasedImage,
the best-matching registered format reads it, and on a concrete
format, that format does. Any other value is handed to the data
model's own from_other, which reads a mapping field by field,
copies an instance of a similar class, and passes anything else
to the constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
Any
|
A file, its content, a mapping, or an instance of a similar class. |
required |
*args
|
Constructor arguments. A file is read with keyword options only. |
()
|
|
**kwargs
|
Format-specific options when reading a file, and field values otherwise. |
{}
|
Returns:
| Type | Description |
|---|---|
obj
|
The object that was built. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If positional arguments come with a file to read. |
compute
compute(
mode: ModeLike = True,
*,
simplify: SimplifyLike = "analytic",
factor: bool = False,
) -> Transformation
Compute the resulting transform of the sequence of transformations.
Assuming that mode=True:
-
If all transformations in the sequence are affine-like transformations,
compute()returns an affine-like transform. -
If the first (= rightmost) transform in the sequence is a coordinate field,
compute()returns a coordinate field. -
If the first (= rightmost) transform in the sequence is an affine-like transform, and the sequence contains at least one non-affine-like transform,
compute()returns a sequence of two transformations: -
the composition of all affine-like transformations that appear before the first non-affine-like transform in the sequence, and
- the composition of all transformations in the sequence, starting from the first non-affine-like transform in the sequence.
Parameters
mode : [list of] name or type, optional
Kinds of transformations to compose.
* If True (default): compose every kind in the sequence.
* If False: compose nothing (simplify-only).
* If a (list of) transformation type(s): compose only pairs
of transformations of these kinds.
simplify : simplify policy, default="analytic"
Whether to simplify sub-transformations prior to composition,
and how hard to try to simplify them.
* "analytic" (the default) looks at the type structure only;
* "numeric" looks at the numeric values of the transformation;
* False/"none"/None disables simplification.
factor : bool, default=False
Whether to rewrite the sequence into its axis-group normal
form [grid?, F_1..F_m, Pi_perm?]: a leading grid (if any),
one axis-preserving subspace factor per group of axes that
transform together, and a trailing reindex permutation. Off
by default, so the result is byte-for-byte the plain
compute() result. Nothing is ever composed across groups;
mode still decides whether the restricted pieces inside a
group compose. A chain that creates or drops axes is left
unfactored. With mode=False nothing is computed, so
factor has nothing to act on and is ignored.
simplify
simplify(
policy: SimplifyLike = "analytic",
*,
compute: ModeLike | bool | None = False,
) -> Self
Simplify this transformation under a per-kind policy.
Convenience sugar for
compute: t.simplify(policy, compute=mode) is
t.compute(mode, simplify=policy).
By default simplify() does no computation at all: compute=False
maps to mode=False, which composes nothing (no matrices multiplied,
no fields sampled, no lazy inverse materialized). It only downcasts
each leaf under policy (analytic by default). Pass an explicit
compute=<mode> to also compose that kind.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
policy
|
simplify policy
|
The simplify policy, in the grammar |
"analytic"
|
compute
|
[list of] name or type
|
The compose mode. The default, |
False
|
square
square(compute: bool = False, **kwargs) -> Transformation
Return the square of this transformation, self @ self.
The square is the sequence [self, self], which composes when it
is computed. It is defined for a transformation that maps a space
to itself.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
compute
|
bool
|
Whether to compute the result now rather than return it lazily. |
False
|
**kwargs
|
Passed to |
{}
|
Raises:
| Type | Description |
|---|---|
DomainError
|
If the transformation does not map a space to itself. |
sqrt
sqrt(compute: bool = False, **kwargs) -> Transformation
Return the principal square root of this chain.
The chain is first simplified, which costs nothing. A chain
[P, *X, P^-1], where P^-1 is the lazy inverse of P, or both
are affines whose product is exactly the identity, is a change of
coordinates around X, and its square root is
[P, sqrt(X), P^-1]: a field stored in voxels between a
world-to-voxel affine and its lazy inverse keeps that form. Any
other chain is composed now, and the square root of the
transformation it composes to is returned.
Raises:
| Type | Description |
|---|---|
DomainError
|
If the chain does not map a space to itself, or if the square root of what it reduces to is not defined. |
NotImplementedError
|
If the chain does not compose to a single transformation. |
to
to(
cls: Type[Transformation] | None = None, **kwargs
) -> Transformation
Convert this chain to a different type or encoding.
See Transformation.to. A chain has no tangent of its own -- the tangent of a
composition is not the sum of the tangents -- so log= re-encodes
the transformation it reduces to, and anything else is refused
before it is computed:
- a chain that simplifies to one transformation is that one;
- a change of coordinates
[P, *X, P^-1](seesqrt) keeps its ends, and re-encodesX: the flow of a velocity commutes with the conjugation, so this is exact. A velocity read between a world-to-voxel affine and its inverse (|svf) is turned into its displacement that way; - a chain of affines is composed, which is cheap and exact.
Any other chain -- one with a field, between ends that do not undo
each other -- raises ConversionError.