pythonpython-imaging-librarygif

How to create a GIF using pillow and imageio with python?


I have the following code that creates 60 frames using pillow in python 3.11.10 and that I want to use to create a gif (repeating endless), with a duration per frame of 0.1 seconds. The first 4 frames should show a red square, and the rest of the time (almost 6 seconds) it should be basically black. However, the created GIF only seems to contain 2 frames, one frame with the red square and one without. How to create the gif properly?

from PIL import Image, ImageDraw
import imageio
import numpy as np

# Colors
red = (255, 0, 0)
black = (0, 0, 0)
grey = (30, 30, 30)

frames = []
framerate = 10
frame_count = 60
for i in range(frame_count):
    # Create an image with black background
    img = Image.new('RGB', (55, 50), black)
    draw = ImageDraw.Draw(img)
    # Draw rectangle
    draw.rectangle((10, 20, 45, 30), fill=red if i<5 else grey)
    # Append frame to the list
    frames.append(np.array(img))
# Save frames as a GIF
imageio.mimsave("test.gif", frames, duration=1./framerate, loop=0, plugin='pillow')

Here is the created gif:

enter image description here

Expected behavior: 0.4 seconds showing that red square, then 5.6 seconds showing basically black.

With Grismar's suggestion:

enter image description here


Solution

  • The image gets optimised to have the flickering behaviour, you need to explicitly set the disposal method to completely replace the first frame (as well as applying the fix in the other answer since that's just a mistake):

    from PIL import Image, ImageDraw
    import imageio
    import numpy as np
    
    # Colors
    red = (255, 0, 0)
    black = (0, 0, 0)
    grey = (30, 30, 30)
    
    frames = []
    framerate = 10
    frame_count = 60
    for i in range(frame_count):
        # Create an image with black background
        img = Image.new('RGB', (55, 50), black)
        draw = ImageDraw.Draw(img)
        # Draw rectangle
        draw.rectangle((10, 20, 45, 30), fill=red if i<5 else grey)
        # Append frame to the list
        frames.append(img)
    
    # Save frames as a GIF using PIL directly, with the correct disposal method
    frames[0].save(
        "test.gif",
        save_all=True,
        append_images=frames[1:],
        duration=int(1000 / framerate),  # duration in ms per frame
        loop=0,
        disposal=0  # use the appropriate disposal method here, no transparency
    )
    

    See also https://giflib.sourceforge.net/whatsinagif/animation_and_transparency.html