How to create a pygame surface from a numpy array of float32?

onjre picture onjre · Dec 15, 2016 · Viewed 11.9k times · Source

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?

Answer

eyllanesc picture eyllanesc · Dec 15, 2016

convert data range to range[0-255], the data size must be mxn or mxnx3

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

enter image description here

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

enter image description here