From 69c108ea4e1aabd4dbc17e201523b64b70452387 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauldmccarthy@gmail.com>
Date: Thu, 11 Apr 2019 12:39:02 +0100
Subject: [PATCH] RF: VoxelwiseEVMixin has a new getData method which takes
 into account compressed voxelwise EVs

---
 fsl/data/featdesign.py | 36 +++++++++++++++++++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/fsl/data/featdesign.py b/fsl/data/featdesign.py
index 633b712b4..ce5b41f59 100644
--- a/fsl/data/featdesign.py
+++ b/fsl/data/featdesign.py
@@ -214,7 +214,7 @@ class FEATFSFDesign(object):
                             'for ev {}'.format(ev.index))
                 continue
 
-            design[:, ev.index] = ev.image[x, y, z, :]
+            design[:, ev.index] = ev.getData(x, y, z)
 
         return design
 
@@ -228,6 +228,17 @@ class VoxelwiseEVMixin(object):
     ``filename`` Path to the image file containing the data for this EV
     ``image``    Reference to the :class:`.Image` object
     ============ ======================================================
+
+    Some FSL tools (e.g. `PNM
+    <https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/PNM>`_) generate *compressed*
+    voxelwise EVs, where there is only one voxel per slice. For example,
+    if the input data has shape ``(x, y, z, t)``, and slices are acquired
+    along the Z plane, voxelwise EV files generated by PNM will have shape
+    ``(1, 1, z, t)``.
+
+    Therefore, using the :meth:`getData` method is preferable to accessing
+    the :meth:`image` directly, as ``getData`` will check for compressed
+    images, and adjust the voxel coordinates accordingly.
     """
 
     def __init__(self, filename):
@@ -272,6 +283,29 @@ class VoxelwiseEVMixin(object):
         return self.__image
 
 
+    def getData(self, x, y, z):
+        """Returns the data at the specified voxel, taking into account
+        compressed voxelwise EVs.
+        """
+        image = self.image
+
+        if image is None:
+            return None
+
+        dx, dy, dz = image.shape[:3]
+
+        # "Compressed" images have one voxel
+        # per slice, i.e. they have shape
+        # [1, 1, nslices, ntimepoints] (if
+        # Z is the slice plane).
+        if sum((dx == 1, dy == 1, dz == 1)) == 2:
+            if dx == 1: x = 0
+            if dy == 1: y = 0
+            if dz == 1: z = 0
+
+        return image[x, y, z, :]
+
+
 class EV(object):
     """Class representing an explanatory variable in a FEAT design matrix.
 
-- 
GitLab