1

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:

enter image description here

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:

enter image description here

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

Download gif: https://i.stack.imgur.com/mXkBn.gif

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

lofidevops
  • 15,528
  • 14
  • 79
  • 119
Bart
  • 524
  • 7
  • 17

3 Answers3

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.

Glenn Randers-Pehrson
  • 11,940
  • 3
  • 37
  • 61
  • Doesn't work on a single frame "orig.gif[4]", as it generates a malformed frame. the "-coalesce" is needed, because otherwise the subsequent frames skip the GIF pallete: http://www.imagemagick.org/script/command-line-options.php#coalesce. I have posted a working answer using PIL. – Bart Feb 24 '16 at 23:36
  • It works with a single frame but if the GIF is optimized it'll only show the parts that changed from the previous frame. You have to use the range [0-N]. – Glenn Randers-Pehrson Feb 24 '16 at 23:39
  • That's right, that's why a single frame appears malformed. The solution I posted copies the pallete for every frame, so it saves a single frame as it is in a playback. – Bart Feb 24 '16 at 23:42
0

You can do it like this:

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

enter image description here

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

lofidevops
  • 15,528
  • 14
  • 79
  • 119
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • It works, as I showed in the original question. It doesn't work when you try to extract only a single frame pour.gif[2] or a range of frames pour.gif[2-5] – Bart Feb 24 '16 at 22:17
  • I can answer my question in 10 minutes. Thanks for heads up. – Bart Feb 26 '16 at 21:29
0

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:

Community
  • 1
  • 1
Bart
  • 524
  • 7
  • 17