io.nifti_ref¶
Module: io.nifti_ref
¶
Inheritance diagram for nipy.io.nifti_ref
:
An implementation of some of the NIFTI conventions as desribed in:
http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1.h
A version of the same file is in the nibabel repisitory at
doc/source/external/nifti1.h
.
Background¶
We (nipystas) make an explicit distinction between:
an input coordinate system of an image (the array == voxel coordinates)
output coordinate system (usually millimeters in some world for space, seconds for time)
the mapping between the two.
The collection of these three is the coordmap
attribute of a NIPY image.
There is no constraint that the number of input and output coordinates should be the same.
We don’t specify the units of our output coordinate system, but assume spatial units are millimeters and time units are seconds.
NIFTI is mostly less explicit, but more constrained.
NIFTI input coordinate system¶
NIFTI files can have up to seven voxel dimensions (7 axes in the input coordinate system).
The first 3 voxel dimensions of a NIFTI file must be spatial but can be in any order in relationship to directions in mm space (the output coordinate system)
The 4th voxel dimension is assumed to be time. In particular, if you have some
other meaning for a non-spatial dimension, the NIFTI standard suggests you set
the length of the 4th dimension to be 1, and use the 5th dimension of the image
instead, and set the NIFTI “intent” fields to state the meaning. If the
intent
field is set correctly then it should be possible to set meaningful
input coordinate axis names for dimensions > (0, 1, 2).
There’s a wrinkle to the 4th axis is time story; the xyxt_units
field in the
NIFTI header can specify the 4th dimension units as Hz (frequency), PPM
(concentration) or Radians / second.
NIFTI also has a ‘dim_info’ header attribute that optionally specifies that 0 or
more of the first three voxel axes are ‘frequency’, ‘phase’ or ‘slice’. These
terms refer to 2D MRI acquisition encoding, where ‘slice’s are collected
sequentially, and the two remaining dimensions arose from frequency and phase
encoding. The dim_info
fields are often not set. 3D acquisitions don’t have
a ‘slice’ dimension.
NIFTI output coordinate system¶
In the NIFTI specification, the order of the output coordinates (at least the first 3) are fixed to be what might be called RAS+, that is (‘x=L->R’, ‘y=P->A’, ‘z=I->S’). This RAS+ output order is not allowed to change and there is no way of specifying such a change in the NIFTI header.
The world in which these RAS+ X, Y, Z axes exist can be one of the recognized spaces, which are: scanner, aligned (to another file’s world space), Talairach, MNI 152 (aligned to the MNI 152 atlas).
By implication, the 4th output dimension is likely to be seconds (given the 4th
input dimension is likely time), but there’s a field xyzt_units
(see above)
that can be used to imply the 4th output dimension is actually frequency,
concentration or angular velocity.
NIFTI input / output mapping¶
NIFTI stores the relationship between the first 3 (spatial) voxel axes and the RAS+ coordinates in an XYZ affine. This is a homogenous coordinate affine, hence 4 by 4 for 3 (spatial) dimensions.
NIFTI also stores “pixel dimensions” in a pixdim
field. This can give you
scaling for individual axes. We ignore the values of pixdim
for the first 3
axes if we have a full (“sform”) affine stored in the header, otherwise they
form part of the affine above. pixdim``[3:] provide voxel to output scalings
for later axes. The units for the 4th dimension can come from ``xyzt_units
as
above.
We take the convention that the output coordinate names are (‘x=L->R’, ‘y=P->A’, ‘z=I->S’,’t’,’u’,’v’,’w’) unless there is no time axis (see below) in which case we just omit ‘t’. The first 3 axes are also named after the output space (‘scanner-x=L->R’, ‘mni-x=L-R’ etc).
The input axes are ‘ijktuvw’ unless there is no time axis (see below), in which case they are ‘ijkuvw’ (remember, NIFTI only allows 7 dimensions, and one is used up by the time length 1 axis).
Time-like axes¶
A time-like axis is an axis that is any of time, Hz, PPM or radians / second.
We recognize time in a NIPY coordinate map by an input or an output axis named ‘t’ or ‘time’. If it’s an output axis we work out the corresponding input axis.
A Hz axis can be called ‘hz’ or ‘frequency-hz’.
A PPM axis can be called ‘ppm’ or ‘concentration-ppm’.
A radians / second axis can be called ‘rads’ or ‘radians/s’.
Does this NIFTI image have a time-like axis?¶
We take there to be no time axis if there are only three NIFTI dimensions, or if:
the length of the fourth NIFTI dimension is 1 AND
There are more than four dimensions AND
The
xyzt_units
field does not indicate time or time-like units.
What we do about all this¶
For saving a NIPY image to NIFTI, see the docstring for nipy2nifti()
.
For loading a NIFTI image to NIPY, see the docstring for nifti2nipy()
.
Class¶
Functions¶
-
nipy.io.nifti_ref.
nifti2nipy
(ni_img)[source]¶ Return NIPY image from NIFTI image ni_image
- Parameters
ni_img : nibabel.Nifti1Image
NIFTI image
- Returns
img :
Image
nipy image
- Raises
NiftiError : if image is < 3D
Notes
Lacking any other information, we take the input coordinate names for axes 0:7 to be (‘i’, ‘j’, ‘k’, ‘t’, ‘u’, ‘v’, ‘w’).
If the image is 1D or 2D then we have a problem. If there’s a defined (sform, qform) affine, this has 3 input dimensions, and we have to guess what the extra input dimensions are. If we don’t have a defined affine, we don’t know what the output dimensions are. For example, if the image is 2D, and we don’t have an affine, are these X and Y or X and Z or Y and Z? In the presence of ambiguity, resist the temptation to guess - raise a NiftiError.
If there is a time-like axis, name the input and corresponding output axis for the type of axis (‘t’, ‘hz’, ‘ppm’, ‘rads’).
Otherwise remove the ‘t’ axis from both input and output names, and squeeze the length 1 dimension from the input data.
If there’s a ‘t’ axis get
toffset
and put into affine at position [3, -1].If
dim_info
is set coherently, set input axis names to ‘slice’, ‘freq’, ‘phase’ fromdim_info
.Get the output spatial coordinate names from the ‘scanner’, ‘aligned’, ‘talairach’, ‘mni’ XYZ spaces (see
nipy.core.reference.spaces
).We construct the N-D affine by taking the XYZ affine and adding scaling diagonal elements from
pixdim
.If the space units in NIFTI
xyzt_units
are ‘microns’ or ‘meters’ we adjust the affine to mm units, but warn because this might be a mistake.If the time units in NIFTI xyzt_units are ‘msec’ or ‘usec’, scale the time axis
pixdim
values accordingly.Ignore the intent-related fields for now, but warn that we are doing so if there appears to be specific information in there.
-
nipy.io.nifti_ref.
nipy2nifti
(img, data_dtype=None, strict=None, fix0=True)[source]¶ Return NIFTI image from nipy image img
- Parameters
img : object
An object, usually a NIPY
Image
, having attributes coordmap and shapedata_dtype : None or dtype specifier
None means try and use header dtype, otherwise try and use data dtype, otherwise use np.float32. A dtype specifier means set the header output data dtype using
np.dtype(data_dtype)
.strict : bool, optional
Whether to use strict checking of input image for creating NIFTI
fix0: bool, optional
Whether to fix potential 0 column / row in affine. This option only used when trying to find time etc axes in the coordmap output names. In order to find matching input names, we need to use the corresponding rows and columns in the affine. Sometimes time, in particular, has 0 scaling, and thus all 0 in the corresponding row / column. In that case it’s hard to work out which input corresponds. If fix0 is True, and there is only one all zero (matrix part of the) affine row, and only one all zero (matrix part of the) affine column, fix scaling for that combination to zero, assuming this a zero scaling for time.
- Returns
ni_img :
nibabel.Nifti1Image
NIFTI image
- Raises
NiftiError: if space axes not orthogonal to non-space axes
NiftiError: if non-space axes not orthogonal to each other
NiftiError: if `img` output space does not match named spaces in NIFTI
NiftiError: if input image has more than 7 dimensions
NiftiError: if input image has 7 dimensions, but no time dimension, because
we need to add an extra 1 length axis at position 3
NiftiError: if we find a time-like input axis but the matching output axis
is a different time-like.
NiftiError: if we find a time-like output axis but the matching input axis
is a different time-like.
NiftiError: if we find a time output axis and there are non-zero non-spatial
offsets in the affine, but we can’t find a corresponding input axis.
Notes
First, we need to create a valid XYZ Affine. We check if this can be done by checking if there are recognizable X, Y, Z output axes and corresponding input (voxel) axes. This requires the input image to be at least 3D. If we find these requirements, we reorder the image axes to have XYZ output axes and 3 spatial input axes first, and get the corresponding XYZ affine.
If the spatial dimensions are not orthogonal to the non-spatial dimensions, raise a NiftiError.
If the non-spatial dimensions are not orthogonal to each other, raise a NiftiError.
We check if the XYZ output fits with the NIFTI named spaces of scanner, aligned, Talairach, MNI. If so, set the NIFTI code and qform, sform accordingly. If the space corresponds to ‘unknown’ then we must set the NIFTI transform codes to 0, and the affine must match the affine we will get from loading the NIFTI with no qform, sform. If not, we’re going to lose information in the affine, and raise an error.
If any of the first three input axes are named (‘slice’, ‘freq’, ‘phase’) set the
dim_info
field accordingly.Set the
xyzt_units
field to indicate millimeters and seconds, if there is a ‘t’ axis, otherwise millimeters and 0 (unknown).We look to see if we have a time-like axis in the inputs or the outputs. A time-like axis has labels ‘t’, ‘hz’, ‘ppm’, ‘rads’. If we have an axis ‘t’ in the inputs and the outputs, check they either correspond, or both inputs and output correspond with no other axis, otherwise raise NiftiError. Do the same check for ‘hz’, then ‘ppm’, then ‘rads’.
If we do have a time-like axis, roll that axis to be the 4th axis. If this axis is actually time, take the
affine[3, -1]
and put into thetoffset
field. If there’s no time-like axis, but there are other non-spatial axes, make a length 1 4th array axis to indicate this.If the resulting NIFTI image has more than 7 dimensions, raise a NiftiError.
Set
pixdim
for axes >= 3 using vector length of corresponding affine columns.We don’t set the intent-related fields for now.