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

Only the sform (and potentially its code) is modified when the image

voxToWorldMat is changed.
parent 3568f7db
No related branches found
No related tags found
No related merge requests found
...@@ -87,6 +87,55 @@ class Nifti(notifier.Notifier): ...@@ -87,6 +87,55 @@ class Nifti(notifier.Notifier):
================= ==================================================== ================= ====================================================
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.
The ``shape`` attribute may not precisely match the image shape as
reported in the NIFTI header, because trailing dimensions of size 1 are
squeezed out. See the :meth:`__determineShape` and :meth:`mapIndices`
methods.
**The affine transformation**
The :meth:`voxToWorldMat` and :meth:`worldToVoxMat` attributes contain
transformation matrices for transforming between voxel and world
coordinates. The ``Nifti`` class follows the same process as ``nibabel``
in selecting the affine (see
http://nipy.org/nibabel/nifti_images.html#the-nifti-affines):
1. If ``sform_code != 0`` ("unknown") use the sform affine; else
2. If ``qform_code != 0`` ("unknown") use the qform affine; else
3. Use the fall-back affine.
However, the *fall-back* affine used by the ``Nifti`` class differs to
that used by ``nibabel``. In ``nibabel``, the origin (world coordinates
(0, 0, 0)) is set to the centre of the image. Here in the ``Nifti``
class, we set the world coordinate orign to be the corner of the image,
i.e. the corner of voxel (0, 0, 0).
You may change the ``voxToWorldMat`` of a ``Nifti`` instance (unless it
is an Analyze image). When you do so:
- Only the ``sform`` of the underlying ``Nifti1Header`` object is changed
- The ``qform`` is not modified.
- If the ``sform_code`` was previously set to ``NIFTI_XFORM_UNKNOWN``,
it is changed to ``NIFTI_XFORM_ALIGNED_ANAT``. Otherwise, the
``sform_code`` is not modified.
**ANALYZE support**
A ``Nifti`` instance expects to be passed either a A ``Nifti`` instance expects to be passed either a
``nibabel.nifti1.Nifti1Header`` or a ``nibabel.nifti2.Nifti2Header``, but ``nibabel.nifti1.Nifti1Header`` or a ``nibabel.nifti2.Nifti2Header``, but
can als encapsulate a ``nibabel.analyze.AnalyzeHeader``. In this case: can als encapsulate a ``nibabel.analyze.AnalyzeHeader``. In this case:
...@@ -104,6 +153,9 @@ class Nifti(notifier.Notifier): ...@@ -104,6 +153,9 @@ class Nifti(notifier.Notifier):
:attr:`.constants.NIFTI_XFORM_ANALYZE`. :attr:`.constants.NIFTI_XFORM_ANALYZE`.
**Notification**
The ``Nifti`` class implements the :class:`.Notifier` interface - The ``Nifti`` class implements the :class:`.Notifier` interface -
listeners may register to be notified on the following topics: listeners may register to be notified on the following topics:
...@@ -111,19 +163,6 @@ class Nifti(notifier.Notifier): ...@@ -111,19 +163,6 @@ class Nifti(notifier.Notifier):
``'transform'`` The affine transformation matrix has changed. This topic ``'transform'`` The affine transformation matrix has changed. This topic
will occur when the ``voxToWorldMat`` is changed. will occur when the ``voxToWorldMat`` is changed.
=============== ======================================================== =============== ========================================================
.. 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
as reported in the NIFTI header, because trailing dimensions of
size 1 are squeezed out. See the :meth:`__determineShape` and
:meth:`mapIndices` methods.
""" """
...@@ -294,19 +333,27 @@ class Nifti(notifier.Notifier): ...@@ -294,19 +333,27 @@ class Nifti(notifier.Notifier):
@voxToWorldMat.setter @voxToWorldMat.setter
def voxToWorldMat(self, xform): def voxToWorldMat(self, xform):
"""Update the ``voxToWorldMat``. The ``worldToVoxMat`` and ``pixdim`` """Update the ``voxToWorldMat``. The ``worldToVoxMat`` value is also
values are also updated. This will result in notification on the updated. This will result in notification on the ``'transform'``
``'transform'`` topic. topic.
""" """
header = self.header # Can't do much with
# an analyze image
if self.niftiVersion == 0:
raise Exception('voxToWorldMat cannot be '
'changed for an ANALYZE image')
header = self.header
sformCode = header['sform_code']
header.set_qform(xform) if sformCode == constants.NIFTI_XFORM_UNKNOWN:
header.set_sform(xform) sformCode = constants.NIFTI_XFORM_ALIGNED_ANAT
header.set_sform(xform, code=sformCode)
self.__voxToWorldMat = self.__determineTransform(header) self.__voxToWorldMat = self.__determineTransform(header)
self.__worldToVoxMat = transform.invert(self.__voxToWorldMat) self.__worldToVoxMat = transform.invert(self.__voxToWorldMat)
self.__pixdim = header.get_zooms()
log.debug('Affine changed:\npixdims: ' log.debug('Affine changed:\npixdims: '
'{}\nsform: {}\nqform: {}'.format( '{}\nsform: {}\nqform: {}'.format(
...@@ -366,12 +413,11 @@ class Nifti(notifier.Notifier): ...@@ -366,12 +413,11 @@ class Nifti(notifier.Notifier):
if code is not None: if code is not None:
code = self.header[code] code = self.header[code]
# If a specific code is not # If the caller did not specify
# specified, we check both. # a code, we check both. If the
# If the sform is present, # sform is present, we return it.
# we return it. Otherwise, # Otherwise, if the qform is
# if the qform is present, # present, we return that.
# we return that.
else: else:
sform_code = self.header['sform_code'] sform_code = self.header['sform_code']
...@@ -574,7 +620,7 @@ class Image(Nifti): ...@@ -574,7 +620,7 @@ class Image(Nifti):
:arg name: A name for the image. :arg name: A name for the image.
:arg header: If not ``None``, assumed to be a :arg header: If not ``None``, assumed to be a
:class:`nibabel.nifti1.Nifti1Header` or :class:`nibabel.nifti1.Nifti1Header` or
:class:`nibabel.nifti2.Nifti2Header` to be used as the :class:`nibabel.nifti2.Nifti2Header` to be used as the
image header. Not applied to images loaded from file, image header. Not applied to images loaded from file,
...@@ -582,8 +628,10 @@ class Image(Nifti): ...@@ -582,8 +628,10 @@ class Image(Nifti):
:arg xform: A :math:`4\\times 4` affine transformation matrix :arg xform: A :math:`4\\times 4` affine transformation matrix
which transforms voxel coordinates into real world which transforms voxel coordinates into real world
coordinates. Only used if ``image`` is a ``numpy`` coordinates. If not provided, and a ``header`` is
array, and ``header`` is ``None``. provided, the transformation in the header is used.
If neither a ``xform`` nor a ``header`` are provided,
an identity matrix is used.
:arg loadData: If ``True`` (the default) the image data is loaded :arg loadData: If ``True`` (the default) the image data is loaded
in to memory. Otherwise, only the image header in to memory. Otherwise, only the image header
...@@ -681,6 +729,10 @@ class Image(Nifti): ...@@ -681,6 +729,10 @@ class Image(Nifti):
Nifti.__init__(self, nibImage.get_header()) Nifti.__init__(self, nibImage.get_header())
print name
print self.voxToWorldMat
self.name = name self.name = name
self.__lName = '{}_{}'.format(id(self), self.name) self.__lName = '{}_{}'.format(id(self), self.name)
self.__dataSource = dataSource self.__dataSource = dataSource
...@@ -801,9 +853,9 @@ class Image(Nifti): ...@@ -801,9 +853,9 @@ class Image(Nifti):
Nifti.voxToWorldMat.fset(self, xform) Nifti.voxToWorldMat.fset(self, xform)
xform = self.voxToWorldMat xform = self.voxToWorldMat
code = self.header['sform_code']
self.__nibImage.set_sform(xform) self.__nibImage.set_sform(xform, code)
self.__nibImage.set_qform(xform)
def __transformChanged(self, *args, **kwargs): def __transformChanged(self, *args, **kwargs):
......
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