summaryrefslogtreecommitdiff
path: root/silx/gui/plot/backends/glutils
diff options
context:
space:
mode:
authorAlexandre Marie <alexandre.marie@synchrotron-soleil.fr>2020-07-21 14:45:14 +0200
committerAlexandre Marie <alexandre.marie@synchrotron-soleil.fr>2020-07-21 14:45:14 +0200
commit328032e2317e3ac4859196bbf12bdb71795302fe (patch)
tree8cd13462beab109e3cb53410c42335b6d1e00ee6 /silx/gui/plot/backends/glutils
parent33ed2a64c92b0311ae35456c016eb284e426afc2 (diff)
New upstream version 0.13.0+dfsg
Diffstat (limited to 'silx/gui/plot/backends/glutils')
-rw-r--r--silx/gui/plot/backends/glutils/GLPlotCurve.py15
-rw-r--r--silx/gui/plot/backends/glutils/GLPlotImage.py122
-rw-r--r--silx/gui/plot/backends/glutils/PlotImageFile.py6
3 files changed, 89 insertions, 54 deletions
diff --git a/silx/gui/plot/backends/glutils/GLPlotCurve.py b/silx/gui/plot/backends/glutils/GLPlotCurve.py
index 3a0ebac..9ab85fd 100644
--- a/silx/gui/plot/backends/glutils/GLPlotCurve.py
+++ b/silx/gui/plot/backends/glutils/GLPlotCurve.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2014-2019 European Synchrotron Radiation Facility
+# Copyright (c) 2014-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
@@ -35,7 +35,6 @@ __date__ = "03/04/2017"
import math
import logging
-import warnings
import numpy
@@ -1129,11 +1128,9 @@ class GLPlotCurve2D(object):
_baseline = numpy.repeat(_baseline,
len(self.xData))
if isYLog is True:
- with warnings.catch_warnings(): # Ignore NaN comparison warnings
- warnings.simplefilter('ignore',
- category=RuntimeWarning)
+ with numpy.errstate(divide='ignore', invalid='ignore'):
log_val = numpy.log10(_baseline)
- _baseline = numpy.where(_baseline>0.0, log_val, -38)
+ _baseline = numpy.where(_baseline>0.0, log_val, -38)
return _baseline
_baseline = deduce_baseline(baseline)
@@ -1277,8 +1274,7 @@ class GLPlotCurve2D(object):
if self.lineStyle is not None:
# Using Cohen-Sutherland algorithm for line clipping
- with warnings.catch_warnings(): # Ignore NaN comparison warnings
- warnings.simplefilter('ignore', category=RuntimeWarning)
+ with numpy.errstate(invalid='ignore'): # Ignore NaN comparison warnings
codes = ((self.yData > yPickMax) << 3) | \
((self.yData < yPickMin) << 2) | \
((self.xData > xPickMax) << 1) | \
@@ -1335,8 +1331,7 @@ class GLPlotCurve2D(object):
indices.sort()
else:
- with warnings.catch_warnings(): # Ignore NaN comparison warnings
- warnings.simplefilter('ignore', category=RuntimeWarning)
+ with numpy.errstate(invalid='ignore'): # Ignore NaN comparison warnings
indices = numpy.nonzero((self.xData >= xPickMin) &
(self.xData <= xPickMax) &
(self.yData >= yPickMin) &
diff --git a/silx/gui/plot/backends/glutils/GLPlotImage.py b/silx/gui/plot/backends/glutils/GLPlotImage.py
index 5d79023..e985a3d 100644
--- a/silx/gui/plot/backends/glutils/GLPlotImage.py
+++ b/silx/gui/plot/backends/glutils/GLPlotImage.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2014-2019 European Synchrotron Radiation Facility
+# Copyright (c) 2014-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
@@ -56,7 +56,7 @@ class _GLPlotData2D(object):
sx, sy = self.scale
col = int((x - ox) / sx)
row = int((y - oy) / sy)
- return ((row, col),)
+ return (row,), (col,)
else:
return None
@@ -141,10 +141,8 @@ class GLPlotColormap(_GLPlotData2D):
""",
'fragTransform': """
uniform bvec2 isLog;
- uniform struct {
- vec2 oneOverRange;
- vec2 originOverRange;
- } bounds;
+ uniform vec2 bounds_oneOverRange;
+ uniform vec2 bounds_originOverRange;
vec2 textureCoords(void) {
vec2 pos = coords;
@@ -154,7 +152,7 @@ class GLPlotColormap(_GLPlotData2D):
if (isLog.y) {
pos.y = pow(10., coords.y);
}
- return pos * bounds.oneOverRange - bounds.originOverRange;
+ return pos * bounds_oneOverRange - bounds_originOverRange;
// TODO texture coords in range different from [0, 1]
}
"""},
@@ -163,12 +161,11 @@ class GLPlotColormap(_GLPlotData2D):
#version 120
uniform sampler2D data;
- 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;
uniform float alpha;
varying vec2 coords;
@@ -179,19 +176,33 @@ class GLPlotColormap(_GLPlotData2D):
void main(void) {
float value = texture2D(data, textureCoords()).r;
- if (cmap.isLog) {
+ if (cmap_normalization == 1) { /*Logarithm mapping*/
if (value > 0.) {
- value = clamp(cmap.oneOverRange *
- (oneOverLog10 * log(value) - cmap.min),
+ value = clamp(cmap_oneOverRange *
+ (oneOverLog10 * log(value) - cmap_min),
0., 1.);
} else {
value = 0.;
}
- } else { /*Linear mapping*/
- value = clamp(cmap.oneOverRange * (value - cmap.min), 0., 1.);
+ } else if (cmap_normalization == 2) { /*Square root mapping*/
+ if (value >= 0.) {
+ value = clamp(cmap_oneOverRange * (sqrt(value) - cmap_min),
+ 0., 1.);
+ } else {
+ value = 0.;
+ }
+ } else if (cmap_normalization == 3) { /*Gamma correction mapping*/
+ value = pow(
+ clamp(cmap_oneOverRange * (value - cmap_min), 0., 1.),
+ 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., 1.);
+ } else { /*Linear mapping and fallback*/
+ value = clamp(cmap_oneOverRange * (value - cmap_min), 0., 1.);
}
- gl_FragColor = texture2D(cmap.texture, vec2(value, 0.5));
+ gl_FragColor = texture2D(cmap_texture, vec2(value, 0.5));
gl_FragColor.a *= alpha;
}
"""
@@ -217,8 +228,10 @@ class GLPlotColormap(_GLPlotData2D):
_SHADERS['log']['fragTransform'],
attrib0='position')
+ SUPPORTED_NORMALIZATIONS = 'linear', 'log', 'sqrt', 'gamma', 'arcsinh'
+
def __init__(self, data, origin, scale,
- colormap, cmapIsLog=False, cmapRange=None,
+ colormap, normalization='linear', gamma=0., cmapRange=None,
alpha=1.0):
"""Create a 2D colormap
@@ -231,7 +244,9 @@ class GLPlotColormap(_GLPlotData2D):
:type scale: 2-tuple of floats.
:param str colormap: Name of the colormap to use
TODO: Accept a 1D scalar array as the colormap
- :param bool cmapIsLog: If True, uses log10 of the data value
+ :param str normalization: The colormap normalization.
+ One of: 'linear', 'log', 'sqrt', 'gamma'
+ ;param float gamma: The gamma parameter (for 'gamma' normalization)
:param cmapRange: The range of colormap or None for autoscale colormap
For logarithmic colormap, the range is in the untransformed data
TODO: check consistency with matplotlib
@@ -239,10 +254,12 @@ class GLPlotColormap(_GLPlotData2D):
:param float alpha: Opacity from 0 (transparent) to 1 (opaque)
"""
assert data.dtype in self._INTERNAL_FORMATS
+ assert normalization in self.SUPPORTED_NORMALIZATIONS
super(GLPlotColormap, self).__init__(data, origin, scale)
self.colormap = numpy.array(colormap, copy=False)
- self.cmapIsLog = cmapIsLog
+ self.normalization = normalization
+ self.gamma = gamma
self._cmapRange = (1., 10.) # Colormap range
self.cmapRange = cmapRange # Update _cmapRange
self._alpha = numpy.clip(alpha, 0., 1.)
@@ -263,8 +280,10 @@ class GLPlotColormap(_GLPlotData2D):
@property
def cmapRange(self):
- if self.cmapIsLog:
+ if self.normalization == 'log':
assert self._cmapRange[0] > 0. and self._cmapRange[1] > 0.
+ elif self.normalization == 'sqrt':
+ assert self._cmapRange[0] >= 0. and self._cmapRange[1] > 0.
return self._cmapRange
@cmapRange.setter
@@ -319,6 +338,7 @@ class GLPlotColormap(_GLPlotData2D):
def _setCMap(self, prog):
dataMin, dataMax = self.cmapRange # If log, it is stricly positive
+ param = 0.
if self.data.dtype in (numpy.uint16, numpy.uint8):
# Using unsigned int as normalized integer in OpenGL
@@ -326,19 +346,35 @@ class GLPlotColormap(_GLPlotData2D):
maxInt = float(numpy.iinfo(self.data.dtype).max)
dataMin, dataMax = dataMin / maxInt, dataMax / maxInt
- if self.cmapIsLog:
+ if self.normalization == 'log':
dataMin = math.log10(dataMin)
dataMax = math.log10(dataMax)
-
- gl.glUniform1i(prog.uniforms['cmap.texture'],
+ normID = 1
+ elif self.normalization == 'sqrt':
+ dataMin = math.sqrt(dataMin)
+ dataMax = math.sqrt(dataMax)
+ normID = 2
+ elif self.normalization == 'gamma':
+ # Keep dataMin, dataMax as is
+ param = self.gamma
+ normID = 3
+ elif self.normalization == 'arcsinh':
+ dataMin = numpy.arcsinh(dataMin)
+ dataMax = numpy.arcsinh(dataMax)
+ normID = 4
+ else: # Linear and fallback
+ normID = 0
+
+ gl.glUniform1i(prog.uniforms['cmap_texture'],
self._cmap_texture.texUnit)
- gl.glUniform1i(prog.uniforms['cmap.isLog'], self.cmapIsLog)
- gl.glUniform1f(prog.uniforms['cmap.min'], dataMin)
+ gl.glUniform1i(prog.uniforms['cmap_normalization'], normID)
+ gl.glUniform1f(prog.uniforms['cmap_parameter'], param)
+ gl.glUniform1f(prog.uniforms['cmap_min'], dataMin)
if dataMax > dataMin:
oneOverRange = 1. / (dataMax - dataMin)
else:
oneOverRange = 0. # Fall-back
- gl.glUniform1f(prog.uniforms['cmap.oneOverRange'], oneOverRange)
+ gl.glUniform1f(prog.uniforms['cmap_oneOverRange'], oneOverRange)
self._cmap_texture.bind()
@@ -393,9 +429,9 @@ class GLPlotColormap(_GLPlotData2D):
xOneOverRange = 1. / (ex - ox)
yOneOverRange = 1. / (ey - oy)
- gl.glUniform2f(prog.uniforms['bounds.originOverRange'],
+ gl.glUniform2f(prog.uniforms['bounds_originOverRange'],
ox * xOneOverRange, oy * yOneOverRange)
- gl.glUniform2f(prog.uniforms['bounds.oneOverRange'],
+ gl.glUniform2f(prog.uniforms['bounds_oneOverRange'],
xOneOverRange, yOneOverRange)
gl.glUniform1f(prog.uniforms['alpha'], self.alpha)
@@ -500,10 +536,8 @@ class GLPlotRGBAImage(_GLPlotData2D):
uniform sampler2D tex;
uniform bvec2 isLog;
- uniform struct {
- vec2 oneOverRange;
- vec2 originOverRange;
- } bounds;
+ uniform vec2 bounds_oneOverRange;
+ uniform vec2 bounds_originOverRange;
uniform float alpha;
varying vec2 coords;
@@ -516,7 +550,7 @@ class GLPlotRGBAImage(_GLPlotData2D):
if (isLog.y) {
pos.y = pow(10., coords.y);
}
- return pos * bounds.oneOverRange - bounds.originOverRange;
+ return pos * bounds_oneOverRange - bounds_originOverRange;
// TODO texture coords in range different from [0, 1]
}
@@ -530,7 +564,8 @@ class GLPlotRGBAImage(_GLPlotData2D):
_DATA_TEX_UNIT = 0
_SUPPORTED_DTYPES = (numpy.dtype(numpy.float32),
- numpy.dtype(numpy.uint8))
+ numpy.dtype(numpy.uint8),
+ numpy.dtype(numpy.uint16))
_linearProgram = Program(_SHADERS['linear']['vertex'],
_SHADERS['linear']['fragment'],
@@ -582,9 +617,14 @@ class GLPlotRGBAImage(_GLPlotData2D):
def prepare(self):
if self._texture is None:
- format_ = gl.GL_RGBA if self.data.shape[2] == 4 else gl.GL_RGB
+ formatName = 'GL_RGBA' if self.data.shape[2] == 4 else 'GL_RGB'
+ format_ = getattr(gl, formatName)
- self._texture = Image(format_,
+ if self.data.dtype == numpy.uint16:
+ formatName += '16' # Use sized internal format for uint16
+ internalFormat = getattr(gl, formatName)
+
+ self._texture = Image(internalFormat,
self.data,
format_=format_,
texUnit=self._DATA_TEX_UNIT)
@@ -639,9 +679,9 @@ class GLPlotRGBAImage(_GLPlotData2D):
xOneOverRange = 1. / (ex - ox)
yOneOverRange = 1. / (ey - oy)
- gl.glUniform2f(prog.uniforms['bounds.originOverRange'],
+ gl.glUniform2f(prog.uniforms['bounds_originOverRange'],
ox * xOneOverRange, oy * yOneOverRange)
- gl.glUniform2f(prog.uniforms['bounds.oneOverRange'],
+ gl.glUniform2f(prog.uniforms['bounds_oneOverRange'],
xOneOverRange, yOneOverRange)
try:
diff --git a/silx/gui/plot/backends/glutils/PlotImageFile.py b/silx/gui/plot/backends/glutils/PlotImageFile.py
index 83c7ae0..5fb6853 100644
--- a/silx/gui/plot/backends/glutils/PlotImageFile.py
+++ b/silx/gui/plot/backends/glutils/PlotImageFile.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2014-2017 European Synchrotron Radiation Facility
+# Copyright (c) 2014-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
@@ -59,7 +59,7 @@ def convertRGBDataToPNG(data):
0, 0, interlace)
# Add filter 'None' before each scanline
- preparedData = b'\x00' + b'\x00'.join(line.tostring() for line in data)
+ preparedData = b'\x00' + b'\x00'.join(line.tobytes() for line in data)
compressedData = zlib.compress(preparedData, 8)
IDATdata = struct.pack("cccc", b'I', b'D', b'A', b'T')
@@ -134,7 +134,7 @@ def saveImageToFile(data, fileNameOrObj, fileFormat):
fileObj.write(b'P6\n')
fileObj.write(b'%d %d\n' % (width, height))
fileObj.write(b'255\n')
- fileObj.write(data.tostring())
+ fileObj.write(data.tobytes())
elif fileFormat == 'png':
fileObj.write(convertRGBDataToPNG(data))