Animating pngs in matplotlib using ArtistAnimation

2019-06-21 04:02发布

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()

1条回答
贪生不怕死
2楼-- · 2019-06-21 04:27

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()
查看更多
登录 后发表回答