3

I'd like to display some images while doing a numpy computation:

import numpy as np
import matplotlib.pyplot as plt
plt.ion()  # Turn the interactive mode on.
for i in range(100):
    A = np.random.randn(10,10)
    plt.imshow(A)
    plt.pause(0.001)
    # do some other numpy computations here (they take < 1 ms)

Instead of displaying the images quickly, it is rather slow.

I'm not asking for 100 frames per second, but I thought 30 fps would be possible, but it's not: after a few iterations, I'm close to 2 fps on my standard i5 laptop (Windows 7 x64).

How to have a faster imshow refresh rate?

Notes:

  • I've already tried the main answer from Fast Live Plotting in Matplotlib / PyPlot, but here it seems a complex method (using blit parameter) for such a simple task and also I don't get 28 fps but only 15 fps.

  • I only want to display the matrix as image: no border, no axes, no subplot, etc., I imagine this could be done faster than the solution Fast Live Plotting in Matplotlib / PyPlot, maybe not with matplotlib but another library?

enter image description here

Basj
  • 29,668
  • 65
  • 241
  • 451
  • 1
    Possible duplicate of [Matplotlib, refresh image with imshow faster](https://stackoverflow.com/questions/45935179/matplotlib-refresh-image-with-imshow-faster) – G. Anderson Nov 15 '18 at 16:54
  • @G.Anderson See the edited question (see the notes). – Basj Nov 15 '18 at 16:56
  • Possible duplicate of [Faster plotting of real time audio signal](https://stackoverflow.com/questions/48697184/faster-plotting-of-real-time-audio-signal) – user2699 Nov 15 '18 at 18:38
  • I already tried similar things @user2699, but it did not give more than 20 fps. See the edited question and also the answer I posted. – Basj Nov 15 '18 at 22:35

2 Answers2

3

This is because you create a new image in each iteration, eventually leading to 100 images in your figure.

The recommended way to create an animation would be to use FuncAnimation and to only change the image's data, instead of plotting a new image all the time.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

im = plt.imshow(np.random.randn(10,10))

def update(i):
    A = np.random.randn(10,10)
    im.set_array(A)
    return im, text

ani = FuncAnimation(plt.gcf(), update, frames=range(100), interval=5, blit=False)

plt.show()

Even though the interval is set to 5 ms, the above code runs at 50 fps on my computer. It will not go faster than it can. You may now use blitting, i.e. blit=True, in which case I see 100 fps. This is the limit of what matplotlib can achieve, but it will of course vary depending on the computer power.

Note however, that the human brain is not capable of resolving 100 fps. One says, 25 is the usual framerate, such that most movies use such framerate as well. So there shouldn't even be the need to use blitting here, as 50 fps is larger than what you are able to perceive.

If for any reason you want to go faster with your animation, you need to use other libraries than matplotlib.

See e.g.


One sentence in the edited question says that there should be no border. This is accomplished by making the figure size obey the aspect of the image (square image -> square figure) and setting all margins to zero

plt.figure(figsize=(5,5))
plt.subplots_adjust(0,0,1,1)

A comment below the answer insists on using a for-loop. That would look like

im = plt.imshow(np.random.randn(10,10))

plt.ion()
for i in range(100):
    
    A = np.random.randn(10,10)
    im.set_array(A)
    plt.pause(0.005)

plt.ioff()
plt.show()

It's will be a bit slower than using FuncAnimation, because the animation happens outside the GUI eventloop. Also note that implementing blitting for such case is much more work as seen in Fast Live Plotting in Matplotlib / PyPlot

Jacob Jones
  • 123
  • 8
ImportanceOfBeingErnest
  • 251,038
  • 37
  • 461
  • 518
  • Thanks. But I think your code "precomputes" an animation and dislays it at the end. I would like to show the images while looping and not wait until the end, before showing the animation. How to do this ? – Basj Nov 15 '18 at 17:15
  • No, if you want to precompute the animation, you can use `ArtistAnimation` instead of `FuncAnimation`. – ImportanceOfBeingErnest Nov 15 '18 at 17:24
  • Oh right, I see. Something else: I would prefer to do a for loop "normally" (100k iterations in my real code) like in the question and once every 100 iterations show/plot the current state of A. Having to start everything from a FuncAnimation object is a bit unconvenient for my application. Is there a way to keep a simple for loop and call the plotting from there? – Basj Nov 15 '18 at 17:30
  • You *can* still use a for-loop if you want to. I updated the answer. There mostly isn't any reason to do this though. Why not compute the complete data first, then create an animation with every 100th step? – ImportanceOfBeingErnest Nov 15 '18 at 17:44
  • @ImportanceOfBeingErnest How to keep same colorbar shown in `FuncAnimation`? – Xin Zhang May 30 '19 at 13:30
0

I found a much faster solution thanks to OpenCV. The following code runs in 2 seconds on my computer, i.e. able to render at 500 fps (I know the human eye isn't able to see this, but good to know that this method is super fast).

import numpy as np
import cv2

cv2.namedWindow('img', cv2.WINDOW_NORMAL)

for i in range(1000):
    A = np.random.randn(10,10)
    cv2.imshow("img", A)
    cv2.waitKey(1)  # it's needed, but no problem, it won't pause/wait
Basj
  • 29,668
  • 65
  • 241
  • 451