OpenCV: max value is -1 after apply adaptiveThreshold function - java

I'm trying to binarize an RGB image using the adaptiveThreshold method. My code is as follow:
public byte[] filter(byte[] buff, int width, int height) {
Mat img_rgb = new Mat(width, height, CvType.CV_8UC3);
Mat img_gray = new Mat(width, height, CvType.CV_8U);
Mat img_bin = new Mat(width, height, CvType.CV_8U);
img_rgb.put(0, 0, buff);
Imgproc.cvtColor(img_rgb, img_gray, Imgproc.COLOR_RGB2GRAY);
Imgproc.adaptiveThreshold(img_gray, img_bin, 255,
Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, 5, 2);
int size = (int) img_bin.total() * img_bin.channels();
byte[] bin_buff = new byte[size];
img_bin.get(0, 0, bin_buff);
return bin_buff;
}
The max value of the img_bin data after apply the adaptiveThreshold should be 255, but it is -1 instead. Why is this happening? I'm new to OpenCV and i can't find any explanation.
Thanks in advance.

The two's complement representation of −1 in a signed byte is equal to that of 255 in an unsigned byte.It may be that you are missing a cast somewhere in your code.

Related

Convert 8bit Grayscale image byte array to a BufferedImage

I have a byte array containing data of the raw grayscale 8bit image, which I need to convert to a BufferedImage. I've tried doing:
BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
However, the resulting image object is null which means I'm doing something wrong here.
What's the correct way of making such a conversion?
There are two good ways to do this, depending on your use case.
Either create a new, gray image, and copy the data into it. This will keep the image "managed", which may lead to better rendering performance (ie. on screen). But it will need twice as much memory, and copy the data from your input to the image.
The other, is to create the gray image directly "around" your existing pixel data. This will be faster, and use almost no extra heap, as it avoids copying the pixel data. But the image will not be managed (as the backing array is exposed and mutable).
Both options are demonstrated below:
int w = 640;
int h = 480;
byte[] imageBytes = new byte[w * h];
// 1 Keeps the image "managed" at the expense of twice the memory + a large array copy
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
image.getRaster().setDataElements(0, 0, w, h, imageBytes);
System.out.println("image: " + image);
// 2 Faster, and uses less memory, but will make the image "unmanaged"
ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
WritableRaster raster = Raster.createInterleavedRaster(new DataBufferByte(imageBytes, imageBytes.length), w, h, w, 1, new int[]{0}, null);
BufferedImage image2 = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
System.out.println("image2: " + image2);
If the image data isn't in linear gray color space, one could use an IndexColorModel to map the input into whatever range you want:
// Alternate, using IndexColorModel, if your input isn't in linear gray color space
int[] cmap = new int[256]; // TODO: Add ARGB packed colors here...
IndexColorModel icm = new IndexColorModel(8, 256, cmap, 0, false, -1, DataBuffer.TYPE_BYTE);
// As 1
BufferedImage image3 = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_INDEXED, icm);
image3.getRaster().setDataElements(0, 0, w, h, imageBytes);
System.out.println("image3: " + image3);
// As 2
BufferedImage image4 = new BufferedImage(icm, raster, cm.isAlphaPremultiplied(), null);
System.out.println("image4: " + image4);
I've managed to did the conversion for the 640x480 resolution the following way:
BufferedImage image = new BufferedImage(640,480,BufferedImage.TYPE_BYTE_INDEXED);
int i = 0;
for(int y = 0; y < 480; y++)
{
for(int x = 0; x < 640; x++)
{
int g = imageBytes[i++] & 0xFF;
image.setRGB(x,y,new Color(g,g,g).getRGB());
}
}
EDIT: removed useless code (thanks to Marco13)
Java
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
image.getRaster().setDataElements(0, 0, width, height, array));
Kotlin
val image = BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY)
image.raster.setDataElements(0, 0, width, height, byteArray )

How do I use PixelReader's getPixels() method?

How do I convert a javafx.scene.image.Image to a byte array in the format bgra?
I tried doing:
PixelReader pixelReader = img.getPixelReader();
int width = (int)img.getWidth();
int height = (int)img.getHeight();
byte[] buffer = new byte[width * height * 4];
pixelReader.getPixels(
0,
0,
width,
height,
PixelFormat.getByteBgraInstance(),
buffer,
0,
width
);
but it didn't work, my byte[] array buffer is still filled with zeros.
The scanlineStride i. e. width must be multiplied by 4, i. e.
PixelReader pixelReader = img.getPixelReader();
int width = (int)img.getWidth();
int height = (int)img.getHeight();
byte[] buffer = new byte[width * height * 4];
pixelReader.getPixels(
0,
0,
width,
height,
PixelFormat.getByteBgraInstance(),
buffer,
0,
width * 4
);

Int Pixel Array To Image

I am trying to take an int array of pixels from a Bitmap on Android and use the int array to make a buffered image. But I'm having some problems. I am able to get the bitmap from android with no problem:
Bitmap b = BitmapFactory.decodeByteArray(data, 0, data.length);
b = Bitmap.createScaledBitmap(b, (int)(b.getWidth() * .3125), (int)(b.getHeight() * .44444444444), false);
int[] arr = new int[b.getHeight() * b.getWidth()];
b.getPixels(arr, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
I scale the bitmap down and then get the pixels and put them into the int array. Then I send them over a network to the PC where I try to recreate it to a BufferedImage (networking isn't important). Here is the code I use to convert the pixel array to a BufferedImage.
int width = 600;
int height = 479;
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
bi.setRGB(0, 0, width, height, arr, 0, width);
g2d.drawImage(bi, 10, 50, null);
The variable "arr" is the byte array of pixels. Converting to a BufferedImage gives no error and shows the following image:
Obviously this isn't the picture I am trying to get. When I print out 5 of the pixels in the array (just for testing) I get the following and this is the format of the pixels:
-15528956
-15200766
-13558523
-11718123
-12243954
-13294582

Does .Net PixelFormat have Java equivalent?

What in Java matches to C# PixelFormat's members.
i.e. Format24bppRgb matches to BufferedImage.TYPE_INT_RGB?
Here is my code. I got an image which has .Net's PixelFormat = Format32bppArgb. I am creating BufferedImage like this:
int sizeBytes = width * height;
DataBufferByte dataBuffer = new DataBufferByte(myImageBytes, sizeBytes);
WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, // dataBuffer
width, // width
height, // height
width * 4, // scanlineStride
4, // pixelStride
new int[]{0, 1, 2, 3}, // bandOffsets
null); // location
java.awt.image.ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), // ColorSpace
new int[]{8, 8, 8, 8}, // bits
true, // hasAlpha
false, // isPreMultiplied
ComponentColorModel.TRANSLUCENT, DataBuffer.TYPE_BYTE);
BufferedImage result = new BufferedImage(colorModel, raster, false, null);
After I create a bufferedImage red and blue colors are swapped in it.
Next, I tried to create an image as follow
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
WritableRaster r = result.getRaster();
int[] pixels = byteToInt(bytes);
r.setPixels(0, 0, width, height , pixels); // ! Here an exception occures, because after I converted the byte array to int one the width becomes too long.
Byte array was convert by this method
private int[] byteToInt(byte[] pixels) {
int[] ints = new int[pixels.length / 3];
int byteIdx = 0;
for (int pixel = 0; pixel < ints.length; pixel++) {
int red = (int) pixels[byteIdx++] & 0xFF;
int green = (int) pixels[byteIdx++] & 0xFF;
int blue = (int) pixels[byteIdx++] & 0xFF;
int rgb = (red << 16) | (green << 8) | blue;
ints[pixel] = rgb;
}
return ints;
}
The colors look fine now, but I got the exception
java.lang.ArrayIndexOutOfBoundsException: 27600
at sun.awt.image.ByteInterleavedRaster.setPixels(ByteInterleavedRaster.java:1106)
If I use the smaller width (e.g. width / 3) the colors look fine but the picture itself shrinks.
BufferedImage is definately a good place to start. Many of the values in PixelFormat will match up to values in BufferedImage - they each have 24-bit and 32-bit RGB/ARGB values, both have 5-5-5 and 5-6-5 combinations, etc.
If you're having trouble, post some code and we'll have a look at it, try to help. The thing I would recommend would be to play around with the byte ordering (in the pixel ints) until you get the result that you expect - try drawing the BufferedImage onto a GUI object like JPanel so you can see what it looks like.
If you've got an array of int[] for your pixel values, this is the code I usually use for displaying the array as an image...
int[] pixels;
ColorModel model = new DirectColorModel(32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000);
Image image = new JLabel().createImage(new MemoryImageSource(width,height,model,pixels,0,width));

How to convert array of bytes into Image in Java SE

What is the right way to convert raw array of bytes into Image in Java SE.
array consist of bytes, where each three bytes represent one pixel, with each byte for corresponding RGB component.
Can anybody suggest a code sample?
Thanks,
Mike
You can do it using Raster class. It's better because it does not require iterating and copying of byte arrays.
byte[] raw = new byte[width*height*3]; // raw bytes of our image
DataBuffer buffer = new DataBufferByte(raw, raw.length);
//The most difficult part of awt api for me to learn
SampleModel sampleModel = new ComponentSampleModel(DataBuffer.TYPE_BYTE, width, height, 3, width*3, new int[]{2,1,0});
Raster raster = Raster.createRaster(sampleModel, buffer, null);
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
image.setData(raster);
Assuming you know the height and width of the image.
BufferedImage img=new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for(int r=0; r<height; r++)
for(int c=0; c<width; c++)
{
int index=r*width+c;
int red=colors[index] & 0xFF;
int green=colors[index+1] & 0xFF;
int blue=colors[index+2] & 0xFF;
int rgb = (red << 16) | (green << 8) | blue;
img.setRGB(c, r, rgb);
}
Roughly. This assumes the pixel data is encoded as a set of rows; and that the length of colors is 3 * width * height (which should be valid).
folkyatina's approach works if your RGB values are in B,G,R order, but if they are in R,G,B order I have found the following code to work:
DataBuffer rgbData = new DataBufferByte(rgbs, rgbs.length);
WritableRaster raster = Raster.createInterleavedRaster(
rgbData, width, height,
width * 3, // scanlineStride
3, // pixelStride
new int[]{0, 1, 2}, // bandOffsets
null);
ColorModel colorModel = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[]{8, 8, 8}, // bits
false, // hasAlpha
false, // isPreMultiplied
ComponentColorModel.OPAQUE,
DataBuffer.TYPE_BYTE);
return new BufferedImage(colorModel, raster, false, null);
There is a setRGB variant which accepts an int array of RGBA values:
BufferedImage img=new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
int[] raw = new int[data.length * 4 / 3];
for (int i = 0; i < data.length / 3; i++) {
raw[i] = 0xFF000000 |
((data[3 * i + 0] & 0xFF) << 16) |
((data[3 * i + 1] & 0xFF) << 8) |
((data[3 * i + 2] & 0xFF));
}
img.setRGB(0, 0, width, height, raw, 0, width);
The performance characteristics is similar to CoderTao's solution.
Assuming that your raw data is a 1d array like:
byte[] imageBytes = new byte[1024];
// transform to bufferImage
BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(imageBytes));
// if you want to do some operations to the image, like resize,
// use the lib (net.coobird.thumbnailator)
BufferedImage image = Thumbnails.of(bufferedImage).forceSize(WIDTH, HEIGHT)
.outputFormat("bmp").asBufferedImage();

Categories

Resources