Search code examples
pythonimageanimationpngwebp

How to convert .webp to .apng in python?


I'm trying to convert animate image in .webp to .apng ; i've tried the following:

from PIL import Image, ImageSequence
from apng import APNG
im = Image.open('/content/animate_w.webp')
#im.save('/content/animate_w.apng', 'apng', save_all = True, optimize = True, background=0) # not work
im.save('/content/animate_w.png', 'png', save_all = True, optimize = True, background=0)
im = Image.open('/content/animate_w.png')

i = 0
for frame in ImageSequence.Iterator(im):
  ex_command = f'frame_{i} = frame'
  exec(ex_command)
  i += 1


# not really sure what's next
files = [
  ("frame_1.png", 100),
  ("frame_2.png", 200),
  ("frame_3.png", 300)
]

im = APNG()
for file, delay in files:
  im.append_file(file, delay=delay)
im.save("result.apng")

the individually saving frame part does not work and i'm not sure how to proceed next. Any idea?


Solution

  • You have the correct direction, you just need to add steps to extract the frames from the webp file.

    I hope the following code can add more ideas on how to achieve it.

    I am using webptools to extract the frames

    from webptools import webpmux_getframe
    from PIL import Image, ImageSequence
    from apng import APNG
    
    # Load the webp file
    # Downloaded from https://pullzone1-corydowdywebdesi.netdna-ssl.com/assets/blog/apngwebp/squirrel.q70.m6.mixed.webp
    im = Image.open('squirrel.q70.m6.mixed.webp')
    
    # Get the number of frames
    num_of_frame = 0
    for frame in ImageSequence.Iterator(im):
        ex_command = f'frame_{num_of_frame} = frame'
        exec(ex_command)
        num_of_frame += 1
    
    # Extract the frames
    list_of_files = []
    for i in range(num_of_frame):
        webpmux_getframe(input_image='squirrel.q70.m6.mixed.webp', output_image=f'output_frame{i}.png', frame_number=i)
        list_of_files.append(f'output_frame{i}.png')
    
    # Save to APNG
    im = APNG()
    for filename in list_of_files:
        im.append_file(filename)
    im.save('result.apng')
    
    # Load frame from APNG file
    im = APNG.open('result.apng')
    for i, (png, control) in enumerate(im.frames):
        png.save(f'apng_frame_{i}.png')
    

    Another solution without using webptools is using WebPimageFile from PIL

    from PIL import WebPImagePlugin
    from apng import APNG
    
    # Load webp and extract the frames
    imwebp = WebPImagePlugin.WebPImageFile('squirrel.q70.m6.mixed.webp')
    nframes = 0
    list_of_files = []
    while imwebp:
        imwebp.seek(nframes)
        imwebp.save(f'output_frame{nframes}.png', 'PNG')
        list_of_files.append(f'output_frame{nframes}.png')
        nframes += 1
        try:
            imwebp.seek(nframes)
        except EOFError:
            break
    
    # Save to APNG
    im = APNG()
    for filename in list_of_files:
        im.append_file(filename)
    im.save('result.apng')
    
    # Load frame from APNG file
    im = APNG.open('result.apng')
    for i, (png, control) in enumerate(im.frames):
        png.save(f'apng_frame_{i}.png')