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:
Ogi Moore 2021-04-19 22:12:05 -07:00
parent 85c726e49a
commit c4a1cf11a1
17 changed files with 34 additions and 34 deletions

View File

@ -8,6 +8,7 @@ Demonstrates use of GLScatterPlotItem with rapidly-updating plots.
import initExample
import pyqtgraph as pg
from pyqtgraph import functions as fn
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl
import numpy as np
@ -84,10 +85,10 @@ def update():
global phase, sp2, d2
s = -np.cos(d2*2+phase)
color = np.empty((len(d2),4), dtype=np.float32)
color[:,3] = np.clip(s * 0.1, 0, 1)
color[:,0] = np.clip(s * 3.0, 0, 1)
color[:,1] = np.clip(s * 1.0, 0, 1)
color[:,2] = np.clip(s ** 3, 0, 1)
color[:,3] = fn.clip_array(s * 0.1, 0., 1.)
color[:,0] = fn.clip_array(s * 3.0, 0., 1.)
color[:,1] = fn.clip_array(s * 1.0, 0., 1.)
color[:,2] = fn.clip_array(s ** 3, 0., 1.)
sp2.setData(color=color)
phase -= 0.1

View File

@ -11,7 +11,7 @@ import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl
from pyqtgraph.functions import clip_array
from pyqtgraph import functions as fn
app = pg.mkQApp("GLVolumeItem Example")
w = gl.GLViewWidget()
@ -40,8 +40,8 @@ def psi(i, j, k, offset=(50,50,100)):
data = np.fromfunction(psi, (100,100,200))
positive = np.log(clip_array(data, 0, data.max())**2)
negative = np.log(clip_array(-data, 0, -data.min())**2)
positive = np.log(fn.clip_array(data, np.finfo(data.dtype).eps, data.max())**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[..., 0] = positive * (255./positive.max())

View File

@ -6,14 +6,9 @@ Distributed under MIT/X11 license. See license.txt for more information.
"""
from .Qt import QtCore
from . import functions as fn
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):
"""Extension of QPointF which adds a few missing methods."""
@ -112,7 +107,7 @@ class Point(QtCore.QPointF):
if n1 == 0. or n2 == 0.:
return None
## 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)
if c > 0:
ang *= -1.

View File

@ -4,8 +4,9 @@ Vector.py - Extension of QVector3D which adds a few missing methods.
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more information.
"""
from math import acos
from .Qt import QtGui, QtCore, QT_LIB
from . import functions as fn
import numpy as np
class Vector(QtGui.QVector3D):
@ -88,7 +89,7 @@ class Vector(QtGui.QVector3D):
if n1 == 0. or n2 == 0.:
return None
## 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)
# if c > 0:
# ang *= -1.

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import numpy as np
from ..Qt import QtGui, QtCore, QtSvg, QT_LIB
from .. import functions as fn
from ..graphicsItems.ROI import ROI
from .. import SRTTransform, ItemGroup
import importlib
@ -240,7 +241,7 @@ class CanvasItem(QtCore.QObject):
self._graphicsItem.setOpacity(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):
return self.alphaSlider.value() / 1023.

View File

@ -63,7 +63,7 @@ class CurvePoint(GraphicsObject):
pos = self.property('position')
if 'QVariant' in repr(pos): ## need to support 2 APIs :(
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
i1 = int(index)

View File

@ -147,7 +147,7 @@ class GridItem(UIGraphicsItem):
continue
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']
lineColor = self.opts['pen'].color()

View File

@ -593,7 +593,7 @@ class InfLineLabel(TextItem):
return
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()
if ev.isFinish():
self._moving = False

View File

@ -372,7 +372,7 @@ class PlotItem(GraphicsWidget):
if y is not None:
self.ctrl.yGridCheck.setChecked(y)
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)
def close(self):

View File

@ -1073,7 +1073,7 @@ class ScatterPlotItem(GraphicsObject):
# 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 = 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()
if self.opts['useCache'] and self._exportOpts is False:

View File

@ -666,7 +666,7 @@ class ViewBox(GraphicsWidget):
def suggestPadding(self, axis):
l = self.width() if axis==0 else self.height()
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:
padding = 0.02
return padding

View File

@ -17,10 +17,11 @@ from math import log10
import numpy as np
from ..Qt import QtCore, QtGui, QT_LIB
from .. import functions as fn
import importlib
ui_template = importlib.import_module(
f'.ImageViewTemplate_{QT_LIB.lower()}', package=__package__)
from ..graphicsItems.ImageItem import *
from ..graphicsItems.ROI import *
from ..graphicsItems.LinearRegionItem import *
@ -513,7 +514,7 @@ class ImageView(QtGui.QWidget):
def setCurrentIndex(self, ind):
"""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
# Implicitly call timeLineChanged
self.timeLine.setValue(self.tVals[index])

View File

@ -344,7 +344,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget):
self.opts['rotation'] = q
else: # default euler rotation method
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()
def pan(self, dx, dy, dz, relative='global'):

View File

@ -3,7 +3,7 @@ from OpenGL.GL import *
from OpenGL.arrays import vbo
from .. GLGraphicsItem import GLGraphicsItem
from .. import shaders
from ...functions import clip_array
from ... import functions as fn
from ...Qt import QtGui
import numpy as np
@ -62,12 +62,12 @@ class GLScatterPlotItem(GLGraphicsItem):
## Generate texture for rendering points
w = 64
def fn(x,y):
def genTexture(x,y):
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[:] = 255
pData[:,:,3] = np.fromfunction(fn, pData.shape[:2])
pData[:,:,3] = np.fromfunction(genTexture, pData.shape[:2])
pData = pData.astype(np.ubyte)
if getattr(self, "pointTexture", None) is None:

View File

@ -2,6 +2,7 @@ from collections import OrderedDict
import numpy as np
import copy
from math import log2
from .. import functions as fn
class SystemSolver(object):
@ -391,7 +392,7 @@ if __name__ == '__main__':
sh = self.shutter # this raises RuntimeError if shutter has not
# been specified
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:
# program mode; we can select a suitable shutter
# value at the same time.

View File

@ -161,7 +161,7 @@ class ColorMapParameter(ptree.types.GroupParameter):
elif op == 'Set':
colors[mask] = colors2[mask]
colors = np.clip(colors, 0, 1)
colors = fn.clip_array(colors, 0., 1.)
if mode == 'byte':
colors = (colors * 255).astype(np.ubyte)
@ -210,7 +210,7 @@ class RangeColorMapItem(ptree.types.SimpleParameter):
def map(self, data):
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()
colors = cmap.map(scaled, mode='float')

View File

@ -378,7 +378,7 @@ class GraphicsView(QtGui.QGraphicsView):
return
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
self.scale(scale[0], scale[1], center=self.mapToScene(self.mousePressPos))
self.sigDeviceRangeChanged.emit(self, self.range)