I am reading gifs with Pillow, this code outputs every frame from a gif file as .gif and .png with and without converting to RGBA.
I'll discuss the issues below, after showing the results
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')
Results, console output then explorer.exe screenshot:
#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
Issues:
Why did rgba gifs lose the transparency? And where does the background color comes from
Why are there black borders everywhere? I know from using GIMP a bit that gif frames can be combined or replace the previous one, does this explain what I'm seeing or is something else? (while on the topic, does Pillow handle combine/replace frames gifs on itw own?)
Not sure what's happening with the image size, explorer.exe says 68x104 but frames saved by Pillow are all 85x112 (firefox also says 85x112 when viewing the input gif file)
Find PepePls.gif here
I have a similar issue with this one too
EDIT:
I am pretty sure after a lot of research and reading the Pillow source code https://github.com/python-pillow/Pillow/blob/master/src/PIL/GifImagePlugin.py that black borders are a bug, and it must be linked to the fact that each frame is drawn on a background of the inappropriate color (probably not checking the palette/color/transparency index at all)
Here's what I achieved replacing frame = image.copy() in the above code by
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]))
May want to use image.tile[0][1] instead of image.dispose_extent but I'm really not sure
It also seems to show that Pillow doesn't like gifs with transparency at all
EDIT 2:
I fixed black borders and combine/replace frames by using this:
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')
One issue I still see is if you zoom on f2.png you can see some of the black in the eyes is missing. This is not an issue from the gif file, as gimp can open and display each frame properly:
EDIT3:
EDIT4:
The Github issue has been marked as fixed!



