Java - Binary Image Pixel Index - java

How can I tell if a binary image that I am generating is 0-indexed or 1-indexed?
I have made a program which reads in an image, generates a binary image and performs some other functions on the image. I would like to know, however, how to tell which 'index' the pixel values in the binary image are?
How is this done?
Is there a simple built-in function (such as image.getRGB();, for example) which can be called to determine this?

I don't know what you mean with 0- or 1-indexed, but here are some facts.
BufferedImage is a generic image, so pixels start at coordinate (0,0)
If you want an array to work on, coming from this image, the upper-left corner will be in index 0 (unless otherwise specified)
image.getRGB(0, 0, image.getWidth(), image.getHeight(), array, 0, image.getWidth());
BufferedImage doesn't support 1 BPP images natively, but either via a Packed mode with a Colormodel, or a 2-index palette. I can't tell which one you have without examples.
Regardless of the internal format, the different getRGB() methods should always return one value per pixel, and one pixel per value. Note that the full-opacity value (0xFF000000, -16777216) will also be included in results.
eg.
BufferedImage image = new BufferedImage(16, 16, BufferedImage.TYPE_BYTE_BINARY);
image.setRGB(0, 0, 0xFFFFFFFF);
image.setRGB(1, 0, 0xFF000000);
image.setRGB(0, 1, 0xFF000000);
image.setRGB(1, 1, 0xFFFFFFFF);
System.out.println(image.getRGB(0, 0));
System.out.println(image.getRGB(1, 0));
System.out.println(image.getRGB(0, 1));
System.out.println(image.getRGB(1, 1));
int[] array = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
System.out.println(array[0]); // at (0,0)
System.out.println(array[1]); // at (1,0)
System.out.println(array[16]); // at (0,1)
System.out.println(array[17]); // at (1,1)

Related

How can I set pixels in Java BufferedImages using non-ARGB color spaces?

I'm writing an application that needs to work with 16-bit "5-5-5" RGB colors (that is, 5 bits for each color and one bit of padding). In order to handle these images, I am using the BufferedImage class provided by AWT. The BufferedImage class specifically allows for the usage of non-RGB color spaces by taking either a ColorModel object or a predefined image type constant - one of which is the 5-5-5 pixel format that I need.
My problem is this: the BufferedImage "setRGB()" method states in its description that color values provided are "assumed to be in the default RGB color model, TYPE_INT_ARGB, and default sRGB color space" (per the BufferedImage documentation page). No other method seems to accept values designed for different color spaces, either.
Is there a way to use my non-standard color space directly with BufferedImage, or would I have to rely on the class's internal color conversion mechanisms to handle all of my colors? (Or am I just misreading/misunderstanding something about how the class works?)
BufferedImage.TYPE_USHORT_555_RGB still uses a completely standard RGB color space (in fact, it uses sRGB), so I don't think a different color space is what you are looking for.
If you want to perform painting or other operations in Java, just use the normal methods like setRGB/getRGB() and createGraphics()/Grapics2D. Everything will be properly converted to and from the packed USHORT_555_RGB format for you.
For example:
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_USHORT_555_RGB);
// Do some custom painting
Graphics2D g = image.createGraphics();
g.drawImage(otherImage, 0, 0, null); // image type here does not matter
g.setColor(Color.ORANGE); // Color in sRGB, but does not matter
g.fillOval(0, 0, w, h);
g.dispose();
image.setRGB(0, h/2, w, 1, new int[w]); // Silly way to create a horizontal black line at the center of the image... Don't do this, use fillRect(0, h/2, 1, w)! ;-)
// image will still be USHORT_555_RGB *internally*
However, if you have pixel data in the USHORT_555_RGB format (ie. from an external library/api/service), it may be faster and more accurate to set these values directly to the raster/databuffer. Or if you need to pass the pixel values back to the same library/api/service.
For example, using the Raster:
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_USHORT_555_RGB);
// Some fictional API. It's assumed that data.length == w * h
short[] apiPixels = api.getPixelsUSHORT_555_RGB(w, h);
WritableRaster raster = image.getRaster();
// Set short values to image
raster.setDataElements(0, 0, w, h, apiPixels);
// Get short values from image
short[] pixels = (short[]) raster.getDataElements(0, 0, w, h, null); // TYPE_USHORT_555_RGB -> always short[]
api.setPixels(pixels, w, h); // Another fictional API
Or, alternatively, use the DataBuffer:
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_USHORT_555_RGB);
// Some fictional API. It's assumed that data.length == w * h
short[] apiPixels = api.getPixelsUSHORT_555_RGB(w, h);
DataBufferUShort buffer = (DataBufferUShort) image.getRaster().getDataBuffer(); // TYPE_USHORT_555_RGB -> always DataBufferUShort
// Set short values to image
System.arraycopy(apiPixels, 0, buffer.getData(), 0, apiPixels.length);
// Get short values from image
api.setPixels(buffer.getData(), w, h);
In most cases it does not matter which method you use, but the first approach (using Raster only) may keep the image managed, which will make images display faster on screen from your Java process.
PS: If a different color space is really what you need (ie. the pixel array from the external library/api/service uses a different color space, and you need to view the pixels in this color space), you can create a BufferedImage in USHORT_555_RGB style with a custom color space like this:
// Either use one of the built-in color spaces, or load one from disk
ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
ColorSpace colorSpaceToo = new ICC_ColorSpace(ICC_Profile.getInstance(Files.newInputStream(new File("/path/to/custom_rgb_profile.icc").toPath())));
// Create a color model using your color space, TYPE_USHORT and 5/5/5 mask, no transparency
ColorModel colorModel = new DirectColorModel(colorSpace, 15, 0x7C00, 0x03E0, 0x001F, 0, false, DataBuffer.TYPE_USHORT);
// And finally, create an image from the color model and a compatible raster
BufferedImage imageToo = new BufferedImage(colorModel, colorModel.createCompatibleWritableRaster(w, h), colorModel.isAlphaPremultiplied(), null);
Just remember that as the Java2D graphics operations and setRGB/getRGB are still using sRGB, now all operations on your image will be converted back and forth between your color space and sRGB. Performance will not be as good.

(LWJGL3) OpenGL 2D Texture Array stays empty after uploading image data with glTexSubImage3D

So I'm currently trying to replace my old texture atlas stitcher with a 2D texture array to make life simpler with anisotropic filtering and greedy meshing later on.
I'm loading the png files with stb and I know that the buffers are filled properly because if I export every single layer of the soon to be atlas right before uploading it it's the correct png file.
My setup works like this:
I'm loading every single texture in my jar file with stb and create an object with it that stores the width, height, layer and pixelData in it.
When every texture is loaded i look for the biggest texture and scale every smaller texture to the same size as the biggest because i know that 2D texture arrays only work if every single one of the layers has the same size.
Then I initialize the 2d texture array like this:
public void init(int layerCount, boolean supportsAlpha, int textureSize) {
this.textureId = glGenTextures();
this.maxLayer = layerCount;
int internalFormat = supportsAlpha ? GL_RGBA8 : GL_RGB8;
this.format = supportsAlpha ? GL_RGBA : GL_RGB;
glBindTexture(GL_TEXTURE_2D_ARRAY, this.textureId);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, internalFormat, textureSize, textureSize, layerCount, 0, this.format, GL_UNSIGNED_BYTE, 0);
}
After that i go through my map of textureLayer objects and upload every single one of them like this:
public void upload(ITextureLayer textureLayer) {
if (textureLayer.getLayer() >= this.maxLayer) {
LOGGER.error("Tried uploading a texture with a too big layer.");
return;
} else if (this.textureId == 0) {
LOGGER.error("Tried uploading texture layer to uninitialized texture array.");
return;
}
glBindTexture(GL_TEXTURE_2D_ARRAY, this.textureId);
// Tell openGL how to unpack the RGBA bytes
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Tell openGL to not blur the texture when it is stretched
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Upload the texture data
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureLayer.getLayer(), textureLayer.getWidth(), textureLayer.getHeight(), 0, this.format, GL_UNSIGNED_BYTE, textureLayer.getPixels());
int errorCode = glGetError();
if (errorCode != 0) LOGGER.error("Error while uploading texture layer {} to graphics card. {}", textureLayer.getLayer(), GLHelper.errorToString(errorCode));
}
The error code for every single one of my layers is 0, so I assume that everything went well. But when I debug the game with RenderDoc I can see that on every single layer every bit is 0 and therefore it's just a transparent texture with the correct width and height.
I can't figure out what I'm doing wrong since openGL tells me everything went well. It is important to me that I only use openGL 3.3 and lower since I want the game to be playable on older PCs aswell so pre allocating memory with glTexStorage3D is not an option.
The 8th paramter of glTexSubImage3D should be 1 (depth).
Note, the size of the layer is textureLayer.getWidth(), textureLayer.getHeight(), 1:
glTexSubImage3D(
GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureLayer.getLayer(),
textureLayer.getWidth(), textureLayer.getHeight(), 1, // depth is 1
this.format, GL_UNSIGNED_BYTE, textureLayer.getPixels());
It is not an error to pass a width, height or depth of 0 to glTexSubImage3D, but it won't have any effect to the texture objects data store.

Is there a way to not stretch the Bitmap when using Matrix.polyToPoly() on a bitmap?

I'm trying to do perspective transformation on my bitmap with a given quadrilateral. However, the Matrix.polyToPoly function stretches not only the part of the image I want but also the pixels outside the given area, so that a huge image is formed in some edge cases which crashes my app because of OOM.
Is there any way to sort of drop the pixels outside of the said area to not be stretched?
Or are there any other possibilities to do a perspective transform which is more memory friendly?
I`m currenty doing it like this:
// Perspective Transformation
float[] destination = {0, 0,
width, 0,
width, height,
0, height};
Matrix post = new Matrix();
post.setPolyToPoly(boundingBox.toArray(), 0, destination, 0, 4);
Bitmap transformed = Bitmap.createBitmap(temp, 0, 0, cropWidth, cropHeight, post, true);
where cropWidth and cropHeight are the size of the bitmap (I cropped it to the edges of the quadrilateral to save memory) and temp is said cropped bitmap.
Thanks to pskink I got it working, here is a post with the code in it:
Distorting an image to a quadrangle fails in some cases on Android

Process ALPHA_8 bitmap in Android and show it in ImageView

I'm implementing sobel edge detection algo. After processing I apply new pixel values, either 255 or 0. my problem is that the resulting bitmap is not showed in the imageView. I'm using Alpha_8 Configuration because it takes less memory and most suitable for edge results.
How can I preview the results?
The ALPHA_8 format was made to be used as a mask because it only contains alpha and no color information. You should convert it to another format or if you want to use it anyway then you should put it as a mask for background for example. Check out this response from an Android project member to a similar issue. You can also check out my response to another question, there I put a code example about how to apply a mask to another bitmap.
You can use ColorMatrixColorFilter to display ALPHA_8 bitmap.
For example, use the following matrix
mColorFilter = new ColorMatrixColorFilter(new float[]{
0, 0, 0, 1, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 255
});
to draw your bitmap using RED color.
public void onDraw(Canvas canvas){
mPaint.setColorFilter(mColorFilter);
canvas.drawBitmap(mBitmap,x,y,mPaint);
}

How do ColorModels and WritableRasters work in java BufferedImages?

When working with the BufferedImage class in Java, I usually use the constructor with parameters int width, int height, int type. For a certain application, though, I wanted an image which would store the color data using bytes in the order ARGB, which can't be done in that way (it has only TYPE_4BYTE_ABGR).
I found the following solution, which worked fine:
WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height, 4, null);
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[]{8,8,8,8}, true, false, ColorModel.TRANSLUCENT, DataBuffer.TYPE_BYTE);
img = new BufferedImage(colorModel, raster, false, new Hashtable<>());
I don't understand why this works?
Though - I understand that the WritableRaster is the data structure that holds the pixel data of the picture, but past that I am lost. Which of these two objects - the Raster, or the ColorModel - determines that the pixel data is in the order RGBA? And how could I simulate any of the types in BufferedImage's (int, int, int) constructor using the (ColorModel, WritableRaster, boolean, HashTable) constructor?
It's the method
Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height, 4, null);
...that specifies the byte order. It does so implicitly, by assuming for 4 bands, you want the band offsets to be 0, 1, 2, 3 (which corresponds to RGBA; see the source for details). For RGB color space, band 0 = Red, 1 = Green, 2 = Blue and 3 = Alpha.
If you wanted a different order, you could have used a different factory method, for instance to create a raster with ARGB order:
Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height,
width * 4, 4, new int[] {3, 0, 1, 2}, null);
Both of these methods will create an instance of PixelInterleavedSampleModel for you, and it's this SampleModel that really controls the sample order.
For how the BufferedImage(int, int, int) constructor works, and how you could do similar things, I think the best would be to just look at the source code for yourself. It's basically one big switch statement, where for each constant TYPE_* it creates a WritableRaster and a ColorModel similar to how you do it above.
For example:
ColorModel colorModel = ColorModel.getRGBdefault();
WritableRaster raster = colorModel.createCompatibleWritableRaster(width, height);
new BufferedImage(colorModel, raster, colorModel.isAlpahPremultiplied(), null);
...will create an image with type TYPE_INT_ARGB (the way this reverse lookup actually works is somewhat nasty, but it works... :-)). If no corresponding type exists in BufferedImage the type will be TYPE_CUSTOM (0).

Categories

Resources