【问题标题】:Fastest way to load an animated GIF in Python into a numpy array在 Python 中将动画 GIF 加载到 numpy 数组中的最快方法
【发布时间】:2022-12-08 23:06:52
【问题描述】:

令人惊讶的是,我看不到对此的任何报道。

我发现了 3 种公认的执行此操作的方法 - Pillow、OpenCV 和 Imageio。结果令我吃惊,所以我将它们作为自答问答(如下)发布。

【问题讨论】:

    标签: python numpy opencv python-imaging-library python-imageio


    【解决方案1】:

    这似乎是在每个库中加载 GIF 的标准方式:

    import os
    import cv2
    import time
    import imageio
    import numpy as np
    from tqdm import tqdm
    from glob import glob
    from PIL import Image, ImageSequence
    
    gifs = glob(os.path.join("/folder/of/gifs", "*"))
    print(f"Found {len(gifs)} GIFs")
    
    def load_gif_as_video_pil(gif_path):
        im = Image.open(gif_path)
        frames = []
        for frame in ImageSequence.Iterator(im):
            frame = np.array(frame.copy().convert('RGB').getdata(), dtype=np.uint8).reshape(frame.size[1],
                                                                                            frame.size[0],
                                                                                            3)
            frames.append(frame)
    
        return np.array(frames)
    
    def load_gif_as_video_imageio(gif_path):
        return imageio.mimread(gif_path)
    
    def load_gif_as_video_opencv(filename):
        gif = cv2.VideoCapture(filename)
        frames = []
        while True:
            ret, frame = gif.read()
            if not ret:
                break
            frames.append(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
        return np.array(frames)
    
    
    start = time.time()
    [load_gif_as_video_imageio(path) for path in tqdm(gifs)]
    end = time.time()
    print(f"ImageIO: {end - start}")
    
    start = time.time()
    [load_gif_as_video_opencv(path) for path in tqdm(gifs)]
    end = time.time()
    print(f"OpenCV: {end - start}")
    
    start = time.time()
    [load_gif_as_video_pil(path) for path in tqdm(gifs)]
    end = time.time()
    print(f"PIL: {end - start}")
    

    超过 250 个 GIF,这些是结果:

    100%|██████████| 250/250 [00:13<00:00, 18.32it/s]
    ImageIO: 13.829721689224243
    100%|██████████| 250/250 [00:06<00:00, 39.04it/s]
    OpenCV: 6.478164434432983
    100%|██████████| 250/250 [03:00<00:00,  1.38it/s]
    PIL: 181.03292179107666
    

    OpenCV 的速度是 imageio 的两倍,比 PIL 快 15 倍(无论如何,使用我的方法)。

    【讨论】:

      【解决方案2】:

      您使用 Pillow 的代码效率很低! Images 与Numpy's array interface 兼容,因此您的转换代码使事情变得复杂。

      我会使用以下助手将帧输出到 Numpy 数组中:

      from PIL import Image, ImageSequence
      import numpy as np
      
      def load_frames(image: Image, mode='RGBA'):
          return np.array([
              np.array(frame.convert(mode))
              for frame in ImageSequence.Iterator(image)
          ])
      
      with Image.open('animated.gif') as im:
          frames = load_frames(im)
      

      这与其他运行时间基本相同。例如,我有一个 400x400 像素、21 帧的 GIF,它需要 mimread ~140ms,而 Pillow 需要~130ms。

      更新:我刚刚玩过 CV2 并注意到它的“挂钟”时间更好(即你正在测量的时间),因为它在其他线程中工作。例如,如果我使用 Jupyter %time magic 运行,我会得到以下输出:

      图像IO

      CPU times: user 135 ms, sys: 9.81 ms, total: 145 ms
      Wall time: 145 ms
      

      太平船务

      CPU times: user 127 ms, sys: 3.03 ms, total: 130 ms
      Wall time: 130 ms
      

      CV2

      CPU times: user 309 ms, sys: 95 ms, total: 404 ms
      Wall time: 89.7 ms
      

      IE。虽然它在 90 毫秒内完成了循环,但它总共使用了大约 4.5 倍的 CPU 时间。

      因此,如果您对完成单个大图像的时间感兴趣,您可能需要使用 CV2。但是如果你批处理大量图像,我建议在 multiprocessing Pool 中使用 Pillow。

      【讨论】:

        猜你喜欢
        • 2014-04-06
        • 1970-01-01
        • 2016-02-21
        • 1970-01-01
        • 2015-07-27
        • 1970-01-01
        • 2021-11-12
        • 2011-11-12
        • 1970-01-01
        相关资源
        最近更新 更多