diff --git a/fsl/data/image.py b/fsl/data/image.py index e6b15709153a8e3ea56d078810ebc7c4eb5e68be..dd423d4b5f8fc5341ea104378e5cf1d58e3aed1f 100644 --- a/fsl/data/image.py +++ b/fsl/data/image.py @@ -147,7 +147,7 @@ class Nifti(notifier.Notifier, meta.Meta): transformed into a millimetre coordinate system, defined by the ``sform`` and/or ``qform`` elements of the NIFTI header. - - The ``fsl`` coordinate system, where voxel coordinated 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 if the voxel-to-world affine has a positive determinant. diff --git a/fsl/transform/__init__.py b/fsl/transform/__init__.py index 2c86ce512aeda3a909c8cae11e2a11a06796cb70..31bad1d68dcd5676ffa319f42a906ce92170fca4 100644 --- a/fsl/transform/__init__.py +++ b/fsl/transform/__init__.py @@ -11,9 +11,9 @@ transformations. Functionality is split across the following modules: .. autosummary:: :nosignatures: - .affine - .flirt - .fnirt - .nonlinear - .x5 + ~fsl.transform.affine + ~fsl.transform.flirt + ~fsl.transform.fnirt + ~fsl.transform.nonlinear + ~fsl.transform.x5 """ diff --git a/fsl/transform/flirt.py b/fsl/transform/flirt.py index 2fdc38b015a21fce527d897c76b1c99f6d5769d8..996a1e7081a86ef732f80c7fb08fac1c2a3e34b5 100644 --- a/fsl/transform/flirt.py +++ b/fsl/transform/flirt.py @@ -51,9 +51,8 @@ def fromFlirt(xform, src, ref, from_='voxel', to='world'): Valid values for the ``from_`` and ``to`` arguments are: - ``voxel``: The voxel coordinate system - - ``fsl``: The FSL coordiante system (voxels scaled by pixdims, with - the X axis inverted if the image sform/qform has a positive - determinant) + - ``fsl``: The FSL coordiante system (voxels scaled by pixdims, with the + X axis inverted if the image sform/qform has a positive determinant) - ``world`` The world coordinate system See the :class:`.Nifti` class documentation and the @@ -64,10 +63,11 @@ def fromFlirt(xform, src, ref, from_='voxel', to='world'): :arg src: :class:`.Nifti` object, the ``xform`` source image :arg ref: :class:`.Nifti` object, the ``xform`` reference image :arg from_: Desired source coordinate system - :arg to: Desired target coordinate system + :arg to: Desired reference coordinate system :returns: ``numpy`` array of shape ``(4, 4)`` containing a matrix encoding a transformation from the source ``from_`` to the reference ``to`` coordinate systems. + """ premat = src.getAffine(from_, 'fsl') postmat = ref.getAffine('fsl', to) diff --git a/fsl/transform/fnirt.py b/fsl/transform/fnirt.py index 74589fa0793a079d7ed9917a9af373fc099de31e..dbf0550f8af4ca46b85397f7498670a2c5084011 100644 --- a/fsl/transform/fnirt.py +++ b/fsl/transform/fnirt.py @@ -157,7 +157,7 @@ def readFnirt(fname, src, ref, dispType=None): def toFnirt(field): """Convert a :class:`.NonLinearTransform` to a FNIRT-compatible - :class:`.DisplacementField` or:class:`.CoefficientField`. + :class:`.DisplacementField` or :class:`.CoefficientField`. :arg field: :class:`.NonLinearTransform` to convert :return: A FNIRT-compatible :class:`.DisplacementField` or diff --git a/fsl/transform/x5.py b/fsl/transform/x5.py index df0ffe528d3a2fbc9442c503d18c1bd5f3404b63..5035bbfae430c884b7811e0931063fb1d83807a2 100644 --- a/fsl/transform/x5.py +++ b/fsl/transform/x5.py @@ -22,6 +22,12 @@ an X5 file, the source image is referred to as the ``From`` space, and the reference image the ``To`` space. +X5 files enable a transformation between **source image world coordinates** +and **reference image world coordinates**. The *world coordinate system* of +an image is defined by its ``sform`` or ``qform`` (hereafter referred to as +the ``sform``), which is contained in the NIfTI header. + + Custom HDF5 groups ================== @@ -68,10 +74,7 @@ another. Groups of type "affine" have the following fields: A HDF5 group which is listed as being of type "space" contains all of the information required to define the space of a NIfTI image, including its -shape, dimensions, and voxel-to-world affine transformation. The *world* -coordinate system of an image is defined by its ``sform`` or ``qform`` -(hereafter referred to as the ``sform``), which is contained in the NIfTI -header. +shape, dimensions, and voxel-to-world affine transformation. Groups of type "space" have the following fields: @@ -96,49 +99,70 @@ Linear X5 files Linear X5 transformation files contain an affine transformation matrix of -shape ``(4, 4)``, which can be used to transform **source image world -coordinates** into **reference image world coordinates**. +shape ``(4, 4)``, which can be used to transform source image world +coordinates into reference image world coordinates. Linear X5 transformation files are assumed to adhere to the HDF5 structure defined in the table below. All fields are required. -+-----------------------------+-----------+-----------------------------------+ -| **Name** | **Type** | **Value/Description** | -+-----------------------------+-----------+-----------------------------------+ ++---------------+-----------+-------------------------------------------------+ +| **Name** | **Type** | **Value/Description** | ++---------------+-----------+-------------------------------------------------+ | *Metadata* | -+-----------------------------+-----------+-----------------------------------+ -| ``/Format`` | attribute | ``'X5'`` | -+-----------------------------+-----------+-----------------------------------+ -| ``/Version`` | attribute | ``'0.0.1'`` | -+-----------------------------+-----------+-----------------------------------+ -| ``/Metadata`` | attribute | JSON string containing | -| | | unstructured metadata. | -+-----------------------------+-----------+-----------------------------------+ ++---------------+-----------+-------------------------------------------------+ +| ``/Format`` | attribute | ``'X5'`` | ++---------------+-----------+-------------------------------------------------+ +| ``/Version`` | attribute | ``'0.0.1'`` | ++---------------+-----------+-------------------------------------------------+ +| ``/Metadata`` | attribute | JSON string containing unstructured metadata. | ++---------------+-----------+-------------------------------------------------+ | *Transformation* | -+-----------------------------+-----------+-----------------------------------+ -| ``/`` | affine | Affine transformation from source | -| | | image world coordinates to | -| | | reference image world coordinates | -+-----------------------------+-----------+-----------------------------------+ -| ``/From/`` | space | Source image definition | -+-----------------------------+-----------+-----------------------------------+ -| ``/To/`` | space | Reference image definition | -+-----------------------------+-----------+-----------------------------------+ ++---------------+-----------+-------------------------------------------------+ +| ``/`` | affine | Affine transformation from source image world | +| | | coordinates to reference image world | +| | | coordinates | ++---------------+-----------+-------------------------------------------------+ +| ``/From/`` | space | Source image definition | ++---------------+-----------+-------------------------------------------------+ +| ``/To/`` | space | Reference image definition | ++---------------+-----------+-------------------------------------------------+ Storage of FSL FLIRT matrices in linear X5 files ------------------------------------------------ +FLIRT outputs the result of a linear registration from a source image to a +reference image as an affine matrix of shape ``(4, 4)``. This matrix encodes a +transformation from source image **FSL coordinates** to reference image **FSL +coordinates** [*]_. + + +In contrast, X5 matrices encode a transformation in **world coordinates**, +i.e. they can be used to transform coordinates from the source image to the +reference image, after both images have been transformed into a common +coordinate system via their respective ``sform`` affines. + + .. image:: images/x5_linear_transform_file.png :width: 80% :align: center -Non-linear X5 transformation files -================================== +The :mod:`fsl.transform.flirt` module contains functions for converting +between FLIRT-style matrices and X5 style matrices. + + +.. [*] For a given image, FSL coordinates are voxel coordinates scaled by the + ``pixdim`` values in the NIFTI header, and an inversion along the X + axis if the voxel-to-world affine (the ``sform``) has a positive + determinant. + + +Non-linear X5 files +=================== Non-linear X5 transformation files contain a non-linear transformation between @@ -159,8 +183,8 @@ Displacement fields A displacement field is a ``float64`` array of shape ``(X, Y, Z, 3)``, defined -in the same space as the reference image (i.e. the reference image is assumed -to have shape ``(X, Y, Z)``. A displacement field may contain either: +in the same space as the reference image. A displacement field may contain +either: - *relative* displacements, where each voxel in the displacement field contains an offset which can be added to the reference image coordinates @@ -212,7 +236,7 @@ some other coordinate system. Howewer, if the transformation does not transform between source and reference -image **world** coordinates, the ``/Pre/`` and ``/Post/`` affine +image **world coordinates**, the ``/Pre/`` and ``/Post/`` affine transformations must be provided. @@ -224,8 +248,8 @@ transform the resulting source image coordinates into the source image world coordinate system. -Initial affine alignment ------------------------- +Initial linear registration +--------------------------- Non-linear transformations are often accompanied by an initial affine @@ -245,20 +269,20 @@ Now we have three spaces, and three sets of coordinate systems, to consider: 3. Reference image space -The initial affine registration encodes a transformation between spaces 1 and -2, and the non-linear registration encodes a transformation between spaces 2 -and 3. Note that the fields-of-view for spaces 2 and 3 are typically +The initial affine registration calculates a transformation between spaces 1 +and 2, and the non-linear registration calculates a transformation between +spaces 2 and 3. Note that the fields-of-view for spaces 2 and 3 are typically equivalent. -The initial affine transformation may be included in an X5 file, in the +This initial affine transformation may be included in an X5 file, in the ``/InitialAlignment/`` group. If provided, this initial transformation is assumed to provide a transformation: - - *From* the **source image** world coordinate system (or the coordinate + - *From* the **source image world coordinate system** (or the coordinate system used as input to the ``/Post/`` affine, if provided). - - *To* the **aligned source** image coordinate system used within the + - *To* the **aligned source image coordinate system** used within the non-linear transformation. @@ -270,18 +294,17 @@ Non-linear X5 transformation files are assumed to adhere to the following HDF5 structure. All fields are required unless otherwise noted: -+---------------------+-----------+-------------------------------------------+ -| **Name** | **Type** | **Value/Description** | -+---------------------+-----------+-------------------------------------------+ ++---------------+-----------+-------------------------------------------------+ +| **Name** | **Type** | **Value/Description** | ++---------------+-----------+-------------------------------------------------+ | *Metadata* | -+---------------------+-----------+-------------------------------------------+ -| ``/Format`` | attribute | ``'X5'`` | -+---------------------+-----------+-------------------------------------------+ -| ``/Version`` | attribute | ``'0.0.1'`` | -+---------------------+-----------+-------------------------------------------+ -| ``/Metadata`` | attribute | JSON string containing unstructured | -| | | metadata. | -+---------------------+-----------+-------------------------------------------+ ++---------------+-----------+-------------------------------------------------+ +| ``/Format`` | attribute | ``'X5'`` | ++---------------+-----------+-------------------------------------------------+ +| ``/Version`` | attribute | ``'0.0.1'`` | ++---------------+-----------+-------------------------------------------------+ +| ``/Metadata`` | attribute | JSON string containing unstructured metadata. | ++---------------+-----------+-------------------------------------------------+ | *Transformation* | +------------------------+-----------+----------------------------------------+ | **Name** | **Type** | **Value/Description** | @@ -338,8 +361,12 @@ HDF5 structure. All fields are required unless otherwise noted: +-----------------------------------+-----------+-----------------------------+ -Storage of FSL FNIRT files in nonlinear X5 files ------------------------------------------------- +Storage of FSL FNIRT transformations in non-linear X5 files +----------------------------------------------------------- + + +Non-linear registration using FNIRT generally follows the process depicted +here: .. image:: images/nonlinear_registration_process.png @@ -347,8 +374,33 @@ Storage of FSL FNIRT files in nonlinear X5 files :align: center -Displacement fields -^^^^^^^^^^^^^^^^^^^ +First, an initial linear registration is performed from the source image to +the reference image using FLIRT; this provides an initial global alignment +which can be used as the starting point for the non-linear registration. Next, +FNIRT is used to non-linearly register the aligned source image to the +reference image. Importantly, both of these steps are performed using FSL +coordinates. + + +The non-linear transformation file generated by FNIRT typically contains the +initial linear registration, with it either being encoded directly into the +displacements, or being stored in the NIfTI header. + + +Storage of FNIRT displacement fields +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +A FNIRT displacement field contains either: + + - relative displacements from reference image FSL coordinates to source + image FSL coordinates, or + - absolute source image FSL coordinates. + + +If an initial linear registration was used as the starting point for FNIRT, +this is encoded into the displacements/coordinates themselves, so they can be +used to transform from the reference image to the *original* source image. .. image:: images/fnirt_displacement_field.png @@ -356,13 +408,31 @@ Displacement fields :align: center +When a FNIRT displacement field is stored in a X5 file, the ``/Pre/`` and +``/Post/`` affine transformations are set so that the displacement field can +be used to perform a transformation from reference image world coordinates to +source image world coordinates. + + .. image:: images/x5_nonlinear_displacement_field_file.png :width: 95% :align: center -Coefficient fields -^^^^^^^^^^^^^^^^^^ +Storage of FNIRT coefficient fields +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +A FNIRT coefficient field contains the coefficients of a grid of B-spline +functions; these coefficients can be used to generate a displacement field +which encodes a transformation from reference image FSL coordinates to source +image FSL coordinates. + + +If an initial linear registration was used as the starting point for FNIRT, +the generated displacement field will encode a transformtion to *aligned* +source image coordinates, and the initial affine will be stored in the NIfTI +header of the coefficient field file. .. image:: images/fnirt_coefficient_field.png @@ -370,6 +440,14 @@ Coefficient fields :align: center +When a FNIRT coefficient field is stored in an X5 file, the ``/Pre/`` and +``/Post/`` affine transformations are set as for displacement +fields. Additionally, the initial linear registration transformation is stored +as the ``/InitialAlignment/``, so that its inverse can be used to transform +the aligned source image FSL coordinates back into the original source image +FSL coordinates. + + .. image:: images/x5_nonlinear_coefficient_field_file.png :width: 95% :align: center @@ -418,7 +496,7 @@ def readLinearX5(fname): def writeLinearX5(fname, xform, src, ref): - """Write a linear transformation to the specified file. + """Write a linear transformation to ``fname``. :arg fname: File name to write to :arg xform: ``(4, 4)`` ``numpy`` array containing the affine transformation @@ -458,7 +536,7 @@ def readNonLinearX5(fname): def writeNonLinearX5(fname, field): - """Write a nonlinear X5 transformation file to ``fname``. + """Write a nonlinear X5 transformation to ``fname``. :arg fname: File name to write to :arg field: A :class:`.DisplacementField` or :class:`.CoefficientField`