首页 > 解决方案 > 为什么我的代码中的 GIF 调色板损坏了?

问题描述

我制作了一个为 GIF 或图像添加字幕的函数:

import textwrap
from io import BytesIO

from PIL import Image, ImageDraw, ImageFont, ImageOps, ImageSequence


def caption(fn: str, text: str):
    old_im = Image.open(fn)
    ft = old_im.format
    W = old_im.size[0]
    font = ImageFont.truetype('BebasNeue.ttf', 50) # replace with your own font

    width = 10
    while True:
        lines = textwrap.wrap(text, width=width)
        if (font.getsize(max(lines, key=len))[0]) > (0.9 * W):
            break
        width += 1

    # amount of lines * height of one line
    bar_height = len(lines) * (font.getsize(lines[0])[1])
    frames = []
    for frame in ImageSequence.Iterator(old_im):
        frame = ImageOps.expand(
            frame,
            border=(0, bar_height, 0, 0),
            fill='white'
        )
        draw = ImageDraw.Draw(frame)
        for i, line in enumerate(lines):
            w, h = draw.multiline_textsize(line, font=font)
            # Position is x: centered, y: line number * height of line
            draw.text(
                ((W - w) / 2, i * h),
                line,
                font=font,
                fill='black'
            )

        del draw
        b = BytesIO()
        frame.save(b, format=ft)
        b.seek(0)
        frames.append(Image.open(b))

    frames[0].save(
        f'out.{ft}',
        save_all=True,
        append_images=frames[1:],
        format=ft,
        loop=0,
        optimize=True
    )


caption(
    'in.gif',
    'this is a test message this is a test message this is a test message this is a test message this is a test message this is a test message'
)

这有轻微的变化,会产生奇怪的结果,这是不需要的。

这是in.gif

在此处输入图像描述

  1. 不更改上面的代码:

在此处输入图像描述

  1. palette=old_im.palette传入frames[0].save():_

在此处输入图像描述

  1. .convert('RGB'))将帧在展开后立即转换为“RGB”( ):

在此处输入图像描述

  1. palette=old_im.palette传入frames[0].save(...) .convert('RGB'))扩展后立即将帧转换为“RGB”( ):

在此处输入图像描述

  1. palette=old_im.getpalette()传入frames[0].save(...):_

在此处输入图像描述

  1. palette=old_im.getpalette()传入frames[0].save(...) .convert('RGB'))扩展后立即将帧转换为“RGB”( ):

在此处输入图像描述

正如您所看到的,没有一个选项具有所需的输出,尽管数字 5 似乎有最好的结果,除了白色画布上的黑色文本现在突然变成红色画布上的深红色文本。是什么原因造成的,我怎样才能得到正常的输出?

标签: pythonpython-3.xpython-imaging-librarygif

解决方案


'RGB'在扩展之前转换为frame

# [...]
for frame in ImageSequence.Iterator(old_im):
    frame = frame.convert('RGB')                # <--
    frame = ImageOps.expand(
        frame,
        border=(0, bar_height, 0, 0),
        fill='white'
    )
    draw = ImageDraw.Draw(frame)
# [...]

这就是我得到的输出:

输出

我认为,填充以white某种方式破坏了现有的调色板。如果您在扩展之前转换为'RGB',我猜“白色”将是“正确的白色”。

----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.16299-SP0
Python:        3.9.1
Pillow:        8.1.2
----------------------------------------

推荐阅读