Comparison of rendering speeds of several toolkits and wx

Hi there :wave:

I want to report the result that I compared the drawing speed of wx, qt, cv, and matplotlib using the following packages.

  • wx.version 4.1.1 msw (phoenix) wxWidgets 3.1.5
  • scipy/numpy version 1.6.0/1.20.1
  • matplotlib verison 3.4.2
  • Image verison 8.1.0
  • cv2 verison 4.5.1
  • pyqtgraph 0.12.2
  • PySide2 5.15.2

Result

Tested with VAIO CORE i3-2350M Windows10 64 bit OS (my poor PC)

Rank test script Speed Modules
1 test_qt 47.4 fps pyqtgraph/PySide2
2 test_cv2 43.5 fps opencv
3 test_wx_bmp 22.5 fps wx using StaticBitmap
4 test_wx_dc1 22.5 fps wx using DC single buffering
5 test_wx_dc2 22.2 fps wx using DC double buffering
6 test_wx_agg 16.1 fps matplotlib with wxagg backend
7 test_mpl 10.6 fps matplotlib with pyplot

test-drawing-speed.zip (5.2 KB)

NOTE

  • Pyqtgraph (1) is the fastest as far as I know.
  • OpenCV (2) is a little bit slower than Qt but fast enough.
  • Wx is twice as slow as OpenCV. Interestingly, using StaticBitmap (3), PaintDC (4), and BufferedPaintDC (5) makes no difference except for the effect of flicker.
  • wxWidgets doesn’t support grayscale images. So, we have to make an RGB image first and convert it to a grayscale bitmap. I think this is probably the cause of the overhead.
  • It is interesting that wxagg (6) is faster than the native viewer of matplotlib (7). In addition, when you make the window size smaller, it becomes even faster than wx bitmap (3,4,5) drawing!

That’s it!

I would appreciate it if you could tell me anything about other high-speed libraries, how to make wx code even faster, anything to improve code in my attached file, etc.

2 Likes

komoto48g,

Those results are pretty consistent with what I’ve seen. A couple of comments:

a) you’re rescaling and interpolating some of those images, and in slightly inconsistent ways. It won’t really change the results, but I might suggest using a slightly smaller size, say 800x600, consistently and not doing any rescaling.
b) wx.Timer.Start() needs an argument, and that argument needs to be greater than 0. I would say to make it “10”.

For matplotlib using the pyplot interface is going to be slower than updating the WXAgg backend, and a factor of 2 is about what I see too.

On MacOS (and also “not new” hardware), I see that the absolute rates are faster, but the results mostly consistent, except that opencv is not ~80% to 90% of PyQtGraph, more like 60%, and the fastest wx times are comparable to that.

But finally, and actually most importantly, what frame rate do you think you need? Are you rendering high-speed video for sports or games? For many decades, film - like real film on reels - was displayed at 24 fps (“Photography is Truth, and Film is Truth 24 times a second”, said Godard). Video was 30 fps, but interlaced, so really 15 fps. That is, one interpretation is that all of these approaches (except for you, pyplot) are “there”.

1 Like

Thank you very much for your advice, Matthew.

I modified the part of the code that you pointed out and tested it for the same (600,900) client size.
From (1) to (5), the speed does not depend on the client size, but (6), (7) matplot strongly does.

Rank test script Speed Modules
1 test_qt 74.4 fps pyqtgraph/PySide2
2 test_cv2 63.6 fps opencv
3 test_wx_bmp 36.1 fps wx using StaticBitmap
4 test_wx_dc1 35.8 fps wx using DC single buffering
5 test_wx_dc2 36.1 fps wx using DC double buffering
6 test_wx_agg 11.1 fps matplotlib with wxagg backend
7 test_mpl 7.3 fps matplotlib with pyplot

test-drawing-speed-ver2.zip (5.5 KB)

On MacOS (and also “not new” hardware), I see that the absolute rates are faster, but the results mostly consistent, except that opencv is not ~80% to 90% of PyQtGraph, more like 60%, and the fastest wx times are comparable to that.

Hmmm, is that due to the version? I tested with OpenCV4.

But finally, and actually most importantly, what frame rate do you think you need?

It is difficult. :exploding_head: 10 fps can be stressful and I think 20 fps is enough. 30 fps is good for in-situ observation. 50 fps is a bit meaningless as it exceeds the refresh rate of the device.

I work with electron microscopy images that are 2-byte grayscale and my application handles live-view of 512 x 512 at 20 fps (= 10 MB/s) in a remote. I plan to deal with bigger images in the future. The size of 2k x 2k image was normal, 4k x 4k CMOS image is becoming standard, and 8k x 8k sometimes.

For now, the faster the better. “The practical scientist is trying to solve tomorrow’s problem with yesterday’s computer”

“Photography is Truth, and Film is Truth 24 times a second”, said Godard

Impressing! Did Godard talk about Japanese anime? :wink:

Two points in addition.

  1. I modified it as follows,
-    wx.BufferedPaintDC(self.panel, self.bitmap)
+    dc = wx.BufferedPaintDC(self.panel, self.bitmap)

I attach the last modified files. I hope these would help people who want to start dealing with images.
test-drawing-speed-ver2+.zip (5.5 KB)

  1. Someone may want to use wx GUI and pyqtgraph function lIke this.
import wx
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui

class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        self.btn = wx.Button(self, label="Press me")
        self.btn.Bind(wx.EVT_BUTTON, self.run)
    
    def run(self, evt):
        self.qfrm = pg.GraphicsLayoutWidget(title=self.__module__)
        image = pg.ImageItem() # create image
        view = self.qfrm.addViewBox() # create view
        view.setAspectLocked(True)
        view.addItem(image)
        
        def update():
            h, w = 768, 1152
            src = (255 * np.random.rand(h, w)).astype(np.uint8)
            image.setImage(src.T)
        
        timer = QtCore.QTimer()
        timer.timeout.connect(update)
        timer.start()
        
        self.qfrm.show()
        pg.exec()

if __name__ == "__main__":
    qApp = QtGui.QApplication()
    app = wx.App()
    frm = Frame(None)
    frm.Show()
    app.MainLoop()

WX-QT Dream Tag!