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