如何在 Pygame 中加载动画 GIF 并获取所有单独的帧?

Posted

技术标签:

【中文标题】如何在 Pygame 中加载动画 GIF 并获取所有单独的帧?【英文标题】:How can I load an animated GIF and get all of the individual frames in Pygame? 【发布时间】:2015-06-16 18:15:43 【问题描述】:

首先,抱歉,如果这是重复的。我找到的答案似乎无关紧要,但也许我正在使用错误的关键字进行搜索。我想做的是获取动画 GIF 并将其拆分为帧列表。基本上是这样的:

frames = []
for frame in split_animated_gif("some_animated_gif.gif"):
    frames.append(frame)

其中 split_animated_gif 按顺序返回一个表面列表,每个表面都是 GIF 的一个帧。感谢您的帮助。

编辑:经过一番窥探,我找到了一段代码,它成功地在 pygame 中为我显示了一个动画 GIF。可以在https://github.com/piantado/kelpy/blob/master/kelpy/GIFImage.py 找到它。不过,我们非常感谢您的帮助。

【问题讨论】:

我知道在本机 Python 中无法做到这一点。您必须连接到像 ImageMagick 这样的外部库。 (注意:IM 的文档非常令人失望)。 您可以将 GIF 转换为支持 Pygame 播放的 MPEG-1 电影。 任何一种方式都需要使用外部程序。 【参考方案1】:

Pygame 本身不支持动画 gif,这在文档中有说明。所以你必须

1) 在加载精灵或图像时,使用其他一些代码/库来分割 gif。那些你可以通过谷歌搜索“python split gif”或其他东西找到的。例如。 Python: Converting GIF frames to PNG

2) 如果您自己创建了 gif,只需逐帧再次导出您的精灵

无论哪种方式,您都必须手动完成动画部分。您可以通过谷歌搜索“pygame 动画”找到大量教程。例如。 Animated sprite from few images

【讨论】:

【参考方案2】:

此代码使用 PIL / pillow 库来实现。

from PIL import Image

def split_animated_gif(gif_file_path):
    ret = []
    gif = Image.open(gif_file_path)
    for frame_index in range(gif.n_frames):
        gif.seek(frame_index)
        frame_rgba = gif.convert("RGBA")
        pygame_image = pygame.image.fromstring(
            frame_rgba.tobytes(), frame_rgba.size, frame_rgba.mode
        )
        ret.append(pygame_image)
    return ret

【讨论】:

【参考方案3】:

PyGame分别是pygame.image模块只能处理非动画GIFs。 但是在 PyGame 主页上介绍了GIFImage 库:

这个库为pygame添加了GIF动画播放。


另一种选择是使用库将 GIF 逐帧加载到列表中。

例如使用 Pillow 库 (pip install Pillow)。

编写一个函数,可以将 PIL 图像转换为 pygame.Surface 并使用 PIL 库逐帧加载 GIF: (另见Extracting The Frames Of An Animated GIF Using Pillow和PIL and pygame.image)

from PIL import Image, ImageSequence
def pilImageToSurface(pilImage):
    mode, size, data = pilImage.mode, pilImage.size, pilImage.tobytes()
    return pygame.image.fromstring(data, size, mode).convert_alpha()

def loadGIF(filename):
    pilImage = Image.open(filename)
    frames = []
    if pilImage.format == 'GIF' and pilImage.is_animated:
        for frame in ImageSequence.Iterator(pilImage):
            pygameImage = pilImageToSurface(frame.convert('RGBA'))
            frames.append(pygameImage)
    else:
        frames.append(pilImageToSurface(pilImage))
    return frames

或者使用OpenCV/opencv-python库和VideoCapture

编写一个函数,可以将 cv2 pygame.image 图像转换为 pygame.Surface 并使用库逐帧加载 GIF: (另见Read GIF files in Python和How do I convert an OpenCV (cv2) image (BGR and BGRA) to a pygame.Surface object)

import cv2
def cv2ImageToSurface(cv2Image):
    size = cv2Image.shape[1::-1]
    format = 'RGBA' if cv2Image.shape[2] == 4 else 'RGB'
    cv2Image[:, :, [0, 2]] = cv2Image[:, :, [2, 0]]
    surface = pygame.image.frombuffer(cv2Image.flatten(), size, format)
    return surface.convert_alpha() if format == 'RGBA' else surface.convert()

def loadGIF(filename):
    gif = cv2.VideoCapture(filename)
    frames = []
    while True:
        ret, cv2Image = gif.read()
        if not ret:
            break
        pygameImage = cv2ImageToSurface(cv2Image)
        frames.append(pygameImage)
    return frames

另请参阅Load animated GIF 和一个简单的动画 gif 查看器示例:

import pygame
import cv2

def cv2ImageToSurface(cv2Image):
    size = cv2Image.shape[1::-1]
    format = 'RGBA' if cv2Image.shape[2] == 4 else 'RGB'
    cv2Image[:, :, [0, 2]] = cv2Image[:, :, [2, 0]]
    surface = pygame.image.frombuffer(cv2Image.flatten(), size, format)
    return surface.convert_alpha() if format == 'RGBA' else surface.convert()

def loadGIF(filename):
    gif = cv2.VideoCapture(filename)
    frames = []
    while True:
        ret, cv2Image = gif.read()
        if not ret:
            break
        pygameImage = cv2ImageToSurface(cv2Image)
        frames.append(pygameImage)
    return frames

pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()

gifFrameList = loadGIF(r"rubber_ball.gif")
currentFrame = 0

run = True
while run:
    clock.tick(20)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    window.fill(0)

    rect = gifFrameList[currentFrame].get_rect(center = (250, 250))
    window.blit(gifFrameList[currentFrame], rect)
    currentFrame = (currentFrame + 1) % len(gifFrameList)

    pygame.display.flip()

【讨论】:

以上是关于如何在 Pygame 中加载动画 GIF 并获取所有单独的帧?的主要内容,如果未能解决你的问题,请参考以下文章

我如何通过文本文件在Pygame中加载图像

在页面中加载 gif:未能加载资源 Cordova

在 Pygame 中加载资产时出现 FileNotFound 错误 [重复]

如何在 FLutter 中从 URL 加载动画 GIF?

如何在 Swift 3 中加载/删除带有动画的自定义视图

如何在vb窗体中显示gif动画