Imagemagick & Pillow generate malformed GIF frames

2020-04-16 02:23发布

问题:

I need to extract the middle frame of a gif animation.

Imagemagick:

convert C:\temp\orig.gif -coalesce C:\temp\frame.jpg

generates the frames properly:

However when I extract a single frame:

convert C:\temp\orig.gif[4] -coalesce C:\temp\frame.jpg

then the frame is malformed, as if the -coalesce option was ignored:

Extraction of individual frames with Pillow and ffmpeg also results in malformed frames, tested on a couple of gifs.

Download gif: https://i.imgur.com/Aus8JpT.gif

I need to be able to extract middle frames of every gif version in either PIL, Imagemagick of ffmpeg (ideally PIL).

回答1:

You are attempting to coalesce a single input image into single output image. What you got is what you asked for.

Instead you should "flatten" frames 0-4 into a single output image:

convert C:\temp\orig.gif[0-4] -flatten C:\temp\frame.jpg

If you use "-coalesce" you'll get 5 frames of output in frame-0.jpg through frame-4.jpg, the last of them being the image you wanted.



回答2:

Ok, this script will find and save the middle frame of an animated GIF using Pillow.

It will also display the duration of the GIF by counting the milliseconds of each frame.

from PIL import Image

def iter_frames(im):
    try:
        i = 0
        while 1:
            im.seek(i)
            frame = im.copy()
            if i == 0:
                # Save pallete of the first frame
                palette = frame.getpalette()
            else:
                # Copy the pallete to the subsequent frames
                frame.putpalette(palette)
            yield frame
            i += 1
    except EOFError:  # End of gif
        pass

im = Image.open('animated.gif')
middle_frame_pos = int(im.n_frames / 2)
durations = []

for i, frame in enumerate(iter_frames(im)):
    if i == middle_frame_pos:
        middle_frame = frame.copy()

    try:
        durations.append(frame.info['duration'])
    except KeyError:
        pass

middle_frame.save('middle_frame.png', **frame.info)

duration = float("{:.2f}".format(sum(durations)))
print('Total duration: %d ms' % (duration))

Helpful code:

  • Python: Converting GIF frames to PNG
  • https://github.com/alimony/gifduration


回答3:

You can do it like this:

convert pour.gif -coalesce -delete 0-3,5-8 frame4.png

Basically, it generates in full, all the frames and then deletes all frames other than from 4.