desarrollo-web-br-bd.com

¿Generar vídeo o GIF animado mediante programación en Python?

Tengo una serie de imágenes de las que quiero crear un video. Idealmente, podría especificar una duración de cuadro para cada cuadro, pero una velocidad de cuadro fija también estaría bien. Estoy haciendo esto en wxPython, para poder renderizar a un wxDC o guardar las imágenes en archivos, como PNG. ¿Hay una biblioteca de Python que me permita crear un video (AVI, MPG, etc.) o un GIF animado a partir de estos marcos?

Edit: Ya probé PIL y parece que no funciona. ¿Puede alguien corregirme con esta conclusión o sugerir otro conjunto de herramientas? Este enlace parece respaldar mi conclusión respecto a la PIL: http://www.somethinkodd.com/oddthinking/2005/12/06/python-imaging-library-pil-and-animated-gifs/

172
FogleBird

Bueno, ahora estoy usando ImageMagick. Guardo mis marcos como archivos PNG y luego invoco el convert.exe de ImageMagick de Python para crear un GIF animado. Lo bueno de este enfoque es que puedo especificar una duración de cuadro para cada cuadro individualmente. Desafortunadamente, esto depende de que ImageMagick esté instalado en la máquina. Tienen un envoltorio de Python, pero se ve bastante horrible y no es compatible. Todavía abierto a otras sugerencias.

38
FogleBird

Recomiendo no usar images2gif de visvis porque tiene problemas con PIL/Almohada y no se mantiene activamente (debería saberlo porque soy el autor).

En su lugar, use imageio , que fue desarrollado para resolver este problema y más, y está destinado a permanecer.

Solución rápida y sucia:

import imageio
images = []
for filename in filenames:
    images.append(imageio.imread(filename))
imageio.mimsave('/path/to/movie.gif', images)

Para películas más largas, utilice el enfoque de transmisión:

import imageio
with imageio.get_writer('/path/to/movie.gif', mode='I') as writer:
    for filename in filenames:
        image = imageio.imread(filename)
        writer.append_data(image)
186
Almar

A partir de junio de 2009, la publicación del blog originalmente citada tiene un método para crear GIF animados en los comentarios . Descargue el script images2gif.py (anteriormente images2gif.py , actualización cortesía de @geographika).

Luego, para revertir los marcos en un gif, por ejemplo:

#!/usr/bin/env python

from PIL import Image, ImageSequence
import sys, os
filename = sys.argv[1]
im = Image.open(filename)
original_duration = im.info['duration']
frames = [frame.copy() for frame in ImageSequence.Iterator(im)]    
frames.reverse()

from images2gif import writeGif
writeGif("reverse_" + os.path.basename(filename), frames, duration=original_duration/1000.0, dither=0)
41
kostmo

Utilicé images2gif.py que fue fácil de usar. Aunque parecía duplicar el tamaño del archivo ...

26 archivos PNG de 110kb, esperaba 26 * 110kb = 2860kb, pero my_gif.GIF era de 5.7 mb

También debido a que el GIF era de 8 bits, los png de Niza se volvieron un poco borrosos en el GIF

Aquí está el código que utilicé:

__author__ = 'Robert'
from images2gif import writeGif
from PIL import Image
import os

file_names = sorted((fn for fn in os.listdir('.') if fn.endswith('.png')))
#['animationframa.png', 'animationframb.png', 'animationframc.png', ...] "

images = [Image.open(fn) for fn in file_names]

print writeGif.__doc__
# writeGif(filename, images, duration=0.1, loops=0, dither=1)
#    Write an animated gif from the specified images.
#    images should be a list of numpy arrays of PIL images.
#    Numpy images of type float should have pixels between 0 and 1.
#    Numpy images of other types are expected to have values between 0 and 255.


#images.extend(reversed(images)) #infinit loop will go backwards and forwards.

filename = "my_gif.GIF"
writeGif(filename, images, duration=0.2)
#54 frames written
#
#Process finished with exit code 0

Aquí están 3 de los 26 cuadros:

Here are 3 of the 26 frames

encogiendo las imágenes redujo el tamaño:

size = (150,150)
for im in images:
    im.thumbnail(size, Image.ANTIALIAS)

smaller gif

25
robert king

Para crear un video, puede usar opencv ,

#load your frames
frames = ...
#create a video writer
writer = cvCreateVideoWriter(filename, -1, fps, frame_size, is_color=1)
#and write your frames in a loop if you want
cvWriteFrame(writer, frames[i])
18
attwad

No es una biblioteca de Python, pero mencoder puede hacer eso: Codificación de múltiples archivos de imagen de entrada . Puedes ejecutar mencoder desde python así:

import os

os.system("mencoder ...")
5
willurd

Me encontré con esta publicación y ninguna de las soluciones funcionó, así que aquí está mi solución que funciona

Problemas con otras soluciones hasta el momento:
1) No hay una solución explícita en cuanto a cómo se modifica la duración
2) No hay solución para la iteración del directorio fuera de orden, que es esencial para los GIF
3) No hay explicación de cómo instalar imageio para python 3

Instala imageio de esta manera: python3 -m pip install imageio

Nota: querrá asegurarse de que sus marcos tengan algún tipo de índice en el nombre del archivo para que puedan ordenarse, de lo contrario no tendrá forma de saber dónde comienza o termina el GIF

import imageio
import os

path = '/Users/myusername/Desktop/Pics/' # on Mac: right click on a folder, hold down option, and click "copy as pathname"

image_folder = os.fsencode(path)

filenames = []

for file in os.listdir(image_folder):
    filename = os.fsdecode(file)
    if filename.endswith( ('.jpeg', '.png', '.gif') ):
        filenames.append(filename)

filenames.sort() # this iteration technique has no built in order, so sort the frames

images = list(map(lambda filename: imageio.imread(filename), filenames))

imageio.mimsave(os.path.join('movie.gif'), images, duration = 0.04) # modify duration as needed
5
Daniel McGrath

Como dijo Warren el año pasado , esta es una vieja pregunta. Como la gente todavía parece estar viendo la página, me gustaría redirigirlos a una solución más moderna. Al igual que blakev dijo aquí , hay un ejemplo de almohada en github .

 import ImageSequence
 import Image
 import gifmaker
 sequence = []

 im = Image.open(....)

 # im is your original image
 frames = [frame.copy() for frame in ImageSequence.Iterator(im)]

 # write GIF animation
 fp = open("out.gif", "wb")
 gifmaker.makedelta(fp, frames)
 fp.close()

Nota: este ejemplo está desactualizado (gifmaker no es un módulo importable, solo un script). La almohada tiene un GifImagePlugin (cuya fuente es en GitHub ), pero el documento en ImageSequence parece indicar un soporte limitado (solo lectura)

5
Nathan

Con windows7, python2.7, opencv 3.0, lo siguiente funciona para mí:

import cv2
import os

vvw           =   cv2.VideoWriter('mymovie.avi',cv2.VideoWriter_fourcc('X','V','I','D'),24,(640,480))
frameslist    =   os.listdir('.\\frames')
howmanyframes =   len(frameslist)
print('Frames count: '+str(howmanyframes)) #just for debugging

for i in range(0,howmanyframes):
    print(i)
    theframe = cv2.imread('.\\frames\\'+frameslist[i])
    vvw.write(theframe)
4
Jacek Słoma

Antigua pregunta, muchas buenas respuestas, pero aún podría haber interés en otra alternativa ...

El módulo numpngw que recientemente instalé en github ( https://github.com/WarrenWeckesser/numpngw ) puede escribir archivos PNG animados desde una gran cantidad de arrays. ( Actualizar : numpngw ahora está en pypi: https://pypi.python.org/pypi/numpngw .)

Por ejemplo, este script:

import numpy as np
import numpngw


img0 = np.zeros((64, 64, 3), dtype=np.uint8)
img0[:32, :32, :] = 255
img1 = np.zeros((64, 64, 3), dtype=np.uint8)
img1[32:, :32, 0] = 255
img2 = np.zeros((64, 64, 3), dtype=np.uint8)
img2[32:, 32:, 1] = 255
img3 = np.zeros((64, 64, 3), dtype=np.uint8)
img3[:32, 32:, 2] = 255
seq = [img0, img1, img2, img3]
for img in seq:
    img[16:-16, 16:-16] = 127
    img[0, :] = 127
    img[-1, :] = 127
    img[:, 0] = 127
    img[:, -1] = 127

numpngw.write_apng('foo.png', seq, delay=250, use_palette=True)

crea:

 animated png 

Necesitará un navegador que admita PNG animado (ya sea directamente o con un complemento) para ver la animación.

4
Warren Weckesser

Como un miembro mencionado anteriormente, imageio es una excelente manera de hacer esto. imageio también te permite establecer la velocidad de cuadros, y en realidad escribí una función en Python que te permite establecer una retención en el cuadro final. Utilizo esta función para animaciones científicas donde el bucle es útil pero el reinicio inmediato no lo es. Aquí está el enlace y la función:

Cómo hacer un GIF usando Python

import matplotlib.pyplot as plt
import os
import imageio

def gif_maker(gif_name,png_dir,gif_indx,num_gifs,dpi=90):
    # make png path if it doesn't exist already
    if not os.path.exists(png_dir):
        os.makedirs(png_dir)

    # save each .png for GIF
    # lower dpi gives a smaller, grainier GIF; higher dpi gives larger, clearer GIF
    plt.savefig(png_dir+'frame_'+str(gif_indx)+'_.png',dpi=dpi)
    plt.close('all') # comment this out if you're just updating the x,y data

    if gif_indx==num_gifs-1:
        # sort the .png files based on index used above
        images,image_file_names = [],[]
        for file_name in os.listdir(png_dir):
            if file_name.endswith('.png'):
                image_file_names.append(file_name)       
        sorted_files = sorted(image_file_names, key=lambda y: int(y.split('_')[1]))

        # define some GIF parameters

        frame_length = 0.5 # seconds between frames
        end_pause = 4 # seconds to stay on last frame
        # loop through files, join them to image array, and write to GIF called 'wind_turbine_dist.gif'
        for ii in range(0,len(sorted_files)):       
            file_path = os.path.join(png_dir, sorted_files[ii])
            if ii==len(sorted_files)-1:
                for jj in range(0,int(end_pause/frame_length)):
                    images.append(imageio.imread(file_path))
            else:
                images.append(imageio.imread(file_path))
        # the duration is the time spent on each image (1/duration is frame rate)
        imageio.mimsave(gif_name, images,'GIF',duration=frame_length)

 Example GIF using this method 

¿Has probado PyMedia ? No estoy 100% seguro, pero parece que este ejemplo de tutorial aborda su problema.

4
Jakub Šturc

Estoy publicando una respuesta más, ya que es más simple que todo lo publicado hasta ahora:

from PIL import Image

width = 300
height = 300
im1 = Image.new("RGBA", (width, height), (255, 0, 0))
im2 = Image.new("RGBA", (width, height), (255, 255, 0))
im3 = Image.new("RGBA", (width, height), (255, 255, 255))
im1.save("out.gif", save_all=True, append_images=[im2, im3], duration=100, loop=0)

utilizando imágenes existentes:

from PIL import Image

im1 = Image.open('a.png')
im2 = Image.open('b.png')
im3 = Image.open('c.png')
im1.save("out.gif", save_all=True, append_images=[im2, im3], duration=100, loop=0)

Y, como las versiones demasiado bajas de la almohada están fallando silenciosamente aquí es como una versión adicional con la versión de la biblioteca.

from packaging import version
from PIL import Image

im1 = Image.open('a.png')
im2 = Image.open('b.png')
im3 = Image.open('c.png')
if version.parse(Image.PILLOW_VERSION) < version.parse("3.4"):
    print("Pillow in version not supporting making animated gifs")
    print("you need to upgrade library version")
    print("see release notes in")
    print("https://pillow.readthedocs.io/en/latest/releasenotes/3.4.0.html#append-images-to-gif")
else:
    im1.save("out.gif", save_all=True, append_images=[
             im2, im3], duration=100, loop=0)
3
Mateusz Konieczny

Lo más fácil que lo hace funcionar para mí es llamar un comando de Shell en Python.

Si sus imágenes se almacenan como dummy_image_1.png, dummy_image_2.png ... dummy_image_N.png, entonces puede usar la función:

import subprocess
def grid2gif(image_str, output_gif):
    str1 = 'convert -delay 100 -loop 1 ' + image_str  + ' ' + output_gif
    subprocess.call(str1, Shell=True)

Solo ejecuta:

grid2gif("dummy_image*.png", "my_output.gif")

Esto construirá su archivo gif my_output.gif.

2
Pkjain

La tarea se puede completar ejecutando el script de python de dos líneas desde la misma carpeta que la secuencia de archivos de imágenes. Para archivos formateados en png el script es -

from scitools.std import movie
movie('*.png',fps=1,output_file='thisismygif.gif')
2
ArKE

Estaba buscando un código de línea única y encontré que lo siguiente funciona para mi aplicación. Aquí esta lo que hice:

Primer paso: Instale ImageMagick desde el siguiente enlace

https://www.imagemagick.org/script/download.php

 enter image description here 

Segundo paso: Señale la línea cmd a la carpeta donde se colocan las imágenes (en mi caso, formato .png)

 enter image description here 

Tercer paso: Escriba el siguiente comando

magick -quality 100 *.png outvideo.mpeg

 enter image description here 

Gracias FogleBird por la idea!

1
MedImage

Acabo de probar lo siguiente y fue muy útil:

Primero descargue las bibliotecas Figtodat y images2gif a su directorio local.

En segundo lugar, reúne las figuras en una matriz y conviértelas en un gif animado:

import sys
sys.path.insert(0,"/path/to/your/local/directory")
import Figtodat
from images2gif import writeGif
import matplotlib.pyplot as plt
import numpy

figure = plt.figure()
plot   = figure.add_subplot (111)

plot.hold(False)
    # draw a cardinal sine plot
images=[]
y = numpy.random.randn(100,5)
for i in range(y.shape[1]):
    plot.plot (numpy.sin(y[:,i]))  
    plot.set_ylim(-3.0,3)
    plot.text(90,-2.5,str(i))
    im = Figtodat.fig2img(figure)
    images.append(im)

writeGif("images.gif",images,duration=0.3,dither=0)
0
Cro-Magnon

Me topé con el módulo ImageSequencede PIL, que ofrece una mejor (y más estándar) información GIF. También uso el método after () de Tk esta vez, que es mejor que time.sleep ().

from Tkinter import * 
from PIL import Image, ImageTk, ImageSequence

def stop(event):
  global play
  play = False
  exit() 

root = Tk()
root.bind("<Key>", stop) # Press any key to stop
GIFfile = {path_to_your_GIF_file}
im = Image.open(GIFfile); img = ImageTk.PhotoImage(im)
delay = im.info['duration'] # Delay used in the GIF file 
lbl = Label(image=img); lbl.pack() # Create a label where to display images
play = True;
while play:
  for frame in ImageSequence.Iterator(im):
    if not play: break 
    root.after(delay);
    img = ImageTk.PhotoImage(frame)
    lbl.config(image=img); root.update() # Show the new frame/image

root.mainloop()
0
Apostolos