I have a piece of code that works using
my_surface = pygame.image.load('some_image.png')
This returns a pygame surface. I'd like to use the same code everywhere else but instead pass in a numpy array. (Actually, I'm going to have an if statement that determines whether we have an array or a path to an image. In either case the function has to return the same type of object, a pygame surface. It already works using the above code. Now I have to add a second way of generating the same object if the script is being used differently.) I have tried using
my_surface = pygame.pixelcopy.make_surface(my_array)
but the problem is that this function requires an INTEGER array. My array is a float32. I can force it through by passing the array like so
(my_array*10000).astype(int)
But when I display it later it looks like garbage (imagine my surprise). So my question is, how do we create a pygame surface elegantly out of a numpy array of floats?
convert data range to range[0-255], the data size must be M x N
or M x N x 3
pygame.init()
display = pygame.display.set_mode((350, 350))
x = np.arange(0, 300)
y = np.arange(0, 300)
X, Y = np.meshgrid(x, y)
Z = X + Y
Z = 255*Z/Z.max()
surf = pygame.surfarray.make_surface(Z)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
display.blit(surf, (0, 0))
pygame.display.update()
pygame.quit()
If you want in grayscale:
import pygame
import numpy as np
def gray(im):
im = 255 * (im / im.max())
w, h = im.shape
ret = np.empty((w, h, 3), dtype=np.uint8)
ret[:, :, 2] = ret[:, :, 1] = ret[:, :, 0] = im
return ret
pygame.init()
display = pygame.display.set_mode((350, 350))
x = np.arange(0, 300)
y = np.arange(0, 300)
X, Y = np.meshgrid(x, y)
Z = X + Y
Z = 255 * Z / Z.max()
Z = gray(Z)
surf = pygame.surfarray.make_surface(Z)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
display.blit(surf, (0, 0))
pygame.display.update()
pygame.quit()
This is a supplement to eyllanesc's answer, since I found that answer's examples helpful, but they can be optimised slightly if you want to update the image.
The main optimisations are to use an 8-bit array, and to use surfarray.blit_array
instead of blit
and display.flip
instead of display.update
. (The latter two require the display and the array to be the same size.)
The difference is not huge, however - the code below gets me about 15 fps without the optimisations and 20-21 fps with them. An OpenGL based method would be much faster, but I don't know a convenient library for that.
import pygame
import numpy as np
from pygame import surfarray
from timeit import default_timer as timer
pygame.init()
w = 1000
h = 800
display = pygame.display.set_mode((w, h))
img = np.zeros((w,h,3),dtype=np.uint8)
init_time = timer()
frames_displayed = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# updating the image
for i in range(100):
img[np.random.randint(w),np.random.randint(h)] = np.random.randint(255,size=3,dtype=np.uint8)
surfarray.blit_array(display, img)
pygame.display.flip()
frames_displayed+=1
print("average frame rate:", frames_displayed/(timer()-init_time), "fps")
pygame.quit()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With