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

RF,BF: Refactored x5/fnirt modules for new specs - I think the refactoring is

complete?
parent ed5835ab
No related branches found
No related tags found
No related merge requests found
...@@ -124,33 +124,40 @@ header of the coefficient field file. ...@@ -124,33 +124,40 @@ header of the coefficient field file.
import logging import logging
import numpy as np
import nibabel as nib import nibabel as nib
import numpy as np
import fsl.data.constants as constants import fsl.data.constants as constants
import fsl.data.image as fslimage
from . import affine
from . import nonlinear
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def _readFnirtDisplacementField(fname, img, src, ref, dispType=None): def _readFnirtDeformationField(fname, img, src, ref, defType=None):
"""Loads ``fname``, assumed to be a FNIRT displacement field. """Loads ``fname``, assumed to be a FNIRT deformation field.
:arg fname: File name of FNIRT displacement field :arg fname: File name of FNIRT deformation field
:arg img: ``fname`` loaded as an :class:`.Image`
:arg src: Source image :arg img: ``fname`` loaded as an :class:`.Image`
:arg ref: Reference image
:arg dispType: Displacement type - either ``'absolute'`` or ``'relative'``. :arg src: Source image
If not provided, is automatically inferred from the data.
:return: A :class:`.DisplacementField` :arg ref: Reference image
:arg defType: Deformation type - either ``'absolute'`` or ``'relative'``.
If not provided, is automatically inferred from the data.
:return: A :class:`.DeformationField` object
""" """
from . import nonlinear return nonlinear.DeformationField(fname,
return nonlinear.DisplacementField(fname, src,
src, ref,
ref, srcSpace='fsl',
srcSpace='fsl', refSpace='fsl',
refSpace='fsl', defType=defType)
dispType=dispType)
def _readFnirtCoefficientField(fname, img, src, ref): def _readFnirtCoefficientField(fname, img, src, ref):
...@@ -163,9 +170,6 @@ def _readFnirtCoefficientField(fname, img, src, ref): ...@@ -163,9 +170,6 @@ def _readFnirtCoefficientField(fname, img, src, ref):
:return: A :class:`.CoefficientField` :return: A :class:`.CoefficientField`
""" """
from . import affine
from . import nonlinear
# FNIRT uses NIFTI header fields in # FNIRT uses NIFTI header fields in
# non-standard ways to store some # non-standard ways to store some
# additional information about the # additional information about the
...@@ -226,22 +230,22 @@ def _readFnirtCoefficientField(fname, img, src, ref): ...@@ -226,22 +230,22 @@ def _readFnirtCoefficientField(fname, img, src, ref):
fieldToRefMat=fieldToRefMat) fieldToRefMat=fieldToRefMat)
def readFnirt(fname, src, ref, dispType=None): def readFnirt(fname, src, ref, defType=None):
"""Reads a non-linear FNIRT transformation image, returning """Reads a non-linear FNIRT transformation image, returning
a :class:`.DisplacementField` or :class:`.CoefficientField` depending a :class:`.DeformationField` or :class:`.CoefficientField` depending
on the file type. on the file type.
:arg fname: File name of FNIRT transformation :arg fname: File name of FNIRT transformation
:arg src: Source image :arg src: Source image
:arg ref: Reference image :arg ref: Reference image
:arg dispType: Displacement type - either ``'absolute'`` or ``'relative'``. :arg defType: Deformation type - either ``'absolute'`` or ``'relative'``.
If not provided, is automatically inferred from the data. Only used if the file is a deformation field. If not
provided, is automatically inferred from the data.
""" """
# Figure out whether the file # Figure out whether the file
# is a displacement field or # is a deformation field or
# a coefficient field # a coefficient field
import fsl.data.image as fslimage
img = fslimage.Image(fname, loadData=False) img = fslimage.Image(fname, loadData=False)
disps = (constants.FSL_FNIRT_DISPLACEMENT_FIELD, disps = (constants.FSL_FNIRT_DISPLACEMENT_FIELD,
...@@ -253,7 +257,7 @@ def readFnirt(fname, src, ref, dispType=None): ...@@ -253,7 +257,7 @@ def readFnirt(fname, src, ref, dispType=None):
constants.FSL_TOPUP_QUADRATIC_SPLINE_COEFFICIENTS) constants.FSL_TOPUP_QUADRATIC_SPLINE_COEFFICIENTS)
if img.intent in disps: if img.intent in disps:
return _readFnirtDisplacementField(fname, img, src, ref, dispType) return _readFnirtDeformationField(fname, img, src, ref, defType)
elif img.intent in coefs: elif img.intent in coefs:
return _readFnirtCoefficientField(fname, img, src, ref) return _readFnirtCoefficientField(fname, img, src, ref)
else: else:
...@@ -263,15 +267,13 @@ def readFnirt(fname, src, ref, dispType=None): ...@@ -263,15 +267,13 @@ def readFnirt(fname, src, ref, dispType=None):
def toFnirt(field): def toFnirt(field):
"""Convert a :class:`.NonLinearTransform` to a FNIRT-compatible """Convert a :class:`.NonLinearTransform` to a FNIRT-compatible
:class:`.DisplacementField` or :class:`.CoefficientField`. :class:`.DeformationField` or :class:`.CoefficientField`.
:arg field: :class:`.NonLinearTransform` to convert :arg field: :class:`.NonLinearTransform` to convert
:return: A FNIRT-compatible :class:`.DisplacementField` or :return: A FNIRT-compatible :class:`.DeformationField` or
:class:`.CoefficientField`. :class:`.CoefficientField`.
""" """
from . import nonlinear
# If we have a coefficient field # If we have a coefficient field
# which transforms between fsl # which transforms between fsl
# space, we can just create a copy. # space, we can just create a copy.
...@@ -330,36 +332,35 @@ def toFnirt(field): ...@@ -330,36 +332,35 @@ def toFnirt(field):
srcToRefMat=field.srcToRefMat) srcToRefMat=field.srcToRefMat)
# Otherwise we have a non-FSL coefficient # Otherwise we have a non-FSL coefficient
# field, or a displacement field. # field, or a deformation field.
#
# We can't convert a CoefficientField
# which doesn't transform in FSL
# coordinates, because the coefficients
# will have been calculated between some
# other source/reference coordinate
# systems, and we can't adjust the
# coefficients to encode an FSL->FSL
# deformation.
else: else:
# We can't convert a CoefficientField
# which doesn't transform in FSL
# coordinates, because the coefficients
# will have been calculated between some
# other source/reference coordinate
# systems, and we can't adjust the
# coefficients to encode an FSL->FSL
# deformation.
if isinstance(field, nonlinear.CoefficientField): if isinstance(field, nonlinear.CoefficientField):
field = nonlinear.coefficientFieldToDisplacementField(field) field = nonlinear.coefficientFieldToDeformationField(field)
# Again, if we have a displacement # Again, if we have a displacement
# field which transforms between # field which transforms between
# fsl spaces, we can just take a copy # fsl spaces, we can just take a copy
if field.srcSpace == 'fsl' and field.refSpace == 'fsl': if field.srcSpace == 'fsl' and field.refSpace == 'fsl':
field = nonlinear.DisplacementField( field = nonlinear.DeformationField(
field.data, field.data,
header=field.header,
src=field.src, src=field.src,
ref=field.ref, ref=field.ref,
xform=field.voxToWorldMat, defType=field.deformationType)
dispType=field.displacementType)
# Otherwise we have to adjust the # Otherwise we have to adjust the
# displacements so they transform # displacements so they transform
# between fsl coordinates. # between fsl coordinates.
field = nonlinear.convertDisplacementSpace( field = nonlinear.convertDeformationSpace(
field, from_='fsl', to='fsl') field, from_='fsl', to='fsl')
field.header['intent_code'] = constants.FSL_FNIRT_DISPLACEMENT_FIELD field.header['intent_code'] = constants.FSL_FNIRT_DISPLACEMENT_FIELD
...@@ -369,23 +370,19 @@ def toFnirt(field): ...@@ -369,23 +370,19 @@ def toFnirt(field):
def fromFnirt(field, from_='world', to='world'): def fromFnirt(field, from_='world', to='world'):
"""Convert a FNIRT-style :class:`.NonLinearTransform` to a generic """Convert a FNIRT-style :class:`.NonLinearTransform` to a generic
:class:`.DisplacementField`. :class:`.DeformationField`.
:arg field: A FNIRT-style :class:`.CoefficientField` or :arg field: A FNIRT-style :class:`.CoefficientField` or
:class:`.DisplacementField` :class:`.DeformationField`
:arg from_: Desired reference image coordinate system :arg from_: Desired reference image coordinate system
:arg to: Desired source image coordinate system :arg to: Desired source image coordinate system
:return: A :class:`.DisplacementField` which contains displacements :return: A :class:`.DeformationField` which contains displacements
from the reference image ``from_`` cordinate system to the from the reference image ``from_`` cordinate system to the
source image ``to`` coordinate syste. source image ``to`` coordinate syste.
""" """
from . import nonlinear
# see comments in toFnirt
if isinstance(field, nonlinear.CoefficientField): if isinstance(field, nonlinear.CoefficientField):
field = nonlinear.coefficientFieldToDisplacementField(field) field = nonlinear.coefficientFieldToDeformationField(field)
return nonlinear.convertDeformationSpace(field, from_=from_, to=to)
return nonlinear.convertDisplacementSpace(field, from_=from_, to=to)
...@@ -397,16 +397,17 @@ def readNonLinearX5(fname): ...@@ -397,16 +397,17 @@ def readNonLinearX5(fname):
_readMetadata(f) _readMetadata(f)
ref = _readSpace( f['/A']) ref = _readSpace( f['/A'])
src = _readSpace( f['/B']) src = _readSpace( f['/B'])
field, space = _readDeformation(f['/Transform']) field, xform, defType = _readDeformation(f['/Transform'])
return nonlinear.DeformationField(field, return nonlinear.DeformationField(field,
header=space.header, xform=xform,
src=src, src=src,
ref=ref, ref=ref,
srcSpace='world', srcSpace='world',
refSpace='world') refSpace='world',
defType=defType)
def writeNonLinearX5(fname, field): def writeNonLinearX5(fname, field):
...@@ -421,8 +422,8 @@ def writeNonLinearX5(fname, field): ...@@ -421,8 +422,8 @@ def writeNonLinearX5(fname, field):
f.attrs['Type'] = 'nonlinear' f.attrs['Type'] = 'nonlinear'
_writeMetadata(f) _writeMetadata(f)
_writeSpace( f.create_group('/A'), f.ref) _writeSpace( f.create_group('/A'), field.ref)
_writeSpace( f.create_group('/B'), f.src) _writeSpace( f.create_group('/B'), field.src)
_writeDeformation(f.create_group('/Transform'), field) _writeDeformation(f.create_group('/Transform'), field)
...@@ -538,8 +539,8 @@ def _readDeformation(group): ...@@ -538,8 +539,8 @@ def _readDeformation(group):
- A ``numpy.arrayThe`` containing the deformation field - A ``numpy.arrayThe`` containing the deformation field
- A :class:`.Nifti` object representing the deformation - A ``numpy.array`` of shape ``(4, 4) `` containing the
field space voxel to world affine for the deformation field
- The deformation type - either ``'absolute'`` or - The deformation type - either ``'absolute'`` or
``'relative'`` ``'relative'``
...@@ -554,7 +555,7 @@ def _readDeformation(group): ...@@ -554,7 +555,7 @@ def _readDeformation(group):
if subtype not in ('absolute', 'relative'): if subtype not in ('absolute', 'relative'):
raise X5Error('Unknown deformation type: {}'.format(subtype)) raise X5Error('Unknown deformation type: {}'.format(subtype))
mapping = _readSpace(group['Mapping']) mapping = _readAffine(group['Mapping'])
field = group['Matrix'] field = group['Matrix']
if len(field.shape) != 4 or field.shape[3] != 3: if len(field.shape) != 4 or field.shape[3] != 3:
...@@ -570,10 +571,15 @@ def _writeDeformation(group, field): ...@@ -570,10 +571,15 @@ def _writeDeformation(group, field):
:arg field: A :class:`.DeformationField` object :arg field: A :class:`.DeformationField` object
""" """
if field.srcSpace != 'world' or \
field.refSpace != 'world':
raise X5Error('Deformation field must encode a '
'world<->world transformation')
group.attrs['Type'] = 'deformation' group.attrs['Type'] = 'deformation'
group.attrs['SubType'] = field.deformationType group.attrs['SubType'] = field.deformationType
mapping = group.create_group('Mapping') mapping = group.create_group('Mapping')
group.create_dataset('Matrix', data=field.data) group.create_dataset('Matrix', data=field.data)
_writeSpace(mapping, field) _writeAffine(mapping, field.getAffine('voxel', 'world'))
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