I have been trying to animate a series of surface plots that I created for a 2D heat flow problem using finite element method. At each time step, I saved a plot instead of the whole matrix, in order to be more efficient.
I had trouble with the FuncAnimation in the matplotlib.animation library, so I decided to render a surface plot at each time, save the surface plot as a .png file, and then read that image using pyplot.imread. From there, I want to store each image into a list so that I can use the ArtistAnimation ( example). However it is not making the animation, instead I get two separate blank plots and then my surface plot .pngs when I print imgplot to the screen.
Additionally, when I try to save the animation, I get the following error message:
AttributeError: 'module' object has no attribute 'save'.
Any help with reading in a set of .pngs from the current directory, saving them in a list, and then using ArtistAnimation to "animate" those .pngs would be greatly appreciated. I do not need anything fancy.
(Note - I have to make the code automated, so unfortunately I cannot use an outside source to animate my images like iMovie or ffmpeg.)
Below is my code:
from numpy import *
from pylab import *
import matplotlib.pyplot as plt
import matplotlib.image as mgimg
from matplotlib import animation
## Read in graphs
p = 0
myimages = []
for k in range(1, len(params.t)):
fname = "heatflow%03d.png" %p
# read in pictures
img = mgimg.imread(fname)
imgplot = plt.imshow(img)
myimages.append([imgplot])
p += 1
## Make animation
fig = plt.figure()
animation.ArtistAnimation(fig, myimages, interval=20, blit=True, repeat_delay=1000)
animation.save("animation.mp4", fps = 30)
plt.show()
Issue 1: Images are not displayed
You need to store your animation object in a variable:
my_anim = animation.ArtistAnimation(fig, myimages, interval=100)
This requirement is specific for animation and is not consistent with other plotting function in matplotlib, where you can usually use my_plot=plt.plot() or plt.plot() indifferently.
This question is further discussed here.
Issue 2: Save does not work
Without any animation instance, it will not be possible to save a figure either. This is because the save method
belongs to the ArtistAnimation class. What you did was calling save from the animation module, this is what raised the error.
Issue 3: Two windows
The last issue is that you get two figures popping up. The reason is that when you call plt.imshow(), it displays an image on the current figure, but since no figure has been created yet, pyplot implicitly creates one for you.
When python later interprets the fig = plt.figure() statement, it creates a new figure (another window) and labels it "Figure 2".
Moving this statement to the beginning of your code, solves that problem.
Here is the modified code:
import matplotlib.pyplot as plt
import matplotlib.image as mgimg
from matplotlib import animation
fig = plt.figure()
# initiate an empty list of "plotted" images
myimages = []
#loops through available png:s
for p in range(1, 4):
## Read in picture
fname = "heatflow%03d.png" %p
img = mgimg.imread(fname)
imgplot = plt.imshow(img)
# append AxesImage object to the list
myimages.append([imgplot])
## create an instance of animation
my_anim = animation.ArtistAnimation(fig, myimages, interval=1000, blit=True, repeat_delay=1000)
## NB: The 'save' method here belongs to the object you created above
#my_anim.save("animation.mp4")
## Showtime!
plt.show()
(To run the code above, just add 3 images into your working folder with name "heatflow001.png" through "heatflow003.png".)
Alternative approach using FuncAnimation
You were probably right when you first tried to use FuncAnimation, since gathering images in a list is costly in terms of memory. I tested the code below against the one above, by comparing memory usage on the
system monitor. It appears that the FuncAnimation approach is more efficient. I believe the difference will grow even bigger as you use more images.
Here is the second code:
from matplotlib import pyplot as plt
from matplotlib import animation
import matplotlib.image as mgimg
import numpy as np
#set up the figure
fig = plt.figure()
ax = plt.gca()
#initialization of animation, plot array of zeros
def init():
imobj.set_data(np.zeros((100, 100)))
return imobj,
def animate(i):
## Read in picture
fname = "heatflow%03d.png" % i
## here I use [-1::-1], to invert the array
# IOtherwise it plots up-side down
img = mgimg.imread(fname)[-1::-1]
imobj.set_data(img)
return imobj,
## create an AxesImage object
imobj = ax.imshow( np.zeros((100, 100)), origin='lower', alpha=1.0, zorder=1, aspect=1 )
anim = animation.FuncAnimation(fig, animate, init_func=init, repeat = True,
frames=range(1,4), interval=200, blit=True, repeat_delay=1000)
plt.show()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With