Python:提取帧时检测罕见的奇怪GIF

Python:提取帧时检测罕见的奇怪GIF,python,python-imaging-library,gif,moviepy,python-imageio,Python,Python Imaging Library,Gif,Moviepy,Python Imageio,我从互联网上收集了大量GIF数据集(超过100k),当我试图用python提取GIF帧时,遇到了一些罕见的奇怪GIF。三个常用的软件包(moviepy,PIL,imageio)提供了这种罕见的奇怪GIF的完全不同的结果 moviepy>=1.0.3将在VideoFileClip.iter\u frames()中阻止。iter\u frames()将永远在第二帧循环,并且代码不会引发异常 PIL>=7.1.2将输出与第一帧相同的多帧 imageio>=2.6.1可以正确提取帧,而输出帧是奇怪的 然

我从互联网上收集了大量GIF数据集(超过100k),当我试图用python提取GIF帧时,遇到了一些罕见的奇怪GIF。三个常用的软件包(
moviepy
PIL
imageio
)提供了这种罕见的奇怪GIF的完全不同的结果

  • moviepy>=1.0.3
    VideoFileClip.iter\u frames()中阻止。iter\u frames()将永远在第二帧循环,并且代码不会引发异常
  • PIL>=7.1.2
    将输出与第一帧相同的多帧
  • imageio>=2.6.1
    可以正确提取帧,而输出帧是奇怪的
  • 然后,您可以从上述软件包提供的
    frame\u迭代器
    中转储帧:

    def dump_video_frames(video_frames):
        root = 'data/frames'
        if os.path.exists(root):
            shutil.rmtree(root)
        os.makedirs(root)
    
        for i, frame in enumerate(video_frames):
            frame.save(os.path.join(root, '%d.jpg' % i))
    
    frames = []
    for frame in frame_iterator:
        if isinstance(frame, np.ndarray):
            frame = Image.fromarray(np.uint8(frame))
        frames.append(frame.convert('RGB'))
    
    dump_video_frames(frames)
    
    以下是一个例子:

    原始GIF:

    PIL的输出

    imageio的输出

    您可以看到
    PIL
    仅获取第一帧,没有任何黑色区域,这与
    imageio
    的输出非常不同

    所以我的问题是如何在python中检测如此奇怪的gif?由于我首先使用
    moviepy
    是因为它在其他GIF中的良好性能,因此我需要在代码使用
    moviepy
    提取其帧之前检测此类GIF,以避免在
    VideoFileClip.iter\u frames()
    中出现无限循环,这不会引发任何异常。我无法从谷歌获得如此罕见的gif的任何信息

    我将在下面提供另外两个GIF示例:


    建议:查阅GIF格式规范。浏览这些GIF的结构(例如使用hexdump工具),以确定正确的解释。完成此操作后,您可以向所使用的三个软件包中的至少两个提交错误报告,因为它们没有完全实现标准。您的web浏览器是否具有正确显示GIF的库?如果是的话,找出它是什么,看看它是否有python绑定。这些似乎是严重损坏的GIF,我想是由一个有缺陷的软件创建的。具体来说,它们包含的帧不符合图像的边界,如文件头中所示。在您尝试的三个软件包中,一个被无效数据阻塞,一个只显示给定图像边界内的像素(该边界不包括任何实际正在设置动画的区域),另一个扩展边界以包括所有帧。要检测这些损坏的文件,您需要扫描文件和帧头,查看是否有任何帧超出边界(或者您可以重写文件头,使其边界包含所有帧)。这可以在不解压缩任何像素数据的情况下完成,因此即使在纯Python中也应该相当快。不幸的是,我不知道有任何现有代码或包可以这样做。供参考:
    from PIL import Image, ImageSequence
    from PIL import ImageFile
    ImageFile.LOAD_TRUNCATED_IMAGES = True
    
    video = Image.open(path)
    frame_iterator = ImageSequence.Iterator(video)
    
    import imageio
    
    frame_iterator = imageio.get_reader(path)
    
    def dump_video_frames(video_frames):
        root = 'data/frames'
        if os.path.exists(root):
            shutil.rmtree(root)
        os.makedirs(root)
    
        for i, frame in enumerate(video_frames):
            frame.save(os.path.join(root, '%d.jpg' % i))
    
    frames = []
    for frame in frame_iterator:
        if isinstance(frame, np.ndarray):
            frame = Image.fromarray(np.uint8(frame))
        frames.append(frame.convert('RGB'))
    
    dump_video_frames(frames)