I have problems with displaying a numpy array with pyglet. I have found a very similar topic (how to display a numpy array with pyglet?) that I used. I want to display the array in greyscale, but pyglet displays it with colours see the image: http://i.stack.imgur.com/pL6Yr.jpg
def create(self, X,Y):
IMG = random((X,Y)) * 255
self.IMG = dstack((IMG,IMG,IMG))
return self.IMG
def image(self):
self.img_data = self.create(X,Y).data.__str__()
self.image = pyglet.image.ImageData(X,Y, 'RGB', self.img_data, pitch = -X*3)
return self.image
If I save and load the array instead it works (but it is horrobly slower):
def image(self):
self.im_save=scipy.misc.toimage(self.create(X,Y),cmin=0, cmax=255)
self.im_save.save('outfile.png')
self.image = pyglet.image.load('outfile.png')
return self.image
And I get what I wanted:
i.stack.imgur.com/FCY1v.jpg
I can't find the mistake in the first code example :(
EDIT:
Many thanks for your answers. With the hint from Bago I got this to code to work :) And indeed nfirvine suggestion is reasonable, since I only want to display the matrix in greyscale.
def create(self, X,Y):
self.IMG = (random((X,Y)) * 255).astype('uint8')
return self.IMG
def image(self):
self.img_data = self.create(X,Y).data.__str__()
self.image = pyglet.image.ImageData(X,Y, 'L', self.img_data)
return self.image
I've spent the last week playing around with using NumPy to generate random textures. I came across this post and tried the accepted answers.
I can confirm that the previously accepted answer is NOT CORRECT.
It seems correct because you are using grey-scale images. But if you were to use a colour image (RGBA for example) and zero the GBA channels you would have discovered this because you would still be getting green and blue showing up in your texture.
By using __str__() you are actually sending garbage and not the values you really want.
I'll use my code to demonstrate this.
import numpy
import pyglet
from pyglet.gl import *
# the size of our texture
dimensions = (16, 16)
# we need RGBA textures
# which has 4 channels
format_size = 4
bytes_per_channel = 1
# populate our array with some random data
data = numpy.random.random_integers(
low = 0,
high = 1,
size = (dimensions[ 0 ] * dimensions[ 1 ], format_size)
)
# convert any 1's to 255
data *= 255
# set the GB channels (from RGBA) to 0
data[ :, 1:-1 ] = 0
# ensure alpha is always 255
data[ :, 3 ] = 255
# we need to flatten the array
data.shape = -1
Using the answer above, you would do the following
tex_data = data.astype('uint8').__str__()
If you try the code out, you will get all colours, not just red!
The proper way is to convert to the ctype GLubytes.
# convert to GLubytes
tex_data = (GLubyte * data.size)( *data.astype('uint8') )
You can then pass this into your texture.
# create an image
# pitch is 'texture width * number of channels per element * per channel size in bytes'
return pyglet.image.ImageData(
dimensions[ 0 ],
dimensions[ 1 ],
"RGBA",
tex_data,
pitch = dimensions[ 1 ] * format_size * bytes_per_channel
)
I've been playing with this to get a dynamic view of a numpy array. The answer by @Rebs worked, but became inefficient when I wanted to update the image at every frame. After profiling, I found that the ctypes value casting was the rate limiting step, and could be sped up by instead using the from_buffer
method of the ctype type object to share the underlying bits in memory between the numpy array and the GLubyte array.
Here is a class that will map between a 2d numpy array and a pyglet image, using matplotlib's colour maps to do so. If you have a numpy array, create an ArrayView wrapper around it and then update and blit it in the window on_draw
method:
my_arr = np.random.random((nx, ny))
arr_img = ArrayImage(my_arr)
@window.event
def on_draw():
arr_img.update()
arr_img.image.blit(x, y)
Full class implementation:
import numpy as np
import matplotlib.cm as cmaps
from matplotlib.colors import Normalize
import pyglet
import pyglet.gl
class ArrayImage:
"""Dynamic pyglet image of a 2d numpy array using matplotlib colormaps."""
def __init__(self, array, cmap=cmaps.viridis, norm=None, rescale=True):
self.array = array
self.cmap = cmap
if norm is None:
norm = Normalize()
self.norm = norm
self.rescale = rescale
self._array_normed = np.zeros(array.shape+(4,), dtype=np.uint8)
# this line below was the bottleneck...
# we have removed it by setting the _tex_data array to share the buffer
# of the normalised data _array_normed
# self._tex_data = (pyglet.gl.GLubyte * self._array_normed_data.size)( *self._array_normed_data )
self._tex_data = (pyglet.gl.GLubyte * self._array_normed.size).from_buffer(self._array_normed)
self._update_array()
format_size = 4
bytes_per_channel = 1
self.pitch = array.shape[1] * format_size * bytes_per_channel
self.image = pyglet.image.ImageData(array.shape[0], array.shape[1], "RGBA", self._tex_data)
self._update_image()
def set_array(self, data):
self.array = data
self.update()
def _update_array(self):
if self.rescale:
self.norm.autoscale(self.array)
self._array_normed[:] = self.cmap(self.norm(self.array), bytes=True)
# don't need the below any more as _tex_data points to _array_normed memory
# self._tex_data[:] = self._array_normed
def _update_image(self):
self.image.set_data("RGBA", self.pitch, self._tex_data)
def update(self):
self._update_array()
self._update_image()
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