简体   繁体   中英

How can I load an animated GIF and get all of the individual frames in Pygame?

First of all, sorry if this is a duplicate. The answers that I found either seemed irrelevant but perhaps I'm searching with the wrong keywords. What I'd like to do is to take an animated GIF and split it up into a list of frames. Basically, something like this:

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

where split_animated_gif returns a list of surfaces, each of which is a frame of the GIF, in order. Thank you for your help.

EDIT: After some more snooping, I found a piece of code that successfully displayed an animated GIF in pygame for me. It can be found athttps://github.com/piantado/kelpy/blob/master/kelpy/GIFImage.py . Your help was much appreciated, though.

Pygame itself does not support animated gifs, which is stated in the documentation. So you'll have to

1) use some other piece of code / library to split the gif when you're loading your sprites or images. Those you would find just by googling for "python split gif" or something. Eg Python: Converting GIF frames to PNG

2) if you created the gif yourself, just export your sprites again frame by frame

Either way, you'll have to do the animating part by hand. Which for you'll find plenty of tutorials by googling "pygame animation". Eg Animated sprite from few images

This code does it using PIL / pillow library.

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

PyGame respectively the pygame.image module can only handle non-animated GIFs .
But on the PyGame homepage is introduced the GIFImage library:

This library adds GIF animation playback to pygame.


Another option is to use a library to load the GIF frame by frame to a list.

For instance use the Pillow library ( pip install Pillow ).

Write a function, that can convert a PIL image to a pygame.Surface and use the PIL library to load a GIF frame by frame:
(see also Extracting The Frames Of An Animated GIF Using Pillow and 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

Or use OpenCV / opencv-python library and VideoCapture

Write a function, that can convert a cv2 pygame.image image to a pygame.Surface a nd use the library to load a GIF frame by frame:
(see also Read GIF files in Python and 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

See also Load animated GIF and a simple animated gif viewer example:

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()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM