From 9477aecab088932eab0fe6443552da3d1914b31e Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauld.mccarthy@gmail.com>
Date: Thu, 29 Oct 2015 15:39:54 +0000
Subject: [PATCH] Simplified ortho orientation labelling, and made it less
 inaccurate.

---
 fsl/data/image.py                        | 46 ++++-------------
 fsl/data/strings.py                      |  9 ++--
 fsl/fsleyes/controls/overlayinfopanel.py | 27 +++++-----
 fsl/fsleyes/profiles/orthoviewprofile.py |  2 +-
 fsl/fsleyes/views/orthopanel.py          | 65 ++++++++++--------------
 fsl/tools/render.py                      | 20 ++------
 6 files changed, 59 insertions(+), 110 deletions(-)

diff --git a/fsl/data/image.py b/fsl/data/image.py
index 31988dc6e..3dda62827 100644
--- a/fsl/data/image.py
+++ b/fsl/data/image.py
@@ -333,9 +333,14 @@ class Image(props.HasProperties):
         return int(code)
 
 
-    def getWorldOrientation(self, axis, code=None):
-        """Returns a code representing the orientation of the specified axis
-        in world space.
+    def getOrientation(self, axis, xform):
+        """Returns a code representing the orientation of the specified data
+        axis in the coordinate system defined by the given transformation
+        matrix.
+
+        :arg xform: A transformation matrix which is assumed to transform
+                    coordinates from the image world coordinate system to
+                    some other coordinate system.
 
         This method returns one of the following values, indicating the
         direction in which coordinates along the specified axis increase:
@@ -357,45 +362,16 @@ class Image(props.HasProperties):
         increases from inferior to superior).
         """
 
-        if self.getXFormCode(code) == constants.NIFTI_XFORM_UNKNOWN:
-            return constants.ORIENT_UNKNOWN
-
-        if   axis == 0: return constants.ORIENT_L2R
-        elif axis == 1: return constants.ORIENT_P2A
-        elif axis == 2: return constants.ORIENT_I2S
-
-        else: return constants.ORIENT_UNKNOWN
-
-
-    def getVoxelOrientation(self, axis, code=None):
-        """Returns a code representing the (estimated) orientation of the
-        specified data axis.
-
-        :arg code: May be either ``qform`` or ``sform``, specifying which
-                   transformation to use.
-
-        See the :meth:`getWorldOrientation` method for a description
-        of the return value.
-        """
-        
-        if self.getXFormCode(code) == constants.NIFTI_XFORM_UNKNOWN:
-            return constants.ORIENT_UNKNOWN
-
-        if   code is None:    xform = self.nibImage.get_affine()
-        elif code == 'sform': xform = self.nibImage.get_sform()
-        elif code == 'qform': xform = self.nibImage.get_qform()
-        else: raise ValueError('code must be None, qform, or sform')
+        if self.getXFormCode() == constants.NIFTI_XFORM_UNKNOWN:
+            return constants.ORIENT_UNKNOWN 
         
-        # the aff2axcodes returns one code for each 
-        # axis in the image array (i.e. in voxel space),
-        # which denotes the real world direction
         import nibabel as nib
         code = nib.orientations.aff2axcodes(
             xform,
             ((constants.ORIENT_R2L, constants.ORIENT_L2R),
              (constants.ORIENT_A2P, constants.ORIENT_P2A),
              (constants.ORIENT_S2I, constants.ORIENT_I2S)))[axis]
-        
+
         return code
 
 
diff --git a/fsl/data/strings.py b/fsl/data/strings.py
index ba3d48926..a3846b2c8 100644
--- a/fsl/data/strings.py
+++ b/fsl/data/strings.py
@@ -616,12 +616,9 @@ nifti = TypeDict({
     'voxOrient.0'   : 'X voxel orientation',
     'voxOrient.1'   : 'Y voxel orientation',
     'voxOrient.2'   : 'Z voxel orientation',
-    'sformOrient.0' : 'X sform orientation',
-    'sformOrient.1' : 'Y sform orientation',
-    'sformOrient.2' : 'Z sform orientation',
-    'qformOrient.0' : 'X qform orientation',
-    'qformOrient.1' : 'Y qform orientation',
-    'qformOrient.2' : 'Z qform orientation', 
+    'worldOrient.0' : 'X world orientation',
+    'worldOrient.1' : 'Y world orientation',
+    'worldOrient.2' : 'Z world orientation',
 
     'qform' : 'QForm matrix',
     'sform' : 'SForm matrix',
diff --git a/fsl/fsleyes/controls/overlayinfopanel.py b/fsl/fsleyes/controls/overlayinfopanel.py
index f44baf590..7d340e171 100644
--- a/fsl/fsleyes/controls/overlayinfopanel.py
+++ b/fsl/fsleyes/controls/overlayinfopanel.py
@@ -14,9 +14,11 @@ import collections
 import wx
 import wx.html as wxhtml
 
-import fsl.data.strings   as strings
-import fsl.data.constants as constants
-import fsl.fsleyes.panel  as fslpanel
+import numpy as np
+
+import fsl.data.strings    as strings
+import fsl.data.constants  as constants
+import fsl.fsleyes.panel   as fslpanel
 
 
 class OverlayInfoPanel(fslpanel.FSLEyesPanel):
@@ -178,8 +180,10 @@ class OverlayInfoPanel(fslpanel.FSLEyesPanel):
         
         info = OverlayInfo('{} - {}'.format(
             display.name, strings.labels[self, overlay]))
+        
         img  = overlay.nibImage
         hdr  = img.get_header()
+        opts = display.getDisplayOpts()
 
         voxUnits, timeUnits = hdr.get_xyzt_units()
         qformCode           = int(hdr['qform_code'])
@@ -240,7 +244,8 @@ class OverlayInfoPanel(fslpanel.FSLEyesPanel):
                          section=xformSect) 
 
         for i in range(3):
-            orient = overlay.getVoxelOrientation(i)
+            xform  = opts.getTransform('world', 'id')
+            orient = overlay.getOrientation(i, xform)
             orient = '{} - {}'.format(
                 strings.anatomy['Image', 'lowlong',  orient],
                 strings.anatomy['Image', 'highlong', orient])
@@ -249,23 +254,15 @@ class OverlayInfoPanel(fslpanel.FSLEyesPanel):
                          section=orientSect)
 
         for i in range(3):
-            orient = overlay.getWorldOrientation(i, code='sform')
+            xform  = np.eye(4)
+            orient = overlay.getOrientation(i, xform)
             orient = '{} - {}'.format(
                 strings.anatomy['Image', 'lowlong',  orient],
                 strings.anatomy['Image', 'highlong', orient])
-            info.addInfo(strings.nifti['sformOrient.{}'.format(i)],
+            info.addInfo(strings.nifti['worldOrient.{}'.format(i)],
                          orient,
                          section=orientSect)
 
-        for i in range(3):
-            orient = overlay.getWorldOrientation(i, code='qform')
-            orient = '{} - {}'.format(
-                strings.anatomy['Image', 'lowlong',  orient],
-                strings.anatomy['Image', 'highlong', orient])
-            info.addInfo(strings.nifti['qformOrient.{}'.format(i)],
-                         orient,
-                         section=orientSect) 
-
         return info
 
 
diff --git a/fsl/fsleyes/profiles/orthoviewprofile.py b/fsl/fsleyes/profiles/orthoviewprofile.py
index aa90f2099..dc5f3559c 100644
--- a/fsl/fsleyes/profiles/orthoviewprofile.py
+++ b/fsl/fsleyes/profiles/orthoviewprofile.py
@@ -309,7 +309,7 @@ class OrthoViewProfile(profiles.Profile):
         if zoom == 0:
             return
 
-        self._zoomModeMouseWheel(canvas, zoom)
+        self._zoomModeMouseWheel(None, canvas, zoom)
 
         
     def _zoomModeLeftMouseDrag(self, ev, canvas, mousePos, canvasPos):
diff --git a/fsl/fsleyes/views/orthopanel.py b/fsl/fsleyes/views/orthopanel.py
index 96e2f20ce..25ca142f5 100644
--- a/fsl/fsleyes/views/orthopanel.py
+++ b/fsl/fsleyes/views/orthopanel.py
@@ -359,16 +359,12 @@ class OrthoPanel(canvaspanel.CanvasPanel):
                     self.__zLabels.values() 
 
         if overlay is not None:
-            opts = self._displayCtx.getOpts(overlay)
+            opts  = self._displayCtx.getOpts(overlay)
+            xform = opts.getTransform('world', 'display')
             
-            if opts.transform in ('pixdim', 'id'):
-                xorient = overlay.getVoxelOrientation(0)
-                yorient = overlay.getVoxelOrientation(1)
-                zorient = overlay.getVoxelOrientation(2)
-            else:
-                xorient = overlay.getWorldOrientation(0)
-                yorient = overlay.getWorldOrientation(1)
-                zorient = overlay.getWorldOrientation(2)
+            xorient = overlay.getOrientation(0, xform)
+            yorient = overlay.getOrientation(1, xform)
+            zorient = overlay.getOrientation(2, xform)
 
             if constants.ORIENT_UNKNOWN in (xorient, yorient, zorient):
 
@@ -429,13 +425,10 @@ class OrthoPanel(canvaspanel.CanvasPanel):
 
             # Update anatomy labels when 
             # overlay bounds change
-            if i == self._displayCtx.selectedOverlay:
-                opts.addListener('bounds',
-                                 self._name,
-                                 self.__refreshLabels,
-                                 overwrite=True)
-            else:
-                opts.removeListener('bounds', self._name)
+            opts.addListener('bounds',
+                             self._name,
+                             self.__refreshLabels,
+                             overwrite=True)
                 
         # anatomical orientation may have changed with an image change
         self.__refreshLabels()
@@ -480,23 +473,17 @@ class OrthoPanel(canvaspanel.CanvasPanel):
             self.PostSizeEvent()
             return
 
-        opts = self._displayCtx.getOpts(overlay)
 
-        # The image is being displayed as it is stored on
-        # disk - the image.getOrientation method calculates
-        # and returns labels for each voxelwise axis.
-        if opts.transform in ('pixdim', 'id'):
-            xorient = overlay.getVoxelOrientation(0)
-            yorient = overlay.getVoxelOrientation(1)
-            zorient = overlay.getVoxelOrientation(2)
+        log.debug('Refreshing orientation labels '
+                  'according to {}'.format(overlay.name))
 
-        # The overlay is being displayed in 'real world' space -
-        # the definition of this space may be present in the
-        # overlay meta data
-        else:
-            xorient = overlay.getWorldOrientation(0)
-            yorient = overlay.getWorldOrientation(1)
-            zorient = overlay.getWorldOrientation(2)
+        # Figure out the orientation of the
+        # image in the display coordinate system
+        opts    = self._displayCtx.getOpts(overlay)
+        xform   = opts.getTransform('world', 'display')
+        xorient = overlay.getOrientation(0, xform)
+        yorient = overlay.getOrientation(1, xform)
+        zorient = overlay.getOrientation(2, xform)
 
         xlo = strings.anatomy['Image', 'lowshort',  xorient]
         ylo = strings.anatomy['Image', 'lowshort',  yorient]
@@ -505,6 +492,10 @@ class OrthoPanel(canvaspanel.CanvasPanel):
         yhi = strings.anatomy['Image', 'highshort', yorient]
         zhi = strings.anatomy['Image', 'highshort', zorient]
 
+        log.debug('X orientation: {} - {}'.format(xlo, xhi))
+        log.debug('Y orientation: {} - {}'.format(ylo, yhi))
+        log.debug('Z orientation: {} - {}'.format(zlo, zhi))
+
         bg = sceneOpts.bgColour
         fg = colourmaps.complementaryColour(bg)
         bg = [int(round(c * 255)) for c in bg]
@@ -514,16 +505,16 @@ class OrthoPanel(canvaspanel.CanvasPanel):
 
         self.__xLabels['left']  .SetLabel(ylo)
         self.__xLabels['right'] .SetLabel(yhi)
-        self.__xLabels['top']   .SetLabel(zlo)
-        self.__xLabels['bottom'].SetLabel(zhi)
+        self.__xLabels['bottom'].SetLabel(zlo)
+        self.__xLabels['top']   .SetLabel(zhi)
         self.__yLabels['left']  .SetLabel(xlo)
         self.__yLabels['right'] .SetLabel(xhi)
-        self.__yLabels['top']   .SetLabel(zlo)
-        self.__yLabels['bottom'].SetLabel(zhi)
+        self.__yLabels['bottom'].SetLabel(zlo)
+        self.__yLabels['top']   .SetLabel(zhi)
         self.__zLabels['left']  .SetLabel(xlo)
         self.__zLabels['right'] .SetLabel(xhi)
-        self.__zLabels['top']   .SetLabel(ylo)
-        self.__zLabels['bottom'].SetLabel(yhi)
+        self.__zLabels['bottom'].SetLabel(ylo)
+        self.__zLabels['top']   .SetLabel(yhi)
 
         self.PostSizeEvent()
 
diff --git a/fsl/tools/render.py b/fsl/tools/render.py
index e346114e1..f586153e2 100644
--- a/fsl/tools/render.py
+++ b/fsl/tools/render.py
@@ -77,22 +77,10 @@ def buildLabelBitmaps(overlayList,
 
         display = displayCtx.getDisplay(overlay)
         opts    = display.getDisplayOpts()
-
-        # The overlay is being displayed as it is stored on
-        # disk - the image.getOrientation method calculates
-        # and returns labels for each voxelwise axis.
-        if opts.transform in ('pixdim', 'id'):
-            xorient = overlay.getVoxelOrientation(0)
-            yorient = overlay.getVoxelOrientation(1)
-            zorient = overlay.getVoxelOrientation(2)
-
-        # The overlay is being displayed in 'real world' space -
-        # the definition of this space may be present in the
-        # overlay meta data
-        else:
-            xorient = overlay.getWorldOrientation(0)
-            yorient = overlay.getWorldOrientation(1)
-            zorient = overlay.getWorldOrientation(2)
+        xform   = opts.getTransform('world', 'display')
+        xorient = overlay.getOrientation(0, xform)
+        yorient = overlay.getOrientation(1, xform)
+        zorient = overlay.getOrientation(2, xform)
 
     if constants.ORIENT_UNKNOWN in [xorient, yorient, zorient]:
         fgColour = 'red'
-- 
GitLab