使用Pillow读取的GIF文件有黑色边框。

5
我正在使用Pillow读取gif,这段代码会将gif文件中的每一帧输出为.gif和.png格式,并可选择是否将其转换为RGBA格式。
在展示结果后,我将讨论其中的问题。
from PIL import Image

# Download link below
image = Image.open('PepePls.gif')

loop = []
frames = []
durations = []
try:
    while True:
        loop.append(image.info.get('loop', None))
        frame = image.copy()
        frames.append(frame)
        durations.append(image.info.get('duration', None))
        image.seek(image.tell() + 1)
except EOFError:
    pass

for i in range(len(frames)):
    print(f'#{i} loop = {loop[i]} duration = {durations[i]} mode = {frames[i].mode}')
    frames[i].save(f'gif_f{i}.gif')
    frames[i].save(f'png_f{i}.png')
    frames[i].convert(mode='RGBA').save(f'rgba_gif_f{i}.gif')
    frames[i].convert(mode='RGBA').save(f'rgba_png_f{i}.png')

结果,控制台输出,然后是explorer.exe的截图:

#0 loop = 0 duration = 70 mode = P
#1 loop = None duration = 70 mode = P
#2 loop = None duration = 70 mode = P
#3 loop = None duration = 70 mode = P
#4 loop = None duration = 70 mode = P
#5 loop = None duration = 70 mode = P
#6 loop = None duration = 70 mode = P
#7 loop = None duration = 70 mode = P
#8 loop = None duration = 70 mode = P
#9 loop = None duration = 70 mode = P
#10 loop = None duration = 70 mode = P
#11 loop = None duration = 70 mode = P
#12 loop = None duration = 70 mode = P
#13 loop = None duration = 70 mode = P
#14 loop = None duration = 70 mode = P
#15 loop = None duration = 70 mode = P
#16 loop = None duration = 70 mode = P
#17 loop = None duration = 70 mode = P
#18 loop = None duration = 70 mode = P
#19 loop = None duration = 70 mode = P
#20 loop = None duration = 70 mode = P

resulting frames

问题:

  1. 为什么 rgba gif 图像失去了透明度?背景颜色从哪里来?

  2. 为什么到处都有黑边?我知道使用 GIMP 时,gif 帧可以合并或替换前一个帧,这能解释我看到的情况吗?或者是其他原因?(顺便问一下,Pillow 是否自己处理合并/替换帧的 gif?)

  3. 不确定图像大小出现了什么问题,explorer.exe 显示为 68x104,但由 Pillow 保存的帧全部为 85x112(Firefox 在查看输入 gif 文件时也显示为 85x112)。

在此处找到 PepePls.gif

我也遇到了类似的问题 这个

编辑:

我非常确定经过大量研究并阅读了Pillow源代码https://github.com/python-pillow/Pillow/blob/master/src/PIL/GifImagePlugin.py后,黑色边框是一个bug,这可能与每个帧都在不适当颜色的背景上绘制有关(可能根本没有检查调色板/颜色/透明度索引)。
以下是我通过将上述代码中的frame = image.copy()替换为以下内容所实现的
frame = Image.new('RGBA', (85, 112), color=(255,0,0,0))
frame.paste(image.crop(image.dispose_extent), box=(image.dispose_extent[0],image.dispose_extent[1]))

也许应该使用 image.tile[0][1] 而不是 image.dispose_extent,但我不确定

new results

这似乎表明Pillow根本不喜欢带有透明度的gif图像

编辑2:

我使用以下方法解决了黑色边框问题并合并/替换了帧:

from PIL import Image

gif = 'PeepoCreepo.gif'
#gif = 'PepePls.gif'

image = Image.open(gif)

frames = []

disposal_method_last = 0

try:
    while True:
        disposal_method = disposal_method_last
        disposal_method_last = image.__dict__.get('disposal_method', 0)
        if disposal_method == 2 or (disposal_method == 1 and frames == []):
            frame = Image.new('RGBA', image.size, color=(255,0,0,0))
            frame.paste(image.crop(image.dispose_extent), box=(image.dispose_extent[0],image.dispose_extent[1]))
        elif disposal_method == 1:
            newStuff = image.crop(image.dispose_extent)
            frame = frames[-1].copy()
            frame.paste(newStuff, image.dispose_extent, newStuff.convert("RGBA"))
        else:
            frame = image.copy()
        frames.append(frame)
        image.seek(image.tell() + 1)
except EOFError:
    pass

for i in range(len(frames)):
    frames[i].convert(mode='RGBA').save(f'f{i}.png')

PeepoCreepo frames almost perfect

我仍然看到的一个问题是,如果你放大f2.png,你会发现眼睛里的一些黑色部分丢失了。这不是gif文件的问题,因为GIMP可以正确地打开和显示每一帧:

PeepoCreepo gif frames in gimp

编辑3:

在Github上开了一个问题

编辑4:

Github问题已标记为已解决!


检查每个单独帧的大小。你可能会发现有些只是部分更新,而其余的帧需要从之前的帧中复制向前。 - Mark Setchell
frames[i].size始终为(85,112),但我认为它不可能是这样的,因为第一帧也有黑色边框。 - Dragorn421
1个回答

0

这是PIL中已知的一个错误,至少可以追溯到2018年2月,因此我不会期望在可预见的未来有任何修复。

其中一种可能的解决方案是使用外部GIF库(例如我的)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接