如何读写带透明度的GIF动画
How to read and write animated GIF with transparency
这是我手头的一项(理论上)简单的任务:
- 从磁盘(或缓冲区)加载透明 动画 GIF
- 将所有单独的帧转换为 NumPy 数组。每帧 带 ALPHA 通道
- 将 NumPy 数组保存回 透明 动画 GIF
输出文件大小无关紧要,我真正需要的是有两个相同的 GIF - 原始输入图像和在步骤 3 中保存的图像。
尽管 de/encoding 速度如此纯粹 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
用imageio读取:
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
使用 PyVips 阅读:
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
)
我认为您遇到了问题,因为您没有保存与每一帧关联的调色板。当您将每个帧转换为数组时,生成的数组不包含任何指定帧中包含哪些颜色的调色板数据。因此,当您从每一帧构建新图像时,调色板不存在,并且 Pillow 不知道它应该为该帧使用什么调色板。
另外,保存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
)
这是我手头的一项(理论上)简单的任务:
- 从磁盘(或缓冲区)加载透明 动画 GIF
- 将所有单独的帧转换为 NumPy 数组。每帧 带 ALPHA 通道
- 将 NumPy 数组保存回 透明 动画 GIF
输出文件大小无关紧要,我真正需要的是有两个相同的 GIF - 原始输入图像和在步骤 3 中保存的图像。
尽管 de/encoding 速度如此纯粹 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
用imageio读取:
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
使用 PyVips 阅读:
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
)
我认为您遇到了问题,因为您没有保存与每一帧关联的调色板。当您将每个帧转换为数组时,生成的数组不包含任何指定帧中包含哪些颜色的调色板数据。因此,当您从每一帧构建新图像时,调色板不存在,并且 Pillow 不知道它应该为该帧使用什么调色板。
另外,保存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
)