Skip to content

brainhops.io.transformations.fsl.fnirt

FNIRT non-linear transformations stored in NIfTI files.

FNIRT writes its non-linear registration as either a deformation (warp) field or a coefficient field, distinguished by the NIfTI intent code. One degree-parameterized reader handles both. Both express displacements in FSL scaled-mm coordinates.

Classes

FnirtWarpField magic

FnirtWarpField(
    moving: _ImageLike | None = None,
    reference: _ImageLike | None = None,
    deformation_type: str | None = None,
)

Bases: FslTransformationFormat, NiftiBasedTransformation, ImmutableSequence

A FNIRT non-linear transformation stored in a NIfTI file.

FNIRT writes its non-linear registration as one of two things, which a NIfTI intent code tells apart. A deformation field stores, per reference voxel, the moving location that the voxel maps to, in FSL scaled-mm coordinates. A coefficient field stores the coefficients of a B-spline basis on a coarse knot grid overlaid on the reference image. A single reader handles both, because both are B-spline fields on a regular grid and differ only in the spline degree, in whether the grid holds coefficients or sampled values, and in where the grid sits.

Intent Kind Degree Grid
2006 deformation 1 reference voxels
2007 cubic coefficients 3 knot grid
2009 quadratic coefficients 2 knot grid

The reader keeps the field on its own grid and returns an ImmutableSequence of three transformations -- reference RAS to warp-grid voxels, the displacement field, and warp-grid voxels to moving RAS -- that maps reference-image world (RAS) coordinates to moving-image world (RAS) coordinates. The B-spline basis is evaluated only when the sequence is computed, so a coefficient field is never expanded onto the reference grid at read time.

A deformation field carries the reference geometry itself, so only the moving image is required. A coefficient field carries neither image's geometry, so both the reference and the moving image are required. A discrete-cosine-transform coefficient field (intent 2008) is recognized but not supported.

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.

header property writable
header: Nifti1Header | None

The NIfTI header associated with this object.

If a header was explicitly set by the user (at construction or later), this will be pointing to that header.

Otherwise, if the object was created from a NIfTI header, this will be pointing to that header.

Otherwise, if the object was created from a NIfTI image, this will be pointing to the header of that image.

Example

    import nibabel as nb
    image1 = nb.load("image1.nii")
    image2 = nb.load("image2.nii")
    NiftiParser(image1).header                        # `image1.header`
    NiftiParser(header=image2.header).header          # `image2.header`
    NiftiParser(image1, header=image2.header).header  # `image2.header`
    obj = NiftiParser(image1)
    obj.header = image2.header
    obj.header                                        # `image2.header`
data property writable
data: ArrayProtocol | None

The image data, read lazily from image and cached, unless it has been set explicitly.

The axes that the intent code marks as irrelevant, such as a singleton axis before a vector's components, are dropped.

system property writable
system: CoordinateSystem | None

The voxel coordinate system, derived from header, unless it has been set explicitly.

The axes that the intent code marks as irrelevant are dropped. None when there is no header to derive it from.

moving class-attribute instance-attribute
moving: Annotated[
    _ImageLike | None, Alias((moving, mov, src))
] = None

The moving (source) image, a nibabel image or header, or a brainhops image.

reference class-attribute instance-attribute
reference: Annotated[
    _ImageLike | None, Alias((reference, ref))
] = None

The reference image. For a deformation field this defaults to the warp file's own geometry.

deformation_type class-attribute instance-attribute
deformation_type: str | None = None

For a deformation field, either "absolute", "relative", or None to infer it from the data. It has no effect on a coefficient field.

degree property
degree: int | None

The B-spline degree: 1 dense, 3 cubic, 2 quadratic.

coeff property
coeff: bool | None

Whether the field holds spline coefficients rather than values.

transformations property writable
transformations: tuple[Transformation, ...]

The transformations mapping reference RAS to moving RAS.

Reading this property resolves the chain from the warp data and the image geometries. It raises when a required image is missing. The resolved chain is cached, and the cache is rebuilt when the moving image, the reference image, or the deformation type changes.

It is a tuple, like every chain of an ImmutableSequence: the resolved chain is cached and handed out as is, and a list would let an in-place edit change the cache, leaving the warp reporting a chain that its data no longer describes.

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
sniff_file(
    file: FileLike,
    error: bool | Type[Exception] = False,
    **kwargs,
) -> type | None

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 [0, 1].

sniff_fileobj classmethod
sniff_fileobj(
    file: IO,
    error: bool | Type[Exception] = False,
    *,
    version: int | None = None,
    **kwargs,
) -> float

Score how confident the class is that an open file object holds a NIfTI-1 or NIfTI-2 header, or a header of the given version when one is passed.

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
sniff_bytes(
    data: bytes,
    error: bool | Type[Exception] = False,
    **kwargs,
) -> float

Score how confident the class is that bytes hold a NIfTI-1 or NIfTI-2 header.

sniff_text classmethod
sniff_text(
    text: str,
    error: bool | Type[Exception] = False,
    **kwargs,
) -> type | None

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
sniff_lines(
    lines: Iterable[str],
    error: bool | Type[Exception] = False,
    **kwargs,
) -> type | None

On a dispatcher, identify which registered format would read the lines. On a concrete format, score how confident it is that the lines are its own.

sniff_line classmethod
sniff_line(
    line: str,
    error: bool | Type[Exception] = False,
    **kwargs,
) -> type | None

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_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_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_text classmethod
from_text(text: str, **kwargs) -> Self

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
from_lines(lines: Iterable[str], **kwargs) -> Self

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
from_line(line: str, **kwargs) -> Self

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 NIfTI file.

A path is written gzipped when its name ends in .gz: a local path is handed to nibabel by name, and a remote one is opened through its own backend. See _save_nifti. A file-like object is written the uncompressed NIfTI bytes.

to_filename
to_filename(filename: FilenameLike, **kwargs) -> None

Write the object to a filename.

Parameters:

Name Type Description Default
filename FilenameLike

The filename to write to.

required
**kwargs

Parser-specific options.

{}
to_fileobj
to_fileobj(file: IO, **kwargs) -> None

Write the uncompressed NIfTI-1 encoding of the object to an open file object.

to_bytes
to_bytes(**kwargs) -> bytes

Return the uncompressed NIfTI-1 encoding of the object.

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
to_lines(**kwargs) -> Iterator[str]

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
from_dict(other: Mapping, *args, **kwargs) -> Self

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
from_instance(other: Any, *args, **kwargs) -> Self

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
from_other(other: Any, *args, **kwargs) -> Self

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.

from_nibabel classmethod
from_nibabel(nifti: _NiftiObject, **kwargs) -> Self

Build the object from an already-loaded nibabel header or image.

to_nibabel
to_nibabel(**kwargs) -> Nifti1Image

Build the nibabel image that encodes this object.

Each concrete NIfTI format overrides this method to describe how its own contents map onto a NIfTI image. The other writer methods are defined in terms of this one.

sniff_nibabel classmethod
sniff_nibabel(
    nifti: _NiftiObject,
    error: bool | Type[Exception] = False,
    **kwargs,
) -> float

Score how confident the class is that an already-loaded nibabel header or image matches this format.

The header's magic number is checked first. A header that passes is then scored for how well it matches this particular format, as opposed to another kind of NIfTI-based format.

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 compute accepts.

"analytic"
compute [list of] name or type

The compose mode. The default, False, composes nothing (mode=False in compute); None would compose every kind. A real mode passes straight through.

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 compute when compute is true.

{}

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] (see sqrt) keeps its ends, and re-encodes X: 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.