saving matplotlib animation as mp4 - python

I want to save the output animation of the following program in mp4. The program does create a mp4 file, but the file is a blank file it does not contain the animation I wanted. What am I doing wrong here?
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.animation as animation
plt.rcParams['animation.ffmpeg_path'] ='C:\\ffmpeg\\bin\\ffmpeg.exe'
fig=plt.figure()
ax=fig.add_subplot(111,projection="3d")
x=np.linspace(100,150,100)
t=(x-100)/0.5
y=-.01*np.cos(t)+.5*np.sin(t)+100.01
z=.01*np.sin(t)+.5*np.cos(t)+99.5
def animate(i):
line.set_data(x[:i],y[:i])
line.set_3d_properties(z[:i])
ax.set_xlim3d([min(x),max(x)])
ax.set_ylim3d([min(y),max(y)])
ax.set_zlim3d([min(z),max(z)])
ax.set_title("Particle in magnetic field")
ax.set_xlabel("X")
ax.set_xlabel("Y")
ax.set_xlabel("Z")
line,=ax.plot([],[],[])
lin_ani=animation.FuncAnimation(fig,animate)
plt.legend()
FFwriter = animation.FFMpegWriter()
lin_ani.save('animation.mp4', writer = FFwriter, fps=10)
# plt.show()

As I learn up to know, you should write like this:
FFwriter = animation.FFMpegWriter(fps=10)
lin_ani.save('animation.mp4', writer = FFwriter)
I learned this from this site1

Add this line at the end of your function
return line,
so a complete function should look like:
def animate(i):
line.set_data(x[:i],y[:i])
line.set_3d_properties(z[:i])
return line,

Related

Jupiter saves an empty photo, although it shows it before. How to fix that?

I'm reading and output the picture as a plot:
import numpy as np
import os
import matplotlib.pyplot as plt
import matplotlib.ticker as tkr
fig, ax = plt.subplots()
path = 'photo.jpg'
im = plt.imread(path)
ax.imshow(im)
Output
After I want to save it and check(see) the image before saving, but my code generate empty image:(
plt.axis("off")
plt.gca().xaxis.set_major_locator(tkr.NullLocator())
plt.gca().yaxis.set_major_locator(tkr.NullLocator())
filename = os.path.basename(path).split(".")[0]
output_path = os.path.join("test", filename+".png").replace("/", "")
plt.savefig(output_path, bbox_inches="tight", pad_inches=0.0)
plt.show()
plt.close()
Output
Is it necessary to read the image as a graph?
If not, just use PIL library. It is a way more simpler to use:
You can check the code there: https://colab.research.google.com/drive/1Dmhb4aiERRym9bNx5HgCQPOr9iANHMic?usp=sharing
PIL documentation: https://pillow.readthedocs.io/en/stable/handbook/tutorial.html

How to generate matplotlib figures inside a for loop

Hello I have a piece of code which reads an excel data file, does some stuff to it and then plots a figure. Now i want to be able to plot many excel data files at the same time and each should be plotted to its own figure.
example:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import MultiCursor
import matplotlib.pylab as pl
Files_to_read = np.array([r"C:\file1",r"C:\file2"])
for ii in range(len(Files_to_read)):
df=pd.read_excel(Files_to_read[ii])
#do a lot of stuff to "df"
fig, ((ax1,ax2),(ax3,ax4)) = plt.subplots(2,2,sharex=True)
fig.suptitle('some name')
p1 = ax1.plot(df["some vector"], df["some vector"])
p2 = ax2.plot(df["some vector"], df["some vector"])
p3 = ax3.plot(df["some vector"], df["some vector"])
p4 = ax4.plot(df["some vector"], df["some vector"])
multi = MultiCursor(fig.canvas, (ax1, ax2, ax3, ax4), color='r', lw=1)
plt.show()
Doing it like this generates 1 figure with the data of the first file and then overwrites the same figure with the data of the second file, how can I change it to generate a new figure on each pass through the for loop?
Move your call to plt.show() so that it occurs after the for loop has been complete. All created figures should be visualized at once.

Animation not firing un jupyter notebook

Using below code within a cell in Jupyter notebbok I'm attempting to update a simple graph :
%reset -f
import matplotlib.pyplot as plt
import pandas as pd
from datetime import datetime
import random
from matplotlib import animation, rc
from IPython.display import HTML
import numpy as np
%matplotlib inline
fig, ax = plt.subplots()
line, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return (line,)
# animation function. This is called sequentially
def animate(i):
line.set_data([1,random.randint(100,101)], [1,random.randint(100,101)])
return (line,)
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20)
rc('animation', html='jshtml')
HTML(anim.to_html5_video())
But this is the output that is rendered :
How to dynamically update the graph with data ?
Using line.set_data([1,random.randint(100,101)], [1,random.randint(100,101)]) is the not the correct method to add data ?
For now I'm just attempting to update within the result of a call to random but I plan to use this with the result of a REST service.

matplotlib.close() does not close plot

I'm using Python 3.6 in jupyter notebook. plt.close does not close plot. I tried with plt.ion() also and many other ways.
I want to display image, then wait for pause or input() and then remove the previous image and show the new one.
import matplotlib.pyplot as plt
from time import sleep
from scipy import eye
plt.imshow(eye(3))
plt.show()
sleep(1)
plt.close()
Here is an example that shows a sequence of plots, each for one second. Essential are the commants plt.show(block = False) and plt.pause(1) instead of sleep(1):
import numpy as np
import matplotlib.pyplot as plt
def show_image(n):
fig, ax = plt.subplots()
x = np.linspace(0,1,100)
y = x**n
ax.plot(x,y, label = 'x**{}'.format(n))
ax.legend()
plt.show(block=False)
plt.pause(1)
plt.close(fig)
for i in range(10):
show_image(i)
If I understand correctly, what you want is to show a plot, wait 1 second, then let it close automatically.
This would be achieved as follows.
import matplotlib.pyplot as plt
from scipy import eye
plt.imshow(eye(3))
def show_and_close(sec):
timer = plt.gcf().canvas.new_timer(interval=sec*1000)
timer.add_callback(lambda : plt.close())
timer.single_shot = True
timer.start()
plt.show()
show_and_close(1)

Inline animations in Jupyter

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

Categories