Source code for fsleyes.gl.textures.lookuptabletexture

#
# lookuptabletexture.py - The LookupTableTexture class.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`LookupTableTexture` class, a 1D
:class:`.Texture` which stores the colours of a :class:`.LookupTable`
as an OpenGL texture.
"""

import logging

import OpenGL.GL as gl
import numpy     as np

import fsleyes.colourmaps as fslcmaps
from . import                texture


log = logging.getLogger(__name__)


[docs]class LookupTableTexture(texture.Texture): """The ``LookupTableTexture`` class is a 1D :class:`.Texture` which stores the colours of a :class:`.LookupTable` as an OpenGL texture. A :class:`.LookupTable` stores a collection of label values (assumed to be unsigned 16 bit integers), and colours associated with each label. This mapping of ``{label : colour}`` is converted into a ``numpy`` array of size :math:`max(labels)\\times 3` containing the lookup table, where a label value can be used as an array index to retrieve the corresponding colour. All aspects of a ``LookupTableTexture`` can be configured via the :meth:`set` method. As OpenGL textures are indexed by coordinates in the range ``[0.0, 1.0]``, you will need to divide label values by :math:`max(labels)` to convert them into texture coordinates. .. note:: Currently, the maximum label value in a lookup table cannot be greater than the maximum size of an OpenGL texture - this limit differs between platforms. In the future, if this class needs to be modified to support larger lookup tables, it will need to be changed to use a 2D texture, and users will need to ravel/unravel texture indices between 1D and 2D coordinates. """
[docs] def __init__(self, name): """Create a ``LookupTableTexture``. :arg name: A uniqe name for this ``LookupTableTexture``. """ self.__lut = None self.__alpha = None self.__brightness = None self.__contrast = None texture.Texture.__init__(self, name, 1, 4)
[docs] def set(self, **kwargs): """Set any parameters on this ``LookupTableTexture``. Valid keyword arguments are: ============== ====================================================== ``lut`` The :class:`.LookupTable` instance. ``alpha`` Transparency, a value between 0.0 and 1.0. Defaults to 1.0 ``brightness`` Brightness, a value between 0.0 and 1.0. Defaults to 0.5. ``contrast`` Contrast, a value between 0.0 and 1.0. Defaults to 0.5. ============== ====================================================== """ lut = kwargs.get('lut', self) alpha = kwargs.get('alpha', self) brightness = kwargs.get('brightness', self) contrast = kwargs.get('contrast', self) if lut is not self: self.__lut = lut if alpha is not self: self.__alpha = alpha if brightness is not self: self.__brightness = brightness if contrast is not self: self.__contrast = contrast self.__refresh()
[docs] def refresh(self): """Forces a refresh of this ``LookupTableTexture``. This method should be called when the :class:`.LookupTable` has changed, so that the underlying texture is kept consistent with it. """ self.__refresh()
def __refresh(self, *a): """Configures the underlying OpenGL texture. """ lut = self.__lut alpha = self.__alpha brightness = self.__brightness contrast = self.__contrast if lut is None: return if brightness is None: brightness = 0.5 if contrast is None: contrast = 0.5 # Enough memory is allocated for the lut texture # so that shader programs can use label values # as indices into the texture. Not very memory # efficient, but greatly reduces complexity. nvals = lut.max() + 1 data = np.zeros((nvals, 4), dtype=np.uint8) for lbl in lut: value = lbl.value colour = fslcmaps.applyBricon(lbl.colour, brightness, contrast) data[value, :3] = [np.floor(c * 255) for c in colour[:3]] if not lbl.enabled: data[value, 3] = 0 elif alpha is not None: data[value, 3] = 255 * alpha else: data[value, 3] = 255 data = data.ravel('C') self.bindTexture() # Values out of range are clipped gl.glTexParameterfv(gl.GL_TEXTURE_1D, gl.GL_TEXTURE_BORDER_COLOR, np.array([0, 0, 0, 0], dtype=np.float32)) gl.glTexParameteri( gl.GL_TEXTURE_1D, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_BORDER) # Nearest neighbour interpolation gl.glTexParameteri(gl.GL_TEXTURE_1D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST) gl.glTexParameteri(gl.GL_TEXTURE_1D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST) gl.glTexImage1D(gl.GL_TEXTURE_1D, 0, gl.GL_RGBA8, nvals, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, data) self.unbindTexture()