Програмно генерувати відео або анімований GIF у Python?


221

У мене є серія зображень, з яких я хочу створити відео. В ідеалі я міг би вказати тривалість кадру для кожного кадру, але фіксована частота кадрів теж буде добре. Я роблю це в wxPython, тому я можу передати в wxDC або я можу зберегти зображення у файли, як PNG. Чи існує бібліотека Python, яка дозволить мені створити або відео (AVI, MPG тощо), або анімований GIF з цих кадрів?

Редагувати: Я вже спробував PIL, і він, схоже, не працює. Чи може хтось виправити мене з цим висновком чи запропонувати інший інструментарій? Це посилання, начебто, підкріплює мій висновок щодо PIL: http://www.somethinkodd.com/oddthinking/2005/12/06/python-imaging-library-pil-and-animated-gifs/

Відповіді:


281

Я рекомендую не використовувати images2gif від visvis, оскільки він має проблеми з PIL / Pillow і не підтримується активно (я повинен знати, тому що я автор).

Замість цього, будь ласка, використовуйте imageio , розроблений для вирішення цієї проблеми та багато іншого, який має на меті залишитися.

Швидке і брудне рішення:

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

Для більш тривалих фільмів використовуйте потоковий підхід:

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)

37
також тривалість параметра = 0,5 встановлює тривалість 0,5 сек для кожного кадру.
Аллео

3
ValueError: Не вдалося знайти формат для читання зазначеного файлу в режимі 'i' - я отримую цю помилку у Windows 2.7 winpython. Будь-які підказки?
Ванько

1
@Vanko, здається, помилка пов'язана з читанням файлу, ви можете спробувати imagio.mimread, або якщо його фільм з багатьма кадрами, використовуйте об'єкт для читання, як тут: imageio.readthedocs.io/en/latest/…
Алмар

2
@Alleo: "також параметр тривалість = 0,5 встановлює тривалість 0,5 сек для кожного кадру". Є функція тривалості для imageio? Якщо так, то де це документально підтверджено? Я прочитав усі документи і не зміг згадати аргумент тривалості.
Кріс Нільсен

3
Відмінно! imageio in anacondaврожайність True, так!
uhoh

47

Ну, а тепер я використовую ImageMagick. Я зберігаю свої кадри як PNG-файли, а потім викликаю ImageMagick’s convert.exe з Python, щоб створити анімований GIF. Приємно в цьому підході - я можу вказати тривалість кадру для кожного кадру окремо. На жаль, це залежить від встановлення ImageMagick на машині. У них є обгортка Python, але це виглядає досить шалено і не підтримується. Досі відкриті для інших пропозицій.


21
Я хлопець Python, але знайшов ImageMagick набагато простіше тут. Я щойно робив свою послідовність зображень і запускав щось на кшталтconvert -delay 20 -loop 0 *jpg animated.gif
Нік

Я погоджуюся, це найкраще рішення, яке я натрапив. Ось мінімальний приклад (на основі прикладу коду користувача Стіва Б, розміщеного на сайті stackoverflow.com/questions/10922285/… ): pastebin.com/JJ6ZuXdz
andreasdr

Використовуючи ImageMagick, ви також можете легко змінити розмір анімованих gif-файлів, таких якconvert -delay 20 -resize 300x200 -loop 0 *jpg animated.gif
Jun Wang

@Nick, як запустити цей код, щоб створити GIF? Чи потрібно імпортувати що-небудь у ID Spyder?
МІН

@MOON Команда ImageMagic, яку я додала вище, просто виконується через командний рядок.
Нік

43

Станом на червень 2009 р. У цитованій публікації в блозі є метод створення анімованих GIF у коментарях . Завантажте сценарій images2gif.py (раніше images2gif.py , оновіть люб’язно @geographika).

Потім, щоб повернути кадри в gif, наприклад:

#!/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)

2
Існує нова версія цього сценарію, яка забезпечує набагато кращу якість виводу на visvis.googlecode.com/hg/vvmovie/images2gif.py, його можна використовувати як окремий сценарій окремо від пакета.
geographika

1
Сценарій, згаданий у цьому коментарі, послідовно дає помилку сегментації для мене, коли використовується на Mac, навіть коли просто запускається (використовуючи ім'я __ == '__ головний ' приклад). Я пробую сценарій, згаданий у відповіді, сподіваючись, що він справно працює. EDIT - я можу підтвердити, що сценарій, на який посилається у відповіді вище, працює правильно на моєму Mac.
scubbo

6
Замість того, щоб просто завантажити сценарій, використовуйте pip, наприклад pip install visvis, тоді у своєму сценарії from visvis.vvmovie.images2gif import writeGif.
Даніель Фаррелл

8
Я спробував це з Python 2.7.3 на Windows 8, і я отримав UnicodeDecodeError: 'ascii' кодек не може розшифрувати байт 0xc8 в позиції 6: порядковий не знаходиться в діапазоні (128). З запущеного python images2gif.py
reckoner

3
Я автор visivis (і images2gif) і рекомендую не використовувати його для цієї мети. Я працював над кращим рішенням у рамках проекту imageio (див. Мою відповідь).
Альмар

40

Ось як це зробити, використовуючи лише PIL (встановити за допомогою :)pip install Pillow :

import glob
from PIL import Image

# filepaths
fp_in = "/path/to/image_*.png"
fp_out = "/path/to/image.gif"

# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#gif
img, *imgs = [Image.open(f) for f in sorted(glob.glob(fp_in))]
img.save(fp=fp_out, format='GIF', append_images=imgs,
         save_all=True, duration=200, loop=0)


4
це має бути прийнята відповідь, дякую @Kris
ted930511

1
Що містить змінна зірочка ("* imgs")?
denisb411

3
Це особливість мови пітона. Це робить ітерабельне розпакування . Ви можете приблизно сприймати це як розпакування, x = [a, b, c]до *xякого можна вважати a, b, c(без доданих дужок). У викликах функцій вони є синонімами: f(*x) == f(a, b, c). Для розпакування кортежу це особливо корисно у випадках, коли ви хочете розділити ітерабельний на голову (перший елемент) та хвіст (решта), що я роблю в цьому прикладі.
Кріс

25

Я використовував images2gif.py, який був простий у використанні. Здавалося, подвійний розмір файлу, хоча ..

26 файлів PNG у розмірі 110 Кбіт, я очікував 26 * 110 кб = 2860 кб, але мій_gif.GIF був 5,7 Мб

Крім того, оскільки GIF був 8-ти бітним, хороший png став трохи нечітким у GIF

Ось код, який я використав:

__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

Ось 3 з 26 кадрів:

Ось 3 з 26 кадрів

скорочення зображень зменшило розмір:

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

менший gif


Я зробив повідомлення про це в блозі .. robert-king.com/#post2-python-makes-gif
Роберт Кінг

2
Я отримую помилки .. Файл "C: \ Python27 \ lib \ images2gif.py", рядок 418, в writeGifToFile globalPalette = палітри [enter.index (max (відбувається))] ValueError: max () arg - порожня послідовність
Гаррі

відбуваються, ймовірно, порожні. У моєму файлі images2gif.py немає змінної "globalPalette".
Роберт Кінг

як я це можу змінити? Я використовую найсвіжіший скрипт images2gif.py там ( bit.ly/XMMn5h )
Гаррі

4
@robertking з кодом я отримав помилку кажучиfp.write(globalPalette) TypeError: must be string or buffer, not list
LWZ

19

Щоб створити відео, ви можете використовувати 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])

9

Я натрапив на цю посаду, і жодне з рішень не працювало, тому ось моє рішення, яке працює

Проблеми з іншими рішеннями поки що:
1) Немає чіткого рішення щодо модифікації тривалості
2) Немає рішення для ітерації каталогів поза порядком, що важливо для GIF
3) Немає пояснення того, як встановити imageio для python 3

встановити imageio так: python3 -m pip встановити imageio

Примітка. Ви хочете переконатися, що у ваших кадрах є якийсь індекс у назві файлу, щоб їх можна було сортувати, інакше ви не зможете знати, де починається чи закінчується 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

1
sortможе дати несподівані результати, якщо ваша схема нумерації не включає провідні нулі. Також чому ви використовували карту замість простого розуміння списку?
NOhs

Я б запропонував зробитиfilenames.append(os.path.join(path, filename))
trueter

Охороняючи Нош, images = [imageio.imread(f) for f in filenames]чистіший, швидший і пітонічніший.
Брендон Дюб

6

Як сказав Уоррен минулого року , це старе питання. Оскільки люди все ще переглядають сторінку, я хотів би перенаправити їх на більш сучасне рішення. Як сказав blakev тут , є приклад подушки на 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()

Примітка. Цей приклад застарів ( gifmakerне модуль, який можна імпортувати, лише сценарій). У подушці є GifImagePlugin (джерело якого знаходиться на GitHub ), але документ у ImageSequence, схоже, вказує на обмежену підтримку (лише для читання)



5

Старе запитання, багато хороших відповідей, але все ж може бути інтерес до іншої альтернативи ...

numpngwМодуль , який я недавно поставив на GitHub ( https://github.com/WarrenWeckesser/numpngw ) можна записати анімовані файли PNG з Numpy масивів. ( Оновлення : numpngwзараз на pypi: https://pypi.python.org/pypi/numpngw .)

Наприклад, цей сценарій:

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)

створює:

анімовані PNG

Для перегляду анімації вам знадобиться веб-переглядач, який підтримує анімаційний PNG (безпосередньо або з плагіном).


Зараз це також робить Chrome, BTW. Одне питання - чи може послідовність бути ітерабельною? Чи підтримуєте ви "потокове передавання" (тобто відкриття цільового APNG та додавання кадрів по черзі в циклі)?
Томаш Гандор

Він не підтримує довільний ітерабельний або потоковий протокол, але це не означає, що він не міг би в майбутньому. :) Створіть проблему на сторінці github із запропонованим вдосконаленням. Якщо у вас є якісь ідеї щодо API цієї функції, опишіть їх у випуску.
Warren Weckesser

У мене була якась дивна помилка, яку я випустив на ваш репо.
mLstudent33

5

Як згадував один з членів, імідже - чудовий спосіб зробити це. imageio також дозволяє встановити частоту кадрів, і я фактично написав функцію в Python, яка дозволяє встановити затримку на кінцевому кадрі. Я використовую цю функцію для наукових анімацій, де циклічно корисно, але негайний перезапуск - це не так. Ось посилання та функція:

Як зробити GIF за допомогою 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)

Приклад GIF з використанням цього методу



4

З windows7, python2.7, opencv 3.0 для мене працює наступне:

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)

3

Найпростіша річ, яка змушує мене працювати, - це викликати команду shell в Python.

Якщо ваші зображення зберігаються на зразок dummy_image_1.png, dummy_image_2.png ... dummy_image_N.png, ви можете використовувати функцію:

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

Просто виконайте:

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

Це створить ваш gif-файл my_output.gif.


2

Завдання можна виконати, запустивши дворядковий сценарій python із тієї ж папки, що і послідовність файлів зображень. Для файлів, відформатованих у форматі png, сценарій -

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

1
Спробував ... не працював для мене під Python 2.6. Повернуто: "Функція scitools.easyviz.movie виконує команду: / convert -delay 100 g4testC _ *. Png g4testC.gif / Недійсний параметр - 100"
Dan H

Проблема не з Python точно. Перевстановіть Imagemagick у вашу систему та повторіть спробу.
АрКЕ

2

Я шукав код одного рядка і виявив, що працює над моєю програмою. Ось що я зробив:

Перший крок: встановіть ImageMagick за посиланням нижче

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

введіть тут опис зображення

Другий крок: Наведіть рядок cmd на папку, де розміщуються зображення (у моєму випадку .png формат)

введіть тут опис зображення

Третій крок: Введіть таку команду

magick -quality 100 *.png outvideo.mpeg

введіть тут опис зображення

Дякуємо FogleBird за ідею!


0

Я просто спробував наступне і був дуже корисний:

Спочатку завантажте бібліотеки Figtodatта images2gifу свій локальний каталог.

По-друге, зібрати фігури в масив і перетворити їх в анімований gif:

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

Я натрапив на модуль ImageSequence PIL , який пропонує кращу (і більш стандартну) анімацію GIF. Я також використовую метод Tk після () цього разу, що краще, ніж 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

Проста функція, яка створює GIF:

import imageio
import pathlib
from datetime import datetime


def make_gif(image_directory: pathlib.Path, frames_per_second: float, **kwargs):
    """
    Makes a .gif which shows many images at a given frame rate.
    All images should be in order (don't know how this works) in the image directory

    Only tested with .png images but may work with others.

    :param image_directory:
    :type image_directory: pathlib.Path
    :param frames_per_second:
    :type frames_per_second: float
    :param kwargs: image_type='png' or other
    :return: nothing
    """
    assert isinstance(image_directory, pathlib.Path), "input must be a pathlib object"
    image_type = kwargs.get('type', 'png')

    timestampStr = datetime.now().strftime("%y%m%d_%H%M%S")
    gif_dir = image_directory.joinpath(timestampStr + "_GIF.gif")

    print('Started making GIF')
    print('Please wait... ')

    images = []
    for file_name in image_directory.glob('*.' + image_type):
        images.append(imageio.imread(image_directory.joinpath(file_name)))
    imageio.mimsave(gif_dir.as_posix(), images, fps=frames_per_second)

    print('Finished making GIF!')
    print('GIF can be found at: ' + gif_dir.as_posix())


def main():
    fps = 2
    png_dir = pathlib.Path('C:/temp/my_images')
    make_gif(png_dir, fps)

if __name__ == "__main__":
    main()

0

Я розумію, ви запитували про перетворення зображень у gif; однак, якщо вихідний формат MP4, ви можете використовувати FFmpeg :

ffmpeg -i input.mp4 output.gif

-1

Це справді неймовірно ... Усі пропонують якийсь спеціальний пакет для відтворення анімаційного GIF, на даний момент, що це можна зробити за допомогою Tkinter та класичного модуля PIL!

Ось мій власний метод анімації GIF (я створив деякий час тому). Дуже просто:

from Tkinter import * 
from PIL import Image, ImageTk
from time import sleep

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 = float(im.info['duration'])/1000; # Delay used in the GIF file 
lbl = Label(image=img); lbl.pack() # Create a label where to display images
play = True; frame = 0
while play:
  sleep(delay);
  frame += 1
  try:
    im.seek(frame); img = ImageTk.PhotoImage(im)
    lbl.config(image=img); root.update() # Show the new frame/image
  except EOFError:
    frame = 0 # Restart

root.mainloop()

Ви можете встановити власні засоби, щоб зупинити анімацію. Повідомте мене, чи хочете ви отримати повну версію за допомогою кнопок play / pause / quit.

Примітка. Я не впевнений, чи читаються послідовні кадри з пам'яті або з файлу (диска). У другому випадку було б ефективніше, якби всі вони прочитали відразу і збереглися в масив (список). (Мені не так цікаво дізнатися! :)


1
Зазвичай, це не дуже вдалий ідеал для виклику sleepв основній темі графічного інтерфейсу. Ви можете використовувати afterметод для періодичного виклику функції.
Брайан Оуклі

Ви маєте рацію, але це зовсім не в цьому, чи не так? Справа в цілому методі. Тож я б швидше очікував реакції на це!
Апостолос

1
Я просто намагався дати пораду, як покращити вашу відповідь.
Брайан Оуклі

До речі, я зазвичай використовую tk.after()себе. Але тут мені потрібно було зробити код максимально простим. Кожен, хто використовує цей метод анімації GIF, може застосувати власну функцію затримки.
Апостолос

Нарешті! Так, це справді дуже вдалий момент. Я був поза темою! Дякую, @Novel. (Цікаво побачити, як інші пропустили це, наприклад, Брайан, який говорив про метод затримки часу!)
Апостолос
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.