为什么我的代码中的 GIF 调色板会损坏?
Why does the GIF colour palette get corrupted in my code?
我制作了一个为 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'
)
这略有变化,会产生奇怪的结果,其中 none 是需要的。
这是in.gif
:
- 以上代码不变:
- 随着
palette=old_im.palette
传入 frames[0].save()
:
- 框架在展开后立即转换为 'RGB' (
.convert('RGB'))
):
- 将
palette=old_im.palette
传递到 frames[0].save(...)
和 框架在扩展后立即转换为 'RGB' (.convert('RGB'))
):
- 随着
palette=old_im.getpalette()
传入 frames[0].save(...)
:
- 将
palette=old_im.getpalette()
传递到 frames[0].save(...)
和 框架在扩展后立即转换为 'RGB' (.convert('RGB'))
):
如您所见,none 的选项具有所需的输出,尽管数字 5 似乎具有最佳结果,除了白底黑字 canvas 现在突然变成深红色文本在红色 canvas 上。是什么原因造成的,我怎样才能得到正常的输出?
在扩展 frame
之前转换为 'RGB'
:
# [...]
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
----------------------------------------
我制作了一个为 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'
)
这略有变化,会产生奇怪的结果,其中 none 是需要的。
这是in.gif
:
- 以上代码不变:
- 随着
palette=old_im.palette
传入frames[0].save()
:
- 框架在展开后立即转换为 'RGB' (
.convert('RGB'))
):
- 将
palette=old_im.palette
传递到frames[0].save(...)
和 框架在扩展后立即转换为 'RGB' (.convert('RGB'))
):
- 随着
palette=old_im.getpalette()
传入frames[0].save(...)
:
- 将
palette=old_im.getpalette()
传递到frames[0].save(...)
和 框架在扩展后立即转换为 'RGB' (.convert('RGB'))
):
如您所见,none 的选项具有所需的输出,尽管数字 5 似乎具有最佳结果,除了白底黑字 canvas 现在突然变成深红色文本在红色 canvas 上。是什么原因造成的,我怎样才能得到正常的输出?
在扩展 frame
之前转换为 'RGB'
:
# [...]
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
----------------------------------------