From e0d897b54dc04abfdaf36b30188e83fd89becd16 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauld.mccarthy@gmail.com>
Date: Sun, 12 Mar 2017 18:31:55 +0000
Subject: [PATCH] ImageWrapper "adjustment" (sort of but not really a bugfix).
 When an image is written to, the old coverages are not discarded - rather,
 the new data range is calculated on the union of the old coverage and the
 write slice, across all volumes.

---
 fsl/data/image.py        | 10 ++++-----
 fsl/data/imagewrapper.py | 48 +++++++++++++++++++++++++++-------------
 2 files changed, 38 insertions(+), 20 deletions(-)

diff --git a/fsl/data/image.py b/fsl/data/image.py
index c1cf28cc9..ea537f80c 100644
--- a/fsl/data/image.py
+++ b/fsl/data/image.py
@@ -486,11 +486,11 @@ class Nifti(notifier.Notifier):
 
     @memoize.Instanceify(memoize.memoize)
     def isNeurological(self):
-        """Returns ``True`` if it looks like this ``Nifti`` object is in
-        neurological orientation, ``False`` otherwise. This test is purely
-        based on the determinant of the voxel-to-mm transformation matrix -
-        if it has a positive determinant, the image is assumed to be in
-        neurological orientation, otherwise it is assumed to be in
+        """Returns ``True`` if it looks like this ``Nifti`` object has a
+        neurological voxel orientation, ``False`` otherwise. This test is
+        purely based on the determinant of the voxel-to-mm transformation
+        matrix - if it has a positive determinant, the image is assumed to
+        be in neurological orientation, otherwise it is assumed to be in
         radiological orientation.
 
         See http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FLIRT/FAQ#What_is_the\
diff --git a/fsl/data/imagewrapper.py b/fsl/data/imagewrapper.py
index 921dd6e36..458865eaf 100644
--- a/fsl/data/imagewrapper.py
+++ b/fsl/data/imagewrapper.py
@@ -421,13 +421,15 @@ class ImageWrapper(notifier.Notifier):
         log.debug('Updating image {} data range [slice: {}] '
                   '(current range: [{}, {}]; '
                   'number of expansions: {}; '
-                  'current coverage: {})'.format(
+                  'current coverage: {}; '
+                  'volume ranges: {})'.format(
                       self.__name,
                       slices,
                       self.__range[0],
                       self.__range[1],
                       len(expansions),
-                      self.__coverage))
+                      self.__coverage,
+                      self.__volRanges))
 
         # As we access the data for each expansions,
         # we want it to have the same dimensionality
@@ -530,30 +532,46 @@ class ImageWrapper(notifier.Notifier):
         # area and the current coverage, then it's
         # easy - we just expand the coverage to
         # include the newly written area.
+        # 
+        # But if there is overlap between the written
+        # area and the current coverage, things are
+        # more complicated, because the portion of
+        # the image that has been written over may
+        # have contained the currently known data
+        # minimum/maximum. We have no way of knowing
+        # this, so we have to reset the coverage (on
+        # the affected volumes), and recalculate the
+        # data range.
         if overlap in (OVERLAP_SOME, OVERLAP_ALL):
 
-            # If there is overlap between the written
-            # area and the current coverage, things are
-            # more complicated, because the portion of
-            # the image that has been written over may
-            # have contained the currently known data
-            # minimum/maximum. We have no way of knowing
-            # this, so we have to reset the coverage (on
-            # the affected volumes), and recalculate the
-            # data range.
-
             # TODO Could you store the location of the
             #      data minimum/maximum (in each volume),
             #      so you know whether resetting the
             #      coverage is necessary?
             lowVol, highVol = slices[self.__numRealDims - 1]
 
+            # We create a single slice which
+            # encompasses the given slice, and
+            # all existing coverages for each
+            # volume in the given slice. The
+            # data range for this slice will
+            # be recalculated.
+            slices = adjustCoverage(self.__coverage[:, :, lowVol], slices)
+            for vol in range(lowVol + 1, highVol):
+                slices = adjustCoverage(slices, self.__coverage[:, :, vol].T)
+
+            slices = np.array(slices.T, dtype=np.uint32)
+            slices = tuple(it.chain(map(tuple, slices), [(lowVol, highVol)]))
+
             log.debug('Image {} data written - clearing known data '
-                      'range on volumes {} - {} (write slice: {})'.format(
+                      'range on volumes {} - {} (write slice: {}; '
+                      'coverage: {}; volRanges: {})'.format(
                           self.__name,
                           lowVol,
                           highVol,
-                          slices))
+                          slices,
+                          self.__coverage[:, :, lowVol:highVol],
+                          self.__volRanges[lowVol:highVol, :]))
 
             for vol in range(lowVol, highVol):
                 self.__coverage[:, :, vol]    = np.nan
@@ -909,7 +927,7 @@ def adjustCoverage(oldCoverage, slices):
     :return: A ``numpy`` array containing the adjusted/expanded coverage.
     """
 
-    newCoverage = np.zeros(oldCoverage.shape, dtype=np.uint32)
+    newCoverage = np.zeros(oldCoverage.shape, dtype=oldCoverage.dtype)
 
     for dim in range(oldCoverage.shape[1]):
 
-- 
GitLab