summaryrefslogtreecommitdiff
path: root/silx/gui/plot3d/scene/function.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot3d/scene/function.py')
-rw-r--r--silx/gui/plot3d/scene/function.py93
1 files changed, 70 insertions, 23 deletions
diff --git a/silx/gui/plot3d/scene/function.py b/silx/gui/plot3d/scene/function.py
index 7651f75..69a24dd 100644
--- a/silx/gui/plot3d/scene/function.py
+++ b/silx/gui/plot3d/scene/function.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2015-2019 European Synchrotron Radiation Facility
+# Copyright (c) 2015-2020 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -384,31 +384,44 @@ class DirectionalLight(event.Notifier, ProgramFunction):
class Colormap(event.Notifier, ProgramFunction):
_declTemplate = string.Template("""
- uniform struct {
- sampler2D texture;
- bool isLog;
- float min;
- float oneOverRange;
- } cmap;
+ uniform sampler2D cmap_texture;
+ uniform int cmap_normalization;
+ uniform float cmap_parameter;
+ uniform float cmap_min;
+ uniform float cmap_oneOverRange;
const float oneOverLog10 = 0.43429448190325176;
vec4 colormap(float value) {
- if (cmap.isLog) { /* Log10 mapping */
+ if (cmap_normalization == 1) { /* Log10 mapping */
if (value > 0.0) {
- value = clamp(cmap.oneOverRange *
- (oneOverLog10 * log(value) - cmap.min),
+ value = clamp(cmap_oneOverRange *
+ (oneOverLog10 * log(value) - cmap_min),
0.0, 1.0);
} else {
value = 0.0;
}
+ } else if (cmap_normalization == 2) { /* Sqrt mapping */
+ if (value > 0.0) {
+ value = clamp(cmap_oneOverRange * (sqrt(value) - cmap_min),
+ 0.0, 1.0);
+ } else {
+ value = 0.0;
+ }
+ } else if (cmap_normalization == 3) { /*Gamma correction mapping*/
+ value = pow(
+ clamp(cmap_oneOverRange * (value - cmap_min), 0.0, 1.0),
+ cmap_parameter);
+ } else if (cmap_normalization == 4) { /* arcsinh mapping */
+ /* asinh = log(x + sqrt(x*x + 1) for compatibility with GLSL 1.20 */
+ value = clamp(cmap_oneOverRange * (log(value + sqrt(value*value + 1.0)) - cmap_min), 0.0, 1.0);
} else { /* Linear mapping */
- value = clamp(cmap.oneOverRange * (value - cmap.min), 0.0, 1.0);
+ value = clamp(cmap_oneOverRange * (value - cmap_min), 0.0, 1.0);
}
$discard
- vec4 color = texture2D(cmap.texture, vec2(value, 0.5));
+ vec4 color = texture2D(cmap_texture, vec2(value, 0.5));
return color;
}
""")
@@ -421,18 +434,19 @@ class Colormap(event.Notifier, ProgramFunction):
call = "colormap"
- NORMS = 'linear', 'log'
+ NORMS = 'linear', 'log', 'sqrt', 'gamma', 'arcsinh'
"""Tuple of supported normalizations."""
_COLORMAP_TEXTURE_UNIT = 1
"""Texture unit to use for storing the colormap"""
- def __init__(self, colormap=None, norm='linear', range_=(1., 10.)):
+ def __init__(self, colormap=None, norm='linear', gamma=0., range_=(1., 10.)):
"""Shader function to apply a colormap to a value.
:param colormap: RGB(A) color look-up table (default: gray)
:param colormap: numpy.ndarray of numpy.uint8 of dimension Nx3 or Nx4
- :param str norm: Normalization to apply: 'linear' (default) or 'log'.
+ :param str norm: Normalization to apply: see :attr:`NORMS`.
+ :param float gamma: Gamma normalization parameter
:param range_: Range of value to map to the colormap.
:type range_: 2-tuple of float (begin, end).
"""
@@ -441,6 +455,7 @@ class Colormap(event.Notifier, ProgramFunction):
# Init privates to default
self._colormap = None
self._norm = 'linear'
+ self._gamma = -1.
self._range = 1., 10.
self._displayValuesBelowMin = True
@@ -456,6 +471,7 @@ class Colormap(event.Notifier, ProgramFunction):
# Set to param values through properties to go through asserts
self.colormap = colormap
self.norm = norm
+ self.gamma = gamma
self.range_ = range_
@property
@@ -482,8 +498,8 @@ class Colormap(event.Notifier, ProgramFunction):
def norm(self):
"""Normalization to use for colormap mapping.
- Either 'linear' (the default) or 'log' for log10 mapping.
- With 'log' normalization, values <= 0. are set to 1. (i.e. log == 0)
+ One of 'linear' (the default), 'log' for log10 mapping or 'sqrt'.
+ Invalid values (e.g., negative values with 'log' or 'sqrt') are mapped to 0.
"""
return self._norm
@@ -492,11 +508,23 @@ class Colormap(event.Notifier, ProgramFunction):
if norm != self._norm:
assert norm in self.NORMS
self._norm = norm
- if norm == 'log':
+ if norm in ('log', 'sqrt'):
self.range_ = self.range_ # To test for positive range_
self.notify()
@property
+ def gamma(self):
+ """Gamma correction normalization parameter (float >= 0.)"""
+ return self._gamma
+
+ @gamma.setter
+ def gamma(self, gamma):
+ if gamma != self._gamma:
+ assert gamma >= 0.
+ self._gamma = gamma
+ self.notify()
+
+ @property
def range_(self):
"""Range of values to map to the colormap.
@@ -517,6 +545,10 @@ class Colormap(event.Notifier, ProgramFunction):
"Log normalization and negative range: updating range.")
minPos = numpy.finfo(numpy.float32).tiny
range_ = max(range_[0], minPos), max(range_[1], minPos)
+ elif self.norm == 'sqrt' and (range_[0] < 0. or range_[1] < 0.):
+ _logger.warning(
+ "Sqrt normalization and negative range: updating range.")
+ range_ = max(range_[0], 0.), max(range_[1], 0.)
if range_ != self._range:
self._range = range_
@@ -549,16 +581,31 @@ class Colormap(event.Notifier, ProgramFunction):
self._texture.bind()
- gl.glUniform1i(program.uniforms['cmap.texture'],
+ gl.glUniform1i(program.uniforms['cmap_texture'],
self._texture.texUnit)
- gl.glUniform1i(program.uniforms['cmap.isLog'], self._norm == 'log')
min_, max_ = self.range_
+ param = 0.
if self._norm == 'log':
min_, max_ = numpy.log10(min_), numpy.log10(max_)
-
- gl.glUniform1f(program.uniforms['cmap.min'], min_)
- gl.glUniform1f(program.uniforms['cmap.oneOverRange'],
+ normID = 1
+ elif self._norm == 'sqrt':
+ min_, max_ = numpy.sqrt(min_), numpy.sqrt(max_)
+ normID = 2
+ elif self._norm == 'gamma':
+ # Keep min_, max_ as is
+ param = self._gamma
+ normID = 3
+ elif self._norm == 'arcsinh':
+ min_, max_ = numpy.arcsinh(min_), numpy.arcsinh(max_)
+ normID = 4
+ else: # Linear
+ normID = 0
+
+ gl.glUniform1i(program.uniforms['cmap_normalization'], normID)
+ gl.glUniform1f(program.uniforms['cmap_parameter'], param)
+ gl.glUniform1f(program.uniforms['cmap_min'], min_)
+ gl.glUniform1f(program.uniforms['cmap_oneOverRange'],
(1. / (max_ - min_)) if max_ != min_ else 0.)
def prepareGL2(self, context):