![]() |
![]() | ||
teem | / | nrrd |
Definition of NRRD File Format |
When saved, the filenames for NRRD files should end in ".nrrd". Detached headers, discussed in Section 3, should end in ".nhdr". Standard suffixes for data files associated with detached headers are listed in the encoding part of Section 4.
The nrrdRead() and nrrdWrite() functions of the nrrd library are intended to completely support the format as described here, but there is currently no test suite or validation program.
NRRD files come in two forms: with an attached header (in which the header and the data are in the same file), and with a detached header (header and data are in two separate files). The attached-header form is described first, and the minor variation that enables detached headers is described in Section 3. NRRD readers must be able to support attached and detached headers.
The general format of a NRRD file (with attached header) is:
NRRD0001 <field>: <desc> <field>: <desc> # <comment> ... <field>: <desc> <field>: <desc> # <comment> <data><data><data><data><data><data>...
The very first line contains nothing but the NRRD "magic". The magic is what identifies the file as a NRRD file. For NRRD files, the first four characters are always "NRRD". The idea is that the next four numbers give the version number of the file format, but this may or may not be developed in the future. The only current magic for NRRD files is "NRRD0001". For the time being, this is the magic to use when writing all NRRD files. NRRD readers should obviously recognize "NRRD0001", as well as a magic used in early implementations, "NRRD00.01". Readers encountering a magic starting with "NRRD" but ending with something besides "0001" or "00.01" need not make any effort to continue parsing the file.
Each of the "<field>: <desc>" lines specifies information about one of the fields in the nrrd. Each of these lines is called a "field specification", or more loosely, a "field". Each field specification is contained in one line. Each field specification may appear no more than once in the nrrd header; repetitions of a field is sufficient cause for a reader to exit with an error message.
Comment lines start with a pound, "#", with no proceeding whitespace. The comment string itself starts with the first character which is a not a pound or a space (" "). Comment lines with a zero-length comment string should be ignored. Comment lines may be interspersed with field specifications in any order. This allows field specifications to be commented out and commented upon easily. Since comments are effectively a catch-all for peripheral information which doesn't otherwise fit in nrrd, gracious NRRD readers should store all the comments seen in the header, but this is not a requirement. Comments are intended to be case sensitive.
The magic, field specifications, and comments comprise the NRRD header. After the header, there is a single blank line containing zero characters. This separates the header from the data, which follows. Unlike the header, the data segment is not structured in ASCII lines. The encoding of the data (raw, ASCII, compressed, or other) is specified by the encoding field in the header. The header, the blank line, and the data comprise the NRRD file. A single NRRD file can store the information and data for a single array. There is currently no facility for storing multiple arrays in a single NRRD file.
All of the field specifications have the same structure: a string "<field>" identifying the field (called the field identifier), then a colon followed by a single space ": ", and then the information describing the field "<desc>" (called the field descriptor). All field identifiers are case insensitive. The only field descriptors which are not case insensitive are the one which contain strings (the content, labels, and units fields). Whitespace (which does not constitute the previous line's termination) is not allowed before a field identifier. Extra whitespace after the field descriptor and before the line termination should be ignored. The NRRD format is complicated by the fact that some fields are always necessary, while some fields are always optional, and some fields are necessary only some of the time. Whether or not a field is necessary depends on previous information in the header. This is why there is no standard template for all NRRD headers, or a context-free grammar for NRRD headers.
Some field specifications give a piece of information about each and every axis in a nrrd; these are called "per-axis specifications". Identifying how many samples there are along each dimension of the array is an example of this. Per-axis specifications must have as many components as there are dimensions in the array, so that they can identify information for every axis. There is no partial or implied per-axis information. Other field specifications are general with respect to array dimension, such as the type of the array; these are called "non-per-axis specifications".
An important, and always necessary, non-per-axis field specification is the one giving the dimension of the nrrd. The format is:
<int> can be any integer greater than 0. NRRD readers may not have the ability to represent absolutely any dimension, but they must be able to handle nrrds with dimension 16 or less, which is what the current nrrd implementation can do.dimension: <int>
Non-per-axis specifications can appear both before and after the dimension field. Per-axis specifications can only appear after the dimension field specification. This simplifies the task of parsing per-axis specifications, since we know how many pieces of information need to be parsed (the same as the dimension). This also avoids any attempts at cleverness in the form of guessing the nrrd dimension based on the first per-axis specification. Other than this one constraint, field specifications may appear in any order within the header (after the magic).
The number of samples along each axis is the only always-necessary per-axis specification. The format is:
<size[i]> is the number of samples along axis i, with axis ordering going from fastest to slowest. As with all the other per-axis field identifiers, sizes ends with "s" to emphasize the plurality of the field it specifies. The field identifiers do not change, however, for one-dimensional nrrds.sizes: <size[0]> <size[1]> ... <size[dim-1]>
The issue of axis ordering is fundamental. In memory and on disk, there is a strict linear order of all the values in an array. Logically, however, each sample has one or more coordinates which identify its position-- as many coordinates as there are dimensions in the array. The "fastest" axis is the one associated with the coordinate which increments fastest as the samples are traversed in memory order. Typical raster ordering of interleaved RGB data is logically a three-dimensional array. The fastest axis is the color axis (only three samples long), followed by the horizontal axis, with the vertical axis being the slowest. All the per-axis field specifications identify information for each axis, and the axis ordering is always (reading left to right) fastest to slowest. NRRD makes no attempt to "name" the axes, such as "X", "Y", and "Z": they are identified solely by their location in the ordering from fastest to slowest.
Besides dimension, there are two other always-necessary non-per-axis specifications: the type and the encoding specifications. Their format is:
The possible values for type include the C identifiers you would probably use to identify a type: "int" means a 32-bit signed integer, "float" means 32-bit floating point, and so on. Useful variants like "uchar" (same as "unsigned char") are allowed. There is also the block type, which is used to represent some chunk of opaque memory, of user-specified size; see the type specification in Section 4 for all the details.type: <type> encoding: <encoding>
The encoding tells how the data (following the blank line after the header) is written out; "ascii" and "raw" are common values, but "hex" allows extraction of images from some PostScript files, and compression is also supported; see the encoding specification (Section 4). NRRD readers must be able to support raw and ASCII encoding, everything else is optional. See the general description of NRRD format for more information about how optional encodings are handled.
The field specifications described so far provide the means of writing a minimal NRRD header:
This is identical in meaning to the PPM header:NRRD0001 # my first nrrd type: uchar dimension: 3 sizes: 3 640 480 encoding: raw
If "encoding: ascii" had appeared instead in the NRRD header, the PPM magic would be "P3" instead of "P6".P6 # my first nrrd 640 480 255
The field specifiers described so far, and illustrated above, are the only ones which are always necessary. However, other field specifications become necessary as a function of other fields: if the type was "float", and the encoding is something other than "ascii", then the endian of the data would have to be recorded. The details of which fields require which other fields are spelled out in Section 4 and Section 5.
One of the things that makes reading a NRRD header complicated is the idea that the absence of an optional piece of information is just as important to record as the information when it is explicitly given. Whatever data structure is created or updated as the result of reading a NRRD header must enable the writer to write the exact same NRRD header, up to field ordering. If you generate a nrrd at run-time, and you don't have specific values for an optional field, or, if values for an optional field were never specified by an input NRRD header, then specific values for this field must not be invented in an output NRRD header, and the field should not be saved at all. Keeping headers as concise as possible makes them easier to understand when read by humans, and eliminates the risk that misleading information is invented solely for the sake of conforming to a file format.
In order to implement this in NRRD, each of the optional fields must have a way of representing the idea of "don't know"-- a state distinct from knowing a specific default value, or knowing the value specified in the header. All of optional fields can be initialized to "don't know", and only after "known" values are specified in an input header does the field become worthy of being saved in an output NRRD header. For optional fields with string values (content, labels, and units), the empty string ("") is the obvious choice for "don't know". For centers, the string "???" (and the value used to represent it) means "don't know". In contrast, the optional fields with integer values (line skip and byte skip) actually have sensible a sensible "known" default value, namely zero.
But how does one represent "don't know" with optional floating point data? NRRD uses NaN, or Not-a-Number. NaN is a value that can be represented in the ubiquitous IEEE 754 floating point standard, as the result of doing undefined arithmetic operations, such as zero divided by zero. While it may seem overly cute or clever to use NaN as a flag for "don't know", this is in fact exactly in keeping with the purpose of NaN as described in the original 754 standard. Furthermore, as described in the documentation for the air library, it is possible to generate a NaN at compile time (so that it doesn't have to be produced as a result of doing an undefined arithmetic operation), and it is possible to quickly test if a given number is NaN. Even if operations involving NaN are not implemented in the floating point hardware, but in software emulation supplied by the operating system, they will never be the bottle neck in reading and writing a NRRD file. Because of NaN's important role as signifier of "don't know", the NRRD reader must be able to interpret the case-insensitive string "nan" as a NaN, even if this is not already the behavior of sscanf() on a given platform. Writing a small wrapper function around sscanf() is a very small price to pay for the representational convenience of NaN. Section 2 gives the details for how NRRD readers and writers should handle ASCII encoding of the the IEEE 754 special values.
In Section 4 and Section 5, when a field specification is described as harmless, it means that the field specification probably shouldn't be in the header, because its information is either irrelevant or meaningless in that context. However, for the sake of implementation simplicity, its presence shouldn't count as an error. The information in harmless field specifications must be ignored by NRRD readers, but it is okay to complain if the field specification is malformed and unparsable.
In Section 4 and Section 5, numeric field specification descriptions include a "Type", which identifies the minimum precision with which the information must be represented by the NRRD reader. In this context, "int" means a 32-bit signed integer, and "double" means a 64-bit floating point number. Field specifications with alternate equivalent forms are listed together (for example, "block size" is the same as blocksize"). Equivalent field descriptors are listed together in the table enumerating the meanings of the various descriptors (for example, "uchar is the same as "unsigned char"). Quotes are used to delimit the field descriptors in the explanation of their meaning; quotes are not part of the descriptor itself (except for the labels and units specifications, in which the descriptors (strings) are delimited by quotes).
No other fields are allowed in a "NRRD0001" or "NRRD00.01" magic file other than those defined in Section 4 and Section 5. NRRD readers are not expected to make any efforts to deal with non-conformant files (in contrast to PNM recommendations), but specific and intelligent human-readable error messages are of course encouraged.
The difference between a quiet and signaling NaN is a detail of IEEE 754 which was left implementation-specific, so different platforms have different ways of distinguishing between quiet and signaling NaN, and some don't distinguishing between them at all. The intent was that quiet NaNs represent an indeterminate value, as in 0/0, or inf/inf, meaning simply that arithmetic doesn't define a single value for the result. On the other hand, signaling NaNs represent an invalid value, to signal that a non-existent or uninitialized floating point value was accessed, or that the input parameters to a function were so botched that no valid output can be generated; the signaling NaN is supposed to signal "someone goofed". Based on the fact that different portions of 754 can implemented in software, or hardware, or a combination of the two, there may be performance considerations between the two kinds of NaNs. But in any case, its basically all moot, since unfortunately, there is no cross-platform standard API for the floating point exception handlers which can interact with signaling NaNs.
Given this, in the NRRD file format (and in the nrrd library), a NaN is a NaN is a NaN, with no difference between signaling and quiet, and no recognition of the integer value in the fraction field of the NaN. If the signaling/quiet distinction mattered, then when writing raw floating point data, not only would endianness have to be recorded, but also the convention for representing quiet NaN, and if the data came from a platform that knows the difference between the two NaNs. Readers would have to possibly traverse the whole array after input to detect and switch NaN representations. Doing this checking is not practical or efficient, and the consequences of not doing it are either moot or non-existent. Thankfully, there are unique and fully specified bit patterns for positive and negative infinity.
NRRD writers should verify that their printf() function behaves in accordance with these rules.
There is one new field specification which is required in detached headers. The format is:
"datafile:" is also valid. "filename" identifies the file that contains the data. The addition of this field is the only difference between attached headers and detached headers. The magic at the beginning of the header is the same, so there is currently no way to immediately detect if the header being parsed is attached or detached. The rest the field specifications are the same, and because the data file field specification is a non-per-axis specification, it may appear anywhere in the header. Detached headers may end with the the last field specification, or with a single blank line following the last field specification (in which case anything following the blank line is ignored).data file: <filename>
Breaking the dataset into two files raises new concerns, namely that the header file can't know if the data file has been erased, renamed, or moved. NRRD provides no means to overcome these problems once they've been created. On the other hand, moving the header and data files together to a new place is a common operation, and is supported by the special semantics associated with the data filename descriptor:
dimension: <int>
type: <type>
<type> | Meaning | C type |
"signed char", "int8", "int8_t" | signed 1-byte integer | signed char |
"uchar", "unsigned char", "uint8", "uint8_t" | unsigned 1-byte integer | unsigned char |
"short", "short int", "signed short", "signed short int", "int16", "int16_t" | signed 2-byte integer | short |
"ushort", "unsigned short", "unsigned short int", "uint16", "uint16_t" | unsigned 2-byte integer | unsigned short |
"int", "signed int", "int32", "int32_t" | signed 4-byte integer | int |
"uint", "unsigned int", "uint32", "uint32_t" | unsigned 4-byte integer | unsigned int |
"longlong", "long long", "long long int", "signed long long", "signed long long int", "int64", "int64_t" | signed 8-byte integer | long long int |
"ulonglong", "unsigned long long", "unsigned long long int", "uint64", "uint64_t" | unsigned 8-byte integer | unsigned long long int |
"float" | 4-byte floating point | float |
"double" | 8-byte floating point | double |
"block" | An opaque chunk of memory with user-defined size (via the "block size:" specifier) |
The type descriptors used are valid type declarations in C, C99, Matlab, Microsoft-land, or some other program. Notice that "char" is not a NRRD type descriptor, to avoid potential confusion associated with the inherent signed/unsigned ambiguity of the "char" C type. If the platform has different C type names for the types described, there will have to be a disconnect between the type implied by the type descriptor, and the actual types used. In other words, the NRRD format requires a binding between the first two columns in the chart above. The third column is just what the current nrrd implementation uses on most supported platforms; this has proven surprisingly portable. In Windows, however, there is no "long long", so "__int64" is used instead.
As currently defined, NRRD is simply not portable to platforms on which all the types described above (second column) are not available via some C type declaration or another. We will eventually have many computers in which the minimum addressable unit is larger than 8 bits, in which case NRRD will either have to be expanded to allow types with unaddressable values (in which case a bit type might as well be added), or, some rules will have to be defined for converting a smaller type into an addressable type during data read. Having addressable samples vastly simplifies the task of implementing array operations.
The block type is unlike the others. It is included for completeness in representation of the types available in the nrrd library, which uses this type to represent C structs or C++ objects: opaque chunks of memory that can be copied and permuted, but not interpreted as (or generated from) scalar values. The size of that chunk is given in the block size field specification. But block is not safe as a cross-platform general purpose type. Here are the special considerations:
block size: <int> blocksize: <int>
encoding: <encoding>
<encoding> | Meaning | Standard detached suffix |
"raw" | The data appears on disk exactly the same as in memory, in terms of byte values and byte ordering. Produced by write() and fwrite(), suitable for read() or fread(). | ".raw" |
"txt", "text", "ascii" | Integral values are written/read as with printf()/sscanf(), and floating point values are used in a way consistent with Section 2. The individual values are separated by one or more whitespace characters (from the C string " \t\n\r\v\f"). No line terminations are required anywhere. Their presence is no different than any other kind of whitespace. | ".txt" |
"hex" | The data is raw, but written with two (case-insensitive) hexadecimal characters per byte. White space characters (as defined above) are ignored on reading. Writers should put a line termination after every 70 characters, and after the last line of numbers. | ".hex" |
"gz", "gzip" | The data is raw, but compressed with the gzip program. Implementation and specification is available from http://www.gzip.org/, but the nrrd library actually uses the zlib library available from http://www.gzip.org/zlib/. However, the compressed data must start with the gzip binary header, the same as is produced/read by the gzip/gunzip command-line tools. Compressed data starting with only the zlib binary header (from the underlying library) is not allowed. | ".raw.gz" |
"bz2", "bzip2" | The data is raw, but compressed with the bzip2 program. Analogous to the gzip encoding, the compressed data must start with the same binary header as produced by the command-line bzip2 program, to ensure inter-operability with it. Implementation and information is available from http://sources.redhat.com/bzip2/. | ".raw.bz2" |
The formatting for hex is mostly the same as the ASCIIHexDecode and the ASCIIHexEncode filters of PostScript, but they are not identical: PostScript allows multiple filters (data can be run-length encoded as well as hex-encoded), null ('\0') characters count as whitespace, and the end of the data is explicitly indicated by a ">". However, in combination with the line skip specifier, it is usually possible to extract 8-bit image data from PostScript files, assuming you understand enough PostScript to determine the image dimensions.
The "standard detached suffix" is the filename suffix that should be used by NRRD writers producing a separate data file in conjunction with a detached header. This is most important for the compression encodings, since the stand-alone programs expect certain suffixes when decompressing (".gz" and ".bz2" for gzip and bzip2, respectively), and these suffixes are stripped after decompression. Because the result of decompressing compressed data from a NRRD is always raw (as opposed to compressed ascii text), the suffix for the detached file includes ".raw" as well. NRRD readers, however, should not care about the filename suffix of a detached data file.
Data file contents remaining after all data has been read should be ignored. This sanctions the strategy of using a detached nrrd header to refer to some smaller chunk of data in a separate larger data file. Data before the region of interest can be passed over with line skip and/or byte skip.
See the byte skip specification for information about how compression encoding changes its meaning.
There is complete orthogonality between the encoding of the data, and whether the header is attached or detached. The header is never compressed- it is necessarily straight ASCII text.
endian: <endian>
<endian> | Meaning | Who |
"little" | Most significant bytes are at higher addresses ("little end first") | Intel and compatible |
"big" | Most significant bytes are at lower addresses ("big end first") | Everyone else |
The convention with NRRD files is that non-ascii data should reflect the byte ordering of the current platform. There is no preference for one endian or the other in NRRD files, and NRRD writers should never have to worry about fixing endianness, only recording it when necessary. Fixing endianness is the responsibility of the NRRD reader. This way, NRRD readers and writers used within one platform never pay the overhead of fixing endianness. That overhead should only be incurred when going between platforms with different endiannesses.
content: <string>
This field is intended as the place to store a very concise textual description of the information in the array, similar to the second line (the "title") of a VTK file format header. The nrrd library, for instance, uses content to store a textual representation of a summary of the operations applied to a nrrd. If nrrdSlice() slices a nrrd with content "engine" along axis 0 at position 50, then the content of the result will be "slice(engine,0,50)".
min: <min>
max: <max>
old min: <min> oldmin: <min>
For example, if a floating point nrrd with values ranging from 0.0 to 1.0 is quantized to 8 bits, old min will be 0.0. This is not the middle of the range of values that were all mapped to the lowest output integer, but the lowest of those values.
Infinite values are not valid, "nan" means "don't know".
old max: <max> oldmax: <max>
data file: <file> datafile: <file>
line skip: <skip> lineskip: <skip>
When used in combination with byte skip, the line skipping is done before the byte skipping. The meaning of line skip is not affected by the encoding field.
byte skip: <skip> byteskip: <skip>
If skip is greater than or equal to zero, it tells how many bytes to skip in a data file in order to get to the beginning of the data. By definition, the bytes are skipped according to the action of fgetc(). When used in combination with line skip, the byte skipping is done after the line skipping. When this field does not appear, skip is taken to be zero. Negative values are not valid.
The interpretation of byte skip changes according to whether or not the encoding used is a form of compression or not. The only compressions supported in NRRD0001 are gzip and bzip2. In uncompressed encodings, the byte skipping is done just like the line skipping: within the data file, so as to locate the beginning of the data, and prior to the decoding of any data. In compressed encodings however, the line skipping is done first, and then the decompression begins. The byte skipping is done within the stream of decompressed data.
The reason for skipping bytes but not lines in the decompressed stream is basically motivated by the conceptual difference between ASCII and binary headers. One reason to write headers in ASCII is to make them human readable, so they probably shouldn't be compressed to begin with. Also, ASCII headers (such as in PNM images) often allow multiple lines of optional comments, so the number of lines to skip has to be determined on a per-file basis by looking at the (uncompressed) file, at which point the data might as well be written out as a NRRD file. In contrast, binary headers are very often fixed length, and not human readable, which means that when the header and data are compressed together, the beginning of the data can be easily found via a byte skip offset. This also applies to large datasets written by FORTRAN programs, for which even "raw" data can be proceeded by a four-byte representation of the data length.
As a special trick copied from the MetaImage file format, the value of skip can be -1. This is valid only with raw encoding, and on a data file which isn't standard input (stdin). The action of this byte skip is to fseek() backwards from the end of the data file, to the beginning of the data. The distance to seek is calculated from the nrrd type and axis sizes. This is a useful trick for getting at binary data in other formats with unknown (or variable) length binary headers, such as DICOM, TIFF, and BMP, but only if the data is uncompressed.
If skip is -1, the action of lineskip is entirely moot.
number: <string>
sizes: <size[0]> <size[1]> ... <size[dim-1]>
spacings: <space[0]> <space[1]> ... <space[dim-1]>
Because there must be one spacing for each axis, spacings must be given for axes which don't logically have a spatial component, such as the RGB axis of color image data, which is usually axis 0. Rather than invent a value (such as 1.0) for sample spacing where no value is sensible, a spacing value of "nan" should be used instead. In addition, "nan" can represent the fact that spacing information would be sensible here, but simply isn't known. Of course, if spacings are NaN for every axis, the field probably shouldn't be in the header.
The meaning and interpretation of the spacings field is independent of the centers, axis min and axis maxs fields, even though mutually incompatible settings are possible.
axis mins: <min[0]> <min[1]> ... <min[dim-1]> axismins: <min[0]> <min[1]> ... <min[dim-1]>
Infinite values are not valid as axis mins. Any non-infinite values, including zero, are valid. As with spacings information, the use of "nan" as an axis min value is probably preferable to inventing one where no value is meaningful or known.
Presence of the axis mins field does not require presence of the axis maxs field, although it is often useful for these to appear together. However, using the axis mins field alone can emulate the ORIGIN field of the VTK file format header.
axis maxs: <max[0]> <max[1]> ... <max[dim-1]> axismaxs: <max[0]> <max[1]> ... <max[dim-1]>
The settings of axis mins and axis maxs would seem to imply a value for spacings, but this also depends on the values of centers. Mutually incompatible settings of these fields are possible to save in a NRRD header, but is not the job of the NRRD reader to ensure their consistency, only to check that the individual values in isolation are sensible (for instance, an axis max can't be infinite).
centers: <center[0]> <center[1]> ... <center[dim-1]>
<cent[i]> | Meaning | Examples |
"cell" | The location of the sample is centered in the interior of the grid element. | Histograms, scatterplots, images for mip-maps, images in contexts in which a pixel can be correctly thought of as "a little square", volumes as a grid of cuberilles, in which the logical element is a cube with a single value at the center. |
"node" | The location of the sample is at the boundary between grid elements. | Volumes as a grid of "voxels", in which the logical element is a cube with a value at each of its eight corners. |
"???" | Centering information for this axis is either meaningless or unknown | Any non-spatial axis, such as a short axis for vector or tensor components, preceding all the spatial axes. |
labels: "<label[0]>" "<label[1]>" ... "<label[dim-1]>"
As shown above, each label is delimited by double quotes. Within each label, double quotes may be included by escaping them (\"), but no other form of escaping is supported. For axes with no labels, use a quoted empty string ("").
There is no fixed limit on how long the line containing the labels field can be.
units: "<unit[0]>" "<unit[1]>" ... "<unit[dim-1]>"