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

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

问题描述

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

我正在尝试为这个 gif 制作动画:

可以在

在下面的代码中,处理方法 1 的处理方式与方法 0 等效.(因为它只需要当前帧而不是先将其粘贴到最后一帧)这样做是因为我得到了棕褐色部分帧正在渗漏,并以代码中处理它的方式处理它提供了理想的结果.

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

from PIL import Image, ImageSequencedef unpack_gif(src):# 加载 Gif图像 = Image.open(src)# 获取每一帧的帧和处理方法帧 = []处置 = []对于 ImageSequence.Iterator(image) 中的 gifFrame:处置.追加(gifFrame.disposal_method)frame.append(gifFrame.convert('P'))# 遍历帧,并根据其处理方法对其进行编辑输出 = []最后一帧 = 无thisFrame = 无对于我,枚举(帧)中的loadedFrame:# 更新这个框架thisFrame = 加载的Frame# 如果处置方法为2如果处置[i] == 2:# 检查这不是第一帧如果我 != 0:# 将 thisFrames 不透明像素粘贴到 lastFrame 并将 lastFrame 附加到输出lastFrame.paste(thisFrame, mask=thisFrame.convert('RGBA'))output.append(lastFrame)别的:output.append(thisFrame)# 如果处置方法为 1 或 0elif 处置[i] == 1 或处置[i] == 0:# 附加 thisFrame 到输出output.append(thisFrame)# If 处理方法 if 2、1 或 0 以外的任何值别的:raise ValueError('除 2:Restore to Background, 1:Do Not Dispose, and 0:No Disposal 之外的处理方法目前都支持')# 更新最后一帧lastFrame = 加载帧返回输出

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

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.

I am trying to animate this gif:

This gifs source can be found here.

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

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

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?

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.

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

Implimentation:

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()

解决方案

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, 2, 1, 2...

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) 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.

Source: www.w3.org

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.

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

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天全站免登陆