I have a python animation script (using matplotlib's funcAnimation), which runs in Spyder but not in Jupyter. I have tried following various suggestions such as adding "%matplotlib inline" and changing the matplotlib backend to "Qt4agg", all without success. I have also tried running several example animations (from Jupyter tutorials), none of which have worked. Sometimes I get an error message and sometimes the plot appears, but does not animate. Incidentally, I have gotten pyplot.plot() to work using "%matplotlib inline".
Does anyone know of a working Jupyter notebook with a simple inline animation example that uses funcAnimation.
[Note: I am on Windows 7]
notebook backend
'Inline' means that the plots are shown as png graphics. Those png images cannot be animated. While in principle one could build an animation by successively replacing the png images, this is probably undesired.
A solution is to use the notebook backend, which is fully compatible with FuncAnimation as it renders the matplotlib figure itself:
%matplotlib notebook
jsanimation
From matplotlib 2.1 on, we can create an animation using JavaScript. This is similar to the ani.to_html5() solution, except that it does not require any video codecs.
from IPython.display import HTML
HTML(ani.to_jshtml())
Some complete example:
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
t = np.linspace(0,2*np.pi)
x = np.sin(t)
fig, ax = plt.subplots()
ax.axis([0,2*np.pi,-1,1])
l, = ax.plot([],[])
def animate(i):
l.set_data(t[:i], x[:i])
ani = matplotlib.animation.FuncAnimation(fig, animate, frames=len(t))
from IPython.display import HTML
HTML(ani.to_jshtml())
Alternatively, make the jsanimation the default for showing animations,
plt.rcParams["animation.html"] = "jshtml"
Then at the end simply state ani to obtain the animation.
Also see this answer for a complete overview.
There is a simple example within this tutorial here: http://louistiao.me/posts/notebooks/embedding-matplotlib-animations-in-jupyter-notebooks/
To summarise the tutorial above, you basically need something like this:
from matplotlib import animation
from IPython.display import HTML
# <insert animation setup code here>
anim = animation.FuncAnimation() # With arguments of course!
HTML(anim.to_html5_video())
However...
I had a lot of trouble getting that to work. Essentially, the problem was that the above uses (by default) ffmpeg and the x264 codec in the background but these were not configured correctly on my machine. The solution was to uninstall them and rebuild them from source with the correct configuration. For more details, see the question I asked about it with a working answer from Andrew Heusser: Animations in ipython (jupyter) notebook - ValueError: I/O operation on closed file
So, try the to_html5_video solution above first, and if it doesn't work then also try the uninstall / rebuild of ffmpeg and x264.
Another option:
import matplotlib.animation
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["animation.html"] = "jshtml"
plt.rcParams['figure.dpi'] = 150
plt.ioff()
fig, ax = plt.subplots()
x= np.linspace(0,10,100)
def animate(t):
plt.cla()
plt.plot(x-t,x)
plt.xlim(0,10)
matplotlib.animation.FuncAnimation(fig, animate, frames=10)
Here is the answer that I put together from multiple sources including the official examples. I tested with the latest versions of Jupyter and Python.
Download FFmpeg ( http://ffmpeg.zeranoe.com/builds/ )
Install FFmpeg making sure that you update the environmental variable ( http://www.wikihow.com/Install-FFmpeg-on-Windows ).
Run this script in Jupyter below. The variable imageList is the only thing that you need to modify. It is an list of images (your input).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML
#=========================================
# Create Fake Images using Numpy
# You don't need this in your code as you have your own imageList.
# This is used as an example.
imageList = []
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
for i in range(60):
x += np.pi / 15.
y += np.pi / 20.
imageList.append(np.sin(x) + np.cos(y))
#=========================================
# Animate Fake Images (in Jupyter)
def getImageFromList(x):
return imageList[x]
fig = plt.figure(figsize=(10, 10))
ims = []
for i in range(len(imageList)):
im = plt.imshow(getImageFromList(i), animated=True)
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000)
plt.close()
# Show the animation
HTML(ani.to_html5_video())
#=========================================
# Save animation as video (if required)
# ani.save('dynamic_images.mp4')
If you have a list of images and want to animate through them, you can use something like this:
from keras.preprocessing.image import load_img, img_to_array
from matplotlib import animation
from IPython.display import HTML
import glob
%matplotlib inline
def plot_images(img_list):
def init():
img.set_data(img_list[0])
return (img,)
def animate(i):
img.set_data(img_list[i])
return (img,)
fig = figure()
ax = fig.gca()
img = ax.imshow(img_list[0])
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=len(img_list), interval=20, blit=True)
return anim
imgs = [img_to_array(load_img(i)) for i in glob.glob('*.jpg')]
HTML(plot_images(imgs).to_html5_video())
Thank to Kolibril. I finally can run animation on Jupyter and Google Colab.
I modify some code which will generate animation of drawing random line instead.
import matplotlib.animation
import matplotlib.pyplot as plt
from itertools import count
import random
plt.rcParams["animation.html"] = "jshtml"
plt.rcParams['figure.dpi'] = 150
fig, ax = plt.subplots()
x_value = []
y_value = []
index = count();
def animate(t):
x_value.append(next(index))
y_value.append(random.randint(0,10))
ax.cla()
ax.plot(x_value,y_value)
ax.set_xlim(0,10)
matplotlib.animation.FuncAnimation(fig, animate, frames=10, interval = 500)
enter image description here
Related
I made an animation from the list of images saved as numpy arrays.
Then I want to add a text onto the animation like like a subtitle whose text changes for each frame but placing plt.text(some_string) only adds the string at the first iteration and it does not work if I change the passed string in the loop. Below is my attempt. Please note HTML is just for Jupyter Lab.
import matplotlib.animation as animation
from PIL import Image
from IPython.display import HTML
import matplotlib.pyplot as plt
folderName = "hogehoge"
picList = glob.glob(folderName + "\*.npy")
fig = plt.figure()
ims = []
for i in range(len(picList)):
plt.text(10, 10, i) # This does not add the text properly
tmp = Image.fromarray(np.load(picList[i]))
ims.append(plt.imshow(tmp))
ani = animation.ArtistAnimation(fig, ims, interval=200)
HTML(ani.to_jshtml())
You have also to add the text object to the list of artists for each frame:
import matplotlib.animation as animation
from PIL import Image
from IPython.display import HTML
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ims = []
for i in range(10):
artists = ax.plot(np.random.rand(10), np.random.rand(10))
text = ax.text(x=0.5, y=0.5, s=i)
artists.append(text)
ims.append(artists)
ani = animation.ArtistAnimation(fig, ims, interval=200)
HTML(ani.to_jshtml())
I am trying to create a plotting object that produces an animated matplotlib pcolor plot with a polar projection. Currently the object can either create a set of polar plots or try to create an animation of those plots.
When creating the set of polar plots (but not the animation) the object works as planned.
The animation portion of the object is based on this example, which works on my system. Unfortunately the animation as implemented in my object is not working. There is a figure and an MP4 file produced for the animation but both the figure and the too-short animation both show just some mis-shaped axes.
Does anyone have a suggestion of how to capture this figure series in an animation when embedded in an object?
I am using python 3.7, matplotlib 3.03 on a windows 10 machine
The code for the object and the code to run its instantiation are given below.
class Polar_smudge(object):
# object for creating polar contour plots
def __init__(self, azimuth_grid, range_grid):
import numpy as np
self.azimuth_grid = np.deg2rad(azimuth_grid)
self.range_grid = range_grid
self.fig = None
self.ax = None
self.images = []
#------------------------------------------------------------------
def add_data(self, value_grid):
import numpy as np
self.value_grid = value_grid
self.value_grid[self.value_grid<=0] = np.nan
#------------------------------------------------------------------
def add_figure(self, value_grid):
import matplotlib.pyplot as plt
# make and set-up figure
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
ax.set_rlim([0,10])
# make plot
cax = ax.pcolor(self.azimuth_grid, self.range_grid, value_grid, cmap=plt.cm.viridis_r)
ax.grid()
plt.show()
#------------------------------------------------------------------
def start_figure(self):
import matplotlib.pyplot as plt
# make and set-up figure
if self.fig is None :
self.fig, self.ax = plt.subplots(111, subplot_kw=dict(projection='polar'))
self.ax[0].set_theta_zero_location("N")
self.ax[0].set_theta_direction(-1)
def update_figure(self, value_grid):
import matplotlib.pyplot as plt
# make figure and add to image list
self.images.append((self.ax[0].pcolor(self.azimuth_grid, self.range_grid, value_grid, cmap=plt.cm.viridis_r),))
def end_figure(self):
import matplotlib.animation as animation
# animate the figure list
im_ani = animation.ArtistAnimation(self.fig, self.images, interval=50, repeat_delay=3000,blit=True)
im_ani.save('smudge.mp4')
#============This runs the object ====================================
import numpy as np
azimuth_bins = np.linspace(0, 360, 360)
range_bins = np.linspace(0, 10, 30)
# make plotting azim range grids
range_grid, azimuth_grid = np.meshgrid(range_bins, azimuth_bins)
# this works but isnt what I want
good_smudge = Polar_smudge(azimuth_grid,range_grid)
for ix in range(3):
val_grid = np.random.randn(360,30)
good_smudge.add_figure(val_grid)
# this doesnt work
bad_smudge = Polar_smudge(azimuth_grid,range_grid)
bad_smudge.start_figure()
for ix in range(3):
val_grid = np.random.randn(360,30)
bad_smudge.update_figure(val_grid)
bad_smudge.end_figure()
In response to the comment from Earnest, I did some further refinement and it appears that the problem is not linked to being embedded in an object, and also that increasing the number of frames (to eg. 30) does not solve the problem. The code snippet below provides a more concise demonstration of the problem (but lacks the correctly produced figure output option).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
azimuth_bins = np.linspace(0, 360, 60)
range_bins = np.linspace(0, 10, 30)
images = []
# make plotting azim range grids
range_grid, azimuth_grid = np.meshgrid(range_bins, azimuth_bins)
fig,ax = plt.subplots(111, subplot_kw=dict(projection='polar'))
ax[0].set_theta_zero_location("N")
ax[0].set_theta_direction(-1)
for ix in range(30):
val_grid = np.random.randn(60,30)
images.append((ax[0].pcolor(azimuth_grid, range_grid, val_grid, cmap=plt.cm.viridis_r),))
# animate the figure list
im_ani = animation.ArtistAnimation(fig, images, interval=50, repeat_delay=3000,blit=False)
im_ani.save('smudge2.mp4')
In this Kaggle Kernel https://www.kaggle.com/asindico/new-york-taxi-exploration I can't manage to show a matplotlib animation using on Basemap and hexbin.
Here is the code snippet
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib import cm
from matplotlib import animation, rc
from IPython.display import HTML
%matplotlib inline
#40.758896, -73.985130.
west, south, east, north = -74.1, 40.60, -73.80, 40.9
day=1
df_day=df[((df.pickup_datetime<'2016-01-'+str(day+1))&
(df.pickup_datetime>='2016-01-'+str(day)))]
fig,axarr = plt.subplots()
m = Basemap(projection='merc', llcrnrlat=south, urcrnrlat=north,
llcrnrlon=west, urcrnrlon=east, lat_ts=south, resolution='i',ax=axarr)
def init():
df_time= df_day[((df_day.pu_hour>=0)&(df_day.pu_hour<0))]
x, y = m(df_time['pickup_longitude'].values, df_time['pickup_latitude'].values)
m.hexbin(x, y, gridsize=300, bins='log', cmap=cm.YlOrRd_r);
return (m,)
def animate(i):
df_time= df_day[((df_day.pu_hour>=i)&(df_day.pu_hour<i+1))]
x, y = m(df_time['pickup_longitude'].values, df_time['pickup_latitude'].values)
m.hexbin(x, y, gridsize=300, bins='log', cmap=cm.YlOrRd_r);
return (m,)
anim = animation.FuncAnimation(fig, animate, init_func=init,frames=5, interval=1,blit=False,repeat=True)
fig.set_size_inches(12,10)
anim.save('pickup_animation.gif', writer='imagemagick', fps=2)
To display the animated gif inside the notebook you may use
from IPython.display import Image, display
display(Image(url='pickup_animation.gif'))
You may also show the animation as html5 video
from IPython.display import HTML
HTML(anim.to_html5_video())
You may also show the matplotlib animation directly, using the notebook backend instead of the inline backend
%matplotlib notebook
I searched for other similar questions but that didn't solve my problem. Below is a simple code that generates an animation in the form of a gif image in matplotlib:
import numpy as np
import matplotlib.pylab as plt
import matplotlib.animation as anm
fig = plt.figure()
def draw(i):
x = np.linspace(0, 5, num = 1000)
y = np.sin(x-i*0.1)
plt.clf()
plt.plot(x, y, 'r-')
anim = anm.FuncAnimation(fig, draw, frames = 10, interval = 500, repeat = False)
anim.save('test.gif', fps = 1, writer = 'imagemagick')
This generates the animation I want but once I open the final image (with eog), it keeps repeating. Since I would be presenting animation in the presentation, I would like it to stop after it is shown once. As you can notice, I have added repeat = False in the FuncAnimation but that doesn't stop repeating the image. What is wrong with my code?
Thanks in advance
My solution to the problem was to change from imagemagick to pillow as a writer.
Try the following:
from matplotlib.animation import FuncAnimation, PillowWriter
anim = FuncAnimation(fig, update, frames=range(1, 51), interval=.001, repeat=False)
anim.save('test.gif', dpi=80, writer=PillowWriter(fps=5))
The quality of the image is a bit worse than with imagemagick from my opinion, but it's a much easier and faster solution.
Using PillowWriter didn't solve the problem for me, so I came up with a workaround: just remove the loop as a post-processing step with PIL. Here is the code:
from PIL import Image
import io
def convert(old_filename, new_filename, duration):
images = []
with Image.open(old_filename) as im:
for i in range(im.n_frames):
im.seek(i)
buf = io.BytesIO()
im.save(buf, format='png')
buf.seek(0)
images.append(Image.open(buf))
images[0].save(new_filename, save_all=True, append_images=images[1:], optimize=False, duration=duration)
convert("myAnimation.gif", "myAnimationNoLoop.gif", 100)
I'm trying to plot the movement of particles with pyplot.
The problem is that I can't figure out how to create the animation.
Here is the notebook : http://nbviewer.ipython.org/gist/lhk/949c7bf7007445033fd9
Apparently the update function doesn't work properly, but the error message is too cryptic for me. What do I need to change ?
Do you have a good tutorial on animation with pyplot ?
As #s0upa1t comments you should have figure handle as the first argument to animation. The criptic error results from animation expecting a fig object, which has attribute canvas but instead gets scatter, a PathCollection object, which does not. As a minimal example of animation in the form you want, consider,
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
dt = 0.005
n=20
L = 1
particles=np.zeros(n,dtype=[("position", float , 2),
("velocity", float ,2),
("force", float ,2),
("size", float , 1)])
particles["position"]=np.random.uniform(0,L,(n,2));
particles["velocity"]=np.zeros((n,2));
particles["size"]=0.5*np.ones(n);
fig = plt.figure(figsize=(7,7))
ax = plt.axes(xlim=(0,L),ylim=(0,L))
scatter=ax.scatter(particles["position"][:,0], particles["position"][:,1])
def update(frame_number):
particles["force"]=np.random.uniform(-2,2.,(n,2));
particles["velocity"] = particles["velocity"] + particles["force"]*dt
particles["position"] = particles["position"] + particles["velocity"]*dt
particles["position"] = particles["position"]%L
scatter.set_offsets(particles["position"])
return scatter,
anim = FuncAnimation(fig, update, interval=10)
plt.show()
There are many good animation tutorials, however the answer here is particularly nice.