From fafbf849303a1980b81b5ec22779ca5d651a8501 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauld.mccarthy@gmail.com> Date: Wed, 22 Oct 2014 15:08:21 +0100 Subject: [PATCH] Bugfixes to transform.py ('x' variable overwriting transformation matrix), LightBoxPanel (not passing displayCtx to LightBoxCanvas), fslview_parseargs (using imageList.bounds instead of displayCtx.bounds), ImageList (didn't remove bounds-related code), ImageDisplay (attempting to offset transformation matrices for affine, when only supposed to do it for id/pixdim) --- fsl/data/image.py | 62 +----------------------------- fsl/fslview/displaycontext.py | 9 +++-- fsl/fslview/views/lightboxpanel.py | 3 +- fsl/tools/fslview_parseargs.py | 6 +-- fsl/utils/transform.py | 16 ++++---- 5 files changed, 19 insertions(+), 77 deletions(-) diff --git a/fsl/data/image.py b/fsl/data/image.py index 42fa6944f..3e3e5595a 100644 --- a/fsl/data/image.py +++ b/fsl/data/image.py @@ -8,7 +8,6 @@ """Classes for representing 3D/4D images and collections of said images.""" import os -import sys import logging import tempfile import collections @@ -322,9 +321,7 @@ class ImageList(props.HasProperties): """Class representing a collection of images to be displayed together. Contains a :class:`props.properties_types.List` property containing - :class:`Image` objects, and some other properties on which listeners may - register themselves to be notified when the properties of the image - collection changes (e.g. image bounds). + :class:`Image` objects. An :class:`ImageList` object has a few wrapper methods around the :attr:`images` property, allowing the :class:`ImageList` to be used @@ -350,14 +347,6 @@ class ImageList(props.HasProperties): self.images = images - self.addListener( - 'images', - self.__class__.__name__, - self._imageListChanged) - - # initialise image bounds - self._imageListChanged() - # set the _lastDir attribute, # used by the addImages method if len(images) == 0: self._lastDir = os.getcwd() @@ -423,55 +412,6 @@ class ImageList(props.HasProperties): return True - def _imageListChanged(self, *a): - """Called whenever an item is added or removed from the :attr:`images` - list. Registers listeners with the properties of each image, and - calls the :meth:`_updateImageBounds` method. - """ - - for img in self.images: - - # This may be called multiple times on each image, - # but it doesn't matter, as any listener which has - # previously been registered with an image will - # just be replaced by the new one here. - img.addListener( - 'transform', - self.__class__.__name__, - self._updateImageBounds, - overwrite=True) - - self._updateImageBounds() - - - def _updateImageBounds(self, *a): - """Called whenever an item is added or removed from the - :attr:`images` list, or an image property changes. Updates - the :attr:`bounds` property. - """ - - if len(self.images) == 0: - minBounds = [0.0, 0.0, 0.0] - maxBounds = [0.0, 0.0, 0.0] - - else: - minBounds = 3 * [ sys.float_info.max] - maxBounds = 3 * [-sys.float_info.max] - - for img in self.images: - - for ax in range(3): - - lo, hi = img.imageBounds(ax) - - if lo < minBounds[ax]: minBounds[ax] = lo - if hi > maxBounds[ax]: maxBounds[ax] = hi - - self.bounds[:] = [minBounds[0], maxBounds[0], - minBounds[1], maxBounds[1], - minBounds[2], maxBounds[2]] - - # Wrappers around the images list property, allowing this # ImageList object to be used as if it is actually a list. def __len__( self): return self.images.__len__() diff --git a/fsl/fslview/displaycontext.py b/fsl/fslview/displaycontext.py index 118a9ddb4..401907b8f 100644 --- a/fsl/fslview/displaycontext.py +++ b/fsl/fslview/displaycontext.py @@ -325,9 +325,10 @@ class ImageDisplay(props.HasProperties): # for pixdim/identity transformations, we want the world # location (0, 0, 0) to map to voxel location (0, 0, 0) - for i in range(3): - self.voxToDisplayMat[3, i] = pixdim[i] * 0.5 - self.displayToVoxMat[3, i] = -0.5 + if self.transform in ('id', 'pixdim'): + for i in range(3): + self.voxToDisplayMat[3, i] = pixdim[i] * 0.5 + self.displayToVoxMat[3, i] = -0.5 # When transform is changed to 'affine', enable interpolation # and, when changed to 'pixdim' or 'id', disable interpolation @@ -483,7 +484,7 @@ class DisplayContext(props.HasProperties): minBounds = 3 * [ sys.float_info.max] maxBounds = 3 * [-sys.float_info.max] - for img in self.images: + for img in self._imageList.images: display = img.getAttribute('display') xform = display.voxToDisplayMat diff --git a/fsl/fslview/views/lightboxpanel.py b/fsl/fslview/views/lightboxpanel.py index bf01aafff..bcd3d2c7a 100644 --- a/fsl/fslview/views/lightboxpanel.py +++ b/fsl/fslview/views/lightboxpanel.py @@ -90,7 +90,8 @@ class LightBoxPanel(canvaspanel.CanvasPanel): self._scrollbar = wx.ScrollBar(self, style=wx.SB_VERTICAL) self._lbCanvas = lightboxcanvas.LightBoxCanvas(self.getCanvasPanel(), - imageList) + imageList, + displayCtx) # My properties are the canvas properties self.bindProps('sliceSpacing', self._lbCanvas) diff --git a/fsl/tools/fslview_parseargs.py b/fsl/tools/fslview_parseargs.py index c07bc0e7e..74fa76398 100644 --- a/fsl/tools/fslview_parseargs.py +++ b/fsl/tools/fslview_parseargs.py @@ -255,9 +255,9 @@ def handleImageArgs(args): loc = imageList[0].voxToWorld([args.voxelloc])[0] else: - loc = [imageList.bounds.xlo + 0.5 * imageList.bounds.xlen, - imageList.bounds.ylo + 0.5 * imageList.bounds.ylen, - imageList.bounds.zlo + 0.5 * imageList.bounds.zlen] + loc = [displayCtx.bounds.xlo + 0.5 * displayCtx.bounds.xlen, + displayCtx.bounds.ylo + 0.5 * displayCtx.bounds.ylen, + displayCtx.bounds.zlo + 0.5 * displayCtx.bounds.zlen] displayCtx.location.xyz = loc diff --git a/fsl/utils/transform.py b/fsl/utils/transform.py index 9cf3b688e..8c08894ba 100644 --- a/fsl/utils/transform.py +++ b/fsl/utils/transform.py @@ -19,7 +19,7 @@ def invert(x): return linalg.inv(x) -def axisBounds(shape, x, axis): +def axisBounds(shape, xform, axis): """Returns the (lo, hi) bounds of the specified axis.""" x, y, z = shape @@ -38,7 +38,7 @@ def axisBounds(shape, x, axis): points[6, :] = [x, y, -0.5] points[7, :] = [x, y, z] - tx = transform(points, x) + tx = transform(points, xform) lo = tx[:, axis].min() hi = tx[:, axis].max() @@ -46,7 +46,7 @@ def axisBounds(shape, x, axis): return (lo, hi) -def axisLength(shape, x, axis): +def axisLength(shape, xform, axis): """Return the length, in real world units, of the specified axis. """ @@ -54,13 +54,13 @@ def axisLength(shape, x, axis): points[:] = [-0.5, -0.5, -0.5] points[1, axis] = shape[axis] - 0.5 - tx = transform(points, x) + tx = transform(points, xform) # euclidean distance between each boundary point return sum((tx[0, :] - tx[1, :]) ** 2) ** 0.5 -def transform(p, x, axes=None): +def transform(p, xform, axes=None): """Transforms the given set of points ``p`` according to the given affine transformation ``x``. The transformed points are returned as a :class:``numpy.float64`` array. @@ -73,9 +73,9 @@ def transform(p, x, axes=None): y = p[:, 1] z = p[:, 2] - t[:, 0] = x * x[0, 0] + y * x[1, 0] + z * x[2, 0] + x[3, 0] - t[:, 1] = x * x[0, 1] + y * x[1, 1] + z * x[2, 1] + x[3, 1] - t[:, 2] = x * x[0, 2] + y * x[1, 2] + z * x[2, 2] + x[3, 2] + t[:, 0] = x * xform[0, 0] + y * xform[1, 0] + z * xform[2, 0] + xform[3, 0] + t[:, 1] = x * xform[0, 1] + y * xform[1, 1] + z * xform[2, 1] + xform[3, 1] + t[:, 2] = x * xform[0, 2] + y * xform[1, 2] + z * xform[2, 2] + xform[3, 2] if axes is None: axes = [0, 1, 2] -- GitLab