I was trying to implement a color picking system by using a PBO(pixel buffer object using OpenGL), and when I finished, I realized the numbers that came out of the PBO when mapped didn't make any sense at all. I made my application render big squares of different colors and the result DID change between these different colors, but even after analyzing the numbers, I can't make sense of it.
For example, clicking on pure red gave me bytes of
(-1,0,0), while pure blue gave (0,0,-1), but against all logic, pure green gives (-1,0,-1), cyan also gives (-1,0,0), and yellow gives (76,-1,0).
Obviously these numbers are wrong, given that two different colors can result in the same byte formation. Shouldn't a fully red color be (127,0,0)?
Here is the code I used for initialization, size 3 because I am only reading one pixel.
pboid = glGenBuffersARB(); //Initialize buffer for pbo
glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, pboid); //bind buffer
glBufferDataARB(GL_PIXEL_PACK_BUFFER_EXT, 3,GL_DYNAMIC_READ); //create a pbo with 3 slots
glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0); //unbind buffer
And here is the code I used for reading the pixels
glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, pboid); //Bind the pbo
glReadPixels((int)lastMousePosition.x,(int)lastMousePosition.y,1,1, GL_RGB, GL_UNSIGNED_BYTE, 0); //Read 1 pixel
ByteBuffer colorBuffer = glMapBufferARB(GL_PIXEL_PACK_BUFFER_EXT, GL_READ_ONLY_ARB); //Map the buffer so we can read it
for(int x = 0; x < colorBuffer.limit(); x++)
{
System.out.println("color byte: " + colorBuffer.get(x)); //Print out the color byte
}
glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_EXT); //Unmap the buffer so it can be used again
glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0); //Unbind the pbo
If I am wrong in any assumptions I have made, please correct me. I am planning perhaps to use this system to tell which gui element is being clicked by rendering each of them to an fbo with a unique color and testing which pixel color was clicked on. Thanks in advance for anyone who can help!
At long last I have finally found the issue!
Firstly, using Byte.toUnsignedInt(byte), you can turn the color that the pbo gives you into your traditional 0-255 range rgb numbers.
Secondly, this being the primary issue, when OpenGL asks for pixel coordinates to fill a pbo, it is relative to bottom right. My issue was that I was using GLFW which gives coordinates relative to top right, meaning color picking in the vertical middle of the screen was accurate, but that it was getting the inverse part of the screen I was looking for when color picking elsewhere. To fix this, simply subtract the mouse click's y coordinate from the window height.
Thanks for the help and ideas!
There are a couple of possibilities, but I don't have my openGL system set up to test - but you can try these anyhow. Also I don't know Java too well ( C,C++ etc is my domain)
EDIT
1) You have asked for GL_UNSIGNED_BYTE data from glReadPixels(), but you are printing out in signed format. GL_UNSIGNED_BYTE has values 0-256, so negative values are not possible! Try to format you printout for UNSIGNED_BYTE and see where that leads. (from your code I can see this is now fixed).
2) As derhass pointed out in his comments, you should not be using the ARB (architecture Review Board) extension versions of OpenGL buffer functions since these are part of OpenGL core for quite a long time now. See https://www.khronos.org/opengl/wiki/History_of_OpenGL for version history. From this I can see glBindBufferARB (for example) was deprecated in 2003. It may or not impact your particular problem, but replace glXXXXXARB() with glXXXXX() thorughout, and make sure your OpenGL libraries are recent (v4 or later).
3) Also credit derhass, and reading your GitHub code, your getMousePosition() via glfwGetCursorPos returns screen coordinates (0,0 is top left of your window) so you need to convert to viewport coordinates (0,0 is bottom left) to read the framebuffer. Your code at GitHub seems not to be making the conversion.
4) Also credit derhass, you dont' need to use PBO at all for basic color picking. glReadPixels() default target is the framebuffer, so you can safely dispense with the VBO and get color, depth and stencil data directly from the framebuffer. (you need to enable the depth and stencil buffers).
5) If you are selecting on a 3D scene, you will also need to convert (unproject) the viewport coordinates and depth back to worldcoordinates to be able to identify which object you have clicked on. See https://en.wikibooks.org/wiki/OpenGL_Programming/Object_selection for some ideas on selection.
I hope all this helps a bit, although it feels a like learning experience for both of us.
Related
Right now I have a composite code that produces and image from recorded data. I am trying to figure out a way that I can fill the spots in the image where no data was recorded (aka where it reads 0.0) with a new color. I have been experimenting a little with Graphics, but am not finding a way that I can just will these empty spots. I would post an image if I had enough points...
But I hope you understand what I am trying to say.
Like the comment suggests we really need more info.
If you use a BufferedImage then you can simply set a single pixel color using this method setRGB(int x, int y, int rgb)
I have an app where the user draws pictures and then these pictures are converted to pdf. I need to be able to crop out the whitespace before conversion. Originally I kept track of the highest and lowest x and y values (http://stackoverflow.com/questions/13462088/cropping-out-whitespace-from-a-user-drawn-image). This worked for a while, but now I want to give the user the ability to erase. This is a problem because if for example the user erases the topmost point the bounding box would change, but I wouldn't the new dimensions of the box.
Right now I'm going through the entire image, pixel by pixel, to determine the bounding box. This isn't bad for one image, but I'm going to have ~70, it's way too slow for 70. I also thought about keeping every pixel in an arraylist, but I don't feel like that would work well at all.
Is there an algorithm that would help me solve this? Perhaps something already built in? Speed is more important to me than accuracy. If there is some whitespace left on each side it won't be a tragedy.
Thank you so much.
You mentioned that you are keeping track of the min and max values for X and Y co-ordinates (that also seems the solution you have chosen in the earlier question).
In similar way to this, you should be able to find the min and max X & Y co-ordinates for the erased area, from the erase event...
When the user erases part of the image, you can simply compare the co-ordinates of the erased part with the actual image to find the final co-ordinates.
There is a related problem of trying to see if 2 rectangles overlap:
Determine if two rectangles overlap each other?
You can use similar logic (though slightly different) and figure out the final min/max X & Y values.
the title must seem somewhat cryptic but I could not really explain there what I want to do, so I drew a picture to visualize my problem:
The black parts are transparent (aka alpha = 0). I have the blue object (left) in the framebuffer and want to render the white bitmap (middle) onto it, so that it looks like the merged bitmap (right).
The problem is that if I use the standard glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); the whole part of the white object is being displayed. I don't want it to completely overlap the stuff in the framebuffer (blue) but only be visible on the parts where it has an alpha value > 0 (is visible). And then it should also still takes its own alpha values into account (notice the hole in the white object).
Is something like this possible with glBlendFunc or do I have to write a shader for this ?
PS: I looked at the documentation of glBlendFunc at http://www.khronos.org/opengles/documentation/opengles1_0/html/glBlendFunc.html but I don't really get anywhere with it.
PPS: I am using OpenGL-ES 2.0 on Android with C++, but I don't think the language/platform matters all that much.
I don't think it will be possible to do this purely with the blend function. You want the source pixel to be multiplied by both the source and destination alpha, while the blendfunc only allows one or the other.
However the result you want may be possible with some use of the stencil buffer. I'm not an expert in it but I think you can set the stencil op to increment while drawing the background image, and then when you draw the bitmap set the stencil test to reject where stencil == 0 (with blending still enabled to get the transparent area of the bitmap correct). You'll have to review the API for glStencilOp and glStencilFunc to figure out the exact right arguments to use.
It might also be possible with some combination of glBlendFunc and glAlphaFunc, but it would depend on the order of which they are evaluated, so I'm not positive.
I'm pretty new to manually manipulating images, so please bear with me.
I have an image that I'm allowing the user to shrink/grow and move around.
The basic behavior works perfectly. However, I need to be able to grab whatever is in the "viewport" (visible clipping region rectangle) and save it out as a separate bitmap.
Before I can do this, I need to get a fix on WHERE the image actually is and what is being displayed. This is proving more tricky than I would have imagined.
My problem is that the Matrix documentation is absurdly vague, and I'm lost as to how I can measure the coordinates and dimensions of my transformed image. As I see it, the X,Y of the image remain constant even as the user shrinks/grows it. So, even though it reports at being at 0,0 it's displayed at (say) 100,100. And the only way I can get those coordinates is to do a fairly ugly computation (again... I'm probably not doing it the most elegant way, since geometry is not my forte).
I'm kind of hoping that I'm missing something and that there's some way to pull the object's auto translated coordinates and dimensions.
in an ideal world I would be able to call (pseudo) myImg.getDisplayedWidth() and myImg.getDisplayedX().
Oh, and I should add that this may all be a problem that I'm causing myself by using the center of the image as the point from which to grow/shrink. If I left the default 0,0 coordinate as the non changing point, I think the location would be correct no matter what its size was. So... maybe the answer to all this is to simply figure out my center offset and apply that to my translations?
All help greatly appreciated (and people not arbitrarily messing with my question's title even more so!).
The Matrix method mapPoints(float[] dst, float[] src) can be used to get a series of translated points by applying the Matrix translation. Or in (slightly) more layman's terms, an instance of the Matrix class contains not only the translation instruction but also convenience methods to apply the Matrix translation to a series of points.
So in your case, you just need the corners of your untranslated Bitmap (x, y, width, height) and pass the corner points into that method to get the translated points.
I would like to resize a Java BufferedImage, making it smaller vertically but without using any type of averaging, so that if a pixel-row is "blank" (white) in the source image, there will be a white pixel-row in the corresponding position of the destination image: the "min" operation. The default algorithms (specified in getScaledInstance) do not allow me a fine-grained enough control. I would like to implement the following logic:
for each pixel row in the w-pixels wide destination image, d = pixel[w]
find the corresponding j pixel rows of the source image, s[][] = pixel[j][w]
write the new line of pixels, so that d[i] = min(s[j][i]) over all j, i
I have been reading on RescaleOp, but have not figured out how to implement this functionality -- it is admittedly a weird type of scaling. Can anyone provide me pointers on how to do this? In the worse case, I figure I can just reserve the destination ImageBuffer and copy the pixels following the pseudocode, but I was wondering if there is better way.
The RescaleOp methods include a parameter called RenderingHints. There is a hint called KEY_INTERPOLATION that decides the color to use when scaling an image.
If you use the value VALUE_INTERPOLATION_NEAREST_NEIGHBOR for the KEY_INTERPOLATION, Java will use the original colors, rather than using some type of algorithm to recalculate the new colors.
So, instead of white lines turning to gray or some mix of color, you'll get either white lines, or you won't get any lines at all. It all depends on the scaling factor, and if it's an even or odd row. For example, if you are scaling by half, then each 1 pixel horizontal line has at least a 50% change of appearing in the new image. However, if the white lines were two pixels in height, you'd have a 100% chance of the white line appearing.
This is probably the closest you're going to get besides writing your own scaling method. Unfortunately, I don't see any other hints that might help further.
To implement your own scaling method, you could create a new class that implements the BufferedImageOp interface, and implement the filter() method. Use getRGB() and setRGB() on the BufferedImage object to get the pixels from the original image and set the pixels on the new image.