Me gustaría crear un h264 o películas divx de tramas que puedo generar en una secuencia de comandos de python en matplotlib. Hay alrededor de 100 mil marcos en esta película.

De ejemplos en la web [por ejemplo. 1], sólo he visto el método de ahorro de cada fotograma como una imagen png y, a continuación, ejecuta mencoder o ffmpeg en estos archivos. En mi caso, el ahorro de cada fotograma es poco práctico. Es allí una manera de tomar una parcela generados a partir de matplotlib y el tubo directamente a ffmpeg, lo que genera sin los archivos intermedios?

Programación con ffmpeg del C-api es demasiado difícil para mí [por ejemplo. 2]. Además, tengo una codificación que tiene un buen nivel de compresión, tales como x264 como el archivo de la película de otra manera va a ser demasiado grande para un paso posterior. Así que sería de gran palo con mencoder y ffmpeg/x264.

Hay algo que se puede hacer con tubos [3]?

[1] http://matplotlib.sourceforge.net/examples/animation/movie_demo.html

[2] ¿Cómo hace uno para codificar una serie de imágenes en H264 utilizando el x264 de la API en C?

[3] http://www.ffmpeg.org/ffmpeg-doc.html#SEC41

  • Todavía tengo que encontrar una manera de hacer esto con la actualidad mantiene las bibliotecas… (yo usé pymedia en el pasado, pero ya no se mantiene, y no construir en cualquier sistema que yo uso…) Si le ayuda, usted puede conseguir un RGB búfer de un matplotlib figura mediante buffer = fig.canvas.tostring_rgb(), y la anchura y la altura de la figura en píxeles con fig.canvas.get_width_height() (o fig.bbox.width, etc)
  • OK, gracias. Que es útil. Me pregunto si algunos de transformación de búfer puede ser canalizada a ffmpeg. pyffmpeg tiene un sofisticado Cython contenedor, recientemente actualizado, para la lectura de un avi fotograma por fotograma. Pero no la escritura. Que suena como un posible lugar para comenzar para alguien familiarizado con la librería ffmpeg. Incluso algo como de matlab im2frame sería genial.
  • Estoy jugando con tener ffmpeg leer desde la entrada de la tubería (con la -f image2pipe opción de manera que se espera una serie de imágenes), o de un socket local (por ejemplo,udp://localhost:some_port) y escribir en el socket en python… hasta ahora, sólo un éxito parcial… me siento como que estoy casi allí, aunque… es solo que no estoy lo suficientemente familiarizado con ffmpeg…
  • Para lo que vale, mi problema era debido a un problema con ffmpeg la aceptación de una corriente de .png o raw RGB buffers, (hay un bug que ya se presentó: roundup.ffmpeg.org/issue1854) funciona si utiliza los archivos jpeg. (Uso ffmpeg -f image2pipe -vcodec mjpeg -i - ouput.whatever. Usted puede abrir una subprocess.Popen(cmdstring.split(), stdin=subprocess.PIPE) y escribir cada fotograma para su stdin) voy a publicar un ejemplo más detallado si se me presenta una oportunidad…
  • Que genial! Voy a probar esta mañana.
  • Como comentario, este es ahora horneados en matplotlib (véase mi respuesta a continuación)

InformationsquelleAutor Paul | 2010-11-04

6 Comentarios

  1. 50

    Esta funcionalidad es ahora (al menos a partir de la versión 1.2.0, quizás 1.1) se cocía en matplotlib a través de la MovieWriter de clases y sub-clases en el animation módulo. También es necesario instalar ffmpeg por adelantado.

    import matplotlib.animation as animation
    import numpy as np
    from pylab import *
    
    
    dpi = 100
    
    def ani_frame():
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.set_aspect('equal')
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)
    
        im = ax.imshow(rand(300,300),cmap='gray',interpolation='nearest')
        im.set_clim([0,1])
        fig.set_size_inches([5,5])
    
    
        tight_layout()
    
    
        def update_img(n):
            tmp = rand(300,300)
            im.set_data(tmp)
            return im
    
        #legend(loc=0)
        ani = animation.FuncAnimation(fig,update_img,300,interval=30)
        writer = animation.writers['ffmpeg'](fps=30)
    
        ani.save('demo.mp4',writer=writer,dpi=dpi)
        return ani

    Documentación para animación

    • Es allí una manera de registrar ciertos ejes, no para el total de la figura? Especialmente, con FFMpegFileWriter?
    • No, el ámbito en el que puede guardar los cuadros es la Figura de alcance (lo mismo es cierto para savefig).
  2. 20

    Después de la revisión de ffmpeg (ver a Joe Kington comentarios a mi pregunta), que era capaz de sacar la tubería de png a ffmpeg como sigue:

    import subprocess
    import numpy as np
    import matplotlib
    matplotlib.use('Agg')
    import matplotlib.pyplot as plt
    
    outf = 'test.avi'
    rate = 1
    
    cmdstring = ('local/bin/ffmpeg',
                 '-r', '%d' % rate,
                 '-f','image2pipe',
                 '-vcodec', 'png',
                 '-i', 'pipe:', outf
                 )
    p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE)
    
    plt.figure()
    frames = 10
    for i in range(frames):
        plt.imshow(np.random.randn(100,100))
        plt.savefig(p.stdin, format='png')

    Que no funcionaría sin la parche, que trivialmente modifica dos archivos y agrega libavcodec/png_parser.c. Tuve que aplicar manualmente el parche para libavcodec/Makefile. Por último, he quitado ‘número’ de Makefile para obtener las páginas man para construir. Con opciones de compilación,

    FFmpeg version 0.6.1, Copyright (c) 2000-2010 the FFmpeg developers
      built on Nov 30 2010 20:42:02 with gcc 4.2.1 (Apple Inc. build 5664)
      configuration: --prefix=/Users/paul/local_test --enable-gpl --enable-postproc --enable-swscale --enable-libxvid --enable-libx264 --enable-nonfree --mandir=/Users/paul/local_test/share/man --enable-shared --enable-pthreads --disable-indevs --cc=/usr/bin/gcc-4.2 --arch=x86_64 --extra-cflags=-I/opt/local/include --extra-ldflags=-L/opt/local/lib
      libavutil     50.15. 1 / 50.15. 1
      libavcodec    52.72. 2 / 52.72. 2
      libavformat   52.64. 2 / 52.64. 2
      libavdevice   52. 2. 0 / 52. 2. 0
      libswscale     0.11. 0 /  0.11. 0
      libpostproc   51. 2. 0 / 51. 2. 0
    • Muy bien hecho! +1 (nunca fui capaz de conseguir ffmpeg para que acepten un flujo de .png, creo que tengo que actualizar mi versión de ffmpeg…) Y, sólo en caso de que se preguntan, que está perfectamente aceptable para marcar su respuesta como la respuesta a su pregunta. Véase la discusión aquí: meta.stackexchange.com/questions/17845/…
    • Ok, me lo marca como respondió. Gracias por los consejos de nuevo.
    • Wow, genial. He estado tratando de hacer la misma cosa.
    • Hola @Pablo, el parche enlace está muerto. ¿Sabe usted si se ha absorbido en la rama principal? Si no hay alguna manera de conseguir que el parche?
    • Supongo que el parche ha sido absorbido en el siguiente post: superuser.com/questions/426193/…
    • Podría usted por favor, actualice su respuesta (y quitar mis ediciones)
    • He cambiado la respuesta a tu respuesta (no sabía que era posible.) Puede usted por favor realizar las necesarias modificaciones?
    • A lo que me refería era para editar tu pregunta para reflejar la nueva funcionalidad, pero esto funciona. Me ha deshecho mi edición. ¿Estás contento con el estado de las cosas?
    • Lo que yo veo. Bien, se ve bien ahora. Cualquier persona tratando de averiguar esto será dirigido a su respuesta, en vez de tratar de parche de ffmpeg. Gracias por tu solución.

  3. 13

    La conversión a los formatos de imagen es bastante lento y añade las dependencias. Después de ver estos y otros tengo que trabajar con raw no codificado buffers usando mencoder (ffmpeg solución todavía quería).

    Detalles en: http://vokicodder.blogspot.com/2011/02/numpy-arrays-to-video.html

    import subprocess
    
    import numpy as np
    
    class VideoSink(object) :
    
        def __init__( self, size, filename="output", rate=10, byteorder="bgra" ) :
                self.size = size
                cmdstring  = ('mencoder',
                        '/dev/stdin',
                        '-demuxer', 'rawvideo',
                        '-rawvideo', 'w=%i:h=%i'%size[::-1]+":fps=%i:format=%s"%(rate,byteorder),
                        '-o', filename+'.avi',
                        '-ovc', 'lavc',
                        )
                self.p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE, shell=False)
    
        def run(self, image) :
                assert image.shape == self.size
                self.p.stdin.write(image.tostring())
        def close(self) :
                self.p.stdin.close()

    Tengo algunas buenas mejoras.

    • He modificado esta para ffmpeg, véase mi respuesta a continuación si usted todavía desea
  4. 8

    Todas estas son muy grandes respuestas. He aquí otra sugerencia. @user621442 es correcto que el cuello de botella es típicamente la escritura de la imagen, así que si usted está escribiendo archivos png para su compresor de vídeo, va a ser bastante lento (incluso si se les envía a través de una tubería, en lugar de escribir en el disco). He encontrado una solución usando puro ffmpeg, que personalmente me parece más fácil de usar que matplotlib.la animación o mencoder.

    También, en mi caso, yo quería simplemente guardar la imagen en un eje, en lugar de salvar a todos los rótulos de marcas de graduación, en la figura del título, la figura de fondo, etc. Básicamente, quería hacer una película de animación utilizando matplotlib código, pero que no tiene «aspecto de un gráfico». He incluido ese código aquí, pero usted puede hacer que el estándar de gráficos y canalizarlas a ffmpeg, si así lo desea.

    import matplotlib.pyplot as plt
    import subprocess
    
    # create a figure window that is the exact size of the image
    # 400x500 pixels in my case
    # don't draw any axis stuff ... thanks to @Joe Kington for this trick
    # https://stackoverflow.com/questions/14908576/how-to-remove-frame-from-matplotlib-pyplot-figure-vs-matplotlib-figure-frame
    f = plt.figure(frameon=False, figsize=(4, 5), dpi=100)
    canvas_width, canvas_height = f.canvas.get_width_height()
    ax = f.add_axes([0, 0, 1, 1])
    ax.axis('off')
    
    def update(frame):
        # your matplotlib code goes here
    
    # Open an ffmpeg process
    outf = 'ffmpeg.mp4'
    cmdstring = ('ffmpeg', 
        '-y', '-r', '30', # overwrite, 30fps
        '-s', '%dx%d' % (canvas_width, canvas_height), # size of image string
        '-pix_fmt', 'argb', # format
        '-f', 'rawvideo',  '-i', '-', # tell ffmpeg to expect raw video from the pipe
        '-vcodec', 'mpeg4', outf) # output encoding
    p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE)
    
    # Draw 1000 frames and write to the pipe
    for frame in range(1000):
        # draw the frame
        update(frame)
        plt.draw()
    
        # extract the image as an ARGB string
        string = f.canvas.tostring_argb()
    
        # write to pipe
        p.stdin.write(string)
    
    # Finish up
    p.communicate()
    • Este es un muy limpio camino a seguir y el que he usado. En lo que es ejecución de un script, usted necesitará hacer un par de mods. A la derecha en la parte superior de la secuencia de comandos, primeras líneas, agregue el siguiente: import matplotlib, a continuación, establezca el backend con matplotlib.use('agg', warn = False, force = True) El único otro mod es para reemplazar plt.draw() en el código original, encima con f.canvas.draw() Estos son necesarios para que funcione en una secuencia de comandos. De lo contrario, el código es sólo dandy como es.
  5. 5

    Esto es genial! Yo quería hacer lo mismo. Pero, yo nunca podría compilar el parcheado ffmpeg fuente (0.6.1) en la Vista con MingW32+MSYS+pr ambiente… png_parser.c producido Error1 durante la compilación.

    Así que se me ocurrió con un jpeg solución a este uso de PIL. Sólo hay que poner su ffmpeg.exe en la misma carpeta que esta secuencia de comandos. Esto debería funcionar con ffmpeg sin el parche en Windows. Tuve que usar stdin.método de la escritura en lugar de la de comunicarse método que se recomienda en la documentación oficial acerca de subproceso. Tenga en cuenta que la 2ª-vcodec opción especifica la codificación de vídeo. El tubo está cerrado por p.stdin.close().

    import subprocess
    import numpy as np
    from PIL import Image
    
    rate = 1
    outf = 'test.avi'
    
    cmdstring = ('ffmpeg.exe',
                 '-y',
                 '-r', '%d' % rate,
                 '-f','image2pipe',
                 '-vcodec', 'mjpeg',
                 '-i', 'pipe:', 
                 '-vcodec', 'libxvid',
                 outf
                 )
    p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE, shell=False)
    
    for i in range(10):
        im = Image.fromarray(np.uint8(np.random.randn(100,100)))
        p.stdin.write(im.tostring('jpeg','L'))
        #p.communicate(im.tostring('jpeg','L'))
    
    p.stdin.close()
  6. 0

    Aquí es una versión modificada de @tacaswell ‘s respuesta. Modificado lo siguiente:

    1. No requieren la pylab dependencia
    2. Arreglar varios lugares s.t. esta función es directamente ejecutable. (El original no se puede copiar-y-pegar-y-ejecutar directamente y tenemos que fijar en varios lugares).

    Muchas gracias por @tacaswell ‘s maravilloso respuesta!!!

    def ani_frame():
        def gen_frame():
            return np.random.rand(300, 300)
    
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.set_aspect('equal')
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)
    
        im = ax.imshow(gen_frame(), cmap='gray', interpolation='nearest')
        im.set_clim([0, 1])
        fig.set_size_inches([5, 5])
    
        plt.tight_layout()
    
        def update_img(n):
            tmp = gen_frame()
            im.set_data(tmp)
            return im
    
        # legend(loc=0)
        ani = animation.FuncAnimation(fig, update_img, 300, interval=30)
        writer = animation.writers['ffmpeg'](fps=30)
    
        ani.save('demo.mp4', writer=writer, dpi=72)
        return ani

Dejar respuesta

Please enter your comment!
Please enter your name here