[...] shape (y,x,4,k) 其中y 是高度,x 是宽度,4 是通道数(红色、绿色、蓝色、alpha),k 是帧数 [...]
是的,只需一行代码即可。以下行将 (y, x, 4, k) numpy 数组 (data) 转换为 pygme.Surface 对象列表 (surf_list):
surf_list = [pygame.image.frombuffer(d[:,:,[2, 1, 0, 3]].flatten(), (data.shape[1::-1]), 'RGBA') for d in data.transpose(3, 0, 1, 2)]
分别
surf_list = []
for d in data.transpose(3, 0, 1, 2):
bytes = d[:,:,[2, 1, 0, 3]].flatten()
size = data.shape[1::-1]
format = 'RGBA'
surface = pygame.image.frombuffer(bytes, size, format)
surf_list.append(surface)
解释:
使用numpy.traspose 将第 3 个(框架)轴移动轴 2 带到前面(请参阅Iterating over arbitrary dimension of numpy.array)并遍历框架:
for d in data.transpose(3, 0, 1, 2):
通过pygame.image.frombuffer()从每一帧创建一个pygame.Surface:
surface = pygame.image.frombuffer(bytes, size, format)
pygame.image.frombuffer() 有 3 个参数,bytes、size、format。 bytes 是像素数据的一维字节数组。 numpy.ndarray.flatten 返回折叠成一维数组的副本。颜色通道的顺序很可能是 BGRA 而不是 RGBA。因此,您必须交换红色和蓝色通道 (d[:,:,[2, 1, 0, 3]])。如果颜色通道的顺序是 RGBA,则可以跳过此步骤:
bytes = d[:,:,[2, 1, 0, 3]].flatten() # for BGRA
bytes = d.flatten() # for RGBA
size 是一个包含 2 个元素(x、y)的元组,并指定图像的大小。尺寸可以从numpy.ndarray.shape:
size = data.shape[1::-1]
或
size = (data.shape[1], data.shape[0])
格式指定图片格式,必须为'RGBA'('BGRA'不存在):
format = 'RGBA'
查看最小示例,它创建一个 (y, x, 4, k) numpy 数组 (data ) 并将其转换为 pygme.Surface 对象列表 (surf_list) :
import pygame
import numpy as np
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
radius = 100
frames = 20
data = np.zeros(shape = (radius*2, radius*2, 4, frames), dtype = "uint8")
for x in range(data.shape[0]):
for y in range(data.shape[1]):
px, py = x - data.shape[0]/2, y - data.shape[1]/2
for i in range(frames):
maxY2 = (radius*radius - px*px) * pow(abs(i-frames/2) / frames, 2)
if px*px + py* py < radius*radius:
if py * py < maxY2:
data[y, x, (0, 3), i] = 255, 255
if (px*px + py*py)*4 > radius*radius:
data[y, x, (1, 2), i] = 255, 255
else:
data[y, x, (2, 3), i] = 255, 255
surf_list = [pygame.image.frombuffer(d[:,:,[2, 1, 0, 3]].flatten(), (data.shape[1::-1]), 'RGBA') for d in data.transpose(3, 0, 1, 2)]
count = 0
run = False
while not run:
clock.tick(20)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = True
window.fill(0)
window.blit(surf_list[count], surf_list[0].get_rect(center = window.get_rect().center))
pygame.display.flip()
count = (count + 1) % len(surf_list)
pygame.quit()