Question

Is there a way to set individual pixel values for image data in Pyglet?

I guess it can also be done by setting the OpenGL rendering mode to points, so if someone has insight on this, please share it as well.

Was it helpful?

Solution

The following is purely from an OpenGL standpoint, I don't know of any pyglet tricks for this.

There is always glDrawPixels, or you could use an OpenGL texture that is the same resolution as your window (best if you can use non power of two texture sizes) and then map that texture on to a quad that takes up the entire window.

The latter I think is the fastest solution for when you don't have to change pixels on every refresh because all the data is already on the card.

You can keep a "local" (in cpu memory) cache of the texture data for easy modification and re-uploading. Or, for bonus points, you can modify the texture data directly via OpenCL ;)

OTHER TIPS

The solution which ended up appearing the most suitable to me is to use the ImageData class, which comes with every AbstractImage subclass.

ImageData array Retrieval

The ImageData class represents an image as an array of bytes, or more precisely a String of which each character represents a coordinate in the desired color space.

To obtain that byte representation of a given image, you would call

image_data = image.get_image_data()
width = image_data.width
data = image_data.get_data('RGB', 3*width)

The second parameter we are passing to get_data here is the number of bytes we are expecting to get for each row of the image. Because we want to have all of the components R, G and B for each and every pixel, we would like to have 3 times the width of the image. If we would only be interested in, say, the intensity for each pixel, we would pass the parameters ('I', image.width). Here's some official documentation.

To get the values only for the very pixel you are interested in, e.g. the one at (x,y), you might want to do sth like this:

pos = (width*y + x) * 3  
rgb = map(ord, data[pos:pos+3])

Instead of retrieving the ImageData for the entire image and picking the desired pixel's values afterwards, you can also call get_image_data() for a certain region, i.e. 1x1 pixel at the desired position:

image_data = image.get_region(x,y,1,1).get_image_data()

That's the way how it is done is this google groups post, where you can also find the useful information that it seems to be more efficient to get your image data as RGBA instead of RGB.

Setting individual pixels

Now, to finally answer question, there is also a setter method for the image byte data of ImageData, set_data, which works just the other way around.

image_data.set_data('RGB', image.width*3, data)

This probably works for regions, too, but I haven't tried. In case you want to set byte data that you have stored in an integer array, pass ''.join(map(chr, data)). I don't know if there's a way to set numerical values.

Hope this helps anybody stumbling upon this quite old question, which yet happens to be a prominent google result for certain search terms.

You can set up a batch using batch = pyglet.graphics.Batch() and then add vertices to the batch by calling batch.add(npts, gl_type, group, pixel_data, color_data), where npts is the integer specifying how many points you will be listing as tuples in pixel_data, and gl_type in your case is pyglet.gl.GL_POINTS. batch.draw() then draws all the vertices that you've added to your batch. The example below draws a line by adding vertices (i.e. GL_POINTS) to the batch at each call of on_draw():

import pyglet

COLOR = (0,255,0)
FRAMERATE = 1.0/60
(x,y) = (50,50)
window = pyglet.window.Window()
batch = pyglet.graphics.Batch()

# the vertex_list will keep a handle on all the vertices we add to the batch
vertex_list = [batch.add(1, pyglet.gl.GL_POINTS, None,('v2i', (x,y)),('c3B', COLOR))]

@window.event
def on_draw():
   window.clear()

   # the next vertex will use the vertices of the vertex currently at the end of the current vertex_list
   (x,y) = vertex_list[-1].vertices 
   vertex_list.append(batch.add(1, pyglet.gl.GL_POINTS, None,('v2i', (x+1,y+1)),('c3B', COLOR)))       
   batch.draw()

pyglet.clock.schedule_interval(lambda dt: None, FRAMERATE) 
pyglet.app.run()

There is probably a more efficient way of drawing lines than the way above, but this way gives you a good idea of how the batch works. Check out the docs for more here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top