2011-03-22 15 views
14

Estoy tratando de acelerar el proceso de guardar mis cuadros en imágenes. En este momento estoy creando un objeto cString donde guardo el gráfico usando savefig; pero realmente agradecería cualquier ayuda para mejorar este método de guardar la imagen. Tengo que hacer esta operación docenas de veces, y el comando savefig es muy lento; debe haber una mejor manera de hacerlo. Leí algo sobre guardarlo como una imagen sin comprimir, pero no tengo ni idea de cómo hacerlo. Realmente no me importa mucho si puedo cambiar a otro servidor más rápido también.Matplotlib, alternativas a savefig() para mejorar el rendimiento al guardar en un objeto CString?

es decir:

RAM = cStringIO.StringIO() 

CHART = plt.figure(.... 
**code for creating my chart** 

CHART.savefig(RAM, format='png') 

He estado usando matplotlib con FigureCanvasAgg backend.

Gracias!

+0

Realmente no sé mucho sobre esto. Pero puede ver si la siguiente ayuda: 'format = 'raw'' o' format =' rgba''. Parece que producen el mismo resultado. –

+0

¿Has probado perfilando el código para ver dónde guarda savefig la mayor parte del tiempo? ¿Ha intentado reducir la resolución (parámetro dpi) u otros tipos de imágenes (jpeg, gif, tif, si es compatible)? – Bernhard

+0

@Bernhard: ¿Cómo lo hago? – relima

Respuesta

33

Si lo que desea es un buffer prima, tratar fig.canvas.print_rgb, fig.canvas.print_raw, etc (la diferencia entre los dos es que raw es RGBA, mientras que rgb es RGB. También hay print_png, print_ps, etc)

Esta voluntad utilice fig.dpi en lugar del valor de ppp predeterminado para savefig (100 ppp). Aún así, incluso comparando fig.canvas.print_raw(f) y fig.savefig(f, format='raw', dpi=fig.dpi), la versión print_canvas es ligeramente más rápida significativamente más rápida, ya que no molesta restablecer el color del parche del eje, etc., que savefig realiza de forma predeterminada.

Sin embargo, sin embargo, la mayor parte del tiempo dedicado a guardar una figura en un formato sin formato es solo dibujar la figura, que no hay forma de moverse.

En cualquier caso, como un ejemplo de la diversión insustancial, pero-, considere lo siguiente:

import matplotlib.pyplot as plt 
import numpy as np 
import cStringIO 

plt.ion() 
fig = plt.figure() 
ax = fig.add_subplot(111) 
num = 50 
max_dim = 10 
x = max_dim/2 * np.ones(num) 
s, c = 100 * np.random.random(num), np.random.random(num) 
scat = ax.scatter(x,x,s,c) 
ax.axis([0,max_dim,0,max_dim]) 
ax.set_autoscale_on(False) 

for i in xrange(1000): 
    xy = np.random.random(2*num).reshape(num,2) - 0.5 
    offsets = scat.get_offsets() + 0.3 * xy 
    offsets.clip(0, max_dim, offsets) 
    scat.set_offsets(offsets) 
    scat._sizes += 30 * (np.random.random(num) - 0.5) 
    scat._sizes.clip(1, 300, scat._sizes) 
    fig.canvas.draw() 

Brownian walk animation

Si nos fijamos en el tiempo de drenaje en bruto:

import matplotlib.pyplot as plt 
import numpy as np 
import cStringIO 

fig = plt.figure() 
ax = fig.add_subplot(111) 
num = 50 
max_dim = 10 
x = max_dim/2 * np.ones(num) 
s, c = 100 * np.random.random(num), np.random.random(num) 
scat = ax.scatter(x,x,s,c) 
ax.axis([0,max_dim,0,max_dim]) 
ax.set_autoscale_on(False) 

for i in xrange(1000): 
    xy = np.random.random(2*num).reshape(num,2) - 0.5 
    offsets = scat.get_offsets() + 0.3 * xy 
    offsets.clip(0, max_dim, offsets) 
    scat.set_offsets(offsets) 
    scat._sizes += 30 * (np.random.random(num) - 0.5) 
    scat._sizes.clip(1, 300, scat._sizes) 
    fig.canvas.draw() 

Este toma ~ 25 segundos en mi máquina.

Si en lugar de volcar un búfer RGBA prima a un búfer cStringIO, en realidad es ligeramente más rápido en ~ 22 segundos (Esto sólo es cierto porque estoy usando un backend interactiva De lo contrario sería equivalente!.):

import matplotlib.pyplot as plt 
import numpy as np 
import cStringIO 

fig = plt.figure() 
ax = fig.add_subplot(111) 
num = 50 
max_dim = 10 
x = max_dim/2 * np.ones(num) 
s, c = 100 * np.random.random(num), np.random.random(num) 
scat = ax.scatter(x,x,s,c) 
ax.axis([0,max_dim,0,max_dim]) 
ax.set_autoscale_on(False) 

for i in xrange(1000): 
    xy = np.random.random(2*num).reshape(num,2) - 0.5 
    offsets = scat.get_offsets() + 0.3 * xy 
    offsets.clip(0, max_dim, offsets) 
    scat.set_offsets(offsets) 
    scat._sizes += 30 * (np.random.random(num) - 0.5) 
    scat._sizes.clip(1, 300, scat._sizes) 
    ram = cStringIO.StringIO() 
    fig.canvas.print_raw(ram) 
    ram.close() 

Si comparamos esto con el uso de savefig, con un conjunto comparable dpi:

import matplotlib.pyplot as plt 
import numpy as np 
import cStringIO 

fig = plt.figure() 
ax = fig.add_subplot(111) 
num = 50 
max_dim = 10 
x = max_dim/2 * np.ones(num) 
s, c = 100 * np.random.random(num), np.random.random(num) 
scat = ax.scatter(x,x,s,c) 
ax.axis([0,max_dim,0,max_dim]) 
ax.set_autoscale_on(False) 

for i in xrange(1000): 
    xy = np.random.random(2*num).reshape(num,2) - 0.5 
    offsets = scat.get_offsets() + 0.3 * xy 
    offsets.clip(0, max_dim, offsets) 
    scat.set_offsets(offsets) 
    scat._sizes += 30 * (np.random.random(num) - 0.5) 
    scat._sizes.clip(1, 300, scat._sizes) 
    ram = cStringIO.StringIO() 
    fig.savefig(ram, format='raw', dpi=fig.dpi) 
    ram.close() 

esta toma ~ 23,5 segundos. Básicamente, savefig solo establece algunos parámetros predeterminados y llama a print_raw, en este caso, por lo que hay muy poca diferencia.

Ahora, si comparamos un formato de imágenes en bruto con un formato de imagen comprimido (PNG), vemos una diferencia mucho más importante:

import matplotlib.pyplot as plt 
import numpy as np 
import cStringIO 

fig = plt.figure() 
ax = fig.add_subplot(111) 
num = 50 
max_dim = 10 
x = max_dim/2 * np.ones(num) 
s, c = 100 * np.random.random(num), np.random.random(num) 
scat = ax.scatter(x,x,s,c) 
ax.axis([0,max_dim,0,max_dim]) 
ax.set_autoscale_on(False) 

for i in xrange(1000): 
    xy = np.random.random(2*num).reshape(num,2) - 0.5 
    offsets = scat.get_offsets() + 0.3 * xy 
    offsets.clip(0, max_dim, offsets) 
    scat.set_offsets(offsets) 
    scat._sizes += 30 * (np.random.random(num) - 0.5) 
    scat._sizes.clip(1, 300, scat._sizes) 
    ram = cStringIO.StringIO() 
    fig.canvas.print_png(ram) 
    ram.close() 

Esto tarda unos 52 segundos! Obviamente, hay una gran sobrecarga al comprimir una imagen.

En cualquier caso, esto es probablemente un ejemplo innecesariamente compleja ... Creo que sólo quería evitar el trabajo real ...

+0

Buen ejemplo Joe, incluso si podría ser excesivo. Me pregunto si ha guardado los fotogramas dibujados por cada iteración en el disco y luego los compiló sin conexión en un archivo .gif animado. ¿O de alguna manera se trata de compilar los marcos dibujados "in-stream" en un gif animado? No me refiero al uso del módulo $ animation $, ya que me gustaría guardar las animaciones producidas por gráficos interactivos (controlados por el evento del mouse). – achennu

+1

Bueno, hice algunas búsquedas y supongo que su sugerencia podría ser la que se muestra aquí: http://stackoverflow.com/a/14986894/467522, ¿verdad? – achennu

+0

En realidad, este gif en particular se creó simplemente guardando cada iteración y compilándolas sin conexión (con imagemagick's 'convert'). (Creo que este ejemplo es anterior al lanzamiento de una versión matplotlib con el módulo 'animation'.) En cualquier caso, debería ser posible usar' ffmpeg' para crear un gif animado, pero si recuerdo correctamente, guardarlo como un gif usando el módulo 'animation' no funciona correctamente. (Puede que esté recordando mal, y puede que ya se haya solucionado, pase lo que pase. Ha pasado un tiempo desde que lo intenté). –

2

que necesitaba para generar rápidamente una gran cantidad de parcelas también. Descubrí que el multiprocesamiento mejoró la velocidad de trazado con la cantidad de núcleos disponibles. Por ejemplo, si 100 tramas tomaron 10 segundos en un proceso, tardaron ~ 3 segundos cuando la tarea se dividió en 4 núcleos.

+7

¿puedes compartir tu código? – bigbug

Cuestiones relacionadas