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

9

首先,如果这是一个重复的问题,我很抱歉。我找到的答案似乎不相关,但也许是我用错误的关键字搜索。我想做的是将动画GIF拆分成一系列帧。基本上,就像这样:

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

split_animated_gif函数返回一个表面(surface)列表,每个表面(surface)都是GIF的一帧(frame),按顺序排列。感谢您的帮助。

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


我不知道在原生的Python中有什么方法可以做到那一点。你必须要连接到一个外部库,比如ImageMagick。(注意:IM的文档相当令人失望)。 - TigerhawkT3
你可以将GIF转换为MPEG-1电影格式,这种格式可以在Pygame中播放。 - Malik Brahimi
无论哪种方式,都需要使用外部程序。 - TigerhawkT3
3个回答

5

Pygame本身不支持动态gif图像,文档中有说明。因此,您需要:

1)使用其他代码/库在加载精灵或图像时拆分gif。您可以通过谷歌搜索“python split gif”等关键词找到相关信息。例如:Python:将GIF帧转换为PNG

2)如果您自己创建了gif,请逐帧重新导出您的精灵

无论哪种方式,您都需要手动完成动画部分。您可以通过谷歌搜索“pygame animation”等关键词找到大量教程。例如:从几张图片创建动画精灵


4

这段代码使用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

0

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

该库为pygame添加了GIF动画播放功能。


另一种选择是使用库逐帧加载GIF到列表中。
例如可以使用Pillow库(pip install Pillow)。
编写一个函数,将PIL图像转换为pygame.Surface并使用PIL库逐帧加载GIF:
(另请参阅使用Pillow提取动画GIF的帧PIL和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:
(另请参见在Python中读取GIF文件如何将OpenCV(cv2)图像(BGR和BGRA)转换为pygame.Surface对象
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

请参考加载动态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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接