Search code examples
pythonpython-imaging-librarypngtransparencygif

How to convert PNG images to a transparent GIF?


I'm trying to convert a list of PNG images with a transparent background to a GIF, while keeping the background transparency. I took this bit of code I found, and adapted it:

import os
from PIL import Image

# Create the frames
frames = []

path = "directory/to/my/png/images"
for frame in os.listdir(path):
    new_frame = Image.open(path + "/" + frame)
    frames.append(new_frame)

# Save into a GIF file
frames[0].save(path + "/../output/animation.gif", format='GIF',
               append_images=frames[1:],
               save_all=True,
               duration=41, loop=1, transparency=0)

It is opening all the PNG images I have in a folder, and export them to a GIF, but the background is black. I have looked at the PIL documentation, but I don't seem to understand how the transparency parameter works, or I think I am using it wrong.


Solution

  • First of all, the GIF format does not support alpha-channel transparency like PNG does. You can only select one out of the 256 possible colors in a GIF to be transparent. So, you also don't get any smooth transparencies, pixels are either fully transparent or opaque.

    When dealing with Image objects having mode RGBA, try to convert all your images to mode PA before saving. Maybe, that helps automagically.

    Let's assume we have the following three images:

    Red

    Green

    Blue

    Your minimized code would look like this:

    from PIL import Image
    
    frames = [Image.open('red.png'), Image.open('green.png'), Image.open('blue.png')]
    
    frames[0].save('test.gif', format='GIF',
                   append_images=frames[1:],
                   save_all=True,
                   duration=200, loop=0, transparency=0)
    

    The resulting GIF in fact doesn't reflect the transparency from the single PNGs, the GIF is totally corrupted:

    Corrupted output

    Adding the conversion to mode PA, the code might look like this:

    from PIL import Image
    
    frames = [Image.open('red.png'), Image.open('green.png'), Image.open('blue.png')]
    
    frames = [frame.convert('PA') for frame in frames]
    
    frames[0].save('test.gif', format='GIF',
                   append_images=frames[1:],
                   save_all=True,
                   duration=200, loop=0, transparency=0)
    

    And, the result is fine, transparency is maintained:

    Proper output

    I don't know, if that route works for arbitrary PNGs, but it's worth testing with your images, isn't it? If that doesn't work, you need to provide some of your input images for further testing.

    The final approach could be to replace all transparent pixels in your PNGs with a certain color, let's say solid yellow. When saving the GIF later, you'd need to make sure, that all images' palettes store that solid yellow at the same index, and then finally set transparency to that index.

    ----------------------------------------
    System information
    ----------------------------------------
    Platform:      Windows-10-10.0.16299-SP0
    Python:        3.9.1
    Pillow:        8.1.0
    ----------------------------------------