如何读取和写入具有透明度的动画GIF

2024-04-25 08:54:00 发布

您现在位置:Python中文网/ 问答频道 /正文

我手头有一个(理论上)简单的任务:

  1. 从磁盘(或缓冲区)加载透明的动画GIF
  2. 将所有单个帧转换为NumPy数组。带有ALPHA通道的每个帧
  3. 将NumPy数组保存回透明动画GIF

输出文件大小与此无关,我真正需要的是两个相同的GIF—原始输入图像和步骤3中保存的图像

对我来说,重要的是它的去编码速度,因此没有考虑纯Python解决方案(没有对底层图像库的C绑定)

附加(在最底部),你会发现一个例子GIF我使用的测试

我几乎尝试了脑海中出现的每一种方法。要么生成的GIF(步骤3)被严重破坏,仅以灰度渲染,要么(最多)失去透明度并保存在白色或黑色背景上

以下是我尝试过的:

用枕头阅读:

from PIL import Image, ImageSequence

im = Image.open("animation.gif")

npArray = []

for frame in ImageSequence.Iterator(im):
    npArray.append(np.array(frame))

return npArray

使用图像IO读取:

import imageio

npArr = []

im = imageio.get_reader("animation.gif")

for frame in im:
    npArr.append(np.array(frame))

return npArr

用MoviePy阅读:

from moviepy.editor import *

npArr = []

clip = VideoFileClip("animation.gif")

for frame in clip.iter_frames():
    npArr.append(np.array(frame))

return npArr

用PyVIP阅读:

vi = pyvips.Image.new_from_file("animation.gif", n=-1)

pageHeight = vi.get("page-height")
frameCount = int(vi.height / pageHeight)

npArr = []

for i in range(0, frameCount):
    vi = vi.crop(0, i * pageHeight + 0, vi.width, pageHeight).write_to_memory()

    frame = np.ndarray(
            buffer = vi,
            dtype = np.uint8,
            shape = [pageHeight, vi.width, 3]
    )

    npArr.append(frame)

return npArr

用枕头保存:

images = []

for frame in frames:
    im = Image.fromarray(frame)
    images.append(im)

images[0].save(
    "output.gif",
    format = "GIF",
    save_all = True,
    loop = 0,
    append_images = images,
    duration = 40,
    disposal = 3
)

enter image description here


Tags: in图像imagefornpgifframevi
1条回答
网友
1楼 · 发布于 2024-04-25 08:54:00

我相信您遇到了一个问题,因为您没有保存与每个帧关联的调色板。将每个帧转换为数组时,生成的数组不包含任何调色板数据,这些调色板数据指定帧中包含的颜色。因此,当你从每一帧构建一个新图像时,调色板不存在,枕头也不知道它应该为帧使用什么调色板

此外,在保存GIF时,您需要指定用于透明度的颜色,我们可以从原始图像中提取该颜色

下面是一些代码(希望)可以产生您想要的结果:

from PIL import Image, ImageSequence
import numpy as np

im = Image.open("ex.gif")

frames = []
# Each frame can have its own palette in a GIF, so we need to store
# them individually
fpalettes = []
transparency = im.info['transparency']

for frame in ImageSequence.Iterator(im):
    frames.append(np.array(frame))
    fpalettes.append(frame.getpalette())

# ... Do something with the frames

images = []

for i, frame in enumerate(frames):
    im = Image.fromarray(frame)
    im.putpalette(fpalettes[i])
    images.append(im)

images[0].save(
        "output.gif",
        format="GIF",
        save_all=True,
        loop=0,
        append_images=images,
        duration=40,
        disposal=2,
        transparency=transparency
)

相关问题 更多 >