Skip to content

GifImageFile and animated gif with mismatched color palettes #2893

@tomaszhlawiczka

Description

@tomaszhlawiczka

I'm trying to resize an animated gif file with a different color palette for each frame.

Each frame should be composed with own colors, but because of buildin frames merge (see GifImageFile.load_end) colors are messed up.

Python: 3.6
PIL: 4.4.0.dev0

from PIL import Image
source = Image.open("src.gif")
w, h = source.size
resized = []
try:
	for i in range(0, 0xffffffff):
		source.seek(i)
		resized.append(source.convert('RGBA').resize((w//2, h//2), Image.ANTIALIAS))
except EOFError:
	pass
resized[0].save("result.gif", "GIF", save_all=True, append_images=resized[1:], loop=1, duration=50)

I see following solutions here:

  1. Merge color palettes as well and reindex frames to new colors (hard one, losing an original data, might be impossible due to the limit of colors in the palette)
  2. First convert each frame from mode PRGBA and then make a merge (losing an original data)
  3. Move merging to another level of abstraction allowing users to open GIF files exactly how there were composed (opening GIF would require additional actions)

So far I've made a workaround as follow (might be helpful for someone):

# Clear up the GifImageFile.load_end()
from PIL import Image, ImageFile
from PIL.GifImagePlugin import GifImageFile, _accept, _save, _save_all

class AnimatedGifImageFile(GifImageFile):
	def load_end(self):
		ImageFile.ImageFile.load_end(self)

Image.register_open(AnimatedGifImageFile.format, AnimatedGifImageFile, _accept)
Image.register_save(AnimatedGifImageFile.format, _save)
Image.register_save_all(AnimatedGifImageFile.format, _save_all)
Image.register_extension(AnimatedGifImageFile.format, ".gif")
Image.register_mime(AnimatedGifImageFile.format, "image/gif")


# Make merge by my own (losing an original palette 
# doesn't matter for this particular case - having RGBA is good enough):
im = Image.open("src.gif")
last_frame = None
all_frames = []
try:
	for i in range(0, 0xffffffff):
		im.seek(i)
		new_frame = im.convert('RGBA')
		if last_frame is not None and im.disposal_method == 1:
			updated = new_frame.crop(im.dispose_extent)
			last_frame.paste(updated, im.dispose_extent, updated)
			new_frame = last_frame
		else:
			last_frame = new_frame

		# do resizing on new_frame here...

		all_frames.append(new_frame)
except EOFError:
	pass

src
result

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugAny unexpected behavior, until confirmed feature.GIF

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions