2013-02-06 17 views
5

Ho cercato di salvare uno scatterplot animato con matplotlib e preferirei che non richiedesse codice completamente diverso per la visualizzazione come una figura animata e per il salvataggio di una copia. La figura mostra tutti i datapoint perfettamente dopo il completamento del salvataggio.Salvataggio animazioni scatterplot con matplotlib

Questo codice è una versione modificata di Giggi's su Animating 3d scatterplot in matplotlib, con una correzione per i colori da Yann's answer su Matplotlib 3D scatter color lost after redraw (in quanto i colori saranno importanti sul mio video, quindi voglio assicurarsi che funzionino).

import matplotlib.pyplot as plt 
import matplotlib.animation as animation 
import numpy as np 
from mpl_toolkits.mplot3d import Axes3D 

FLOOR = -10 
CEILING = 10 

class AnimatedScatter(object): 
    def __init__(self, numpoints=5): 
     self.numpoints = numpoints 
     self.stream = self.data_stream() 
     self.angle = 0 

     self.fig = plt.figure() 
     self.fig.canvas.mpl_connect('draw_event',self.forceUpdate) 
     self.ax = self.fig.add_subplot(111,projection = '3d') 
     self.ani = animation.FuncAnimation(self.fig, self.update, interval=100, 
             init_func=self.setup_plot, blit=True,frames=20) 

    def change_angle(self): 
     self.angle = (self.angle + 1)%360 

    def forceUpdate(self, event): 
     self.scat.changed() 

    def setup_plot(self): 
     X = next(self.stream) 
     c = ['b', 'r', 'g', 'y', 'm'] 
     self.scat = self.ax.scatter(X[:,0], X[:,1], X[:,2] , c=c, s=200, animated=True) 

     self.ax.set_xlim3d(FLOOR, CEILING) 
     self.ax.set_ylim3d(FLOOR, CEILING) 
     self.ax.set_zlim3d(FLOOR, CEILING) 

     return self.scat, 

    def data_stream(self): 
     data = np.zeros((self.numpoints , 3)) 
     xyz = data[:,:3] 
     while True: 
      xyz += 2 * (np.random.random((self.numpoints,3)) - 0.5) 
      yield data 

    def update(self, i): 
     data = next(self.stream) 
     #data = np.transpose(data) 

     self.scat._offsets3d = (np.ma.ravel(data[:,0]) , np.ma.ravel(data[:,1]) , np.ma.ravel(data[:,2])) 

     plt.draw() 
     return self.scat, 

    def show(self): 
     plt.show() 

if __name__ == '__main__': 
    a = AnimatedScatter() 
    a.ani.save("movie.avi", codec='avi') 
    a.show() 

Un .avi perfettamente valido viene generato da questo, ma è vuota per tutti i quattro secondi eccezione per gli assi. La figura attuale mostra sempre esattamente ciò che voglio vedere. Come posso popolare i grafici della funzione di salvataggio nello stesso modo in cui popolo un'animazione normalmente in esecuzione o è possibile in matplotlib?

EDIT: Utilizzando una chiamata dispersione nell'aggiornamento (senza impostare i limiti come nel inizializzatore) fa sì che il .avi per mostrare gli assi crescente, mostrando che i dati viene eseguito ogni volta, non è solo visualizzazione sulla il video stesso. Sto usando matplotlib 1.1.1rc con Python 2.7.3.

+0

si può risolvere il tuo rientro? – tacaswell

+0

Whoops. Dovrei essere più attento a copiare e incollare. Sebbene la mia fonte abbia spazi al posto delle schede, quindi non so cosa sia successo. – Poik

+0

e questo sembra ok se lo guardi in una figura? Quale versione di MPL stai usando? – tacaswell

risposta

2

Rimuovere blit=True da FuncAnimation e animated=True da scatter e funziona. Sospetto che ci sia qualcosa che non va nella logica che fa in modo che solo gli artisti che devono essere aggiornati vengano aggiornati/ridisegnati tra i frame (piuttosto che solo ridisegnare tutto).

Di seguito è esattamente ciò che ho corso e ho ottenuto il filmato di output previsto:

import matplotlib.pyplot as plt 
import matplotlib.animation as animation 
import numpy as np 
from mpl_toolkits.mplot3d import Axes3D 

FLOOR = -10 
CEILING = 10 

class AnimatedScatter(object): 
    def __init__(self, numpoints=5): 
     self.numpoints = numpoints 
     self.stream = self.data_stream() 
     self.angle = 0 

     self.fig = plt.figure() 
     self.fig.canvas.mpl_connect('draw_event',self.forceUpdate) 
     self.ax = self.fig.add_subplot(111,projection = '3d') 
     self.ani = animation.FuncAnimation(self.fig, self.update, interval=100, 
             init_func=self.setup_plot, frames=20) 

    def change_angle(self): 
     self.angle = (self.angle + 1)%360 

    def forceUpdate(self, event): 
     self.scat.changed() 

    def setup_plot(self): 
     X = next(self.stream) 
     c = ['b', 'r', 'g', 'y', 'm'] 
     self.scat = self.ax.scatter(X[:,0], X[:,1], X[:,2] , c=c, s=200) 

     self.ax.set_xlim3d(FLOOR, CEILING) 
     self.ax.set_ylim3d(FLOOR, CEILING) 
     self.ax.set_zlim3d(FLOOR, CEILING) 

     return self.scat, 

    def data_stream(self): 
     data = np.zeros((self.numpoints , 3)) 
     xyz = data[:,:3] 
     while True: 
      xyz += 2 * (np.random.random((self.numpoints,3)) - 0.5) 
      yield data 

    def update(self, i): 
     data = next(self.stream) 
     self.scat._offsets3d = (np.ma.ravel(data[:,0]) , np.ma.ravel(data[:,1]) , np.ma.ravel(data[:,2])) 
     return self.scat, 

    def show(self): 
     plt.show() 

if __name__ == '__main__': 
    a = AnimatedScatter() 
    a.ani.save("movie.avi", codec='avi') 
    a.show() 
+0

Questo ha fatto il trucco! E il mio progetto attuale ha funzionato con quella correzione. Grazie! – Poik

+0

È interessante notare che la rimozione di 'blit' aiuta, considerando il codice di animazione.save imposta 'blit' a false quando si chiama' _draw_next_frame'. Aggiungerò segnalazioni complete di bug (o almeno richieste di wishlist) al sito di matplotlib quando ne avrò la possibilità. – Poik

+0

Con SciPy 0.12.0, Numpy 1.7.1 e Matplotlib 1.2.1 non riuscivo a farlo funzionare. Nel migliore dei casi creerebbe un video con una tonnellata di punti vecchi tracciati sovrascritti dai nuovi punti o da una trama vuota. Sei libero di provare il codice che è collegato al mio video: https://www.youtube.com/watch?v=dCIOOjJ1Sk0 Finalmente ho usato CamStudio e ha funzionato meglio e ha un output video di qualità superiore. – Demolishun