Search code examples
pythonmatplotlibpython-imaging-libraryseabornanimated-gif

Why isn't the full palette being used in an animated gif?


I am making an animated gif of heatmaps using pillow. As there are a lot of different values and each frame can only have 256 colors I was hoping pillow would give me a different palette per frame. But it seems it isn't using most of the available colors. Here is a MWE:

from PIL import Image
import seaborn as sns
import io
import matplotlib.pyplot as plt
import scipy
import math
import numpy as np
import imageio

vmin = 0
vmax = 0.4
images = []
for i in range(3):
    mu = 0
    variance = i+0.1
    sigma = math.sqrt(variance)
    x = np.linspace(mu - 3*sigma, mu + 3*sigma, 400)
    row = scipy.stats.norm.pdf(x, mu, sigma)
    matrix = []
    for i in range(400):
        matrix.append(row)
    cmap = "viridis"
    hmap = sns.heatmap(matrix, vmin=vmin, vmax=vmax, cmap=cmap, cbar=False)
    hmap.set_xticks(range(0, 101, 4))
    buffer = io.BytesIO()
    plt.savefig(buffer, format='png')
    buffer.seek(0)
    images.append(Image.open(buffer))
    plt.clf()

images[0].save("out.gif", save_all=True, duration=1000,  loop=1, append_images=images[1:])

The animated gif produced is:

enter image description here

You can see that later frames use fewer than 256 colors. If I look at the palettes with

identify -verbose  out.gif|grep Colors

I get these:

  • For the first frame: Colors: 46
  • For the second frame: Colors: 28
  • For the third frame: Colors: 19

If I save png's instead we can see the third frame, for example, has many more than 19 colors.

enter image description here

What am I doing wrong?


Solution

  • Matplotlib is making palette images and that is causing PIL to carry on down the same track. Give PIL more latitude by using:

    images.append(Image.open(buffer).convert('RGB')
    

    and you'll get:

    enter image description here