Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to accurately obtain the duration of each frame in gif #6887

Closed
lxccc812 opened this issue Jan 13, 2023 · 4 comments
Closed

How to accurately obtain the duration of each frame in gif #6887

lxccc812 opened this issue Jan 13, 2023 · 4 comments
Labels

Comments

@lxccc812
Copy link

lxccc812 commented Jan 13, 2023

111111.zip
I have a gif composed of multiple pictures. The duration of each picture is different. When I re-compose the gif, I need to know the duration of each picture.

Original gif::
111111

When I use the im object to save, there is no problem with the duration of each image.

import os
from PIL import Image, ImageSequence
from PIL import GifImagePlugin
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY

image_list = []

def test_gif_duration():
    im = Image.open(r'old\111111.gif')
    im.save(r'new\111111.gif', save_all=True)

Only use im objects to generate new gifs::
111111

When I split the gif and put each image into a new list, the duration of each image in the resultant gif is also OK.

from PIL import Image, ImageSequence
from PIL import GifImagePlugin
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY

image_list = []

def test_save_image_list():
    im = Image.open(r'\old\111111.gif')
    for frame in ImageSequence.Iterator(im):
        image_list.append(frame)

    image_list[0].save(
        r'new\111111.gif',
        save_all=True,
        append_images=image_list[1:]
    )
]

Gif generated with the new list::
111111
The composite gif also seems to overlap, which may be caused by the transparent background. Let's not discuss this error first.

But when I fill the background or other operations on each image, the duration of each image in the composite gif seems to become the same time. This is different from the duration of each image in the initial gif.In addition, the old gif is in an endless loop (loop=0), and the repetition number of the newly synthesized gif has also changed to 1. I must add another attribute (loop=0) to ensure that it is the result I want.

from PIL import Image, ImageSequence
from PIL import GifImagePlugin
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY

image_list = []

def test_save_image_list():
    im = Image.open(r'old\111111.gif')
    fill_color = (255, 255, 255)
    for frame in ImageSequence.Iterator(im):
        frame = frame.convert('RGBA')
        bg = Image.new(frame.mode[:-1], frame.size, fill_color)
        bg.paste(frame, frame.split()[-1])
        frame = bg

        # convert_frame = frame.convert('RGB')
        # convert_frame.thumbnail((150, 150))

        image_list.append(frame)

    image_list[0].save(
        r'new\111111.gif',
        save_all=True,
        append_images=image_list[1:],
        loop=0
    )

Gif generated after filling operation::
111111

python 3.7.9
Pillow 9.4.0

Before that, I checked the question of #6259 . I think our situation is the same. Therefore, I need to know the correct duration of each frame in the multi-frame picture in the gif, rather than the duration of the first frame.

@radarhere radarhere added the GIF label Jan 13, 2023
@radarhere
Copy link
Member

You can get the frame.info["duration"] to find out the duration of each frame, create a list using those numbers, and then use that list for the duration keyword when saving.

from PIL import Image, ImageSequence
from PIL import GifImagePlugin
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY

image_list = []

def test_save_image_list():
    im = Image.open(r'old\111111.gif')
    fill_color = (255, 255, 255)
    duration = []
    for frame in ImageSequence.Iterator(im):
        duration.append(frame.info["duration"])
        frame = frame.convert('RGBA')
        bg = Image.new(frame.mode[:-1], frame.size, fill_color)
        bg.paste(frame, frame.split()[-1])
        frame = bg

        # convert_frame = frame.convert('RGB')
        # convert_frame.thumbnail((150, 150))

        image_list.append(frame)

    image_list[0].save(
        r'new\111111.gif',
        save_all=True,
        append_images=image_list[1:],
        loop=0,
        duration=duration
    )

@lxccc812
Copy link
Author

lxccc812 commented Jan 13, 2023

from PIL import Image, ImageSequence
from PIL import GifImagePlugin
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY

image_list = []

def test_save_image_list():
    im = Image.open(r'old\111111.gif')
    fill_color = (255, 255, 255)
    for frame in ImageSequence.Iterator(im):
        print(f'before: {frame.info}')
        frame = frame.convert('RGBA')
        bg = Image.new(frame.mode[:-1], frame.size, fill_color)
        bg.paste(frame, frame.split()[-1])
        frame = bg

        # convert_frame = frame.convert('RGB')
        # convert_frame.thumbnail((150, 150))
        print(f'after: {frame.info}')
        image_list.append(frame)

    image_list[0].save(
        r'\new\111111.gif',
        save_all=True,
        append_images=image_list[1:],

        loop=0
    )

I got the following results
image
Can I put these attributes back

@radarhere
Copy link
Member

Sure. frame.info is just a dictionary.

info_before = frame.info
frame = frame.convert('RGBA')
bg = Image.new(frame.mode[:-1], frame.size, fill_color)
bg.paste(frame, frame.split()[-1])
frame = bg

# convert_frame = frame.convert('RGB')
# convert_frame.thumbnail((150, 150))
frame.info = info_before

or if you want just duration,

duration_before = frame.info["duration"]
frame = frame.convert('RGBA')
bg = Image.new(frame.mode[:-1], frame.size, fill_color)
bg.paste(frame, frame.split()[-1])
frame = bg

# convert_frame = frame.convert('RGB')
# convert_frame.thumbnail((150, 150))
frame.info["duration"] = duration_before

@lxccc812
Copy link
Author

Thank you for your answer. My problem has been solved

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants