Issue
I have a list of matplotlib.image.AxesImage objects that are the output of a fluid model. From what I can gather these are artists and so I should be able to iterate over them using FuncAnimation and make a movie.
However, so far this has not worked, where am I going wrong:
import matplotlib.animation as animation
# frames is a list of matplotlib.image.AxesImage objects
fig = plt.figure()
def animate(frame):
art = [frames[frame]]
return art
ani = animation.FuncAnimation(fig,animate,frames=10)
ani.save('test_anim.mp4', fps=1, extra_args=['-vcodec', 'libx264'])
So far I am suspicious of defining the figure separately to the AxesImage objects and that I may not be returning the actual thing that can be iterated, similar to how you have to return line, in the line drawing case.
Edit1:
From what I understand so far, FuncAnimation works by calling the animation function you define which updates a particular artist (such as a line or AxisImage object). So my question should really be how to initialise and then update an AxisImage object in my scenario?
Edit2:
The answer of furas was very helpful, it turns out the real problem was that imshow does not take objects but rather the data from them. So you need frames[0].get_array!
What worked for me in the end was:
import matplotlib.animation as animation
import matplotlib as mpl
# frames is a list of matplotlib.image.AxesImage objects <- objects not data
fig = plt.figure()
ax = plt.gca()
draw_image = ax.imshow(frames[0].get_array(),animated=True)
def animate(frame):
art = frames[frame].get_array()
draw_image.set_array(art)
return draw_image
ani = animation.FuncAnimation(fig,animate,frames=len(frames),blit=True,interval=25)
plt.show()
ani.save('SQGdemo1.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
Because it's pretty here is what I was trying to make (see pyqg project on Github for the code):
Solution
I use image Lenna from Wikipedia.
You mentioned matplotlib.image.AxesImage
so I load image with Pillow
and generate list of rotated images with matplotlib.image.pil_to_array()
image = Image.open('/home/furas/test/lenna.png')
frames = [
matplotlib.image.pil_to_array(image.rotate(-angle)) for angle in range(0, 360, 10)
]
I animate()
I remove prevous image from axes, get new image from list and I draw it in axes. That's all.
def animate(number):
# remove previous image
ax.clear()
# get new image from list
art = frames[number % len(frames)]
# display new image
ax.imshow(art)
#return (art,) # I don't need it if I don't use `FuncAnimation(..., blit=True)`
I don't have to use return
because I don't use FuncAnimation(..., blit=True)
which gets elements and draw it faster - but I have to draw it on my own in animate.
Full code:
import matplotlib
import matplotlib.image
import matplotlib.animation as animation
import matplotlib.pyplot as plt
from PIL import Image
# frames is a list of matplotlib.image.AxesImage
image = Image.open('lenna.png')
frames = [
matplotlib.image.pil_to_array(image.rotate(-angle)) for angle in range(0, 360, 10)
]
fig = plt.figure()
ax = plt.gca()
def animate(frame):
# remove previous image
ax.clear()
# get new image from list
art = frames[frame % len(frames)]
# display new image
ax.imshow(art)
#return art # I don't need it if I don't use `FuncAnimation(..., blit=True)`
ani = animation.FuncAnimation(fig, animate, frames=36)
plt.show()
#ani.save('animated_lenna.mp4', fps=5, extra_args=['-vcodec', 'libx264'])
ani.save('animated_lenna.gif', fps=5, writer='imagemagick')
I create random data for plot
plot_data_x = list(range(0, 501, 25))
plot_data_y = [randint(0, 500) for x in range(21)]
And in animate()
I clear plot, remove first item from list, add new item at the end, and draw it again.
# remove all from ax
ax.clear()
# remove first item from list
plot_data_y.pop(0)
# add last item on the list
plot_data_y.append(randint(0, 500))
# plot all again
ax.plot(plot_data_x, plot_data_y)
import matplotlib
import matplotlib.image
import matplotlib.animation as animation
import matplotlib.pyplot as plt
from PIL import Image
from random import randint
# frames is a list of matplotlib.image.AxesImage
image = Image.open('/home/furas/test/lenna.png')
frames = [
matplotlib.image.pil_to_array(image.rotate(-angle)) for angle in range(0, 360, 10)
]
plot_data_x = list(range(0, 501, 25))
plot_data_y = [randint(0, 500) for x in range(21)]
fig = plt.figure()
ax = plt.gca()
def animate(frame):
# remove all from ax
ax.clear()
# get new image from list
art = frames[frame % len(frames)]
# display new image
ax.imshow(art)
# remove first item from list
plot_data_y.pop(0)
# add last item on list
plot_data_y.append(randint(0, 500))
# plot all again
ax.plot(plot_data_x, plot_data_y)
#return art # I don't need it if I don't use `FuncAnimation(..., blit=True)`
ani = animation.FuncAnimation(fig, animate, frames=36)
plt.show()
ani.save('animated_lenna_plot.mp4', fps=5, extra_args=['-vcodec', 'libx264'])
#ani.save('animated_lenna_plot.gif', fps=5, writer='imagemagick')
EDIT:
Version which uses blit=True
Previous version can also write so fast animation but it can't display it so fast.
I create plot and image at start to assign them to varaibles and later update data in this object and send them back with return
. And later matplot draws it much fast with blit=True
import matplotlib
import matplotlib.image
import matplotlib.animation as animation
import matplotlib.pyplot as plt
from PIL import Image
from random import randint
# frames is a list of matplotlib.image.AxesImage
image = Image.open('/home/furas/test/lenna.png')
frames = [
matplotlib.image.pil_to_array(image.rotate(-angle)) for angle in range(0, 360, 10)
]
plot_data_x = list(range(0, 501, 25))
plot_data_y = [randint(0, 500) for x in range(21)]
fig = plt.figure()
ax = plt.gca()
draw_image = ax.imshow(frames[0], animated=True)
draw_plot, = plt.plot(plot_data_x, plot_data_y)
def animate(frame):
# remove all from ax
#ax.clear()
# get new image from list
art = frames[frame % len(frames)]
# display new image
draw_image.set_array(art)
# remove first item from list
plot_data_y.pop(0)
# add last item on list
plot_data_y.append(randint(0, 500))
# update data in plot
#draw_plot.set_data(plot_data_x, plot_data_y)
draw_plot.set_ydata(plot_data_y)
return (draw_image, draw_plot)
ani = animation.FuncAnimation(fig, animate, frames=36, interval=25, blit=True)
plt.show()
ani.save('animated_lenna_plot_blit.mp4', fps=50, extra_args=['-vcodec', 'libx264'])
#ani.save('animated_lenna_plot_blit.gif', fps=50, writer='imagemagick')
Answered By - furas
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.