Skip to content
Snippets Groups Projects
Commit 7bdc1072 authored by Paul McCarthy's avatar Paul McCarthy :mountain_bicyclist:
Browse files

ENH: Add support for "scaled" coordinate system to getAffine - scaled voxels

without L/R flip.
parent 7141dc8a
No related branches found
No related tags found
No related merge requests found
...@@ -210,8 +210,12 @@ class Nifti(notifier.Notifier, meta.Meta): ...@@ -210,8 +210,12 @@ class Nifti(notifier.Notifier, meta.Meta):
- The ``fsl`` coordinate system, where voxel coordinates are scaled by - The ``fsl`` coordinate system, where voxel coordinates are scaled by
the ``pixdim`` values in the NIFTI header, and the X axis is inverted the ``pixdim`` values in the NIFTI header, and the X axis is inverted
if the voxel-to-world affine has a positive determinant. if the voxel-to-world affine has a positive determinant. The
coordinates ``(0, 0, 0)`` correspond to the corner of voxel
``(0, 0, 0)``.
- The ``scaled`` coordinate system, where voxel coordinates are scaled by
the ``pixdim`` values in the NIFTI header.
The :meth:`getAffine` method is a simple means of acquiring an affine The :meth:`getAffine` method is a simple means of acquiring an affine
which will transform between any of these coordinate systems. which will transform between any of these coordinate systems.
...@@ -435,7 +439,7 @@ class Nifti(notifier.Notifier, meta.Meta): ...@@ -435,7 +439,7 @@ class Nifti(notifier.Notifier, meta.Meta):
def generateAffines(voxToWorldMat, shape, pixdim): def generateAffines(voxToWorldMat, shape, pixdim):
"""Called by :meth:`__init__`, and the :meth:`voxToWorldMat` setter. """Called by :meth:`__init__`, and the :meth:`voxToWorldMat` setter.
Generates and returns a dictionary containing affine transformations Generates and returns a dictionary containing affine transformations
between the ``voxel``, ``fsl``, and ``world`` coordinate between the ``voxel``, ``fsl``, ``scaled``, and ``world`` coordinate
systems. These affines are accessible via the :meth:`getAffine` systems. These affines are accessible via the :meth:`getAffine`
method. method.
...@@ -454,29 +458,43 @@ class Nifti(notifier.Notifier, meta.Meta): ...@@ -454,29 +458,43 @@ class Nifti(notifier.Notifier, meta.Meta):
import numpy.linalg as npla import numpy.linalg as npla
affines = {} affines = {}
shape = list(shape[ :3]) shape = list(shape[ :3])
pixdim = list(pixdim[:3]) pixdim = list(pixdim[:3])
voxToScaledVoxMat = np.diag(pixdim + [1.0]) voxToScaledMat = np.diag(pixdim + [1.0])
isneuro = npla.det(voxToWorldMat) > 0 voxToFSLMat = np.array(voxToScaledMat)
isneuro = npla.det(voxToWorldMat) > 0
if isneuro: if isneuro:
x = (shape[0] - 1) * pixdim[0] x = (shape[0] - 1) * pixdim[0]
flip = affine.scaleOffsetXform([-1, 1, 1], flip = affine.scaleOffsetXform([-1, 1, 1],
[ x, 0, 0]) [ x, 0, 0])
voxToScaledVoxMat = affine.concat(flip, voxToScaledVoxMat) voxToFSLMat = affine.concat(flip, voxToFSLMat)
affines['fsl', 'fsl'] = np.eye(4) affines['voxel', 'voxel'] = np.eye(4)
affines['voxel', 'voxel'] = np.eye(4) affines['voxel', 'scaled'] = voxToFSLMat
affines['world', 'world'] = np.eye(4) affines['voxel', 'fsl'] = voxToScaledMat
affines['voxel', 'world'] = voxToWorldMat affines['voxel', 'world'] = voxToWorldMat
affines['world', 'voxel'] = affine.invert(voxToWorldMat)
affines['voxel', 'fsl'] = voxToScaledVoxMat affines['scaled', 'scaled'] = np.eye(4)
affines['fsl', 'voxel'] = affine.invert(voxToScaledVoxMat) affines['scaled', 'voxel'] = affine.invert(voxToScaledMat)
affines['fsl', 'world'] = affine.concat(affines['voxel', 'world'], affines['scaled', 'fsl'] = affine.concat(affines['voxel', 'fsl'],
affines['fsl', 'voxel']) affines['scaled', 'voxel'])
affines['world', 'fsl'] = affine.concat(affines['voxel', 'fsl'], affines['scaled', 'world'] = affine.concat(affines['voxel', 'world'],
affines['world', 'voxel']) affines['scaled', 'voxel'])
affines['fsl', 'fsl'] = np.eye(4)
affines['fsl', 'voxel'] = affine.invert(voxToFSLMat)
affines['fsl', 'scaled'] = affine.invert(affines['scaled', 'fsl'])
affines['fsl', 'world'] = affine.concat(affines['voxel', 'world'],
affines['fsl', 'voxel'])
affines['world', 'world'] = np.eye(4)
affines['world', 'voxel'] = affine.invert(voxToWorldMat)
affines['world', 'scaled'] = affine.concat(affines['voxel', 'scaled'],
affines['world', 'voxel'])
affines['world', 'fsl'] = affine.concat(affines['voxel', 'fsl'],
affines['world', 'voxel'])
return affines, isneuro return affines, isneuro
...@@ -515,9 +533,9 @@ class Nifti(notifier.Notifier, meta.Meta): ...@@ -515,9 +533,9 @@ class Nifti(notifier.Notifier, meta.Meta):
return from_, to return from_, to
if from_ is not None: froms = [from_] if from_ is not None: froms = [from_]
else: froms = ['voxel', 'fsl', 'world'] else: froms = ['voxel', 'scaled', 'fsl', 'world']
if to is not None: tos = [to] if to is not None: tos = [to]
else: tos = ['voxel', 'fsl', 'world'] else: tos = ['voxel', 'scaled', 'fsl', 'world']
for from_, to in it.product(froms, tos): for from_, to in it.product(froms, tos):
...@@ -590,7 +608,7 @@ class Nifti(notifier.Notifier, meta.Meta): ...@@ -590,7 +608,7 @@ class Nifti(notifier.Notifier, meta.Meta):
elif isinstance(self.header, nib.nifti1.Nifti1Header): return 1 elif isinstance(self.header, nib.nifti1.Nifti1Header): return 1
elif isinstance(self.header, nib.analyze.AnalyzeHeader): return 0 elif isinstance(self.header, nib.analyze.AnalyzeHeader): return 0
else: raise RuntimeError('Unrecognised header: {}'.format(self.header)) else: raise RuntimeError(f'Unrecognised header: {self.header}')
@property @property
...@@ -724,6 +742,8 @@ class Nifti(notifier.Notifier, meta.Meta): ...@@ -724,6 +742,8 @@ class Nifti(notifier.Notifier, meta.Meta):
sform/qform sform/qform
- ``'fsl'``: The FSL coordinate system (scaled voxels, with a - ``'fsl'``: The FSL coordinate system (scaled voxels, with a
left-right flip if the sform/qform has a positive determinant) left-right flip if the sform/qform has a positive determinant)
- ``'scaled'``: Scaled voxel coordinate system (equivalent to
``'fsl'`` without the flip).
:arg from_: Source coordinate system :arg from_: Source coordinate system
:arg to: Destination coordinate system :arg to: Destination coordinate system
...@@ -732,11 +752,11 @@ class Nifti(notifier.Notifier, meta.Meta): ...@@ -732,11 +752,11 @@ class Nifti(notifier.Notifier, meta.Meta):
from_ = from_.lower() from_ = from_.lower()
to = to .lower() to = to .lower()
if from_ not in ('voxel', 'fsl', 'world') or \ if from_ not in ('voxel', 'scaled', 'fsl', 'world') or \
to not in ('voxel', 'fsl', 'world'): to not in ('voxel', 'scaled', 'fsl', 'world'):
raise ValueError('Invalid source/reference spaces: "{}" -> "{}".' raise ValueError('Invalid source/reference spaces: "{}" -> "{}".'
'Recognised spaces are "voxel", "fsl", and ' 'Recognised spaces are "voxel", "fsl", "scaled", '
'"world"'.format(from_, to)) 'and "world"'.format(from_, to))
return np.copy(self.__affines[from_, to]) return np.copy(self.__affines[from_, to])
......
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