如何在没有错误的情况下打开优化的 GIF?

问题描述

所以这个 GIF 在打开前看起来非常好:

The gif before python opens

但是,当使用 Pillow 打开时使用

imageObject = Image.open(path.join(petGifs,f"{pokemonName}.gif"))

它出错了,添加了与源图像颜色相似的各种框。这是一个示例帧,但几乎每一帧都不同,并且根据 GIF 在不同的位置:

one of the frames after the gif is opened in Python

another frame after the gif is opened in Python

唯一能解决这个问题的是ezgif的unoptimize选项(在他们的optimize page中找到)。但是,我需要在每个 GIF 上都这样做,而且有很多。

我需要一种批量取消优化的方法,或者一种在 Python 中打开 GIF 的新方法(目前使用 Pillow),以解决这个问题。

解决方法

至少对于提取正确的单帧来说,可能有一个解决方案。

所有帧(除了第一个)的 disposal 方法都设置为 2,即“恢复到背景颜色”。

仔细阅读 Pillow 的源代码,您会找到相应的 line where the disposal method 2 is considered,并且在以下内容中,您会发现:

# by convention,attempt to use transparency first
color = (
    frame_transparency
    if frame_transparency is not None
    else self.info.get("background",0)
)
self.dispose = Image.core.fill("P",dispose_size,color)

如果您检查有问题的帧,您会注意到不需要框的这种深绿色位于调色板的位置 0。因此,似乎选择了错误的颜色进行处理,因为 - 我不知道为什么 - 选择了上面的 else 案例而不是使用透明度信息 - 会在那里!

所以,让我们覆盖可能有问题的东西:

from PIL import Image,ImageSequence

# Open GIF
gif = Image.open('223vK.gif')

# Initialize list of extracted frames
frames = []

for frame in ImageSequence.Iterator(gif):

    # If dispose is set,and color is set to 0,use transparency information
    if frame.dispose is not None and frame.dispose[0] == 0:
        frame.dispose = Image.core.fill('P',frame.dispose.size,frame.info['transparency'])

    # Convert frame to RGBA
    frames.append(frame.convert('RGBA'))

# Visualization overhead
import matplotlib.pyplot as plt
plt.figure(figsize=(8,8))
for i,f in enumerate(frames,start=1):
    plt.subplot(8,8,i),plt.imshow(f),plt.axis('off')
plt.tight_layout(),plt.show()

提取的帧如下所示:

Output

我觉得这很好。

如果偶然地将透明度信息设置为 0,这里应该没有什么坏处,因为我们(重新)设置了仍然正确的透明度信息。

我不知道(重新)保存到 GIF 是否可行,因为帧现在处于 RGBA 模式,并且从那里保存到 GIF 也很棘手。

----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.19041-SP0
Python:        3.9.1
PyCharm:       2021.1.3
Matplotlib:    3.4.2
Pillow:        8.3.1
----------------------------------------
,

您可以尝试使用:

from PIL import Image,ImageSequence

im = Image.open(f"{pokemonName}.gif")

index = 1
for frame in ImageSequence.Iterator(im):
    frame.save("frame%d.png" % index)
    index += 1