diff options
author | Alexandre Marie <alexandre.marie@synchrotron-soleil.fr> | 2020-07-21 14:45:14 +0200 |
---|---|---|
committer | Alexandre Marie <alexandre.marie@synchrotron-soleil.fr> | 2020-07-21 14:45:14 +0200 |
commit | 328032e2317e3ac4859196bbf12bdb71795302fe (patch) | |
tree | 8cd13462beab109e3cb53410c42335b6d1e00ee6 /silx/gui/plot/backends/glutils | |
parent | 33ed2a64c92b0311ae35456c016eb284e426afc2 (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.py | 15 | ||||
-rw-r--r-- | silx/gui/plot/backends/glutils/GLPlotImage.py | 122 | ||||
-rw-r--r-- | silx/gui/plot/backends/glutils/PlotImageFile.py | 6 |
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)) |