Skip to content
Snippets Groups Projects
Commit 3aa4accc authored by Paul McCarthy's avatar Paul McCarthy
Browse files

Documentation for texture, colourmaptexture, and lookuptabletexture modules.

parent 404c755f
No related branches found
No related tags found
No related merge requests found
...@@ -965,7 +965,7 @@ class LookupTable(props.HasProperties): ...@@ -965,7 +965,7 @@ class LookupTable(props.HasProperties):
"""Create a new label with the given value, or updates the """Create a new label with the given value, or updates the
colour/name/enabled states associated with the given value. colour/name/enabled states associated with the given value.
:arg value: The label value to add/update. :arg value: The label value to add/update. Must be an integer.
:arg name: Label name :arg name: Label name
:arg colour: Label colour :arg colour: Label colour
:arg enabled: Label enabled state :arg enabled: Label enabled state
......
...@@ -5,11 +5,28 @@ ...@@ -5,11 +5,28 @@
# Author: Paul McCarthy <pauldmccarthy@gmail.com> # Author: Paul McCarthy <pauldmccarthy@gmail.com>
# #
"""This package is a container for a collection of classes which use OpenGL """This package is a container for a collection of classes which use OpenGL
textures for various purposes. textures for various purposes.
The :mod:`.texture` sub-module contains the definition of the :class:`Texture` .. todo:: There is a lot of duplicate code in the various texture sub-classes.
class, the base class for all texture types. This will hopefully be rectified at some stage in the future -
shared code will be moved into the :class:`.Texture` class.
The following texture types are defined in this package:
.. autosummary::
:nosignatures:
Texture
Texture2D
ImageTexture
ColourMapTexture
LookupTableTexture
SelectionTexture
RenderTexture
GLObjectRenderTexture
RenderTextureStack
""" """
......
#!/usr/bin/env python #!/usr/bin/env python
# #
# colourmaptexture.py - # colourmaptexture.py - The ColourMapTexture class.
# #
# Author: Paul McCarthy <pauldmccarthy@gmail.com> # Author: Paul McCarthy <pauldmccarthy@gmail.com>
# #
"""This module provides the :class:`ColourMapTexture` class, a 1D
:class:`.Texture` which can be used to store a RGBA colour map.
"""
import logging import logging
import collections import collections
import numpy as np import numpy as np
import OpenGL.GL as gl import OpenGL.GL as gl
...@@ -19,9 +22,51 @@ log = logging.getLogger(__name__) ...@@ -19,9 +22,51 @@ log = logging.getLogger(__name__)
class ColourMapTexture(texture.Texture): class ColourMapTexture(texture.Texture):
"""The ``ColourMapTexture`` class is a :class:`.Texture` which stores
a RGB or RGBA colour map.
A ``ColourMapTexture`` maps a data range to to a colour map. The data
range may be specified by the :meth:`setDisplayRange` method, and the
colour map by the :meth:`setColourMap` method. Alternately, both can
be specified with the :meth:`set` method.
In OpenGL, textures are indexed with a number between 0.0 and 1.0. So
in order to map the data range to texture coordinates, an offset/scale
transformation must be applied to data values. The ``ColourMapTexture``
calculates this transformation, and makes it available via the
:meth:`getCoordinateTransform` method.
The colour map itself can be specified in a number of ways:
- A ``numpy`` array of size :math:`N\\times 3` or :math:`N\\times 4`,
containing RGB or RGBA colour values, with colour values in the range
``[0, 1]``.
- A function which accepts an array of values in the range ``[0, 1]``,
and returns an array of size :math:`N\\times 3` or :math:`N\\times 4`,
specifying the RGB/RGBA colours that correspond to the input values.
Some other methods are provided, for configuring the colour map:
.. autosummary::
:nosignatures:
setAlpha
setInvert
setResolution
setInterp
setBorder
"""
def __init__(self, name): def __init__(self, name):
"""Create a ``ColourMapTexture``.
:arg name: A unique name for this ``ColourMapTexture``.
"""
texture.Texture.__init__(self, name, 1) texture.Texture.__init__(self, name, 1)
...@@ -35,23 +80,80 @@ class ColourMapTexture(texture.Texture): ...@@ -35,23 +80,80 @@ class ColourMapTexture(texture.Texture):
self.__coordXform = None self.__coordXform = None
# CMAP can be either a function which transforms def setColourMap(self, cmap):
# values to RGBA, or a N*4 numpy array containing """Set the colour map stored by the ``ColourMapTexture``.
# RGBA values
def setColourMap( self, cmap): self.set(cmap=cmap) :arg cmap: The colour map, either a ``numpy`` array of size
def setResolution( self, res): self.set(resolution=res) :math:`N\\times 3` or :math:`N\\times 4`, specifying
def setAlpha( self, alpha): self.set(alpha=alpha) RGB/RGBA colours, or a function which accepts values
def setInvert( self, invert): self.set(invert=invert) in the range ``[0, 1]``, and generates corresponding
def setInterp( self, interp): self.set(interp=interp) RGB/RGBA colours.
def setDisplayRange(self, drange): self.set(displayRange=drange) """
def setBorder( self, border): self.set(border=border) self.set(cmap=cmap)
def setResolution(self, res):
"""Set the resolution (number of colours) of this ``ColourMapTexture``.
This setting is only applicable when the colour map is specified as a
function (see :meth:`setColourMap`).
"""
self.set(resolution=res)
def setAlpha(self, alpha):
"""Set the transparency of all colours in the colour map. This setting
is only applicable when the colour map is specified as RGB values.
"""
self.set(alpha=alpha)
def setInvert(self, invert):
"""Invert the values in the colour map. """
self.set(invert=invert)
def setInterp(self, interp):
"""Set the interpolation used by this ``ColourMapTexture`` - either
``GL_NEAREST`` or ``GL_LINEAR``.
"""
self.set(interp=interp)
def setDisplayRange(self, drange):
"""Set the data range which corresponds to the colours stored in this
``ColourMapTexture``. A matrix which transforms values from from this
data range into texture coordinates is available via the
:meth:`getCoordinateTransform` method.
"""
self.set(displayRange=drange)
def setBorder(self, border):
"""Set the texture border colour. If ``None``, the edge colours of the
colour map are used as the border.
"""
self.set(border=border)
def getCoordinateTransform(self): def getCoordinateTransform(self):
"""Returns a matrix which transforms values from from the colour map
data range (see :meth:`setDisplayRange`) into texture coordinates.
"""
return self.__coordXform return self.__coordXform
def set(self, **kwargs): def set(self, **kwargs):
"""Set any parameters on this ``ColourMapTexture``. Valid keyword
arguments are:
- ``cmap``
- ``invert``
- ``interp``
- ``alpha``
- ``resolution``
- ``displayRange``
- ``border``
"""
# None is a valid value for any attributes, # None is a valid value for any attributes,
# so we are using 'self' to test whether # so we are using 'self' to test whether
...@@ -76,6 +178,14 @@ class ColourMapTexture(texture.Texture): ...@@ -76,6 +178,14 @@ class ColourMapTexture(texture.Texture):
def __prepareTextureSettings(self): def __prepareTextureSettings(self):
"""Called by :meth:`__refresh`. Prepares all of the texture settings,
and returns a tuple containing:
- An array containing the colour map data
- The display range
- The interpolation setting
- The border colour
"""
alpha = self.__alpha alpha = self.__alpha
cmap = self.__cmap cmap = self.__cmap
...@@ -130,6 +240,9 @@ class ColourMapTexture(texture.Texture): ...@@ -130,6 +240,9 @@ class ColourMapTexture(texture.Texture):
def __refresh(self): def __refresh(self):
"""Called when any settings of this ``ColourMapTexture`` are changed.
Re-configures the texture.
"""
cmap, drange, interp, border = self.__prepareTextureSettings() cmap, drange, interp, border = self.__prepareTextureSettings()
......
#!/usr/bin/env python #!/usr/bin/env python
# #
# lookuptabletexture.py - # lookuptabletexture.py - The LookupTableTexture class.
# #
# Author: Paul McCarthy <pauldmccarthy@gmail.com> # 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 logging
...@@ -19,8 +23,29 @@ log = logging.getLogger(__name__) ...@@ -19,8 +23,29 @@ log = logging.getLogger(__name__)
class LookupTableTexture(texture.Texture): 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.
"""
def __init__(self, name): def __init__(self, name):
"""Create a ``LookupTableTexture``.
:arg name: A uniqe name for this ``LookupTableTexture``.
"""
texture.Texture.__init__(self, name, 1) texture.Texture.__init__(self, name, 1)
...@@ -31,6 +56,19 @@ class LookupTableTexture(texture.Texture): ...@@ -31,6 +56,19 @@ class LookupTableTexture(texture.Texture):
def set(self, **kwargs): def set(self, **kwargs):
"""Set any parameters on this ``ColourMapTexture``. 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) lut = kwargs.get('lut', self)
alpha = kwargs.get('alpha', self) alpha = kwargs.get('alpha', self)
...@@ -46,10 +84,15 @@ class LookupTableTexture(texture.Texture): ...@@ -46,10 +84,15 @@ class LookupTableTexture(texture.Texture):
def refresh(self): 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() self.__refresh()
def __refresh(self, *a): def __refresh(self, *a):
"""Configures the underlying OpenGL texture. """
lut = self.__lut lut = self.__lut
alpha = self.__alpha alpha = self.__alpha
......
#!/usr/bin/env python #!/usr/bin/env python
# #
# texture.py - # texture.py - The Texture and Texture2D classes.
# #
# Author: Paul McCarthy <pauldmccarthy@gmail.com> # Author: Paul McCarthy <pauldmccarthy@gmail.com>
# #
"""This module provides the :class:`Texture` and :class:`Texture2D classes,
which are the base classes for all other texture types.
"""
import logging import logging
...@@ -17,12 +20,75 @@ log = logging.getLogger(__name__) ...@@ -17,12 +20,75 @@ log = logging.getLogger(__name__)
class Texture(object): class Texture(object):
"""All subclasses must accept a ``name`` as the first parameter to their """The ``Texture`` class is the base class for all other texture types in
``__init__`` method, and must pass said ``name`` through to this *FSLeyes*. This class is not intended to be used directly - use one of the
``__init__`` method. sub-classes instead. This class provides a few convenience methods for
working with textures:
.. autosummary::
:nosignatures:
getTextureName
getTextureHandle
The :meth:`bindTexture` and :meth:`unbindTexture` methods allow you to
bind a texture object to a GL texture unit. For example, let's say we
have a texture object called ``tex``, and we want to use it::
import OpenGL.GL as gl
# Bind the texture before doing any configuration -
# we don't need to specify a texture unit here.
tex.bindTexture()
# Use nearest neighbour interpolation
gl.glTexParameteri(gl.GL_TEXTURE_2D
gl.GL_TEXTURE_MIN_FILTER,
gl.GL_NEAREST)
gl.glTexParameteri(gl.GL_TEXTURE_2D
gl.GL_TEXTURE_MAG_FILTER,
gl.GL_NEAREST)
tex.unbindTexture()
# ...
# When we want to use the texture in a
# scene render, we need to bind it to
# a texture unit.
tex.bindTexture(gl.GL_TEXTURE0)
# ...
# Do the render
# ...
tex.unbindTexture()
.. note:: Despite what is shown in the example above, you shouldn't need
to manually configure texture objects with calls to
``glTexParameter`` - most things can be performed through
methods of the ``Texture`` sub-classes, for example
:class:`.ImageTexture` and :class:`.Texture2D`.
See the :mod:`.resources` module for a method of sharing texture
resources.
""" """
def __init__(self, name, ndims): def __init__(self, name, ndims):
"""Create a ``Texture``.
:arg name: The name of this texture - should be unique.
:arg ndims: Number of dimensions - must be 1, 2 or 3.
.. note:: All subclasses must accept a ``name`` as the first parameter
to their ``__init__`` method, and must pass said ``name``
through to the :meth:`__init__` method.
"""
self.__texture = gl.glGenTextures(1) self.__texture = gl.glGenTextures(1)
self.__name = name self.__name = name
...@@ -41,15 +107,11 @@ class Texture(object): ...@@ -41,15 +107,11 @@ class Texture(object):
self.__name, self.__name,
self.__texture)) self.__texture))
def getTextureName(self):
return self.__name
def getTextureHandle(self):
return self.__texture
def destroy(self): def destroy(self):
"""Must be called when this ``Texture`` is no longer needed. Deletes
the texture handle.
"""
log.debug('Deleting {} ({}) for {}: {}'.format(type(self).__name__, log.debug('Deleting {} ({}) for {}: {}'.format(type(self).__name__,
id(self), id(self),
...@@ -60,7 +122,24 @@ class Texture(object): ...@@ -60,7 +122,24 @@ class Texture(object):
self.__texture = None self.__texture = None
def getTextureName(self):
"""Returns the name of this texture. This is not the GL texture name,
rather it is the unique name passed into :meth:`__init__`.
"""
return self.__name
def getTextureHandle(self):
"""Returns the GL texture handle for this texture. """
return self.__texture
def bindTexture(self, textureUnit=None): def bindTexture(self, textureUnit=None):
"""Activates and binds this texture.
:arg textureUnit: The texture unit to bind this texture to, e.g.
``GL_TEXTURE0``.
"""
if textureUnit is not None: if textureUnit is not None:
gl.glActiveTexture(textureUnit) gl.glActiveTexture(textureUnit)
...@@ -72,6 +151,7 @@ class Texture(object): ...@@ -72,6 +151,7 @@ class Texture(object):
def unbindTexture(self): def unbindTexture(self):
"""Unbinds this texture. """
if self.__textureUnit is not None: if self.__textureUnit is not None:
gl.glActiveTexture(self.__textureUnit) gl.glActiveTexture(self.__textureUnit)
...@@ -83,8 +163,26 @@ class Texture(object): ...@@ -83,8 +163,26 @@ class Texture(object):
class Texture2D(Texture): class Texture2D(Texture):
"""The ``Texture2D` class represents a two-dimensional RGBA texture. A
``Texture2D`` instance can be used in one of two ways:
- Setting the texture data via the :meth:`setData` method, and then
drawing it to a scene via :meth:`draw` or :meth:`drawOnBounds`.
- Setting the texture size via :meth:`setSize`, and then drawing to it
by some other means (see e.g. the :class:`.RenderTexture` class, a
sub-class of ``Texture2D``).
"""
def __init__(self, name, interp=gl.GL_NEAREST): def __init__(self, name, interp=gl.GL_NEAREST):
"""Create a ``Texture2D` instance.
:arg name: Unique name for this ``Texture2D``.
:arg interp: Initial interpolation - ``GL_NEAREST`` or ``GL_LINEAR``.
This can be changed later on via the
:meth:`setInterpolation` method.
"""
Texture.__init__(self, name, 2) Texture.__init__(self, name, 2)
self.__data = None self.__data = None
...@@ -96,13 +194,15 @@ class Texture2D(Texture): ...@@ -96,13 +194,15 @@ class Texture2D(Texture):
def setInterpolation(self, interp): def setInterpolation(self, interp):
"""Change the texture interpolation - valid values are ``GL_NEAREST``
or ``GL_LINEAR``.
"""
self.__interp = interp self.__interp = interp
self.refresh() self.refresh()
def setSize(self, width, height): def setSize(self, width, height):
""" """Sets the width/height for this texture.
Sets the width/height for this texture.
This method also clears the data for this texture, if it has been This method also clears the data for this texture, if it has been
previously set via the :meth:`setData` method. previously set via the :meth:`setData` method.
...@@ -129,15 +229,13 @@ class Texture2D(Texture): ...@@ -129,15 +229,13 @@ class Texture2D(Texture):
def getSize(self): def getSize(self):
""" """Return the current ``(width, height)`` of this ``Texture2D``. """
"""
return self.__width, self.__height return self.__width, self.__height
def setData(self, data): def setData(self, data):
""" """Sets the data for this texture - the width and height are determined
Sets the data for this texture - the width and height are determined from data shape, which is assumed to be 4*width*height.
from data shape (which is assumed to be 4*width*height).
""" """
self.__setSize(data.shape[1], data.shape[2]) self.__setSize(data.shape[1], data.shape[2])
...@@ -147,6 +245,9 @@ class Texture2D(Texture): ...@@ -147,6 +245,9 @@ class Texture2D(Texture):
def refresh(self): def refresh(self):
"""Configures this ``Texture2D``. This includes setting up
interpolation, and setting the texture size and data.
"""
if any((self.__width is None, if any((self.__width is None,
self.__height is None, self.__height is None,
...@@ -218,6 +319,15 @@ class Texture2D(Texture): ...@@ -218,6 +319,15 @@ class Texture2D(Texture):
def draw(self, vertices, xform=None): def draw(self, vertices, xform=None):
"""Draw the contents of this ``Texture2D`` to a region specified by
the given vertices.
:arg vertices: A ``numpy`` array of shape ``6 * 3`` specifying the
region, made up of two triangles, to which this
``Texture2D`` should be rendered.
:arg xform: A transformation to be applied to the vertices.
"""
if vertices.shape != (6, 3): if vertices.shape != (6, 3):
raise ValueError('Six vertices must be provided') raise ValueError('Six vertices must be provided')
...@@ -262,6 +372,22 @@ class Texture2D(Texture): ...@@ -262,6 +372,22 @@ class Texture2D(Texture):
def drawOnBounds(self, zpos, xmin, xmax, ymin, ymax, xax, yax, xform=None): def drawOnBounds(self, zpos, xmin, xmax, ymin, ymax, xax, yax, xform=None):
"""Draws the contents of this ``Texture2D`` to a rectangle. This is a
convenience method which creates a set of vertices, and passes them to
the :meth:`draw` method.
:arg zpos: Position along the Z axis, in the display coordinate
system.
:arg xmin: Minimum X axis coordinate.
:arg xmax: Maximum X axis coordinate.
:arg ymin: Minimum Y axis coordinate.
:arg ymax: Maximum Y axis coordinate.
:arg xax: Display space axis which maps to the horizontal screen
axis.
:arg yax: Display space axis which maps to the vertical screen
axis.
:arg xform: Transformation matrix to apply to the vertices.
"""
zax = 3 - xax - yax zax = 3 - xax - yax
vertices = np.zeros((6, 3), dtype=np.float32) vertices = np.zeros((6, 3), dtype=np.float32)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment