Skip to content

brainhops.io.base.nrrd

The shared NRRD-reading and NRRD-writing machinery behind every NRRD-based format.

A NRRD ("Nearly Raw Raster Data") file is a text header followed by the sample values. The header and the values are either in the same file (attached, .nrrd) or the header names the file(s) that hold them (detached, .nhdr with its .raw, .raw.gz, ...).

NRRD0004
# Complete NRRD file format specification at:
# http://teem.sourceforge.net/nrrd/format.html
type: short
dimension: 4
space: left-posterior-superior
sizes: 3 64 64 32
space directions: none (2,0,0) (0,2,0) (0,0,2.5)
kinds: vector domain domain domain
endian: little
encoding: gzip
space origin: (-63,-63,-40)
measurement frame: (1,0,0) (0,1,0) (0,0,1)
DWMRI_b-value:=1000

<data>

The conventions below follow the format specification (https://teem.sourceforge.net/nrrd/format.html).

  • Magic. The first line is NRRD000X, with X the version (1-5).
  • Lines. field: value lines (the field names are matched without regard to case, and the spellings without a space -- datafile, byteskip, axismins, centerings, ... -- are accepted), key:=value pairs (\n and \\ are escapes in both), and # comments. The header ends at the first empty line, after which an attached header's data start; a detached header may also end at the end of its file.
  • Types. Every spelling of the specification (short, int16, int16_t, signed short int, ...) of the signed and unsigned 8 to 64 bit integers, float and double. block is not supported.
  • Encodings. raw, ascii (txt, text), hex, gzip (gz) and bzip2 (bz2). endian is required for multi-byte types in a binary encoding.
  • Data files. data file: <name>, relative to the header's directory unless absolute; data file: <format> <min> <max> <step> [<subdim>], a printf pattern expanded over the inclusive range; and data file: LIST [<subdim>], followed by one file name per line until the end of the header. Several files are read in order and concatenated, each holding as many samples.
  • Skips. line skip lines are skipped first, in the file as stored. byte skip bytes are then skipped -- after decompression for gzip and bzip2 (as pynrrd and teem do) -- and byte skip: -1 means that the data are the last bytes of the (decompressed) file.

Reading an attached raw file, or a detached one with a single raw data file, from a local path memory-maps the values, so nothing but the header is read until they are indexed.

Why not pynrrd

NRRD headers are simple text, and pynrrd covers fewer of them than this parser does: it reads neither the LIST nor the pattern forms of data file, nor the hex encoding, and always reads the values into memory. A dedicated parser keeps the dependencies to numpy and the standard library (gzip, bz2).

Attributes

NRRD_MAGIC module-attribute

NRRD_MAGIC = b'NRRD000'

The first seven bytes of every NRRD file; a version digit follows.

SPACES module-attribute

SPACES = {
    "right-anterior-superior": ("RAS", 3),
    "left-anterior-superior": ("LAS", 3),
    "left-posterior-superior": ("LPS", 3),
    "right-anterior-superior-time": ("RAST", 4),
    "left-anterior-superior-time": ("LAST", 4),
    "left-posterior-superior-time": ("LPST", 4),
    "scanner-xyz": ("scanner-xyz", 3),
    "scanner-xyz-time": ("scanner-xyz-time", 4),
    "3d-right-handed": ("3D-right-handed", 3),
    "3d-left-handed": ("3D-left-handed", 3),
    "3d-right-handed-time": ("3D-right-handed-time", 4),
    "3d-left-handed-time": ("3D-left-handed-time", 4),
}

Each space of the specification: its abbreviation and dimension.

Classes

NrrdHeader magic

NrrdHeader(
    version: int = 5,
    fields: dict[str, str] = Factory(dict),
    keyvalue: dict[str, str] = Factory(dict),
    data_files: tuple[str, ...] = (),
)

Bases: Magic

A NRRD header: its fields and key/value pairs, as written.

The fields are kept as text, under their canonical names ("data file", "space directions", ...), so that every one of them is written back as it was read. The methods decode the ones that the readers need.

Attributes

version class-attribute instance-attribute
version: int = 5

The version of the magic line (NRRD000X).

fields class-attribute instance-attribute
fields: dict[str, str] = Factory(dict)

The fields, by canonical name, as text, in the order they were read. The file names of a data file: LIST are in data_files.

keyvalue class-attribute instance-attribute
keyvalue: dict[str, str] = Factory(dict)

The key:=value pairs, unescaped.

data_files class-attribute instance-attribute
data_files: tuple[str, ...] = ()

The data files of a detached header, in order (the LIST and pattern forms expanded); empty for an attached header.

dimension property
dimension: int

The number of axes.

sizes property
sizes: tuple[int, ...]

The number of samples along each axis, fastest first.

encoding property
encoding: str

The canonical encoding: raw, ascii, hex, gzip or bzip2.

endian property
endian: str | None

"little", "big", or None when the header says nothing.

dtype property
dtype: dtype

The numpy type of the stored values, in their byte order.

count property
count: int

The number of samples.

nbytes property
nbytes: int

The number of bytes of the (decoded) values.

line_skip property
line_skip: int

The number of lines to skip before the data.

byte_skip property
byte_skip: int

The number of bytes to skip before the data, or -1.

space property
space: str | None

The canonical space ("left-posterior-superior", ...), or None when the header has none.

space_dimension property
space_dimension: int | None

The dimension of the world space, from space or space dimension; None when the header has neither.

space_directions property
space_directions: list[ndarray | None] | None

One vector per axis (None for a non-spatial axis), or None when the header has no space directions.

space_origin property
space_origin: ndarray | None

The position of the centre of the first sample, or None.

measurement_frame property
measurement_frame: ndarray | None

The measurement frame, one vector per column, or None.

kinds property
kinds: list[str | None]

The kind of each axis, in lower case (None when unknown).

centers property
centers: list[str | None]

The centering of each axis: "cell", "node" or None.

spacings property
spacings: list[float]

The spacing of each axis (nan when unknown).

axis_mins property
axis_mins: list[float]

The axis mins (nan when unknown).

axis_maxs property
axis_maxs: list[float]

The axis maxs (nan when unknown).

units property
units: list[str | None]

The unit of each axis (None when not given).

space_units property
space_units: list[str | None]

The unit of each world axis (None when not given).

Methods:

from_lines classmethod
from_lines(
    lines: Iterable[str], version: int = 5
) -> NrrdHeader

Parse the lines of a header, after its magic line and up to the empty line that ends it.

from_fileobj classmethod
from_fileobj(file: BinaryIO) -> tuple[NrrdHeader, int]

Read a header from a binary stream, from its magic line.

Returns the header and the offset, relative to the start of the stream, of the first byte after the header (where attached data start).

to_text
to_text() -> str

The text of the header, from its magic line, without the empty line that separates it from attached data.

NrrdParser magic

NrrdParser(
    _header: NrrdHeader | None = None,
    dataobj: Any | None = None,
)

Bases: DataModelBase, BinaryFileParserWriter

Base class for objects that are encoded by a NRRD file.

It reads and writes the container -- the header and the sample values -- for every NRRD-based format. What the values mean is for the concrete format to say, through _nrrd_header and _nrrd_data when writing.

Reading a raw file from a local path memory-maps the values, so nothing but the header is read until they are indexed.

Attributes

header property writable
header: NrrdHeader | None

The NRRD 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
from_file(file: FileLike, **kwargs) -> Self

Build the object from a NRRD 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 .nrrd or .nhdr file.

The values of a raw local data file are memory-mapped unless mmap is false. Detached data files are found relative to the header's directory.

from_fileobj classmethod
from_fileobj(file: BinaryIO, **kwargs) -> Self

Build the object from an open NRRD file object.

Detached data files are resolved against the stream's name, when it has one.

from_bytes classmethod
from_bytes(content: bytes, **kwargs) -> Self

Build the object from the bytes of an attached NRRD file.

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

Score how confident the class is that a stream holds a NRRD file of its own kind.

sniff_bytes classmethod
sniff_bytes(
    content: bytes,
    error: bool | Type[Exception] = False,
    **kwargs,
) -> float

Score how confident the class is that bytes hold a NRRD file of its own kind.

to_bytes
to_bytes(**kwargs) -> bytes

The bytes of an attached NRRD file.

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

Write an attached NRRD file to a stream.

to_filename
to_filename(
    filename: FilenameLike,
    data_file: str | None = None,
    **kwargs,
) -> None

Write to a path: an attached file, or, for a .nhdr name (or when data_file is given), a detached header and its data file.

The data file of a detached header is named after it, with an extension that says its encoding (.raw, .raw.gz, .raw.bz2, .txt, .hex), unless data_file names it (relative to the header's directory).

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

sniff_file classmethod
sniff_file(
    file: FileLike,
    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 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 [0, 1].

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

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

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

sniff_lines classmethod
sniff_lines(
    lines: Iterable[str],
    error: bool | Type[Exception] = False,
    **kwargs,
) -> float

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

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

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

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 other is a path to a file that does not exist. It is a FileNotFoundError.

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

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

Build an object from an iterable of lines (e.g., the content of a file).

Parameters:

Name Type Description Default
lines Iterable[str]

The lines to sniff.

required
**kwargs

Parser-specific options.

{}

Returns:

Type Description
obj

The parsed object.

from_line classmethod
from_line(line: str, **kwargs) -> Self

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
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: Self, *args, **kwargs) -> Self

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

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.

Functions:

nrrd_dtype

nrrd_dtype(name: str, endian: str | None = None) -> dtype

The numpy type of a NRRD type, in the byte order of endian ("little" or "big"; the native order when it is not given).

Raises:

Type Description
ParserContentError

If the type is unknown, or is block.

dtype_to_nrrd

dtype_to_nrrd(dtype: Any) -> str

The NRRD type of a numpy type (or of a NRRD type name).

Booleans are stored as uint8 and half floats as float.

Raises:

Type Description
WriterError

If NRRD has no type for it (complex numbers, strings, ...).

read_data

read_data(
    header: NrrdHeader,
    file: Any,
    offset: int,
    mmap: bool = True,
) -> ndarray

The values of a NRRD image, as an array of shape sizes in F order.

Parameters:

Name Type Description Default
header NrrdHeader

The header.

required
file path | bytes

The header file (path) or its content (bytes), used when the data are attached, and to find detached data files.

required
offset int

The offset of the first byte after the header.

required
mmap bool

Memory-map a single uncompressed local data file.

True

encode_data

encode_data(header: NrrdHeader, data: Any) -> bytes

Encode an array of shape sizes (indexed in NRRD axis order) into the bytes of the data file, in the header's type, byte order and encoding.