PIL 向 gif 帧添加文本会增加图片的噪音

PIL adding text to a gif frames adds noise to the picture

我正在使用 PIL 创建一个简单的 GIF 动画:

from PIL import Image, ImageDraw, ImageFont

images = []

for x, i in enumerate(range(10)):
    image = Image.new(mode="RGB", size=(320, 60), color="orange")

    draw = ImageDraw.Draw(image)
    fnt = ImageFont.truetype('font.ttf', size=10)
    draw.text((10, 10), ("%s" % x), fill=(0, 0, 255), font=fnt)
    images.append(image)

images[0].save("result/pil.gif", save_all=True, append_images=images[1:], duration=1000, loop=0, format="GIF")

问题是,每当我使用 Draw.text 时,图像的背景都会出现某种白色喷嘴:

我发现了一些信息,我必须从第一帧使用 getpalette 并为所有其他帧使用 putpalette,如下所示:

for x, i in enumerate(range(10)):
    image = Image.new(mode="RGB", size=(320, 60), color="orange")

    if x == 0:
        palette = image.getpalette()
    else:
        image.putpalette(palette)

但它只是给了我:ValueError: illegal image mode

背景噪音是什么原因,我该如何解决?

UPD 我可以通过将图像模式更改为“P”来修复背景,但在这种情况下我的字体变得不可读了。这些是 RGB 模式(字体很好)和 P 模式(字体很糟糕)的例子:

为什么我得到的不是漂亮的背景或漂亮的字体,而是两者都不是?有解决方法吗?

这是 dithering that happens, because gif can contain only colors from palette of size 256. Most likely PIL uses very basic algorithm to convert from RGB format to indexed format, which is required by gif. As your image contains colors #ff9900 and #ffcc00, then palette presumably consists of hex values 00, 33, 66, 99, cc, ff for each byte and has size 6x6x6 = 216, which fits nicely into 256 possible values. 'orange',值为 #ffa500,无法用此类调色板表示,因此背景由最接近的可用颜色填充。

您可以尝试使用颜色 '#ff9900' 而不是 'orange'。希望这种颜色可以用调色板表示,因为它存在于您的嘈杂图像中。

您也可以尝试使用您自己的调色板作为 quantize 方法中的参数将 RGB 格式转换为索引格式,如 this answer. Adding the following line results in nice solid background:

中所建议
image = image.quantize(method=Image.MEDIANCUT)

或者您可以只将 RGB 图像保存为 PNG 格式。在这种情况下,它将被保存为 APNG 图片。

images[0].save("pil.png", save_all=True, append_images=images[0:],duration=1000, loop=0, format="PNG")

user13044086 给出了问题的通用版本,具体情况是 gif 是一种调色板格式,要将原始 RGB 转换为调色板枕头需要将其从“真彩色”限制为仅 256 种颜色。

为此,它将 convert the image to mode L, and as you can see in the documentation if no transformation matrix is given that's just an alias for calling quantize 使用默认参数。这里相关的默认参数是 dither,这意味着如果图像有超过 256 种颜色,则尝试通过具有邻近颜色的单个点来“模拟”缺失的颜色。

现在你可能会抱怨你没有超过 256 种颜色,但你可能这样做是因为字体抗锯齿,算法不一定有“味道”所以不是在字母中抖动,而是抖动非常在背景中可见。

您可以通过显式量化提供建议的显式方法或仅禁用抖动(这可能会产生较低质量的结果但肯定会更快)来缓解此问题:

images.append(image.quantize(dither=Image.NONE))

手动制作您自己的调色板并将其传递给 quantize 也可以。