Reputation: 889
I'd like to get the color palette of each frame of an animated GIF (I'm using ColorThief to generate color palette from an image). I'm using PIL to save each frame as an image, my problem is that the images of most of the frames have strange colors, different from the one you see if you open the GIF on a browser. I guess this is because of the way animated GIF are compressed. I tried to convert every image in RGB but this doesn't solve the problem. How can I render each frame with its intended colors?
To get every frame I'm using the following code:
from PIL import Image, ImageSequence
im = Image.open("phicons/icontech-3-2019-1.gif")
index = 1
for frame in ImageSequence.Iterator(im):
frame.save("phicons/frame%d.gif" % index)
print(frame)
index += 1
This is the image I'm using for test
This is an example of frame with strange colors
Upvotes: 1
Views: 3344
Reputation: 21
You can try Python libary moviepy.
from moviepy.editor import VideoFileClip
import cv2
import numpy as np
clip = VideoFileClip('sample.gif')
idx = 0
for frame in clip.iter_frames(): # frame is in rgb format
cv2.imwrite('sample-{}.png'.format(idx), frame[:,:,::-1])
Upvotes: 2
Reputation: 241
The problem seems to be a bug at PIL at the class GifImageFile, when the palette is normalized at _normalize_palette it is not taking into account the palette of the current frame:
If you add the following code, it solves the problem:
def _normalize_palette(im, palette, info):
source_palette = None
# fmatheis, note that I am initializing the variable here to solve the bug
if im.palette and im.palette.palette:
source_palette = im.palette.palette[:768]
if palette:
# a bytes palette
if isinstance(palette, (bytes, bytearray, list)):
source_palette = bytearray(palette[:768])
if isinstance(palette, ImagePalette.ImagePalette):
source_palette = bytearray(
itertools.chain.from_iterable(
zip(
palette.palette[:256],
palette.palette[256:512],
palette.palette[512:768],
)
)
)
if im.mode == "P":
if not source_palette:
source_palette = im.im.getpalette("RGB")[:768]
else: # L-mode
if not source_palette:
source_palette = bytearray(i // 3 for i in range(768))
im.palette = ImagePalette.ImagePalette("RGB", palette=source_palette)
used_palette_colors = GifImagePlugin._get_optimize(im, info)
if used_palette_colors is not None:
return im.remap_palette(used_palette_colors, source_palette)
im.palette.palette = source_palette
return im
#You can patch the code like this
from PIL import GifImagePlugin
GifImagePlugin._normalize_palette = _normalize_palette
Upvotes: 1
Reputation: 207355
This looks like a bug in PIL. It appears not to notice/realise each frame can potentially have a different palette, and indeed, does so in this image.
If you look at the image with ImageMagick you can see it has 54 frames, some with 8, some with 16, some with 32 and some with 64 colours.
identify anim.gif
Output
anim.gif[0] GIF 483x483 483x483+0+0 8-bit sRGB 16c 0.000u 0:00.002
anim.gif[1] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[2] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[3] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[4] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[5] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[6] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[7] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[8] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[9] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[10] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[11] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[12] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[13] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[14] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[15] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[16] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[17] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[18] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[19] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[20] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[21] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[22] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.003
anim.gif[23] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.002
anim.gif[24] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.002
anim.gif[25] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[26] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[27] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[28] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[29] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[30] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[31] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[32] GIF 483x483 483x483+0+0 8-bit sRGB 8c 0.000u 0:00.002
anim.gif[33] GIF 483x483 483x483+0+0 8-bit sRGB 8c 0.000u 0:00.002
anim.gif[34] GIF 483x483 483x483+0+0 8-bit sRGB 8c 0.000u 0:00.002
anim.gif[35] GIF 483x483 483x483+0+0 8-bit sRGB 8c 0.000u 0:00.002
anim.gif[36] GIF 483x483 483x483+0+0 8-bit sRGB 8c 0.000u 0:00.002
anim.gif[37] GIF 483x483 483x483+0+0 8-bit sRGB 8c 0.000u 0:00.002
anim.gif[38] GIF 483x483 483x483+0+0 8-bit sRGB 32c 0.000u 0:00.002
anim.gif[39] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[40] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[41] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[42] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[43] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[44] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[45] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[46] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[47] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[48] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[49] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[50] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[51] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[52] GIF 483x483 483x483+0+0 8-bit sRGB 64c 0.000u 0:00.002
anim.gif[53] GIF 483x483 483x483+0+0 8-bit sRGB 16c 383348B 0.000u 0:00.002
And, if you extract all the frames and montage them together with ImageMagick:
for ((f=0; f<54; f++)) ; do
convert anim.gif[$f] miff:-
done | montage miff:- -geometry +5+5 result.png
If you want to see the palettes of each frame, use:
identify -verbose anim.gif
As an alternative, you may like to have a look at wand
which is a Python binding to ImageMagick. You can extract your frames correctly like this:
#!/usr/bin/env python3
from wand.image import Image
with Image(filename='anim.gif') as img:
for n in range(len(img.sequence)):
frame = img.sequence[n]
i = Image(image=frame)
i.save(filename=f'f-{n:02}.gif')
Upvotes: 2