以编程方式在Python中生成视频或动画GIF?

Fog*_*ird 188 python video wxpython animated-gif

我有一系列想要制作视频的图像.理想情况下,我可以为每个帧指定帧持续时间,但固定的帧速率也可以.我在wxPython中这样做,所以我可以渲染到wxDC,或者我可以将图像保存到文件,如PNG.是否有Python库允许我从这些帧创建视频(AVI,MPG等)或动画GIF?

编辑:我已经尝试过PIL,它似乎不起作用.有人可以用这个结论纠正我或建议另一个工具包吗?这个链接似乎支持我关于PIL的结论:http://www.somethinkodd.com/oddthinking/2005/12/06/python-imaging-library-pil-and-animated-gifs/

Alm*_*mar 231

我建议不要使用visvis的images2gif,因为它有PIL/Pillow的问题而且没有主动维护(我应该知道,因为我是作者).

相反,请使用imageio,它是为解决这个问题而开发的,并且旨在留下来.

快速而肮脏的解决方

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

对于较长的电影,请使用流媒体方法:

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)
Run Code Online (Sandbox Code Playgroud)

  • 参数持续时间= 0.5也设置每帧的0.5秒持续时间. (31认同)
  • 请注意,“imageio”不适用于具有透明层的图像。必须以艰难的方式学会这一点...... (6认同)
  • ValueError:找不到在'i'模式下读取指定文件的格式-我在Windows 2.7 winpython上遇到此错误。有什么线索吗? (3认同)
  • 优秀!`anaconda`中的imageio产生'真',是的! (3认同)
  • @ChrisNielsen,'duration = 0.5'进入第2行。 imageio.get_writer('/path/to/movie.gif', mode='I', **duration = 0.5**) 作为作者: (3认同)
  • @Alleo:“也参数duration=0.5 设置每帧0.5 秒的持续时间”。imageio 有持续时间功能吗?如果是这样,这是在哪里记录的?我阅读了所有文档,但找不到任何关于持续时间参数的提及。 (2认同)

Fog*_*ird 42

好吧,现在我正在使用ImageMagick.我将帧保存为PNG文件,然后从Python调用ImageMagick的convert.exe来创建动画GIF.这种方法的好处是我可以单独为每个帧指定帧持续时间.不幸的是,这取决于ImageMagick安装在机器上.他们有一个Python包装器,但它看起来很糟糕且不受支持.仍然接受其他建议.

  • 我是一个Python人,但在这里发现ImageMagick更容易.我只是制作了我的图像序列并运行了类似`convert -delay 20 -loop 0*jpg animated.gif`的内容 (19认同)

kos*_*tmo 42

截至2009年6月,最初引用的博客文章有一种方法可以在评论中创建动画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)
Run Code Online (Sandbox Code Playgroud)

  • 我在Windows 8上尝试使用Python 2.7.3并得到UnicodeDecodeError:'ascii'编解码器无法解码位置6中的字节0xc8:序数不在范围内(128).从运行python images2gif.py (8认同)
  • 而不是只是下载脚本使用pip例如`pip install visvis`,然后在你的脚本`来自visvis.vvmovie.images2gif import writeGif`. (5认同)
  • 我是visivis(和images2gif)的作者,并建议不要将其用于此目的.作为imageio项目的一部分,我一直在研究更好的解决方案(参见我的回答). (3认同)
  • 这个脚本的新版本在http://visvis.googlecode.com/hg/vvmovie/images2gif.py上提供了更好的输出质量,它可以用作独立于程序包的独立脚本. (2认同)

rob*_*ing 25

我使用了易于使用的images2gif.py.它似乎确实加倍文件大小..

26个110kb PNG文件,我预计26*110kb = 2860kb,但my_gif.GIF是5.7mb

另外因为GIF是8bit,所以很好的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
Run Code Online (Sandbox Code Playgroud)

这是26帧中的3帧:

这是26帧中的3帧

缩小图像缩小了尺寸:

size = (150,150)
for im in images:
    im.thumbnail(size, Image.ANTIALIAS)
Run Code Online (Sandbox Code Playgroud)

较小的GIF

  • @robertking与代码我得到一个错误说`fp.write(globalPalette)TypeError:必须是字符串或缓冲区,而不是列表 (4认同)
  • 我得到错误..文件"C:\ Python27\lib\images2gif.py",第418行,在writeGifToFile中globalPalette = palettes [occurrence .index(max(出现))] ValueError:max()arg是一个空序列 (2认同)

att*_*wad 18

要创建视频,您可以使用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])
Run Code Online (Sandbox Code Playgroud)


Dan*_*ath 8

我碰到了这篇文章,没有解决方案,所以这是我的解决方案

迄今为止,其他解决方案存在问题:
1)没有明确修改持续时间的方式
2)没有解决乱序目录迭代的方法,这对于GIF是必不可少的
3)没有说明如何为python安装imageio 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
Run Code Online (Sandbox Code Playgroud)


Mat*_*zny 8

我要发布一个答案,因为它比到目前为止发布的所有内容都更简单:

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)
Run Code Online (Sandbox Code Playgroud)

使用现有图像:

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)
Run Code Online (Sandbox Code Playgroud)

并且,由于枕头的版本太低而无声地失败了,这里是带有库版本检查的附加版本:

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)
Run Code Online (Sandbox Code Playgroud)


Kri*_*ris 7

使用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)

Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案,谢谢@Kris (8认同)
  • 这是Python语言的一个特性。它进行*可迭代的解包*。您可以粗略地将其视为将 `x = [a, b, c]` 解包为 `*x`,可以将其视为 `a, b, c`(不带括号)。在函数调用中,这些是同义的:“f(*x) == f(a, b, c)”。在元组解包中,当您想要将可迭代对象拆分为头(第一个元素)和尾部(其余元素)时,它特别有用,这就是我在本示例中所做的。 (5认同)
  • 这会将所有图像同时加载到内存中,这很容易耗尽内存。 (2认同)

Bin*_*nod 7

from PIL import Image
import glob  #use it if you want to read all of the certain file type in the directory
imgs=[]
for i in range(596,691): 
    imgs.append("snap"+str(i)+'.png')
    print("scanned the image identified with",i)  
Run Code Online (Sandbox Code Playgroud)

标识不同文件名的索引的起始值和结束值+1

imgs = glob.glob("*.png") #do this if you want to read all files ending with .png
Run Code Online (Sandbox Code Playgroud)

我的文件是:snap596.png,snap597.png ...... snap690.png

frames = []
for i in imgs:
    new_frame = Image.open(i)
    frames.append(new_frame)
Run Code Online (Sandbox Code Playgroud)

保存为永久循环的 GIF 文件

frames[0].save('fire3_PIL.gif', format='GIF',
    append_images=frames[1:],
    save_all=True,
    duration=300, loop=0)
Run Code Online (Sandbox Code Playgroud)

我发现 imageio 存在闪烁问题,此方法修复了它。


小智 6

就像沃伦去年所说的那样,这是一个古老的问题。由于人们似乎仍在浏览页面,因此我想将他们重定向到更现代的解决方案。就像blakev 在这里说的那样,github上有一个Pillow示例。

 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()
Run Code Online (Sandbox Code Playgroud)

注意:此示例已过时(gifmaker不是可导入模块,仅是脚本)。Pillow有一个GifImagePlugin(其源代码在GitHub上),但是ImageSequence上的文档似乎表明支持有限(只读)


Sma*_*noj 6

安装

pip install imageio-ffmpeg
pip install imageio
Run Code Online (Sandbox Code Playgroud)

代码

import imageio
images = []
for filename in filenames:
    images.append(imageio.imread(filename))
imageio.mimsave('movie.mp4', images)
Run Code Online (Sandbox Code Playgroud)

与 gif 相比,保存为 mp4 时,质量得到提高,大小从 8Mb 减小到 80Kb


wil*_*urd 5

它不是python库,但mencoder可以做到这一点:从多个输入图像文件编码.你可以像这样从python执行mencoder:

import os

os.system("mencoder ...")
Run Code Online (Sandbox Code Playgroud)


War*_*ser 5

旧问题,很多好的答案,但仍然有可能对另一种替代方案感兴趣...

numpngw我最近在github(https://github.com/WarrenWeckesser/numpngw)上放置的模块可以从numpy数组编写动画PNG文件。(更新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)
Run Code Online (Sandbox Code Playgroud)

创建:

动画png

您需要一个支持动画PNG(直接或通过插件)的浏览器来观看动画。


Eng*_*tal 5

如上所述,imageio是实现此目的的一种好方法。imageio还允许您设置帧速率,实际上我在Python中编写了一个函数,该函数允许您设置最终帧的保持时间。我将此功能用于科学动画,在这种动画中循环很有用,但没有立即重启。这是链接和功能:

如何使用Python制作GIF

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)
Run Code Online (Sandbox Code Playgroud)

使用此方法的示例GIF