Tkinter中的Gif动画,每隔一帧就有PILL闪烁 [英] Gif Animation in tkinter with PILL Flickering On Every Other Frame

查看:124
本文介绍了Tkinter中的Gif动画,每隔一帧就有PILL闪烁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个简单的小部件,以使用PILL在tkinter中对gif进行动画处理,因为tkinter本身不支持它们.我遇到的问题是某些gif闪烁.以下是这种效果的示例.

I am writing a simple widget to animate gifs in tkinter using PILL since tkinter does not natively support them. The issue I am having is that some gifs are flickering. Below is an example if this effect.

我正在为以下gif动画:

I am trying to animate this gif:

可以在此处找到此gif来源.

This gifs source can be found here.

但是,当我运行代码时,动画结果如下:

However, when I run my code, the animation turns out like this:

我将gif分割开了,其他每一帧都是这样的:

I split apart the gif, and every other frame is partial like this:

经过一些研究,我相信这是一种压缩gif文件的方式,以便某些帧仅代表运动.但是我不是100%,在那里我可能是错的.如果是这种情况,我该如何重整图像以重新创建原始gif的质量?

After some research I believe this is a way of compressing gif files so that some frames only represent movement. I am not 100% on this however, and I could be wrong there. If that is the case, how can I reform the images in a way to recreate the quality in the original gif?

我已经能够创建一个简单的解决方案,该方案只是跳过其他所有帧,但这并不能解决实际问题,并且很可能无法与所有此类gif一起使用.

I have been able to create create a simple work around that just skips every other frame, but that does not fix the actual issue, and that most likely will not work with every gif like this.

如何显示帧,以使动画重新创建gif的原始质量.

How can I display the frames as such that the animation recreates the original quality of the gif.

实现方式:

import tkinter as tk
from PIL import Image, ImageTk, ImageSequence


class AnimatedGif:
    def __init__(self, root, src=''):
        self.root = root

        # Load Frames
        self.image = Image.open(src)
        self.frames = []
        self.duration = []
        for frame in ImageSequence.Iterator(self.image):
                self.duration.append(frame.info['duration'])
                self.frames.append(ImageTk.PhotoImage(frame))
        self.counter = 0
        self.image = self.frames[self.counter]

        # Create Label
        self.label = tk.Label(self.root, image=self.image)
        self.label.pack()

        # Start Animation
        self.__step_frame()

    def __step_frame(self):
        # Update Frame
        self.label.config(image=self.frames[self.counter])
        self.image = self.frames[self.counter]

        # Loop Counter
        self.counter += 1
        if self.counter >= len(self.frames):
            self.counter = 0

        # Queue Frame Update
        self.root.after(self.duration[self.counter], lambda: self.__step_frame())

    def pack(self, **kwargs):
        self.label.pack(**kwargs)

    def grid(self, **kwargs):
        self.label.grid(**kwargs)

if __name__ in '__main__':
    root = tk.Tk()
    gif = AnimatedGif(root, '144.gif')
    gif.pack()
    root.mainloop()

推荐答案

处理方法是导致问题的原因,就像我以前认为的那样.部分帧的处理方法设置为2,并且每隔一帧就会发生一次,因为其他帧都设置为1,如下所示:

The disposal method was the cause of the issue like I previously thought. The partial frames have their disposal method set to 2, and it was happening every other frame because the other frames were set to 1 like so:

1、2、1、2、1、1、2、1、2 ...

1, 2, 1, 2, 1, 2, 1, 2...

处置方法2使用前一帧作为背景,并仅更改当前帧中的不透明像素.方法1将整个图像复制到另一幅图像上,同时保持透明度不变.

Disposal method 2 uses the previous frame as a background, and changes only the opaque pixels in the current frame. Method 1 will copy the entire image over the other one while leaving the transparency intact.

iv)处置方法-指示图形的处理方式 显示后将得到处理.

iv) Disposal Method - Indicates the way in which the graphic is to be treated after being displayed.

        Values :    0 -   No disposal specified. The decoder is
                          not required to take any action.
                    1 -   Do not dispose. The graphic is to be left
                          in place.
                    2 -   Restore to background color. The area used by the
                          graphic must be restored to the background color.
                    3 -   Restore to previous. The decoder is required to
                          restore the area overwritten by the graphic with
                          what was there prior to rendering the graphic.
                 4-7 -    To be defined.

来源: www.w3.org

在下面的代码中,处置方法1与方法0等效.(因为它只处理当前帧,而不是先粘贴到最后一帧),因为我得到的是棕褐色部分帧正在渗透,并以代码中处理的方式对其进行处理可提供理想的结果.

In the code below, a disposal method of 1 is handles equivalently to method 0. (In that it takes just the current frame rather than pasting it onto the last frame first) It does this because I was getting the tan color on the partial frames was bleeding through, and handling it in the way it is handled in the code provides the desirable result.

此方法中省略了方法3+,因为它们很少见,并且与该问题无关,因为此gif使用方法0和1.

Methods 3+ are omitted from this script due to them being rare and not being relevant to to this question as this gif uses methods 0 and 1.

from PIL import Image, ImageSequence


def unpack_gif(src):
    # Load Gif
    image = Image.open(src)

    # Get frames and disposal method for each frame
    frames = []
    disposal = []
    for gifFrame in ImageSequence.Iterator(image):
        disposal.append(gifFrame.disposal_method)
        frames.append(gifFrame.convert('P'))

    # Loop through frames, and edit them based on their disposal method
    output = []
    lastFrame = None
    thisFrame = None
    for i, loadedFrame in enumerate(frames):
        # Update thisFrame
        thisFrame = loadedFrame

        # If the disposal method is 2
        if disposal[i] == 2:
            # Check that this is not the first frame
            if i != 0:
                # Pastes thisFrames opaque pixels over lastFrame and appends lastFrame to output
                lastFrame.paste(thisFrame, mask=thisFrame.convert('RGBA'))
                output.append(lastFrame)
            else:
                output.append(thisFrame)

        # If the disposal method is 1 or 0
        elif disposal[i] == 1 or disposal[i] == 0:
            # Appends thisFrame to output
            output.append(thisFrame)

        # If disposal method if anything other than 2, 1, or 0
        else:
            raise ValueError('Disposal Methods other than 2:Restore to Background, 1:Do Not Dispose, and 0:No Disposal are supported at this time')

        # Update lastFrame
        lastFrame = loadedFrame

    return output

此脚本返回图像对象列表,可以进一步修改该图像对象以与tkinter或其他GUI框架一起使用.

This script returns a list of image object that can be further modified to be used with tkinter or other GUI frameworks.

这篇关于Tkinter中的Gif动画,每隔一帧就有PILL闪烁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆