Create an animation with Python3 and Matplotlib - python

I tried to create an animation with matplotlib.pyplot and matplotlib.animation and I encountured two problems:
1st is that, I went to matplotlib animation page and then I tried their code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
def f(x, y):
return np.sin(x) + np.cos(y)
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
# ims is a list of lists, each row is a list of artists to draw in the
# current frame; here we are just animating one artist, the image, in
# each frame
ims = []
for i in range(60):
x += np.pi / 15.
y += np.pi / 20.
im = plt.imshow(f(x, y), animated=True)
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000)
# ani.save('dynamic_images.mp4')
plt.show()
I got an error: AttributeError: 'function' has no attribute 'canvas' (I re-tried and I didn't get any error...
2nd, when I uncomment ani.save('dynamic_images.mp4') I get this error: TypeError: 'MovieWriterRegistry' object is not an iterator. This one bothers me a lot more. If you have any solution about this last problem, please let me now.
WolfGang1710.

As you can read in the documentation of the method Animation.save the default writer is 'ffmpeg'. Therefore, you need to have ffmpeg installed for it to work.

Related

Funcanimation with bit=True is not updating x axis and not making sense with maximizing the window

I am trying to just have a simple life by getting a real-time plot functionality with blit=True but what I get is a plot with wrong x-axis limits and the plot changes when I maximize the plot window.
I want to have a plot of 50000 (say) points made in one go and then use funcanimation to call animate() to update the existing plot with set_data(x,y). Everything works fine if blit=False but I want to have blitting in my GUI. Please help with your thoughts. Attaching a short video for your reference along with the code.
I am pasting my code below:
Thanks in advance!
import time
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from random import randrange
fig = plt.figure(figsize=(6, 3))
varLen = 1000
x=[r for r in range(varLen-300)]
y=[randrange(0, 10) for r in range(varLen-300)]
y2=[10+randrange(0, 10) for r in range(varLen-300)]
# y=[]
# y2=[]
print(type(x))
ln, = plt.plot(x, y, '-')
ln2, = plt.plot(x, y2, '-')
def update(frame):
start=time.time()
global x, y, y2
x.append(x[-1]+1)
val=randrange(0, 10)
y.append(val)
y2.append(10+val)
# print(len(x), len(y), len(y2))
x=x[-varLen:]
y = y[-varLen:]
y2 = y2[-varLen:]
ln.set_data(x, y)
ln2.set_data(x,y2)
# ln.set_data(frame, randrange(0, 10))
# ln2.set_data(frame, 10+randrange(0, 10))
fig.gca().relim()
fig.gca().autoscale_view()
print(f'Time (ms): {round((time.time() - start)*1000,2)}')
return ln,ln2,
animation = FuncAnimation(fig, update, interval=1, blit=True)
plt.show()

How to make an animation of a curve from scratch using Matplotlib

Note this is a follow-up question of How to make an animation of a Lissajous curve;
My first idea was to edit my original question and ask for the animation, but I understand and respect SO way of operating. So the best is making another question.
I want to make an animation of the curve (where you incrementally draw it) with parametrization: x(t) = sin(3t) and y(y) = sin(4t) where t[0, 2pi].
For doing so I would add the code:
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
ln, = plt.plot([], [], 'b')
def init():
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
x.append(np.sin(4*frame))
y.append(np.sin(3*frame))
ln.set_data(x, y)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
The problem is that with this code it doesn't draw the whole curve from scratch. What does is overdrawing it, getting overlapping.
How can I draw it from scratch (i.e. starting with white background)? I've been thinking about an if else but got nothing.
Thanks
EDIT
Let me show you the whole code:
%matplotlib notebook
import matplotlib.pyplot as plt
import math
import numpy as np
from matplotlib.animation import FuncAnimation
# set the minimum potential
rm = math.pow(2, 1 / 6)
t = np.linspace(-10, 10, 1000, endpoint = False)
x = []
y = []
for i in t: #TypeError 'int' object is not iterable
x_i = np.sin( 3 * i )
y_i = np.sin( 4 * i )
x.append(x_i)
y.append(y_i)
# set the title
plt.title('Plot sin(4t) Vs sin(3t)')
# set the labels of the graph
plt.xlabel('sin(3t)')
plt.ylabel('sin(4t)')
fig, ax = plt.subplots()
ln, = plt.plot([], [], 'b')
def init():
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
x.append(np.sin(4*frame))
y.append(np.sin(3*frame))
ln.set_data(x, y)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
# display the graph
plt.show()
This is the image I get at the beginning (screenshot taken after approximately 1s after started running; that's why you see that funny line): https://imgur.com/a/bNoViDA. As you can see it doesn't start from scratch (i.e not from white background)
This is the plot I get at the end: https://imgur.com/a/WQHHUk9
I am seeking getting that ending point but drawing everything from scratch, without starting with the shown plot.

Connector patch between subplots with animation not visible (matplotlib)

I am using an artist animation method with 5 subplots. There is one static plot on the left, with 3 smaller animated imshow plots to the right (the colorbar is the 5th). I have successfully used ConnectionPatch to connect subplots to show where the data is coming from, but only on static plots. No matter what I try, I can't seem to get the patches to show up on the animation. I've tried to include the patch in the image artist list, tried to update the figure with the artist instead of the axis (which I guess doesn't make much sense), among other things. It will be very difficult to extract a working example due to the complexity of the plot, but maybe someone has a tip.
Could setting the facecolor to 'white' with the animation savefig_kwargs be covering up the connector lines? If so, how do I change the z order of the patch/facecolor?
Without a minimal working example, I can only tell you that it is possible to use a ConnectionPatch in an animation. However, as seen below, one has to recreate it for every frame.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
import matplotlib.gridspec as gridspec
from matplotlib.patches import ConnectionPatch
import matplotlib.animation
plt.rcParams["figure.figsize"] = np.array([6,3.6])*0.7
x = np.linspace(-3,3)
X,Y = np.meshgrid(x,x)
f = lambda x,y: (1 - x / 2. + x ** 5 + y ** 3) * np.exp(-x ** 2 - y ** 2)+1.5
Z = f(X,Y)
bins=np.linspace(Z.min(), Z.max(), 16)
cols = plt.cm.PuOr((bins[:-1]-Z.min())/(Z.max()-Z.min()))
gs = gridspec.GridSpec(2, 2, height_ratios=[34,53], width_ratios=[102,53])
fig = plt.figure()
ax=fig.add_subplot(gs[:,0])
ax2=fig.add_subplot(gs[0,1])
ax3=fig.add_subplot(gs[1,1])
ax.imshow(Z, cmap="PuOr")
rec = plt.Rectangle([-.5,-.5], width=9, height=9, edgecolor="crimson", fill=False, lw=2)
conp = ConnectionPatch(xyA=[-0.5,0.5], xyB=[9.5,4], coordsA="data", coordsB="data",
axesA=ax3, axesB=ax, arrowstyle="-|>", zorder=25, shrinkA=0, shrinkB=1,
mutation_scale=20, fc="w", ec="crimson", lw=2)
ax3.add_artist(conp)
ax.add_artist(rec)
im = ax3.imshow(Z[:9,:9], cmap="PuOr", vmin=Z.min(), vmax=Z.max())
ticks = np.array([0,4,8])
ax3.set_yticks(ticks); ax3.set_xticks(ticks)
ax2.hist(Z[:9,:9].flatten(), bins=bins)
def ins(px,py):
global rec, conp, histpatches
ll = [px-.5,py-.5]
rec.set_xy(ll)
conp.remove()
conp = ConnectionPatch(xyA=[-0.5,0.5], xyB=[px+9.5,py+4], coordsA="data", coordsB="data",
axesA=ax3, axesB=ax, arrowstyle="-|>", zorder=25, shrinkA=0, shrinkB=1,
mutation_scale=20, fc="w", ec="crimson", lw=2)
ax3.add_patch(conp)
data = Z[px:px+9,py:py+9]
im.set_data(data)
ax3.set_xticklabels(ticks+px)
ax3.set_yticklabels(ticks+py)
ax2.clear()
ax2.set_ylim(0,60)
h, b_, patches = ax2.hist(data.flatten(), bins=bins, ec="k", fc="#f1a142")
[pat.set_color(cols[i]) for i, pat in enumerate(patches)]
def func(p):
px,py = p
ins(px, py)
phi = np.linspace(0.,2*np.pi)
r = np.sin(2*phi)*20+np.pi/2
xr = (r*np.cos(phi)).astype(np.int8)
yr = (r*np.sin(phi)).astype(np.int8)
plt.subplots_adjust(top=0.93,bottom=0.11,left=0.04,right=0.96,hspace=0.26,wspace=0.15)
frames = np.c_[xr+20, yr+20]
ani = matplotlib.animation.FuncAnimation(fig, func, frames=frames, interval=300, repeat=True)
plt.show()

How to generate an animation with images in subplots (matplotlib)

Is possible to animate pairs of images in a jupyter notebook?
With two lists of images:
greys = io.imread_collection(path_greys)
grdTru= io.imread_collection(path_grdTru)
The following naïve code fails to generate an animation:
for idx in range(1,900):
plt.subplot(121)
plt.imshow(greys[idx], interpolation='nearest', cmap=plt.cm.gray)
plt.subplot(122)
plt.imshow(grdTru[idx], interpolation='nearest', cmap=plt.cm.,vmin=0,vmax=3)
plt.show()
(It generates a list of subplots)
By the way,the example found in matplotlib doc failed if pasted in a notebook.
In order to make the example work in a jupyter notebook you need to include the
%matplotlib notebook
magic command.
import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
def f(x, y):
return np.sin(x) + np.cos(y)
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
im = plt.imshow(f(x, y), animated=True)
def updatefig(*args):
global x, y
x += np.pi / 15.
y += np.pi / 20.
im.set_array(f(x, y))
return im,
ani = animation.FuncAnimation(fig, updatefig, interval=50, blit=True)
plt.show()
You can then easily adapt it to your list of images.
From matplotlib version 2.1 on you also have the option to create a JavaScript animation inline.
from IPython.display import HTML
HTML(ani.to_jshtml())
Complete example:
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def f(x, y):
return np.sin(x) + np.cos(y)
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
im = plt.imshow(f(x, y), animated=True);
def updatefig(*args):
global x, y
x += np.pi / 15.
y += np.pi / 20.
im.set_array(f(x, y))
return im,
ani = animation.FuncAnimation(fig, updatefig, interval=50, blit=True)
from IPython.display import HTML
HTML(ani.to_jshtml())

Matplotlib animation in real time

In the example below I want to make an animation where a point moves around a circle in T seconds (for example T=10). However it is a lot slower and doesn't work. So, what is wrong with my code and how to fix it? As far as I understand the api (http://matplotlib.org/api/animation_api.html) setting interval=1 should update the figure every millisecond.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
R = 3
T = 10
fig = plt.figure()
fig.set_dpi(300)
fig.set_size_inches(7, 6.5)
ax = plt.axes(xlim=(-10, 10), ylim=(-R*1.5, R*1.5))
ax.set_aspect('equal')
patch = plt.Circle((0, 0), 0.1, fc='r')
looping = plt.Circle((0,0),R,color='b',fill=False)
ax.add_artist(looping)
time_text = ax.text(-10,R*1.2,'',fontsize=15)
def init():
time_text.set_text('')
patch.center = (0, 0)
ax.add_patch(patch)
return patch,time_text,
def animate(i):
t=i/1000.0
time_text.set_text(t)
x, y = patch.center
x = R*np.sin(t/T*2*np.pi)
y = R*np.cos(t/T*2*np.pi)
patch.center = (x, y)
return patch,time_text
slow_motion_factor=1
anim = animation.FuncAnimation(fig, animate,
init_func=init,
frames=10000,
interval=1*slow_motion_factor,
blit=True)
plt.show()
I should add that the problem depends on the machine where I run the program. For example on a old Intel dualcore (P8700) (that's the box where the program should run), it is considerable slower than on a newer haswell i7 desktop cpu. But in the latter case it is also much slower as intended.
The problem is, that your computer is not fast enough, to deliver a new image every 1 ms. Which is kind of expected.
You should go for a more realistic speed. 25 frames per second should be enough
and also be possible to render in time.
I also made a few adjustment to you code, mostly style and more semantic variable names.
The biggest change was adapting this answer to your code to get rid of the first frame being still there after the init:
Matplotlib animation: first frame remains in canvas when using blit
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
R = 3
T = 10
time = 3 * T
slow_motion_factor = 1
fps = 50
interval = 1 / fps
fig = plt.figure(figsize=(7.2, 7.2))
ax = fig.add_subplot(1, 1, 1, aspect='equal')
ax.set_xlim(-1.5 * R, 1.5 * R)
ax.set_ylim(-1.5 * R, 1.5 * R)
runner = plt.Circle((0, 0), 0.1, fc='r')
circle = plt.Circle((0, 0), R, color='b', fill=False)
ax.add_artist(circle)
time_text = ax.text(1.1 * R, 1.1 * R,'', fontsize=15)
def init():
time_text.set_text('')
return time_text,
def animate(i):
if i == 0:
ax.add_patch(runner)
t = i * interval
time_text.set_text('{:1.2f}'.format(t))
x = R * np.sin(2 * np.pi * t / T)
y = R * np.cos(2 * np.pi * t / T)
runner.center = (x, y)
return runner, time_text
anim = animation.FuncAnimation(
fig,
animate,
init_func=init,
frames=time * fps,
interval=1000 * interval * slow_motion_factor,
blit=True,
)
plt.show()

Categories