2012-03-02 02:55:32 +00:00
# -*- coding: utf-8 -*-
2012-04-04 16:22:43 +00:00
"""
Tests the speed of image updates for an ImageItem and RawImageWidget .
The speed will generally depend on the type of data being shown , whether
it is being scaled and / or converted by lookup table , and whether OpenGL
is used by the view widget
"""
2021-02-22 10:03:06 +00:00
## Add path to library (just for examples; you do not need this)
import initExample
2021-01-20 05:26:24 +00:00
import argparse
import sys
2012-04-04 16:22:43 +00:00
2012-03-02 02:55:32 +00:00
import numpy as np
2021-01-20 05:26:24 +00:00
2012-03-02 02:55:32 +00:00
import pyqtgraph as pg
import pyqtgraph . ptime as ptime
2021-01-20 05:26:24 +00:00
from pyqtgraph . Qt import QtGui , QtCore , QT_LIB
2012-09-09 23:07:36 +00:00
2021-01-14 03:15:34 +00:00
import importlib
ui_template = importlib . import_module ( f ' VideoTemplate_ { QT_LIB . lower ( ) } ' )
2021-01-20 05:26:24 +00:00
try :
import cupy as cp
pg . setConfigOption ( " useCupy " , True )
_has_cupy = True
except ImportError :
cp = None
_has_cupy = False
2021-02-22 08:13:53 +00:00
try :
from pyqtgraph . widgets . RawImageWidget import RawImageGLWidget
except ImportError :
RawImageGLWidget = None
2021-01-20 05:26:24 +00:00
parser = argparse . ArgumentParser ( description = " Benchmark for testing video performance " )
parser . add_argument ( ' --cuda ' , default = False , action = ' store_true ' , help = " Use CUDA to process on the GPU " , dest = " cuda " )
parser . add_argument ( ' --dtype ' , default = ' uint8 ' , choices = [ ' uint8 ' , ' uint16 ' , ' float ' ] , help = " Image dtype (uint8, uint16, or float) " )
parser . add_argument ( ' --frames ' , default = 3 , type = int , help = " Number of image frames to generate (default=3) " )
parser . add_argument ( ' --image-mode ' , default = ' mono ' , choices = [ ' mono ' , ' rgb ' ] , help = " Image data mode (mono or rgb) " , dest = ' image_mode ' )
parser . add_argument ( ' --levels ' , default = None , type = lambda s : tuple ( [ float ( x ) for x in s . split ( ' , ' ) ] ) , help = " min,max levels to scale monochromatic image dynamic range, or rmin,rmax,gmin,gmax,bmin,bmax to scale rgb " )
parser . add_argument ( ' --lut ' , default = False , action = ' store_true ' , help = " Use color lookup table " )
parser . add_argument ( ' --lut-alpha ' , default = False , action = ' store_true ' , help = " Use alpha color lookup table " , dest = ' lut_alpha ' )
parser . add_argument ( ' --size ' , default = ' 512x512 ' , type = lambda s : tuple ( [ int ( x ) for x in s . split ( ' x ' ) ] ) , help = " WxH image dimensions default= ' 512x512 ' " )
args = parser . parse_args ( sys . argv [ 1 : ] )
2012-03-02 02:55:32 +00:00
2021-02-22 08:13:53 +00:00
if RawImageGLWidget is not None :
# don't limit frame rate to vsync
sfmt = QtGui . QSurfaceFormat ( )
sfmt . setSwapInterval ( 0 )
QtGui . QSurfaceFormat . setDefaultFormat ( sfmt )
2021-01-27 18:59:07 +00:00
app = pg . mkQApp ( " Video Speed Test Example " )
2012-03-02 02:55:32 +00:00
win = QtGui . QMainWindow ( )
2013-02-25 04:09:03 +00:00
win . setWindowTitle ( ' pyqtgraph example: VideoSpeedTest ' )
2021-01-14 03:15:34 +00:00
ui = ui_template . Ui_MainWindow ( )
2012-03-02 02:55:32 +00:00
ui . setupUi ( win )
win . show ( )
2016-10-26 16:26:01 +00:00
2021-02-22 08:13:53 +00:00
if RawImageGLWidget is None :
2016-10-26 16:26:01 +00:00
ui . rawGLRadio . setEnabled ( False )
ui . rawGLRadio . setText ( ui . rawGLRadio . text ( ) + " (OpenGL not available) " )
else :
ui . rawGLImg = RawImageGLWidget ( )
ui . stack . addWidget ( ui . rawGLImg )
2021-01-20 05:26:24 +00:00
# read in CLI args
ui . cudaCheck . setChecked ( args . cuda and _has_cupy )
ui . cudaCheck . setEnabled ( _has_cupy )
ui . framesSpin . setValue ( args . frames )
ui . widthSpin . setValue ( args . size [ 0 ] )
ui . heightSpin . setValue ( args . size [ 1 ] )
ui . dtypeCombo . setCurrentText ( args . dtype )
ui . rgbCheck . setChecked ( args . image_mode == ' rgb ' )
2012-03-02 02:55:32 +00:00
ui . maxSpin1 . setOpts ( value = 255 , step = 1 )
ui . minSpin1 . setOpts ( value = 0 , step = 1 )
2021-01-20 05:26:24 +00:00
levelSpins = [ ui . minSpin1 , ui . maxSpin1 , ui . minSpin2 , ui . maxSpin2 , ui . minSpin3 , ui . maxSpin3 ]
if args . cuda and _has_cupy :
xp = cp
else :
xp = np
if args . levels is None :
ui . scaleCheck . setChecked ( False )
ui . rgbLevelsCheck . setChecked ( False )
else :
ui . scaleCheck . setChecked ( True )
if len ( args . levels ) == 2 :
ui . rgbLevelsCheck . setChecked ( False )
ui . minSpin1 . setValue ( args . levels [ 0 ] )
ui . maxSpin1 . setValue ( args . levels [ 1 ] )
elif len ( args . levels ) == 6 :
ui . rgbLevelsCheck . setChecked ( True )
for spin , val in zip ( levelSpins , args . levels ) :
spin . setValue ( val )
else :
raise ValueError ( " levels argument must be 2 or 6 comma-separated values (got %r ) " % ( args . levels , ) )
ui . lutCheck . setChecked ( args . lut )
ui . alphaCheck . setChecked ( args . lut_alpha )
2012-03-02 02:55:32 +00:00
2012-04-04 13:29:35 +00:00
#ui.graphicsView.useOpenGL() ## buggy, but you can try it if you need extra speed.
2012-03-02 02:55:32 +00:00
vb = pg . ViewBox ( )
ui . graphicsView . setCentralItem ( vb )
vb . setAspectLocked ( )
img = pg . ImageItem ( )
vb . addItem ( img )
2021-01-20 05:26:24 +00:00
2012-03-02 02:55:32 +00:00
LUT = None
def updateLUT ( ) :
global LUT , ui
dtype = ui . dtypeCombo . currentText ( )
if dtype == ' uint8 ' :
n = 256
else :
n = 4096
LUT = ui . gradient . getLookupTable ( n , alpha = ui . alphaCheck . isChecked ( ) )
2021-01-20 05:26:24 +00:00
if _has_cupy and xp == cp :
LUT = cp . asarray ( LUT )
2012-03-02 02:55:32 +00:00
ui . gradient . sigGradientChanged . connect ( updateLUT )
updateLUT ( )
ui . alphaCheck . toggled . connect ( updateLUT )
def updateScale ( ) :
2021-01-20 05:26:24 +00:00
global ui , levelSpins
2012-11-23 21:01:25 +00:00
if ui . rgbLevelsCheck . isChecked ( ) :
2021-01-20 05:26:24 +00:00
for s in levelSpins [ 2 : ] :
2012-03-02 02:55:32 +00:00
s . setEnabled ( True )
else :
2021-01-20 05:26:24 +00:00
for s in levelSpins [ 2 : ] :
2012-03-02 02:55:32 +00:00
s . setEnabled ( False )
2021-01-20 05:26:24 +00:00
updateScale ( )
2012-11-23 21:01:25 +00:00
ui . rgbLevelsCheck . toggled . connect ( updateScale )
2021-01-20 05:26:24 +00:00
2012-03-02 02:55:32 +00:00
cache = { }
def mkData ( ) :
2014-02-18 01:48:22 +00:00
with pg . BusyCursor ( ) :
2021-01-20 05:26:24 +00:00
global data , cache , ui , xp
2014-02-18 01:48:22 +00:00
frames = ui . framesSpin . value ( )
width = ui . widthSpin . value ( )
height = ui . heightSpin . value ( )
2021-01-20 05:26:24 +00:00
cacheKey = ( ui . dtypeCombo . currentText ( ) , ui . rgbCheck . isChecked ( ) , frames , width , height )
if cacheKey not in cache :
if cacheKey [ 0 ] == ' uint8 ' :
dt = xp . uint8
2014-02-18 01:48:22 +00:00
loc = 128
scale = 64
mx = 255
2021-01-20 05:26:24 +00:00
elif cacheKey [ 0 ] == ' uint16 ' :
dt = xp . uint16
2014-02-18 01:48:22 +00:00
loc = 4096
scale = 1024
mx = 2 * * 16
2021-01-20 05:26:24 +00:00
elif cacheKey [ 0 ] == ' float ' :
2021-02-12 22:03:00 +00:00
dt = xp . float32
2014-02-18 01:48:22 +00:00
loc = 1.0
scale = 0.1
2019-09-09 04:27:29 +00:00
mx = 1.0
2021-01-20 05:26:24 +00:00
else :
raise ValueError ( f " unable to handle dtype: { cacheKey [ 0 ] } " )
2014-02-18 01:48:22 +00:00
if ui . rgbCheck . isChecked ( ) :
2021-01-20 05:26:24 +00:00
data = xp . random . normal ( size = ( frames , width , height , 3 ) , loc = loc , scale = scale )
2014-03-11 23:01:34 +00:00
data = pg . gaussianFilter ( data , ( 0 , 6 , 6 , 0 ) )
2014-02-18 01:48:22 +00:00
else :
2021-01-20 05:26:24 +00:00
data = xp . random . normal ( size = ( frames , width , height ) , loc = loc , scale = scale )
2014-03-11 23:01:34 +00:00
data = pg . gaussianFilter ( data , ( 0 , 6 , 6 ) )
2021-01-20 05:26:24 +00:00
if cacheKey [ 0 ] != ' float ' :
data = xp . clip ( data , 0 , mx )
2014-02-18 01:48:22 +00:00
data = data . astype ( dt )
2016-04-28 05:33:51 +00:00
data [ : , 10 , 10 : 50 ] = mx
data [ : , 9 : 12 , 48 ] = mx
data [ : , 8 : 13 , 47 ] = mx
2021-01-20 05:26:24 +00:00
cache = { cacheKey : data } # clear to save memory (but keep one to prevent unnecessary regeneration)
data = cache [ cacheKey ]
2014-02-18 01:48:22 +00:00
updateLUT ( )
updateSize ( )
def updateSize ( ) :
2021-01-20 05:26:24 +00:00
global ui , vb
2014-02-18 01:48:22 +00:00
frames = ui . framesSpin . value ( )
width = ui . widthSpin . value ( )
height = ui . heightSpin . value ( )
2021-01-20 05:26:24 +00:00
dtype = xp . dtype ( str ( ui . dtypeCombo . currentText ( ) ) )
2014-02-18 01:48:22 +00:00
rgb = 3 if ui . rgbCheck . isChecked ( ) else 1
ui . sizeLabel . setText ( ' %d MB ' % ( frames * width * height * rgb * dtype . itemsize / 1e6 ) )
2021-01-20 05:26:24 +00:00
vb . setRange ( QtCore . QRectF ( 0 , 0 , width , height ) )
def noticeCudaCheck ( ) :
global xp , cache
cache = { }
if ui . cudaCheck . isChecked ( ) :
if _has_cupy :
xp = cp
else :
xp = np
ui . cudaCheck . setChecked ( False )
else :
xp = np
mkData ( )
2014-02-18 01:48:22 +00:00
2012-03-02 02:55:32 +00:00
mkData ( )
2014-02-18 01:48:22 +00:00
2012-03-02 02:55:32 +00:00
ui . dtypeCombo . currentIndexChanged . connect ( mkData )
2012-11-23 21:01:25 +00:00
ui . rgbCheck . toggled . connect ( mkData )
2014-02-18 01:48:22 +00:00
ui . widthSpin . editingFinished . connect ( mkData )
ui . heightSpin . editingFinished . connect ( mkData )
ui . framesSpin . editingFinished . connect ( mkData )
ui . widthSpin . valueChanged . connect ( updateSize )
ui . heightSpin . valueChanged . connect ( updateSize )
ui . framesSpin . valueChanged . connect ( updateSize )
2021-01-20 05:26:24 +00:00
ui . cudaCheck . toggled . connect ( noticeCudaCheck )
2014-02-18 01:48:22 +00:00
2012-03-02 02:55:32 +00:00
ptr = 0
lastTime = ptime . time ( )
fps = None
def update ( ) :
global ui , ptr , lastTime , fps , LUT , img
if ui . lutCheck . isChecked ( ) :
useLut = LUT
else :
useLut = None
2021-01-20 05:26:24 +00:00
2014-02-18 01:48:22 +00:00
downsample = ui . downsampleCheck . isChecked ( )
2012-03-02 02:55:32 +00:00
if ui . scaleCheck . isChecked ( ) :
2012-11-23 21:01:25 +00:00
if ui . rgbLevelsCheck . isChecked ( ) :
2012-03-02 02:55:32 +00:00
useScale = [
2021-01-20 05:26:24 +00:00
[ ui . minSpin1 . value ( ) , ui . maxSpin1 . value ( ) ] ,
[ ui . minSpin2 . value ( ) , ui . maxSpin2 . value ( ) ] ,
2012-03-02 02:55:32 +00:00
[ ui . minSpin3 . value ( ) , ui . maxSpin3 . value ( ) ] ]
else :
useScale = [ ui . minSpin1 . value ( ) , ui . maxSpin1 . value ( ) ]
else :
useScale = None
if ui . rawRadio . isChecked ( ) :
ui . rawImg . setImage ( data [ ptr % data . shape [ 0 ] ] , lut = useLut , levels = useScale )
2013-07-10 04:02:16 +00:00
ui . stack . setCurrentIndex ( 1 )
elif ui . rawGLRadio . isChecked ( ) :
ui . rawGLImg . setImage ( data [ ptr % data . shape [ 0 ] ] , lut = useLut , levels = useScale )
ui . stack . setCurrentIndex ( 2 )
2012-03-02 02:55:32 +00:00
else :
2014-02-18 01:48:22 +00:00
img . setImage ( data [ ptr % data . shape [ 0 ] ] , autoLevels = False , levels = useScale , lut = useLut , autoDownsample = downsample )
2013-07-10 04:02:16 +00:00
ui . stack . setCurrentIndex ( 0 )
2012-03-02 02:55:32 +00:00
#img.setImage(data[ptr%data.shape[0]], autoRange=False)
2021-01-20 05:26:24 +00:00
2012-03-02 02:55:32 +00:00
ptr + = 1
now = ptime . time ( )
dt = now - lastTime
lastTime = now
if fps is None :
fps = 1.0 / dt
else :
s = np . clip ( dt * 3. , 0 , 1 )
fps = fps * ( 1 - s ) + ( 1.0 / dt ) * s
ui . fpsLabel . setText ( ' %0.2f fps ' % fps )
app . processEvents ( ) ## force complete redraw for every plot
timer = QtCore . QTimer ( )
timer . timeout . connect ( update )
timer . start ( 0 )
2021-01-20 05:26:24 +00:00
2012-03-02 02:55:32 +00:00
2012-04-04 16:22:43 +00:00
## Start Qt event loop unless running in interactive mode or using pyside.
2012-12-05 05:25:45 +00:00
if __name__ == ' __main__ ' :
import sys
if ( sys . flags . interactive != 1 ) or not hasattr ( QtCore , ' PYQT_VERSION ' ) :
QtGui . QApplication . instance ( ) . exec_ ( )