Saving png sequence as WebP in pil makes background opaque black

Question:

Problem: When saving in Image sequence as gif with transparency, it works great, but when saving as webp, the transparency is lost in all frames.

Using: Python (Latest) with PIL (Latest)

Question: How can this be fixed, and what is causing it?

Code:

from PIL import Image, ImageSequence


def gen_frame(im: Image) -> Image:
    alpha = im.getchannel('A')
    # Convert the image into P mode but only use 255 colors in the palette out of 256
    im = im.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=255)
    # Set all pixel values below 128 to 255 , and the rest to 0
    mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
    # Paste the color of index 255 and use alpha as a mask
    im.paste(255, mask)
    # The transparency index is 255
    im.info['transparency'] = 255
    return im


im = Image.open("input_gif.gif")

im_list = []

for frame in ImageSequence.Iterator(im):
    # perform some functions on the image frames

    frame = frame.convert('RGBA').resize((512, 512), Image.ANTIALIAS)
    frame = gen_frame(frame)

    im_list.append(frame)

img = im_list[0]
imgs = im_list[1:]

img.save("output_gif.gif", save_all=True, append_images=imgs, duration=50, loop=0, optimize=False, disposal=2)
# works correctly, as intended

img.save("output_webp.webp", save_all=True, append_images=imgs, duration=50, loop=0, optimize=False, disposal=2)
# Transparency loss in the webp format

input gif:

1

output gif:

2

output webp:

3

Asked By: Ishan J.

||

Answers:

Fixed it – there was literally 1 character wrong with your code!

Inside of gen_frame, it was im = im.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=255) and it should be im = im.convert('RGBA').convert('P', palette=Image.ADAPTIVE, colors=255). As per one of my favorite YouTubers, "The truth hurts.". (Ben Finegold).

Correct:

from PIL import Image, ImageSequence


def gen_frame(im: Image) -> Image:
    alpha = im.getchannel('A')
    # Convert the image into P mode but only use 255 colors in the palette out of 256
    # -------------------------------------------
    # THE im.convert('RGB') WAS CHANGED TO "RGBA"
    # -------------------------------------------
    im = im.convert('RGBA').convert('P', palette=Image.ADAPTIVE, colors=255)
    # Set all pixel values below 128 to 255 , and the rest to 0
    mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
    # Paste the color of index 255 and use alpha as a mask
    im.paste(255, mask)
    # The transparency index is 255
    im.info['transparency'] = 255
    return im


im = Image.open("input_gif.gif")

im_list = []

for frame in ImageSequence.Iterator(im):
    # perform some functions on the image frames

    frame = frame.convert('RGBA').resize((512, 512), Image.ANTIALIAS)
    frame = gen_frame(frame)

    im_list.append(frame)

img = im_list[0]
imgs = im_list[1:]

img.save("output_gif.gif", save_all=True, append_images=imgs, duration=50, loop=0, optimize=False, disposal=2)
# works correctly, as intended

img.save("output_webp.webp", save_all=True, append_images=imgs, duration=50, loop=0, optimize=False, disposal=2, lossless=True)
# Transparency loss in the webp format

https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#gif

From the docs:

GIF

Pillow reads GIF87a and GIF89a versions of the GIF file format. The library writes run-length encoded files in GIF87a by default, unless GIF89a features are used or GIF89a is already in use.

GIF files are initially read as grayscale (L) or palette mode (P) images, but seeking to later frames in an image will change the mode to either RGB or RGBA, depending on whether the first frame had transparency.

So in essence, when you went to generate a frame for the image, you were not adding the alpha channel to it, despite setting the transparency in the info properties, and when you went to save it, it was lost.

As per the docs:

transparency

Transparency color index. This key is omitted if the image is not transparent.

Answered By: Shmack
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.