matplotlib.animation#
Animation#
Der einfachste Weg, eine Live-Animation in Matplotlib zu erstellen, ist die Verwendung einer der Animation-Klassen.
Siehe auch
Eine Basisklasse für Animationen. |
|
|
|
|
In beiden Fällen ist es entscheidend, eine Referenz auf das Instanzobjekt zu behalten. Die Animation wird durch einen Timer (typischerweise vom Host-GUI-Framework) vorangetrieben, auf den das Animation-Objekt die einzige Referenz hält. Wenn Sie keine Referenz auf das Animation-Objekt halten, wird es (und damit die Timer) vom Garbage Collector aufgeräumt, was die Animation stoppt.
Um eine Animation zu speichern, verwenden Sie Animation.save, Animation.to_html5_video oder Animation.to_jshtml.
Siehe Writer-Klassen unten für Details, welche Filmformate unterstützt werden.
FuncAnimation#
Das Innenleben von FuncAnimation ist mehr oder weniger
for d in frames:
artists = func(d, *fargs)
fig.canvas.draw_idle()
fig.canvas.start_event_loop(interval)
mit Details zur Handhabung von 'Blitting' (zur dramatischen Verbesserung der Live-Leistung), zur Nicht-Blockierung, zur Vermeidung wiederholten Startens/Stoppens der GUI-Ereignisschleife, zur Handhabung von Wiederholungen, mehreren animierten Achsen und zum einfachen Speichern der Animation in einer Filmdatei.
'Blitting' ist eine Standardtechnik in der Computergrafik. Die allgemeine Idee ist, eine vorhandene Bitmap (in unserem Fall eine weitgehend gerasterte Figur) zu nehmen und dann einen weiteren Künstler darüber zu 'blittern'. Dadurch, dass wir eine gespeicherte 'saubere' Bitmap verwalten, können wir nur die wenigen Künstler neu zeichnen, die sich bei jedem Frame ändern, und möglicherweise erhebliche Zeit sparen. Wenn wir Blitting verwenden (durch Übergabe von blit=True), wird die Kernschleife von FuncAnimation etwas komplizierter
ax = fig.gca()
def update_blit(artists):
fig.canvas.restore_region(bg_cache)
for a in artists:
a.axes.draw_artist(a)
ax.figure.canvas.blit(ax.bbox)
artists = init_func()
for a in artists:
a.set_animated(True)
fig.canvas.draw()
bg_cache = fig.canvas.copy_from_bbox(ax.bbox)
for f in frames:
artists = func(f, *fargs)
update_blit(artists)
fig.canvas.start_event_loop(interval)
Dies lässt natürlich viele Details aus (wie z. B. das Aktualisieren des Hintergrunds beim Ändern der Größe der Figur oder beim vollständigen Neuzeichnen). Dieses hoffentlich minimalistische Beispiel vermittelt jedoch ein Gefühl dafür, wie init_func und func innerhalb von FuncAnimation verwendet werden und die Theorie, wie 'Blitting' funktioniert.
Hinweis
Die Z-Reihenfolge von Künstlern wird beim 'Blitting' nicht berücksichtigt, da die 'geblitteten' Künstler immer oben gezeichnet werden.
Die erwartete Signatur von func und init_func ist sehr einfach, um FuncAnimation aus Ihrer Buchhaltungs- und Plotting-Logik herauszuhalten. Dies bedeutet jedoch, dass die von Ihnen übergebenen aufrufbaren Objekte wissen müssen, auf welche Künstler sie sich beziehen sollen. Es gibt mehrere Ansätze zur Handhabung dieses Problems, mit unterschiedlicher Komplexität und Kapselung. Der einfachste Ansatz, der in einem Skript recht gut funktioniert, ist die Definition des Künstlers im globalen Geltungsbereich und die Überlassung der Sortierung an Python. Zum Beispiel
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = ax.plot([], [], 'ro')
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
xdata.append(frame)
ydata.append(np.sin(frame))
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
plt.show()
Die zweite Methode ist die Verwendung von functools.partial, um Argumente an die Funktion zu übergeben
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from functools import partial
fig, ax = plt.subplots()
line1, = ax.plot([], [], 'ro')
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
return line1,
def update(frame, ln, x, y):
x.append(frame)
y.append(np.sin(frame))
ln.set_data(x, y)
return ln,
ani = FuncAnimation(
fig, partial(update, ln=line1, x=[], y=[]),
frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
plt.show()
Eine dritte Methode ist die Verwendung von Closures zum Erstellen der erforderlichen Künstler und Funktionen. Eine vierte Methode ist die Erstellung einer Klasse.
Beispiele#
ArtistAnimation#
Beispiele#
Writer-Klassen#
Die bereitgestellten Writer lassen sich in wenige Hauptkategorien einteilen.
Der Pillow-Writer verwendet die Pillow-Bibliothek zum Schreiben der Animation und hält alle Daten im Speicher.
Der HTML-Writer generiert JavaScript-basierte Animationen.
Writer für JavaScript-basierte HTML-Filme. |
Die Pipe-basierten Writer streamen die aufgenommenen Frames über eine Pipe an einen externen Prozess. Die Pipe-basierten Varianten sind tendenziell performanter, funktionieren aber möglicherweise nicht auf allen Systemen.
Pipe-basierter ffmpeg-Writer. |
|
Pipe-basierter animierter GIF-Writer. |
Die dateibasierten Writer speichern temporäre Dateien für jeden Frame, die am Ende zu einer einzigen Datei zusammengefügt werden. Obwohl langsamer, sind diese Writer leichter zu debuggen.
Dateibasierter ffmpeg-Writer. |
|
Dateibasierter animierter GIF-Writer. |
Die Writer-Klassen bieten eine Möglichkeit, sequentielle Frames von derselben zugrunde liegenden Figure zu erfassen. Sie alle bieten drei Methoden, die nacheinander aufgerufen werden müssen
setupbereitet den Writer vor (z. B. das Öffnen einer Pipe). Pipe-basierte und dateibasierte Writer nehmen unterschiedliche Argumente fürsetup()entgegen.grab_framekann dann beliebig oft aufgerufen werden, um einen einzelnen Frame auf einmal zu erfassenfinishschließt den Film ab und schreibt die Ausgabedatei auf die Festplatte.
Beispiel
moviewriter = MovieWriter(...)
moviewriter.setup(fig, 'my_movie.ext', dpi=100)
for j in range(n):
update_figure(j)
moviewriter.grab_frame()
moviewriter.finish()
Wenn Sie die Writer-Klassen direkt verwenden (nicht über Animation.save), wird dringend empfohlen, den saving Kontextmanager zu verwenden
with moviewriter.saving(fig, 'myfile.mp4', dpi=100):
for j in range(n):
update_figure(j)
moviewriter.grab_frame()
um sicherzustellen, dass Einrichtung und Bereinigung nach Bedarf durchgeführt werden.
Beispiele#
Hilfsklassen#
Animation-Basisklassen#
Eine Basisklasse für Animationen. |
|
|
Writer-Registrierung#
Eine Modul-weite Registrierung wird bereitgestellt, um zwischen dem Namen des Writers und der Klasse abzubilden, damit ein String an Animation.save übergeben werden kann, anstatt einer Writer-Instanz.
Registrierung verfügbarer Writer-Klassen nach menschenlesbarem Namen. |
Writer-Basisklassen#
Um Code-Duplizierung zu reduzieren, Basisklassen
Abstrakte Basisklasse für das Schreiben von Filmen, die eine Möglichkeit bietet, Frames durch Aufrufen von |
|
Basisklasse für das Schreiben von Filmen. |
|
|
und Mixins
Mixin-Klasse für FFMpeg-Ausgabe. |
|
Mixin-Klasse für ImageMagick-Ausgabe. |
werden bereitgestellt.
Sehen Sie sich den Quellcode an, um leicht neue MovieWriter-Klassen zu implementieren.