Use clip_array or clip_scalar instead of np.clip
Significant performance issues have been identified with np.clip and thus clip_array was created to speed up the operation. In addition clip_scalar was created to clip a scalar value between two other values this commit replaces many uses of np.clip from operating on scalars to using clip_scalar instead
This commit is contained in:
parent
85c726e49a
commit
c4a1cf11a1
@ -8,6 +8,7 @@ Demonstrates use of GLScatterPlotItem with rapidly-updating plots.
|
|||||||
import initExample
|
import initExample
|
||||||
|
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
|
from pyqtgraph import functions as fn
|
||||||
from pyqtgraph.Qt import QtCore, QtGui
|
from pyqtgraph.Qt import QtCore, QtGui
|
||||||
import pyqtgraph.opengl as gl
|
import pyqtgraph.opengl as gl
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@ -84,10 +85,10 @@ def update():
|
|||||||
global phase, sp2, d2
|
global phase, sp2, d2
|
||||||
s = -np.cos(d2*2+phase)
|
s = -np.cos(d2*2+phase)
|
||||||
color = np.empty((len(d2),4), dtype=np.float32)
|
color = np.empty((len(d2),4), dtype=np.float32)
|
||||||
color[:,3] = np.clip(s * 0.1, 0, 1)
|
color[:,3] = fn.clip_array(s * 0.1, 0., 1.)
|
||||||
color[:,0] = np.clip(s * 3.0, 0, 1)
|
color[:,0] = fn.clip_array(s * 3.0, 0., 1.)
|
||||||
color[:,1] = np.clip(s * 1.0, 0, 1)
|
color[:,1] = fn.clip_array(s * 1.0, 0., 1.)
|
||||||
color[:,2] = np.clip(s ** 3, 0, 1)
|
color[:,2] = fn.clip_array(s ** 3, 0., 1.)
|
||||||
sp2.setData(color=color)
|
sp2.setData(color=color)
|
||||||
phase -= 0.1
|
phase -= 0.1
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import numpy as np
|
|||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from pyqtgraph.Qt import QtCore, QtGui
|
from pyqtgraph.Qt import QtCore, QtGui
|
||||||
import pyqtgraph.opengl as gl
|
import pyqtgraph.opengl as gl
|
||||||
from pyqtgraph.functions import clip_array
|
from pyqtgraph import functions as fn
|
||||||
|
|
||||||
app = pg.mkQApp("GLVolumeItem Example")
|
app = pg.mkQApp("GLVolumeItem Example")
|
||||||
w = gl.GLViewWidget()
|
w = gl.GLViewWidget()
|
||||||
@ -40,8 +40,8 @@ def psi(i, j, k, offset=(50,50,100)):
|
|||||||
|
|
||||||
|
|
||||||
data = np.fromfunction(psi, (100,100,200))
|
data = np.fromfunction(psi, (100,100,200))
|
||||||
positive = np.log(clip_array(data, 0, data.max())**2)
|
positive = np.log(fn.clip_array(data, np.finfo(data.dtype).eps, data.max())**2)
|
||||||
negative = np.log(clip_array(-data, 0, -data.min())**2)
|
negative = np.log(fn.clip_array(-data, -np.finfo(data.dtype).eps, -data.min())**2)
|
||||||
|
|
||||||
d2 = np.empty(data.shape + (4,), dtype=np.ubyte)
|
d2 = np.empty(data.shape + (4,), dtype=np.ubyte)
|
||||||
d2[..., 0] = positive * (255./positive.max())
|
d2[..., 0] = positive * (255./positive.max())
|
||||||
|
@ -6,14 +6,9 @@ Distributed under MIT/X11 license. See license.txt for more information.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from .Qt import QtCore
|
from .Qt import QtCore
|
||||||
|
from . import functions as fn
|
||||||
from math import sin, acos, atan2, inf, pi, hypot
|
from math import sin, acos, atan2, inf, pi, hypot
|
||||||
|
|
||||||
def clip(x, mn, mx):
|
|
||||||
if x > mx:
|
|
||||||
return mx
|
|
||||||
if x < mn:
|
|
||||||
return mn
|
|
||||||
return x
|
|
||||||
|
|
||||||
class Point(QtCore.QPointF):
|
class Point(QtCore.QPointF):
|
||||||
"""Extension of QPointF which adds a few missing methods."""
|
"""Extension of QPointF which adds a few missing methods."""
|
||||||
@ -112,7 +107,7 @@ class Point(QtCore.QPointF):
|
|||||||
if n1 == 0. or n2 == 0.:
|
if n1 == 0. or n2 == 0.:
|
||||||
return None
|
return None
|
||||||
## Probably this should be done with arctan2 instead..
|
## Probably this should be done with arctan2 instead..
|
||||||
ang = acos(clip(self.dot(a) / (n1 * n2), -1.0, 1.0)) ### in radians
|
ang = acos(fn.clip_scalar(self.dot(a) / (n1 * n2), -1.0, 1.0)) ### in radians
|
||||||
c = self.cross(a)
|
c = self.cross(a)
|
||||||
if c > 0:
|
if c > 0:
|
||||||
ang *= -1.
|
ang *= -1.
|
||||||
|
@ -4,8 +4,9 @@ Vector.py - Extension of QVector3D which adds a few missing methods.
|
|||||||
Copyright 2010 Luke Campagnola
|
Copyright 2010 Luke Campagnola
|
||||||
Distributed under MIT/X11 license. See license.txt for more information.
|
Distributed under MIT/X11 license. See license.txt for more information.
|
||||||
"""
|
"""
|
||||||
|
from math import acos
|
||||||
from .Qt import QtGui, QtCore, QT_LIB
|
from .Qt import QtGui, QtCore, QT_LIB
|
||||||
|
from . import functions as fn
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
class Vector(QtGui.QVector3D):
|
class Vector(QtGui.QVector3D):
|
||||||
@ -88,7 +89,7 @@ class Vector(QtGui.QVector3D):
|
|||||||
if n1 == 0. or n2 == 0.:
|
if n1 == 0. or n2 == 0.:
|
||||||
return None
|
return None
|
||||||
## Probably this should be done with arctan2 instead..
|
## Probably this should be done with arctan2 instead..
|
||||||
ang = np.arccos(np.clip(QtGui.QVector3D.dotProduct(self, a) / (n1 * n2), -1.0, 1.0)) ### in radians
|
ang = acos(fn.clip_scalar(QtGui.QVector3D.dotProduct(self, a) / (n1 * n2), -1.0, 1.0)) ### in radians
|
||||||
# c = self.crossProduct(a)
|
# c = self.crossProduct(a)
|
||||||
# if c > 0:
|
# if c > 0:
|
||||||
# ang *= -1.
|
# ang *= -1.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from ..Qt import QtGui, QtCore, QtSvg, QT_LIB
|
from ..Qt import QtGui, QtCore, QtSvg, QT_LIB
|
||||||
|
from .. import functions as fn
|
||||||
from ..graphicsItems.ROI import ROI
|
from ..graphicsItems.ROI import ROI
|
||||||
from .. import SRTTransform, ItemGroup
|
from .. import SRTTransform, ItemGroup
|
||||||
import importlib
|
import importlib
|
||||||
@ -240,7 +241,7 @@ class CanvasItem(QtCore.QObject):
|
|||||||
self._graphicsItem.setOpacity(alpha)
|
self._graphicsItem.setOpacity(alpha)
|
||||||
|
|
||||||
def setAlpha(self, alpha):
|
def setAlpha(self, alpha):
|
||||||
self.alphaSlider.setValue(int(np.clip(alpha * 1023, 0, 1023)))
|
self.alphaSlider.setValue(int(fn.clip_scalar(alpha * 1023, 0, 1023)))
|
||||||
|
|
||||||
def alpha(self):
|
def alpha(self):
|
||||||
return self.alphaSlider.value() / 1023.
|
return self.alphaSlider.value() / 1023.
|
||||||
|
@ -63,7 +63,7 @@ class CurvePoint(GraphicsObject):
|
|||||||
pos = self.property('position')
|
pos = self.property('position')
|
||||||
if 'QVariant' in repr(pos): ## need to support 2 APIs :(
|
if 'QVariant' in repr(pos): ## need to support 2 APIs :(
|
||||||
pos = pos.toDouble()[0]
|
pos = pos.toDouble()[0]
|
||||||
index = (len(x)-1) * np.clip(pos, 0.0, 1.0)
|
index = (len(x)-1) * clip_scalar(pos, 0.0, 1.0)
|
||||||
|
|
||||||
if index != int(index): ## interpolate floating-point values
|
if index != int(index): ## interpolate floating-point values
|
||||||
i1 = int(index)
|
i1 = int(index)
|
||||||
|
@ -147,7 +147,7 @@ class GridItem(UIGraphicsItem):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
ppl = dim[ax] / nl[ax]
|
ppl = dim[ax] / nl[ax]
|
||||||
c = np.clip(5 * (ppl-3), 0., 50.).astype(int)
|
c = int(fn.clip_scalar(5 * (ppl-3), 0, 50))
|
||||||
|
|
||||||
linePen = self.opts['pen']
|
linePen = self.opts['pen']
|
||||||
lineColor = self.opts['pen'].color()
|
lineColor = self.opts['pen'].color()
|
||||||
|
@ -593,7 +593,7 @@ class InfLineLabel(TextItem):
|
|||||||
return
|
return
|
||||||
|
|
||||||
rel = self._posToRel(ev.pos())
|
rel = self._posToRel(ev.pos())
|
||||||
self.orthoPos = np.clip(self._startPosition + rel - self._cursorOffset, 0, 1)
|
self.orthoPos = fn.clip_scalar(self._startPosition + rel - self._cursorOffset, 0., 1.)
|
||||||
self.updatePosition()
|
self.updatePosition()
|
||||||
if ev.isFinish():
|
if ev.isFinish():
|
||||||
self._moving = False
|
self._moving = False
|
||||||
|
@ -372,7 +372,7 @@ class PlotItem(GraphicsWidget):
|
|||||||
if y is not None:
|
if y is not None:
|
||||||
self.ctrl.yGridCheck.setChecked(y)
|
self.ctrl.yGridCheck.setChecked(y)
|
||||||
if alpha is not None:
|
if alpha is not None:
|
||||||
v = np.clip(alpha, 0, 1)*self.ctrl.gridAlphaSlider.maximum()
|
v = fn.clip_scalar(alpha, 0., 1.)*self.ctrl.gridAlphaSlider.maximum()
|
||||||
self.ctrl.gridAlphaSlider.setValue(v)
|
self.ctrl.gridAlphaSlider.setValue(v)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
@ -1073,7 +1073,7 @@ class ScatterPlotItem(GraphicsObject):
|
|||||||
# Map points using painter's world transform so they are drawn with pixel-valued sizes
|
# Map points using painter's world transform so they are drawn with pixel-valued sizes
|
||||||
pts = np.vstack([self.data['x'], self.data['y']])
|
pts = np.vstack([self.data['x'], self.data['y']])
|
||||||
pts = fn.transformCoordinates(p.transform(), pts)
|
pts = fn.transformCoordinates(p.transform(), pts)
|
||||||
pts = np.clip(pts, -2 ** 30, 2 ** 30) # prevent Qt segmentation fault.
|
pts = fn.clip_array(pts, -2 ** 30, 2 ** 30) # prevent Qt segmentation fault.
|
||||||
p.resetTransform()
|
p.resetTransform()
|
||||||
|
|
||||||
if self.opts['useCache'] and self._exportOpts is False:
|
if self.opts['useCache'] and self._exportOpts is False:
|
||||||
|
@ -666,7 +666,7 @@ class ViewBox(GraphicsWidget):
|
|||||||
def suggestPadding(self, axis):
|
def suggestPadding(self, axis):
|
||||||
l = self.width() if axis==0 else self.height()
|
l = self.width() if axis==0 else self.height()
|
||||||
if l > 0:
|
if l > 0:
|
||||||
padding = np.clip(1./(l**0.5), 0.02, 0.1)
|
padding = fn.clip_scalar(1./(l**0.5), 0.02, 0.1)
|
||||||
else:
|
else:
|
||||||
padding = 0.02
|
padding = 0.02
|
||||||
return padding
|
return padding
|
||||||
|
@ -17,6 +17,7 @@ from math import log10
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from ..Qt import QtCore, QtGui, QT_LIB
|
from ..Qt import QtCore, QtGui, QT_LIB
|
||||||
|
from .. import functions as fn
|
||||||
import importlib
|
import importlib
|
||||||
ui_template = importlib.import_module(
|
ui_template = importlib.import_module(
|
||||||
f'.ImageViewTemplate_{QT_LIB.lower()}', package=__package__)
|
f'.ImageViewTemplate_{QT_LIB.lower()}', package=__package__)
|
||||||
@ -513,7 +514,7 @@ class ImageView(QtGui.QWidget):
|
|||||||
|
|
||||||
def setCurrentIndex(self, ind):
|
def setCurrentIndex(self, ind):
|
||||||
"""Set the currently displayed frame index."""
|
"""Set the currently displayed frame index."""
|
||||||
index = np.clip(ind, 0, self.getProcessedImage().shape[self.axes['t']]-1)
|
index = fn.clip_scalar(ind, 0, self.getProcessedImage().shape[self.axes['t']]-1)
|
||||||
self.ignorePlaying = True
|
self.ignorePlaying = True
|
||||||
# Implicitly call timeLineChanged
|
# Implicitly call timeLineChanged
|
||||||
self.timeLine.setValue(self.tVals[index])
|
self.timeLine.setValue(self.tVals[index])
|
||||||
|
@ -344,7 +344,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget):
|
|||||||
self.opts['rotation'] = q
|
self.opts['rotation'] = q
|
||||||
else: # default euler rotation method
|
else: # default euler rotation method
|
||||||
self.opts['azimuth'] += azim
|
self.opts['azimuth'] += azim
|
||||||
self.opts['elevation'] = np.clip(self.opts['elevation'] + elev, -90, 90)
|
self.opts['elevation'] = fn.clip_scalar(self.opts['elevation'] + elev, -90., 90.)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def pan(self, dx, dy, dz, relative='global'):
|
def pan(self, dx, dy, dz, relative='global'):
|
||||||
|
@ -3,7 +3,7 @@ from OpenGL.GL import *
|
|||||||
from OpenGL.arrays import vbo
|
from OpenGL.arrays import vbo
|
||||||
from .. GLGraphicsItem import GLGraphicsItem
|
from .. GLGraphicsItem import GLGraphicsItem
|
||||||
from .. import shaders
|
from .. import shaders
|
||||||
from ...functions import clip_array
|
from ... import functions as fn
|
||||||
from ...Qt import QtGui
|
from ...Qt import QtGui
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
@ -62,12 +62,12 @@ class GLScatterPlotItem(GLGraphicsItem):
|
|||||||
|
|
||||||
## Generate texture for rendering points
|
## Generate texture for rendering points
|
||||||
w = 64
|
w = 64
|
||||||
def fn(x,y):
|
def genTexture(x,y):
|
||||||
r = np.hypot((x-(w-1)/2.), (y-(w-1)/2.))
|
r = np.hypot((x-(w-1)/2.), (y-(w-1)/2.))
|
||||||
return 255 * (w/2. - clip_array(r, w/2.-1.0, w/2.))
|
return 255 * (w / 2 - fn.clip_array(r, w / 2 - 1, w / 2))
|
||||||
pData = np.empty((w, w, 4))
|
pData = np.empty((w, w, 4))
|
||||||
pData[:] = 255
|
pData[:] = 255
|
||||||
pData[:,:,3] = np.fromfunction(fn, pData.shape[:2])
|
pData[:,:,3] = np.fromfunction(genTexture, pData.shape[:2])
|
||||||
pData = pData.astype(np.ubyte)
|
pData = pData.astype(np.ubyte)
|
||||||
|
|
||||||
if getattr(self, "pointTexture", None) is None:
|
if getattr(self, "pointTexture", None) is None:
|
||||||
|
@ -2,6 +2,7 @@ from collections import OrderedDict
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import copy
|
import copy
|
||||||
from math import log2
|
from math import log2
|
||||||
|
from .. import functions as fn
|
||||||
|
|
||||||
|
|
||||||
class SystemSolver(object):
|
class SystemSolver(object):
|
||||||
@ -391,7 +392,7 @@ if __name__ == '__main__':
|
|||||||
sh = self.shutter # this raises RuntimeError if shutter has not
|
sh = self.shutter # this raises RuntimeError if shutter has not
|
||||||
# been specified
|
# been specified
|
||||||
ap = 4.0 * (sh / (1./60.)) * (iso / 100.) * (2 ** exp) * (2 ** light)
|
ap = 4.0 * (sh / (1./60.)) * (iso / 100.) * (2 ** exp) * (2 ** light)
|
||||||
ap = np.clip(ap, 2.0, 16.0)
|
ap = fn.clip_scalar(ap, 2.0, 16.0)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
# program mode; we can select a suitable shutter
|
# program mode; we can select a suitable shutter
|
||||||
# value at the same time.
|
# value at the same time.
|
||||||
|
@ -161,7 +161,7 @@ class ColorMapParameter(ptree.types.GroupParameter):
|
|||||||
elif op == 'Set':
|
elif op == 'Set':
|
||||||
colors[mask] = colors2[mask]
|
colors[mask] = colors2[mask]
|
||||||
|
|
||||||
colors = np.clip(colors, 0, 1)
|
colors = fn.clip_array(colors, 0., 1.)
|
||||||
if mode == 'byte':
|
if mode == 'byte':
|
||||||
colors = (colors * 255).astype(np.ubyte)
|
colors = (colors * 255).astype(np.ubyte)
|
||||||
|
|
||||||
@ -210,7 +210,7 @@ class RangeColorMapItem(ptree.types.SimpleParameter):
|
|||||||
def map(self, data):
|
def map(self, data):
|
||||||
data = data[self.fieldName]
|
data = data[self.fieldName]
|
||||||
|
|
||||||
scaled = np.clip((data-self['Min']) / (self['Max']-self['Min']), 0, 1)
|
scaled = fn.clip_array((data-self['Min']) / (self['Max']-self['Min']), 0, 1)
|
||||||
cmap = self.value()
|
cmap = self.value()
|
||||||
colors = cmap.map(scaled, mode='float')
|
colors = cmap.map(scaled, mode='float')
|
||||||
|
|
||||||
|
@ -378,7 +378,7 @@ class GraphicsView(QtGui.QGraphicsView):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if ev.buttons() == QtCore.Qt.RightButton:
|
if ev.buttons() == QtCore.Qt.RightButton:
|
||||||
delta = Point(np.clip(delta[0], -50, 50), np.clip(-delta[1], -50, 50))
|
delta = Point(fn.clip_scalar(delta[0], -50, 50), fn.clip_scalar(-delta[1], -50, 50))
|
||||||
scale = 1.01 ** delta
|
scale = 1.01 ** delta
|
||||||
self.scale(scale[0], scale[1], center=self.mapToScene(self.mousePressPos))
|
self.scale(scale[0], scale[1], center=self.mapToScene(self.mousePressPos))
|
||||||
self.sigDeviceRangeChanged.emit(self, self.range)
|
self.sigDeviceRangeChanged.emit(self, self.range)
|
||||||
|
Loading…
Reference in New Issue
Block a user