2

I have a list of PIL images I want to turn into a GIF. I have a gray mouse moving around in a maze, following a cyan dot. However, the mouse renders as cyan in the saved GIF, and I don't know why.

Here's a link to the GIF (in mp4 format). Here's a link to what the mouse should look like.

Here's the code I used to generate the GIF:

import PIL

imgs = []
fig, ax = plt.subplots(1, 1, figsize=(4, 4))

# np_grids is a list of grids I used to render the maze frames; these details seem unimportant
for idx, grid in enumerate(np_grids): 
    # [Code which plots something interesting on ax]
    
    # Get the axis as an image
    fig.tight_layout()
    fig.canvas.draw()
    img = PIL.Image.frombytes('RGB', fig.canvas.get_width_height(),fig.canvas.tostring_rgb())
    imgs.append(img)

target_file = 'example.gif'

start_step = 25 # Start the gif at this step

# Save the gif
imgs[start_step].save(target, format="GIF", save_all=True, append_images=imgs[start_step+1:], duration=100, loop=0)

imgs appears to contain the correct images, as plotting with plt.imshow(imgs[0]) gives the desired result.

I've tried saving via a few different tools, using both imageio and the save function in the PIL.Image class. I hoped one would use a different algorithm to save the GIF and therefore have better coloration, but both approaches produced similar results.

EDIT: I solved the problem thanks to the comments. I changed the img generation code to:

    img = PIL.Image.frombytes('RGB', fig.canvas.get_width_height(),fig.canvas.tostring_rgb())
    if idx == 0: # Quantize the first frame and get the palette
        img = img.quantize(colors=254, method=PIL.Image.Quantize.MAXCOVERAGE)
    else: # Quantize all other frames to the same palette
        img = img.quantize(colors=254, palette=imgs[0], method=PIL.Image.Quantize.MAXCOVERAGE)
    imgs.append(img)
  • GIF degrades colors since it picks the most common colors and forces the less common ones to use one of the 256 available colors. If you want to override that you need to find a way to force some of the palette entries to use specific colors that you choose. Some kind of `set-palette-entries( array of colors)` command. – Dave S Mar 08 '23 at 18:28
  • You can try using `PIL.quantize()`, preferably with `Quantize.LIBIMAGEQUANT` down to a palette of 254 colours on the first frame as long as it has the mouse, the cyan dot and all the colours. Then quantize all the other images in the series to the same palette and in the `save()` at the end, pass in the palette. – Mark Setchell Mar 08 '23 at 21:50
  • If you are not bound by GIF, you can also save it as a WEBP image instead. It works like a GIF in that it supports animations but is more modern (GIF was invented in the 80's). Notably, WEBP doesn't suffer from the 256 color limitation and browser support (on somewhat modern versions) is the same as GIF. – FirefoxMetzger Mar 16 '23 at 15:21

0 Answers0