【问题标题】:How to Extract Frame From GIF, and Reconstruct the Details of each Frame?如何从 GIF 中提取帧,并重建每一帧的细节?
【发布时间】:2018-05-09 01:33:40
【问题描述】:

我尝试从 GIF 文件中提取每一帧。

我找到了两种方法来处理这个问题。

1 找一个在线工具来解决它。

https://ezgif.com/split

这是一个很好的工具。它可以重绘细节。从 GIF 中提取的帧质量很高。

2 尝试使用python库解决这个问题。

我使用 PIL,但它带来了一个棘手的问题。提取的帧丢失了许多带有白边的细节。

所以想问一下EZGif采用什么算法,用python怎么实现?

【问题讨论】:

  • 请添加您用于提取帧的 Python 代码。您要提取什么文件类型?我认为这与透明的背景有关。尝试保存到单个 gif 图像,如图所示 here

标签: python gif


【解决方案1】:

为您的问题找到了有用的reference

import os
from PIL import Image


'''
I searched high and low for solutions to the "extract animated GIF frames in Python"
problem, and after much trial and error came up with the following solution based
on several partial examples around the web (mostly Stack Overflow).
There are two pitfalls that aren't often mentioned when dealing with animated GIFs -
firstly that some files feature per-frame local palettes while some have one global
palette for all frames, and secondly that some GIFs replace the entire image with
each new frame ('full' mode in the code below), and some only update a specific
region ('partial').
This code deals with both those cases by examining the palette and redraw
instructions of each frame. In the latter case this requires a preliminary (usually
partial) iteration of the frames before processing, since the redraw mode needs to
be consistently applied across all frames. I found a couple of examples of
partial-mode GIFs containing the occasional full-frame redraw, which would result
in bad renders of those frames if the mode assessment was only done on a
single-frame basis.
Nov 2012
'''


def analyseImage(path):
    '''
    Pre-process pass over the image to determine the mode (full or additive).
    Necessary as assessing single frames isn't reliable. Need to know the mode 
    before processing all frames.
    '''
    im = Image.open(path)
    results = {
        'size': im.size,
        'mode': 'full',
    }
    try:
        while True:
            if im.tile:
                tile = im.tile[0]
                update_region = tile[1]
                update_region_dimensions = update_region[2:]
                if update_region_dimensions != im.size:
                    results['mode'] = 'partial'
                    break
            im.seek(im.tell() + 1)
    except EOFError:
        pass
    return results


def processImage(path):
    '''
    Iterate the GIF, extracting each frame.
    '''
    mode = analyseImage(path)['mode']

    im = Image.open(path)

    i = 0
    p = im.getpalette()
    last_frame = im.convert('RGBA')

    try:
        while True:
            print "saving %s (%s) frame %d, %s %s" % (path, mode, i, im.size, im.tile)

            '''
            If the GIF uses local colour tables, each frame will have its own palette.
            If not, we need to apply the global palette to the new frame.
            '''
            if not im.getpalette():
                im.putpalette(p)

            new_frame = Image.new('RGBA', im.size)

            '''
            Is this file a "partial"-mode GIF where frames update a region of a different size to the entire image?
            If so, we need to construct the new frame by pasting it on top of the preceding frames.
            '''
            if mode == 'partial':
                new_frame.paste(last_frame)

            new_frame.paste(im, (0,0), im.convert('RGBA'))
            new_frame.save('%s-%d.png' % (''.join(os.path.basename(path).split('.')[:-1]), i), 'PNG')

            i += 1
            last_frame = new_frame
            im.seek(im.tell() + 1)
    except EOFError:
        pass


def main():
    processImage('foo.gif')
    processImage('bar.gif')


if __name__ == "__main__":
    main()

【讨论】:

    【解决方案2】:

    PIL 中的 GIF 透明度已损坏。不知道为什么会这样,但这是事实。

    您可以尝试改用my GIF library,我刚刚为此做了一个 Python 前端:

    from PIL import Image
    
    def GIF_Load(file):
        from platform import system
        from ctypes import string_at, Structure, c_long as cl, c_ubyte, \
                           py_object, pointer, POINTER as PT, CFUNCTYPE, CDLL
        class GIF_WHDR(Structure): _fields_ = \
           [("xdim", cl), ("ydim", cl), ("clrs", cl), ("bkgd", cl),
            ("tran", cl), ("intr", cl), ("mode", cl), ("frxd", cl), ("fryd", cl),
            ("frxo", cl), ("fryo", cl), ("time", cl), ("ifrm", cl), ("nfrm", cl),
            ("bptr", PT(c_ubyte)), ("cpal", PT(c_ubyte))]
        def intr(y, x, w, base, tran): base.paste(tran.crop((0, y, x, y + 1)), w)
        def skew(i, r): return r >> ((7 - (i & 2)) >> (1 + (i & 1)))
        def WriteFunc(d, w):
            cpal = string_at(w[0].cpal, w[0].clrs * 3)
            list = d.contents.value
            if (len(list) == 0):
                list.append(Image.new("RGBA", (w[0].xdim, w[0].ydim)))
            tail = len(list) - 1
            base = Image.frombytes("L", (w[0].frxd, w[0].fryd),
                                   string_at(w[0].bptr, w[0].frxd * w[0].fryd))
            if (w[0].intr != 0):
                tran = base.copy()
                [intr(skew(y, y) + (skew(y, w[0].fryd - 1) + 1, 0)[(y & 7) == 0],
                      w[0].frxd, (0, y), base, tran) for y in range(w[0].fryd)]
            tran = Image.eval(base, lambda indx: (255, 0)[indx == w[0].tran])
            base.putpalette(cpal)
            list[tail].paste(base, (w[0].frxo, w[0].fryo), tran)
            list[tail].info = {"delay" : w[0].time}
            if (w[0].ifrm != (w[0].nfrm - 1)):
                list.append(list[max(0, tail - int(w[0].mode == 3))].copy())
                if (w[0].mode == 2):
                    base = Image.new("L", (w[0].frxd, w[0].fryd), w[0].bkgd)
                    base.putpalette(cpal)
                    list[tail + 1].paste(base, (w[0].frxo, w[0].fryo))
        try: file = open(file, "rb")
        except IOError: return []
        file.seek(0, 2)
        size = file.tell()
        file.seek(0, 0)
        list = []
        CDLL(("%s.so", "%s.dll")[system() == "Windows"] % "gif_load"). \
        GIF_Load(file.read(), size,
                 CFUNCTYPE(None, PT(py_object), PT(GIF_WHDR))(WriteFunc),
                 None, pointer(py_object(list)), 0)
        file.close()
        return list
    
    def GIF_Save(file, fext):
        list = GIF_Load("%s.gif" % file)
        [pict.save("%s_f%d.%s" % (file, indx, fext))
         for (indx, pict) in enumerate(list)]
    
    GIF_Save("insert_gif_name_here_without_extension", "png")
    

    【讨论】:

      猜你喜欢
      • 2014-12-22
      • 2010-10-31
      • 2012-09-29
      • 1970-01-01
      • 2012-03-24
      • 2014-03-26
      • 1970-01-01
      • 2010-11-11
      • 1970-01-01
      相关资源
      最近更新 更多