繁体   English   中英

Python,我如何获得 gif 帧

[英]Python, how i can get gif frames

我正在寻找某种方法来获取 gif 帧数。 我在 Google、StackOverflow 和任何其他网站上查找,但我只找到了垃圾! 有人知道怎么做吗? 我只需要简单数量的 gif 帧。

您使用哪种方法来加载/操作框架? 你在使用 PIL 吗? 如果没有,我建议检查一下: Python Imaging Library ,特别 是 PIL gif page

现在,假设您正在使用 PIL 读取 gif,那么确定您正在查看的帧是一件非常简单的事情。 seek将转到特定帧并告诉将返回您正在查看的帧。

from PIL import Image
im = Image.open("animation.gif")

# To iterate through the entire gif
try:
    while 1:
        im.seek(im.tell()+1)
        # do something to im
except EOFError:
    pass # end of sequence

否则,我相信您只能通过查找直到引发异常(EOFError)来找到 gif 中的帧数。

只需解析文件,gif 就非常简单:

class GIFError(Exception): pass

def get_gif_num_frames(filename):
    frames = 0
    with open(filename, 'rb') as f:
        if f.read(6) not in ('GIF87a', 'GIF89a'):
            raise GIFError('not a valid GIF file')
        f.seek(4, 1)
        def skip_color_table(flags):
            if flags & 0x80: f.seek(3 << ((flags & 7) + 1), 1)
        flags = ord(f.read(1))
        f.seek(2, 1)
        skip_color_table(flags)
        while True:
            block = f.read(1)
            if block == ';': break
            if block == '!': f.seek(1, 1)
            elif block == ',':
                frames += 1
                f.seek(8, 1)
                skip_color_table(ord(f.read(1)))
                f.seek(1, 1)
            else: raise GIFError('unknown block type')
            while True:
                l = ord(f.read(1))
                if not l: break
                f.seek(l, 1)
    return frames

我最近遇到了同样的问题,发现关于 GIF 的文档特别缺乏。 这是我使用imageio 的 get_reader读取图像字节的解决方案(例如,如果您只是通过 HTTP 获取图像则很有用),它可以方便地将帧存储在numpy 矩阵中

import imageio
gif = imageio.get_reader(image_bytes, '.gif')

# Here's the number you're looking for
number_of_frames = len(gif)

for frame in gif:
  # each frame is a numpy matrix

如果您只需要打开文件,请使用:

gif = imageio.get_reader('cat.gif')

好吧,9 年可能有点太长了,但这是我的答案

import tkinter as tk
from PIL import Image  

    

def number_of_frames(gif):
    "Prints and returns the number of frames of the gif"
    print(gif.n_frames)
    return gif.n_frames


def update(ind):
    global root, label

    frame = frames[ind]
    ind += 1
    if ind == frameCnt:
        ind = 0
    label.configure(image=frame)
    root.after(100, update, ind)


file = Image.open("001.gif")
frameCnt = number_of_frames(file)
root = tk.Tk()
frames = [tk.PhotoImage( file='001.gif', format = f'gif -index {i}')
            for i in range(frameCnt)]
label = tk.Label(root)
label.pack()
root.after(0, update, 0)
root.mainloop()

对于那些不想依赖Pillow / PIL等第三方模块的人来说,基于@adw答案的更详尽的解决方案适用于Python 2和3:

import sys
is_py2 = sys.version_info[0] == 2


def gif_frames(image_path):
    """Return frames in an animated gif

    primarily used this great deep dive into the structure of an animated gif
    to figure out how to parse it:

        http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp

    :param image_path: string, assumed to be a path to a gif file
    :returns: integer, how many frames the gif contains
    """
    image_count = 0

    def skip_color_table(fp, packed_byte):
        """this will fp.seek() completely passed the color table"""
        if is_py2:
            packed_byte = int(packed_byte.encode("hex"), 16)

        has_gct = (packed_byte & 0b10000000) >> 7
        gct_size = packed_byte & 0b00000111

        if has_gct:
            global_color_table = fp.read(3 * pow(2, gct_size + 1))

    def skip_image_data(fp):
        """skips the image data, which is basically just a series of sub blocks
        with the addition of the lzw minimum code to decompress the file data"""
        lzw_minimum_code_size = fp.read(1)
        skip_sub_blocks(fp)

    def skip_sub_blocks(fp):
        """skips over the sub blocks

        the first byte of the sub block tells you how big that sub block is, then
        you read those, then read the next byte, which will tell you how big
        the next sub block is, you keep doing this until you get a sub block
        size of zero"""
        num_sub_blocks = ord(fp.read(1))
        while num_sub_blocks != 0x00:
            fp.read(num_sub_blocks)
            num_sub_blocks = ord(fp.read(1))

    with open(image_path, "rb") as fp:
        header = fp.read(6)
        if header == b"GIF89a": # GIF87a doesn't support animation
            logical_screen_descriptor = fp.read(7)
            skip_color_table(fp, logical_screen_descriptor[4])

            b = ord(fp.read(1))
            while b != 0x3B: # 3B is always the last byte in the gif
                if b == 0x21: # 21 is the extension block byte
                    b = ord(fp.read(1))
                    if b == 0xF9: # graphic control extension
                        block_size = ord(fp.read(1))
                        fp.read(block_size)
                        b = ord(fp.read(1))
                        if b != 0x00:
                            raise ValueError("GCT should end with 0x00")

                    elif b == 0xFF: # application extension
                        block_size = ord(fp.read(1))
                        fp.read(block_size)
                        skip_sub_blocks(fp)

                    elif b == 0x01: # plain text extension
                        block_size = ord(fp.read(1))
                        fp.read(block_size)
                        skip_sub_blocks(fp)

                    elif b == 0xFE: # comment extension
                        skip_sub_blocks(fp)

                elif b == 0x2C: # Image descriptor
                    # if we've seen more than one image it's animated
                    image_count += 1

                    # total size is 10 bytes, we already have the first byte so
                    # let's grab the other 9 bytes
                    image_descriptor = fp.read(9)
                    skip_color_table(fp, image_descriptor[-1])
                    skip_image_data(fp)

                b = ord(fp.read(1))

    return image_count

如果您使用 PIL( Python 成像库),您可以使用图像对象的n_frames属性。

看到这个答案

下面是一些代码,可以为您提供一个包含 GIF 中每一帧的持续时间值的列表:

from PIL import Image
gif_image = Image.open("animation.gif")
metadata = []

for i in range(gif_image.n_frames):
    gif_image.seek(i)
    duration = gif_image.info.get("duration", 0)
    metadata.append(duration)

您可以修改上面的代码以从每个帧中捕获其他数据,例如背景颜色索引、透明度或版本。 每个帧上的info字典如下所示:

{'version': b'GIF89a', 'background': 0, 'transparency': 100, 'duration': 70}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM