Skip to content
Snippets Groups Projects
Commit e30514af authored by Paul McCarthy's avatar Paul McCarthy
Browse files

Nifti class explicitly supports ANALYZE images

parent 3623a316
No related branches found
No related tags found
No related merge requests found
...@@ -81,6 +81,10 @@ NIFTI_XFORM_MNI_152 = 4 ...@@ -81,6 +81,10 @@ NIFTI_XFORM_MNI_152 = 4
"""MNI 152 normalized coordinates.""" """MNI 152 normalized coordinates."""
NIFTI_XFORM_ANALYZE = 5
"""Code which indicates that this is an ANALYZE image, not a NIFTI image. """
# NIFTI file intent codes # NIFTI file intent codes
NIFTI_INTENT_NONE = 0 NIFTI_INTENT_NONE = 0
NIFTI_INTENT_CORREL = 2 NIFTI_INTENT_CORREL = 2
......
...@@ -64,20 +64,52 @@ class Nifti(object): ...@@ -64,20 +64,52 @@ class Nifti(object):
================= ==================================================== ================= ====================================================
``header`` The :mod:`nibabel` NIFTI header object. ``header`` The :mod:`nibabel` NIFTI1/NIFTI2/Analyze header
object.
``shape`` A list/tuple containing the number of voxels along ``shape`` A list/tuple containing the number of voxels along
each image dimension. each image dimension.
``pixdim`` A list/tuple containing the length of one voxel ``pixdim`` A list/tuple containing the length of one voxel
along each image dimension. along each image dimension.
``voxToWorldMat`` A 4*4 array specifying the affine transformation ``voxToWorldMat`` A 4*4 array specifying the affine transformation
for transforming voxel coordinates into real world for transforming voxel coordinates into real world
coordinates. coordinates.
``worldToVoxMat`` A 4*4 array specifying the affine transformation ``worldToVoxMat`` A 4*4 array specifying the affine transformation
for transforming real world coordinates into voxel for transforming real world coordinates into voxel
coordinates. coordinates.
``intent`` The NIFTI intent code specified in the header.
``intent`` The NIFTI intent code specified in the header (or
:attr:`.constants.NIFTI_INTENT_NONE` for Analyze
images).
================= ==================================================== ================= ====================================================
A ``Nifti`` instance expects to be passed either a
``nibabel.nifti1.Nifti1Header`` or a ``nibabel.nifti2.Nifti2Header``, but
can als encapsulate a ``nibabel.analyze.AnalyzeHeader``. In this case:
- The image voxel orientation is assumed to be R->L, P->A, I->S.
- The affine will be set to a diagonal matrix with the header pixdims as
its elements (with the X pixdim negated), and an offset specified by
the ANALYZE ``origin`` fields. Construction of the affine is handled
by ``nibabel``.
- The :meth:`niftiVersion` method will return ``0``.
- The :meth:`getXFormCode` method will return
:attr:`.constants.NIFTI_XFORM_ANALYZE`.
.. warning:: The ``header`` field may either be a ``nifti1``, ``nifti2``,
or ``analyze`` header object. Make sure to take this into
account if you are writing code that should work with all
three. Use the :meth:`niftiVersion` property if you need to
know what type of image you are dealing with.
.. note:: The ``shape`` attribute may not precisely match the image shape .. note:: The ``shape`` attribute may not precisely match the image shape
as reported in the NIFTI header, because trailing dimensions of as reported in the NIFTI header, because trailing dimensions of
...@@ -85,19 +117,22 @@ class Nifti(object): ...@@ -85,19 +117,22 @@ class Nifti(object):
:meth:`mapIndices` methods. :meth:`mapIndices` methods.
""" """
def __init__(self, header): def __init__(self, header):
"""Create a ``Nifti`` object. """Create a ``Nifti`` object.
:arg header: A :class:`nibabel.nifti1.Nifti1Header` or :arg header: A :class:`nibabel.nifti1.Nifti1Header`,
:class:`nibabel.nifti2.Nifti2Header` to be used as the :class:`nibabel.nifti2.Nifti2Header`, or
image header. ``nibabel.analyze.AnalyzeHeader`` to be used as the
image header.
""" """
import nibabel as nib import nibabel as nib
# Nifti2Header is a sub-class of Nifti1Header, # Nifti2Header is a sub-class of Nifti1Header,
# so we don't need to test for it # and Nifti1Header a sub-class of AnalyzeHeader,
if not isinstance(header, nib.nifti1.Nifti1Header): # so we only need to test for the latter.
if not isinstance(header, nib.analyze.AnalyzeHeader):
raise ValueError('Unrecognised header: {}'.format(header)) raise ValueError('Unrecognised header: {}'.format(header))
header = header.copy() header = header.copy()
...@@ -121,14 +156,21 @@ class Nifti(object): ...@@ -121,14 +156,21 @@ class Nifti(object):
@property @property
def niftiVersion(self): def niftiVersion(self):
"""Returns the NIFTI file version - either ``1`` or ``2``. """ """Returns the NIFTI file version:
- ``0`` for ANALYZE
- ``1`` for NIFTI1
- ``2`` for NIFTI2
"""
import nibabel as nib import nibabel as nib
# nib.Nifti2 is a subclass of Nifti1, # nib.Nifti2 is a subclass of Nifti1,
# so we have to check it first. # and Nifti1 a subclass of Analyze,
if isinstance(self.header, nib.nifti2.Nifti2Header): return 2 # so we have to check in order
elif isinstance(self.header, nib.nifti1.Nifti1Header): return 1 if isinstance(self.header, nib.nifti2.Nifti2Header): return 2
elif isinstance(self.header, nib.nifti1.Nifti1Header): return 1
elif isinstance(self.header, nib.analyze.AnalyzeHeader): return 0
else: raise RuntimeError('Unrecognised header: {}'.format(self.header)) else: raise RuntimeError('Unrecognised header: {}'.format(self.header))
...@@ -138,12 +180,15 @@ class Nifti(object): ...@@ -138,12 +180,15 @@ class Nifti(object):
coordinate transformation matrix that is associated with this coordinate transformation matrix that is associated with this
``Nifti`` instance. ``Nifti`` instance.
""" """
# We have to treat FSL/FNIRT images # We have to treat FSL/FNIRT images
# specially, as FNIRT clobbers the # specially, as FNIRT clobbers the
# sform section of the NIFTI header # sform section of the NIFTI header
# to store other data. # to store other data.
intent = header.get('intent_code', -1) intent = header.get('intent_code', -1)
qform = header.get('qform_code', -1)
sform = header.get('sform_code', -1)
if intent in (constants.FSL_FNIRT_DISPLACEMENT_FIELD, if intent in (constants.FSL_FNIRT_DISPLACEMENT_FIELD,
constants.FSL_CUBIC_SPLINE_COEFFICIENTS, constants.FSL_CUBIC_SPLINE_COEFFICIENTS,
constants.FSL_DCT_COEFFICIENTS, constants.FSL_DCT_COEFFICIENTS,
...@@ -161,12 +206,12 @@ class Nifti(object): ...@@ -161,12 +206,12 @@ class Nifti(object):
# corresponds to world location (0, 0, 0). # corresponds to world location (0, 0, 0).
# This goes against the NIFTI spec - it # This goes against the NIFTI spec - it
# should just be a straight scaling matrix. # should just be a straight scaling matrix.
elif header['qform_code'] == 0 and header['sform_code'] == 0: elif qform == 0 and sform == 0:
pixdims = header.get_zooms() pixdims = header.get_zooms()
voxToWorldMat = transform.scaleOffsetXform(pixdims, 0) voxToWorldMat = transform.scaleOffsetXform(pixdims, 0)
# Otherwise we let nibabel decide # Otherwise we let nibabel decide
# which transform to use. # which transform to use.
else: else:
voxToWorldMat = np.array(header.get_best_affine()) voxToWorldMat = np.array(header.get_best_affine())
...@@ -238,8 +283,12 @@ class Nifti(object): ...@@ -238,8 +283,12 @@ class Nifti(object):
- :data:`~.constants.NIFTI_XFORM_ALIGNED_ANAT` - :data:`~.constants.NIFTI_XFORM_ALIGNED_ANAT`
- :data:`~.constants.NIFTI_XFORM_TALAIRACH` - :data:`~.constants.NIFTI_XFORM_TALAIRACH`
- :data:`~.constants.NIFTI_XFORM_MNI_152` - :data:`~.constants.NIFTI_XFORM_MNI_152`
- :data:`~.constants.NIFTI_XFORM_ANALYZE`
""" """
if self.niftiVersion == 0:
return constants.NIFTI_XFORM_ANALYZE
if code == 'sform' : code = 'sform_code' if code == 'sform' : code = 'sform_code'
elif code == 'qform' : code = 'qform_code' elif code == 'qform' : code = 'qform_code'
elif code is not None: elif code is not None:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment