From 07fc4c17e05732b2b6915f5a793da30b5070a7f7 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauldmccarthy@gmail.com>
Date: Sun, 21 Jul 2019 13:43:35 +0100
Subject: [PATCH] ENH: resampleToReference now accepts a custom matrix,
 allowing us to apply a transformation (e.g. FLIRT registration) during the
 resampling. Missed imports in deprecated fsl.utils.transform

---
 fsl/utils/image/resample.py | 38 ++++++++++++++++++++++++++++++++-----
 fsl/utils/transform.py      |  4 +++-
 2 files changed, 36 insertions(+), 6 deletions(-)

diff --git a/fsl/utils/image/resample.py b/fsl/utils/image/resample.py
index bf11d7a4e..1aa52b9d2 100644
--- a/fsl/utils/image/resample.py
+++ b/fsl/utils/image/resample.py
@@ -39,7 +39,7 @@ def resampleToPixdims(image, newPixdims, **kwargs):
     return resample(image, newShape, **kwargs)
 
 
-def resampleToReference(image, reference, **kwargs):
+def resampleToReference(image, reference, matrix=None, **kwargs):
     """Resample ``image`` into the space of the ``reference``.
 
     This is a wrapper around :func:`resample` - refer to its documenttion
@@ -49,6 +49,7 @@ def resampleToReference(image, reference, **kwargs):
     along the spatial (first three) dimensions.
 
     :arg image:     :class:`.Image` to resample
+    :arg matrix:    Optional world-to-world affine alignment matrix
     :arg reference: :class:`.Nifti` defining the space to resample ``image``
                     into
     """
@@ -56,6 +57,9 @@ def resampleToReference(image, reference, **kwargs):
     oldShape = list(image.shape)
     newShape = list(reference.shape[:3])
 
+    if matrix is None:
+        matrix = np.eye(4)
+
     # If the input image is >3D, pad the
     # new shape so that we only resample
     # along the first 3 dimensions.
@@ -64,7 +68,9 @@ def resampleToReference(image, reference, **kwargs):
 
     # Align the two images together
     # via their vox-to-world affines.
-    matrix = affine.concat(image.worldToVoxMat, reference.voxToWorldMat)
+    matrix = affine.concat(image.worldToVoxMat,
+                           affine.invert(matrix),
+                           reference.voxToWorldMat)
 
     # If the input image is >3D, we
     # have to adjust the resampling
@@ -82,7 +88,12 @@ def resampleToReference(image, reference, **kwargs):
     kwargs['newShape'] = newShape
     kwargs['matrix']   = matrix
 
-    return resample(image, **kwargs)
+    data = resample(image, **kwargs)[0]
+
+    # The image is now in the same space
+    # as the reference, so it inherits
+    # ref's voxel-to-world affine
+    return data, reference.voxToWorldMat
 
 
 def resample(image,
@@ -113,6 +124,12 @@ def resample(image,
     particularly on the ``order``, ``matrix``, ``mode`` and
     ``cval`` arguments.
 
+    .. note:: If a custom resampling ``matrix`` is specified, the adjusted
+              voxel-to-world afffine cannot be calculated by this function,
+              so ``None`` will be returned instead.
+
+    :arg image:    :class:`.Image` object to resample
+
     :arg newShape: Desired shape. May containg floating point values, in which
                    case the resampled image will have shape
                    ``round(newShape)``, but the voxel sizes will have scales
@@ -159,7 +176,8 @@ def resample(image,
 
                - A ``numpy`` array of shape ``(4, 4)``, containing the
                  adjusted voxel-to-world transformation for the spatial
-                 dimensions of the resampled data.
+                 dimensions of the resampled data, or ``None`` if a ``matrix``
+                 was provided.
     """
 
     if sliceobj is None:     sliceobj = slice(None)
@@ -168,6 +186,8 @@ def resample(image,
     if mode     is None:     mode     = 'nearest'
     if origin   == 'center': origin   = 'centre'
 
+    ownMatrix = matrix is None
+
     if origin not in ('centre', 'corner'):
         raise ValueError('Invalid value for origin: {}'.format(origin))
 
@@ -218,7 +238,15 @@ def resample(image,
         matrix[:3, :3] = rotmat
         matrix[:3, -1] = offsets
 
-    matrix = affine.concat(image.voxToWorldMat, matrix)
+    # We can only adjust the v2w affine if
+    # the input space and resampling space
+    # are aligned (e.g. if we're just
+    # resampling to different dimensions).
+    # We can't assume this when a custom
+    # matrix is specified, so we just give
+    # up and return None.
+    if ownMatrix: matrix = affine.concat(image.voxToWorldMat, matrix)
+    else:         matrix = None
 
     return data, matrix
 
diff --git a/fsl/utils/transform.py b/fsl/utils/transform.py
index b8c003968..e682cc1be 100644
--- a/fsl/utils/transform.py
+++ b/fsl/utils/transform.py
@@ -10,7 +10,9 @@
 
 
 import fsl.utils.deprecated as     deprecated
-from   fsl.transform.affine import *          # noqa
+from   fsl.transform.affine import *                     # noqa
+from   fsl.transform.flirt  import (flirtMatrixToSform,  # noqa
+                                    sformToFlirtMatrix)
 
 
 deprecated.warn('fsl.utils.transform',
-- 
GitLab