Python From array of floats to texture - python

I need to create a texture from matrix of floats([0..1]). Texture should show a grey squares, but only show a white rectangle :(
I have this code:
def _generate_image(self):
i_len = len(self._data)*Config().get_pixels_per_tile()
j_len = len(self._data[0])*Config().get_pixels_per_tile()
data = ''.join([ chr(int(c*255)) for f in self._data for c in f for _ in range(3*Config().get_pixels_per_tile()) ])
print data
return ImageFromData(data, j_len, i_len, GL_RGB, GL_UNSIGNED_BYTE)
class ImageFromData(object):
def __init__(self, data, width, height, colors, type_):
self.w = width
self.h = height
self.image = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, self.image)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexImage2D( GL_TEXTURE_2D, 0, colors, width, height, 0, colors, type_, data )
Thanks!

I think you might be making a mistake while converting your image to a string in that nasty-looking set of nested for loops you have there :-)
So your original data are two nested lists of float intensity values for rows and columns of pixels, and you're going to copy them three times to fill the RGB channels? I think you want this:
def pixlist2string(pixlist):
tmp = []
for row in pixlist:
for pix in row:
for channel in xrange(3):
tmp.append(chr(int(255*pix)))
return ''.join(ss for ss in tmp)
This is a very roundabout way of doing things, though. Converting to 8-bit integers is unnecessary - just tell glTexImage2D that the input data type is GL_FLOAT and give it normalised values between 0 and 1. Similarly there's no need to duplicate input pixels in order to fill RGB channels if you set the input format to single-channel (GL_INTENSITY,GL_LUMINANCE, GL_RED etc).
I would also strongly recommend using Numpy arrays to hold your input pixel data. Then you can just pass the array itself to glTexImage2D without fiddling around with string conversion.

Related

Display numpy array cv2 image in wxpython correctly

I am trying to convert a numpy array (cv2 image) to a wxpython Bitmap and display it properly. I have looked into various solutions on SO and elsewhere, but without success. You can see two of my attempts in the code below.
import wx
import cv2
import numpy as np
def create_wx_bitmap(cv2_image):
# type: (np.ndarray) -> wx.Bitmap
# My Attempt based on https://stackoverflow.com/questions/32995679/converting-wx-bitmap-to-numpy-using-bitmapbufferformat-rgba-python/32995940#32995940
height, width = cv2_image.shape[:2]
array = cv2_image # the OpenCV image
image = wx.Image(width, height)
image.SetData(array.tobytes())
wxBitmap = image.ConvertToBitmap()
return wxBitmap
# My other attempt:
# height, width = cv2_image.shape[:2]
# cv2_image_rgb = cv2.cvtColor(cv2_image, cv2.COLOR_BGR2RGB)
# return wx.Bitmap.FromBuffer(width, height, cv2_image_rgb)
class MyFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title)
cv2_image = cv2.imread("test1.png", cv2.IMREAD_ANYDEPTH | cv2.IMREAD_COLOR) # type: np.ndarray
print(cv2_image.dtype)
bitmap = create_wx_bitmap(cv2_image) # type: wx.Bitmap
wx.StaticBitmap(self, -1, bitmap, (0, 0), self.GetClientSize())
self.SetSize(bitmap.GetSize())
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame(None, "wxPython with OpenCV")
frame.Show()
app.MainLoop()
The code above seems to work for images under 16-bit (24 bit depth). However, an image that has a bit depth of 64 results in banding like the below screenshot. (which was a render from Blender 3D exported with 16 bit depth setting):
I have also tried converting the array datatype, but it didn't seem to make any difference.
Edit (The Final Solution):
The solution to my problem was to convert the array to np.uint8 after normalizing the data as mentioned in this SO answer. Thanks to #PetrBlahos for mentioning that the data needs to be 8bit RGB in his answer.
def create_wx_bitmap(cv2_image):
# type: (np.ndarray) -> wx.Bitmap
height, width = cv2_image.shape[:2]
info = np.iinfo(cv2_image.dtype) # Get the information of the incoming image type
data = cv2_image.astype(np.float64) / info.max # normalize the data to 0 - 1
data = 255 * data # Now scale by 255
cv2_image = data.astype(np.uint8)
cv2_image_rgb = cv2.cvtColor(cv2_image, cv2.COLOR_BGR2RGB)
return wx.Bitmap.FromBuffer(width, height, cv2_image_rgb)
I use
dc.DrawBitmap(wx.Bitmap.FromBuffer(iw, ih, cv_image), 0, 0)
but cv_image must be a numpy byte array of rgb values. So, whatever you do, you must convert your data to 8bit RGB (or possibly RGBA, the use FromBufferRGBA).
I do not quite see how your data is structured. 64bits means you have 4 channels (RGBA) each one is 16b integer?
I think you might use cv2.convertScaleAbs, or perhaps convertTo.

How to combine two ImageSurface objects into one

Based on this answer, I have two cairo.ImageSurface objects generated at runtime. Both are RGBA of equal dimensions. I'd like to combine / stack them before saving them to disk:
new_surface = surface1 + surface2 # pseudo-code
(How) does this work?
Cairo lets you to convert ImageSurface to Numpy array:
import numpy
import cairo
width, height = 255, 255
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
buf = surface.get_data()
data = numpy.ndarray(shape=(width, height),
dtype=numpy.uint32,
buffer=buf)
After you convert two ImageSurface you can sum them using "+" or numpy.add() (choose your sum func for your case)
(How) does this work?
You create a new cairo image surface of the larger size and draw your original images to this surface.
Untested pseudo-code (using the C API instead of pycairo):
cairo_surface_t *merged = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height * 2);
cairo_t *cr = cairo_create(merged);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_surface(cr, surface1, 0, 0);
cairo_paint(cr);
cairo_set_source_surface(cr, surface2, 0, height);
cairo_rectangle(cr, 0, height, width, height);
cairo_fill(cr);
cairo_destroy(cr);
Going through PIL (pillow) actually gives pretty decent and relatively performant results. The following works for me:
import cairo
from PIL import Image
def surface_to_pil(surface):
return Image.frombuffer(
mode = 'RGBA',
size = (surface.get_width(), surface.get_height()),
data = surface.get_data(),
)
def pil_to_surface(image):
return cairo.ImageSurface.create_for_data(
bytearray(image.tobytes('raw', 'BGRa')),
cairo.FORMAT_ARGB32,
image.width,
image.height,
)
def add_surfaces(a, b):
assert a.get_width() == b.get_width() and a.get_height() == b.get_height()
result_pil = Image.new(
mode = 'RGBA',
size = (a.get_width(), a.get_height()),
color = (0.0, 0.0, 0.0, 0.0),
)
for surface in (a, b):
surface_pil = surface_to_pil(surface)
result_pil.paste(im = surface_pil, mask = surface_pil)
return pil_to_surface(result_pil)
new_surface = add_surfaces(surface1, surface2)

How do I read a moderngl fbo(frame buffer object) back into a numpy array?

I have one of 2 FBO's i've been using to ping pong some calculations in glsl, and I need to read the texture data (of dtype='f4') back into a numpy array for further calculations. I haven't found anything in the documentation that explains how to do this. Any help?
I create the textures with this
self.texturePing = self.ctx.texture( (width, height), 4, dtype='f4')
self.texturePong = self.ctx.texture( (width, height), 4, dtype='f4')
And I process them like this:
def render(self, time, frame_time):
self.line_texture.use(0)
self.transform['lineImg'].value = 0
for _ in range (2):
self.fbo2.use()
self.texturePing.use(1)
self.transform['prevData'].value = 1
self.process_vao.render(moderngl.TRIANGLE_STRIP)
#this rendered to texturePong
self.fbo1.use() #texture Ping
self.texturePong.use(1)
self.transform['prevData'].value = 1
self.process_vao.render(moderngl.TRIANGLE_STRIP)
#stop drawing to the fbo and draw to the screen
self.ctx.screen.use()
self.ctx.clear(1.0, 1.0, 1.0, 0.0) #might be unnecessary
#tell the canvas to use this as the final texture
self.texturePing.use(3)
self.canvas_prog['Texture'].value = 3
#bind the ping texture as the Texture in the shader
self.canvas_vao.render(moderngl.TRIANGLE_STRIP)
# this looks good but how do I read texturePong back into a numpy array??
You can read the framebuffer's content with fbo.read.
You can turn the buffer into a numpy array with np.frombuffer
Example:
raw = self.fbo1.read(components=4, dtype='f4') # RGBA, floats
buf = np.frombuffer(raw, dtype='f4')
Use glGetTexImage (or preferably glGetTextureImage) to copy the data into a buffer (from the texture you are using for your colour data).
https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetTexImage.xhtml
glGetTextureImage(textureToReadFrom, 0, GL_RGBA, GL_FLOAT, bufferSize, bufferPointer);

Tiling an array in place with numpy

I have an image stored in RGBA format as a 3d numpy array in python, i.e.
image = np.zeros((500, 500, 4), dtype=np.int16)
would be a transparent, black 500x500 square.
I would like to be able to quickly fill the image with a uniform color. For instance fill_img(some_instance_with_img, (255, 0, 0, 255)) would fill the image stored in some_instance_with_img with opaque red. The following code does the trick, assuming self is an instance that contains an image stored as image:
def fill_img(self, color):
color = np.array(color)
shape = self.image.shape
self.image = np.tile(color, (shape[0] * shape[1])).reshape(shape)
However, it creates a brand new array and simply reassigns self.image to this new array. What I would like to do is avoid this intermediate array. If np.tile had an out argument, it would look like:
def fill_img(self, color):
color = np.array(color)
shape = self.image.shape
np.tile(color, (shape[0] * shape[1]), out=self.image)
self.image.reshape(shape)
but np.tile does not support an out parameter. It feels like I am just missing something, although it is possible that this behavior doesn't exist. Any help would be appreciated. Thanks.

Converting pixel grid to contours?

I have a pixel grid, which can be any kind of 2D array (numpy array for instance) with each value representing a color (max 5 different colors). I'm looking to display it in an OpenGL context, but drawing pixel by pixel is both inefficient and stupid. What I'd like to do is to regroup all adjacent pixel of the same color into one shape, so I can draw those shapes.
Basically, I want to be able to do this:
Going from a 2D array of points to a list of shapes (a shape being a list of vertices).
I have no idea how to proceed, so I'm looking for anything that can do the job, like any algorithm in any language or any Python Library that can do that.
I know you're asking for a vector solution, but I believe that the simple and obvious approach could work just fine for you, and will likely perform better.
I would load your color data into a texture, using a call sequence like (sorry about the C notation, but you should be able to translate this to python bindings easily):
GLuint texId = 0;
glGenTexture(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, colorData);
where width and height are the number of squares in horizontal and vertical direction, and colorData an array with the color for each square in RGB format.
Then it's important to choose the right sampling parameters. Since sharp edges between texels are desirable here, we want "nearest" sampling, instead of the "linear" that is more commonly used for image type textures. This will result in a sharp transition between the squares, instead of interpolating the colors between them:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Then, to render your grid, you render one single textured quad using this texture.
The only advantage I can think of that a vector solution would have over this is if you want to use multisampled anti-aliasing (aka MSAA). Since MSAA only does anti-aliasing on primitive edges, it would not help for the "edges" where you have color transitions between two squares. With a vector based solution, where you render each region as a separate primitive, you would get anti-aliasing for those edges.
As long as you just scale the image during display, the aliasing should not be a problem, though. That would only really come into play if you also wanted to rotate it.
I suggest you to use np.extract function in numpy i think it can be helpful ! this is an example of it :
import numpy as np
arr = np.arange(12).reshape((3, 4))
condition = np.mod(arr, 3)==0 # base on this condition the function extract the special arrays
print np.extract(condition, arr)
and this is result :
[0 3 6 9]
then you can concatenate this array with numpy.concatenate((a1, a2, ...), axis=0) function ! read more at : http://docs.scipy.org/doc/numpy/reference/index.html

Categories