diff --git a/fsl/fslview/gl/globject.py b/fsl/fslview/gl/globject.py index 5c0d1ced5b5b0c935eabadcba3a01460ca8bda60..88e10133759c808c69bdf4634cfd79c9a5a6ae29 100644 --- a/fsl/fslview/gl/globject.py +++ b/fsl/fslview/gl/globject.py @@ -14,6 +14,9 @@ OpenGL representation. """ +import numpy as np + + def createGLObject(image, display): """Create :class:`GLObject` instance for the given :class:`~fsl.data.image.Image` instance. @@ -69,7 +72,7 @@ class GLObject(object): self.__updateListeners[name] = listener - def removeUpdateListener(self, name, listener): + def removeUpdateListener(self, name): """Removes a listener previously registered via :meth:`addUpdateListener`. """ @@ -225,10 +228,20 @@ class GLImageObject(GLObject): def getDataResolution(self, xax, yax): + + image = self.image + display = self.display + res = display.resolution + + if display.transform in ('id', 'pixdim'): + + pixdim = np.array(image.pixdim[:3]) + steps = [res, res, res] / pixdim + res = image.shape[:3] / steps + + return np.array(res.round(), dtype=np.uint32) - if self.display.transform in ('id', 'pixdim'): - return self.image.shape[:3] else: - lo, hi = self.display.getDisplayBounds() - minres = int(round(((hi - lo) / self.display.resolution).min())) + lo, hi = display.getDisplayBounds() + minres = int(round(((hi - lo) / res).min())) return [minres] * 3 diff --git a/fsl/fslview/gl/glvolume.py b/fsl/fslview/gl/glvolume.py index 2efee28a5de2d73c4cb70d87bb104f7eaaffed1c..55800ec6818f96ed3f1a56ed1ad90368ffe654fc 100644 --- a/fsl/fslview/gl/glvolume.py +++ b/fsl/fslview/gl/glvolume.py @@ -258,6 +258,10 @@ class GLVolume(globject.GLImageObject): fslgl.glvolume_funcs.updateShaderState(self) self.onUpdate() + def update(*a): + self.onUpdate() + + display.addListener('resolution', lName, update) display.addListener('interpolation', lName, shaderUpdate) display.addListener('softwareMode', lName, shaderCompile) display.addListener('alpha', lName, colourUpdate) @@ -277,6 +281,7 @@ class GLVolume(globject.GLImageObject): lName = self.name + display.removeListener('resolution', lName) display.removeListener('interpolation', lName) display.removeListener('softwareMode', lName) display.removeListener('alpha', lName) diff --git a/fsl/fslview/gl/routines.py b/fsl/fslview/gl/routines.py index 86a3d01f483d41d1dcb2aa0c262d7dbcef72c724..6b1ff056212090eec1300706456304b1b87d4e27 100644 --- a/fsl/fslview/gl/routines.py +++ b/fsl/fslview/gl/routines.py @@ -461,8 +461,11 @@ def subsample(data, resolution, pixdim=None, volume=None): if pixdim is None: pixdim = (1.0, 1.0, 1.0) + if volume is None: + volume = slice(None, None, None) + xstep = np.round(resolution / pixdim[0]) - ystep = np.round(resolution / pixdim[1]) + ystep = np.round(resolution / pixdim[1]) zstep = np.round(resolution / pixdim[2]) if xstep < 1: xstep = 1 @@ -472,23 +475,14 @@ def subsample(data, resolution, pixdim=None, volume=None): xstart = np.floor(xstep / 2) ystart = np.floor(ystep / 2) zstart = np.floor(zstep / 2) - - if volume is not None: - if len(data.shape) > 3: sample = data[xstart::xstep, - ystart::ystep, - zstart::zstep, - volume] - else: sample = data[xstart::xstep, - ystart::ystep, - zstart::zstep] - else: - if len(data.shape) > 3: sample = data[xstart::xstep, - ystart::ystep, - zstart::zstep, - :] - else: sample = data[xstart::xstep, - ystart::ystep, - zstart::zstep] + + if len(data.shape) > 3: sample = data[xstart::xstep, + ystart::ystep, + zstart::zstep, + volume] + else: sample = data[xstart::xstep, + ystart::ystep, + zstart::zstep] return sample, (xstart, ystart, zstart), (xstep, ystep, zstep) diff --git a/fsl/fslview/gl/slicecanvas.py b/fsl/fslview/gl/slicecanvas.py index 82a838fdacf85e65b4ae3def3c98131afa1554b3..f645a4e0d1eb6c3a2ea8d39ddd53583f33c96f96 100644 --- a/fsl/fslview/gl/slicecanvas.py +++ b/fsl/fslview/gl/slicecanvas.py @@ -382,12 +382,10 @@ class SliceCanvas(props.HasProperties): # by a RenderTexture object. if self.renderMode == 'offscreen': - display = self.displayCtx.getDisplayProperties(image) - name = '{}_{}_{}'.format(image.name, self.xax, self.yax) - rt = textures.ImageRenderTexture( + name = '{}_{}_{}'.format(image.name, self.xax, self.yax) + rt = textures.GLObjectRenderTexture( name, - image, - display, + globj, self.xax, self.yax) @@ -968,7 +966,7 @@ class SliceCanvas(props.HasProperties): # those off-screen textures are all rendered on # to the screen canvas. if self.renderMode == 'offscreen': - textures.ImageRenderTexture.unbindAsRenderTarget() + textures.GLObjectRenderTexture.unbindAsRenderTarget() self._setViewport() self._drawOffscreenTextures() diff --git a/fsl/fslview/gl/textures/__init__.py b/fsl/fslview/gl/textures/__init__.py index 7aaae3c8cf736acb887216127378e7eff0217ddb..a10bec9ac935815a9a1cb983b6339d76dcaebcd5 100644 --- a/fsl/fslview/gl/textures/__init__.py +++ b/fsl/fslview/gl/textures/__init__.py @@ -21,5 +21,5 @@ from imagetexture import ImageTexture from colourmaptexture import ColourMapTexture from selectiontexture import SelectionTexture from rendertexture import RenderTexture -from rendertexture import ImageRenderTexture +from rendertexture import GLObjectRenderTexture from rendertexturestack import RenderTextureStack diff --git a/fsl/fslview/gl/textures/rendertexture.py b/fsl/fslview/gl/textures/rendertexture.py index 29d5aa72e0648ff5e41ba7f61174a4f4aa88a30e..fe216b85f59d95cc84bb22e622e165d316c26710 100644 --- a/fsl/fslview/gl/textures/rendertexture.py +++ b/fsl/fslview/gl/textures/rendertexture.py @@ -11,8 +11,6 @@ import OpenGL.GL as gl import OpenGL.raw.GL._types as gltypes import OpenGL.GL.EXT.framebuffer_object as glfbo -import numpy as np - import texture import fsl.fslview.gl.routines as glroutines @@ -101,41 +99,24 @@ class RenderTexture(texture.Texture2D): self.unbindAsRenderTarget() self.unbindTexture() - -class ImageRenderTexture(RenderTexture): - """A :class:`RenderTexture` for off-screen volumetric rendering of an - :class:`.Image` instance. - """ + +class GLObjectRenderTexture(RenderTexture): - def __init__(self, name, image, display, xax, yax, maxResolution=512): + def __init__(self, name, globj, xax, yax, maxResolution=1024): """ """ - self.__image = image - self.__display = display + self.__globj = globj self.__xax = xax self.__yax = yax self.__maxResolution = maxResolution RenderTexture.__init__(self, name) - self.__addListeners() - self.__updateSize() - - - def __addListeners(self): - - def onInterp(*a): - if self.__display.interpolation == 'none': interp = gl.GL_NEAREST - else: interp = gl.GL_LINEAR - self.setInterpolation(interp) - name = '{}_{}'.format(self.getTextureName(), id(self)) + globj.addUpdateListener(name, self.__updateSize) - self.__display.addListener('imageType', name, self.__updateSize) - self.__display.addListener('resolution', name, self.__updateSize) - self.__display.addListener('interpolation', name, onInterp) - self.__display.addListener('transform', name, self.__updateSize) + self.__updateSize() def setAxes(self, xax, yax): @@ -147,12 +128,8 @@ class ImageRenderTexture(RenderTexture): def destroy(self): name = '{}_{}'.format(self.getTextureName(), id(self)) - + self.__globj.removeUpdateListener(name) RenderTexture.destroy(self) - self.__display.removeListener('imageType', name) - self.__display.removeListener('resolution', name) - self.__display.removeListener('interpolation', name) - self.__display.removeListener('transform', name) def setSize(self, width, height): @@ -162,50 +139,15 @@ class ImageRenderTexture(RenderTexture): def __updateSize(self, *a): - image = self.__image - display = self.__display - maxRes = self.__maxResolution - - resolution = display.resolution / np.array(image.pixdim) - resolution = np.round(resolution) - keepAspectRatio = True - - if resolution[0] < 1: resolution[0] = 1 - if resolution[1] < 1: resolution[1] = 1 - if resolution[2] < 1: resolution[2] = 1 - - # For some image types, the display resolution - # does not affect performance, and needs to be - # higher than the image resolution - if display.imageType == 'linevector': - - keepAspectRatio = False - width = 16 * image.shape[self.__xax] / resolution[self.__xax] - height = 16 * image.shape[self.__yax] / resolution[self.__yax] - - # If the display transformation is 'id' or - # 'pixdim', then the display coordinate system - # axes line up with the voxel coordinate system - # axes, so we can just match the voxel resolution - elif display.transform in ('id', 'pixdim'): - - width = image.shape[self.__xax] / resolution[self.__xax] - height = image.shape[self.__yax] / resolution[self.__yax] - - # However, if we're displaying in world coordinates, - # we cannot assume any correspondence between the - # voxel coordinate system and the display coordinate - # system. So we'll use a fixed size render texture - # instead. - elif display.transform == 'affine': - width = maxRes / resolution.min() - height = maxRes / resolution.min() - - # Limit the width/height to an arbitrary maximum - if not keepAspectRatio: - if width > maxRes: width = maxRes - if height > maxRes: height = maxRes - elif width > maxRes or height > maxRes: + globj = self.__globj + maxRes = self.__maxResolution + + resolution = globj.getDataResolution(self.__xax, self.__yax) + + width = resolution[self.__xax] + height = resolution[self.__yax] + + if width > maxRes or height > maxRes: oldWidth, oldHeight = width, height ratio = min(width, height) / max(width, height) @@ -216,11 +158,15 @@ class ImageRenderTexture(RenderTexture): height = maxRes width = height * ratio - log.debug('Limiting texture resolution to {}x{} ' - '(for image resolution {}x{})'.format( - *map(int, (width, height, oldWidth, oldHeight)))) - - width = int(round(width)) - height = int(round(height)) + width = int(round(width)) + height = int(round(height)) - RenderTexture.setSize(self, width, height) + log.debug('Limiting texture resolution to {}x{} ' + '(for {} resolution {}x{})'.format( + width, + height, + type(globj).__name__, + oldWidth, + oldHeight)) + + RenderTexture.setSize(self, width, height) diff --git a/fsl/fslview/gl/textures/texture.py b/fsl/fslview/gl/textures/texture.py index 9117a32a3c6436642eab2ff98235e4f3e4c9d7eb..0fac58404796aba126c27375f311d5c2b263c9dd 100644 --- a/fsl/fslview/gl/textures/texture.py +++ b/fsl/fslview/gl/textures/texture.py @@ -169,6 +169,12 @@ class Texture2D(Texture): print data.shape, data.dtype data = data.ravel('F') + log.debug('Configuring {} ({}) with size {}x{}'.format( + type(self).__name__, + self.getTextureHandle(), + self.__width, + self.__height)) + # If the width and height have not changed, # then we don't need to re-define the texture. if self.__width == self.__oldWidth and \