Get center pixel from image with format YUV - java

I made image converting from YUV(NV21 Android) to RGB
And i tried get color of pixel by x,y cordinates.
For example i tried get pixel from center. But i get color from wrong place.
Collect image YUV
ByteBuffer yBuff = image.getPlanes()[0].getBuffer();
ByteBuffer uBuff = image.getPlanes()[1].getBuffer();
ByteBuffer vBuff = image.getPlanes()[2].getBuffer();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] yByte = new byte[yBuff.remaining()];
byte[] uByte = new byte[uBuff.remaining()];
byte[] vByte = new byte[vBuff.remaining()];
yBuff.get(yByte);
uBuff.get(uByte);
vBuff.get(vByte);
// Create converting byte[] NV21
try {
outputStream.write(yByte);
for (int i=0; i < uByte.length; i++) {
outputStream.write(vByte[i]);
outputStream.write(uByte[i]);
}
} catch (Exception e) {
e.printStackTrace();
}
byte[] imageBytes = outputStream.toByteArray();
What i have in current moment.
// Coordinates
x = width / 2;
y = height / 2;
// Get YUV coordinates for x y position
int YCord = y * width + x;
int UCord = (y >> 1) * (width) + x + total + 1;
int VCord = (y >> 1) * (width) + x + total;
// Get YUV colors
int Y = (0xFF & imageBytes[YCord]) - 16;
int U = (0xFF & imageBytes[UCord]) - 128;
int V = (0xFF & imageBytes[VCord]) - 128;
I expect color from center of image. But i have color from different place

https://github.com/lirugo/screenDetector
i found solution if some one want more, see my code by link about get pixel by x,y coordinates code below
x = 210; // might be 250
y = height - 215; // might be 150
// Get YUV coordinates for x y position
int YCord = y*width+x;
int UCord = ((y/2)*width+(x&~1)+total+1);
int VCord = ((y/2)*width+(x&~1)+total);

Related

Create simple 2D image preview from panoramic HDR

Is there some really simple and basic code for making preview for HDR images (like getting 2D BufferedImage output or something)?
I am using this HDR image.
I tried this (it uses TwelveMonkeys), but no success at all (it simply stuck/frozen at ImageReader reader = readers.next();)
I edited it a bit to suit my needs like this, testing where it got broken/stuck/frozen...and it always happen after TEST 1, that is TEST 2 is never reached, tho no IllegalArgumentException is thrown - if I remove the if() section, then TEST 3 is never reached (I am using NetBeansIDE v12.4, Win7 x64):
public BufferedImage hdrToBufferedImage(File hdrFile) throws IOException {
BufferedImage bi = null;
// Create input stream
// I WROTE DOWN THE STRING FOR THIS EXAMPLE, normally it is taken from the hdrFile
// HDR image size is 23.7MB if it matters at all?
ImageInputStream input = ImageIO.createImageInputStream(new File("Z:/HDR/spiaggia_di_mondello_4k.hdr"));
try {
// Get the reader
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
System.err.println("=====>>> TEST 1");
if (!readers.hasNext()) {
throw new IllegalArgumentException("No reader for: " + hdrFile);
}
System.err.println("=====>>> TEST 2");
ImageReader reader = readers.next();
System.err.println("=====>>> TEST 3");
try {
reader.setInput(input);
// Disable default tone mapping
HDRImageReadParam param = (HDRImageReadParam) reader.getDefaultReadParam();
param.setToneMapper(new NullToneMapper());
// Read the image, using settings from param
bi = reader.read(0, param);
} finally {
// Dispose reader in finally block to avoid memory leaks
reader.dispose();
}
} finally {
// Close stream in finally block to avoid resource leaks
input.close();
}
// Get float data
float[] rgb = ((DataBufferFloat) bi.getRaster().getDataBuffer()).getData();
// Convert the image to something easily displayable
BufferedImage converted = new ColorConvertOp(null).filter(bi, new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_INT_RGB));
return converted;
}
Well, if you don't mind occasional extreme halucinogenic oversaturation of some colors here and there (I was unable solving the issue - if anyone knows how to, please, feel free to update my code), you can try this (it is using JavaHDR) + I also added a bit of brightness and contrast to it as all HDR I tested looked too dark for the preview, so if you do not like that you can remove that part from the code:
public int rgbToInteger(int r, int g, int b) {
int rgb = r;
rgb = (rgb << 8) + g;
rgb = (rgb << 8) + b;
return rgb;
}
public BufferedImage hdrToBufferedImage(File hdrFile) throws IOException {
HDRImage hdr = HDREncoder.readHDR(hdrFile, true);
int width = hdr.getWidth();
int height = hdr.getHeight();
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int r = (int) (hdr.getPixelValue(x, y, 0) * 255);
int g = (int) (hdr.getPixelValue(x, y, 1) * 255);
int b = (int) (hdr.getPixelValue(x, y, 2) * 255);
bi.setRGB(x, y, rgbToInteger(r, g, b));
}
}
//***** YOU CAN REMOVE THIS SMALL SECTION IF YOU FEEL THE IMAGE IS TOO BRIGHT FOR YOU
float brightness = 2f;
float contrast = 20f;
RescaleOp rescaleOp = new RescaleOp(brightness, contrast, null);
rescaleOp.filter(bi, bi);
//*****
return bi;
}
I can compile and run the code you posted (changing the path obviously) without problems on my two macOS machines, testing on all the LTS Java versions (8, 11 and 17). In addition, I run code similar to this as part of the CI/CD pipeline of my project that tests on Windows and Linux as well. I think there is something wrong with the setup in your IDE or Java on your computer. I am not able to reproduce the "freeze"-situation you describe...
Here is the output of running the program (I also printed the resulting BufferedImage for verification):
=====>>> TEST 1
=====>>> TEST 2
=====>>> TEST 3
image = BufferedImage#5a42bbf4: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 1024 height = 512 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
Running with the code as-is (with the NullToneMapper and no post-processing), the image looks like this, due to unnormalized values:
Running with the default/built-in tone mapper, or simply reading the image with ImageIO.read(hdrFile) as suggested in the comments, the image will look like this:
Finally, playing a bit with the code using a custom global tone mapper; param.setToneMapper(new DefaultToneMapper(0.75f)), I get a result like this:
After a long discussion with #HaraldK and his code addition, I am posting the final correct code for this problem, that is in fact mix of #qraqatit code updated a bit with the #HaraldK addition that corrects wrong color tone mapping, here it is:
public int rgbToInteger(int r, int g, int b) {
int rgb = r;
rgb = (rgb << 8) + g;
rgb = (rgb << 8) + b;
return rgb;
}
public BufferedImage hdrToBufferedImage(File hdrFile) throws IOException {
HDRImage hdr = HDREncoder.readHDR(hdrFile, true);
int width = hdr.getWidth();
int height = hdr.getHeight();
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
float colorToneCorrection = 0.75f;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
float r = hdr.getPixelValue(x, y, 0);
int red = (int) ((r / (colorToneCorrection + r)) * 255);
float g = hdr.getPixelValue(x, y, 1);
int green = (int) ((g / (colorToneCorrection + g)) * 255);
float b = hdr.getPixelValue(x, y, 2);
int blue = (int) (int) ((b / (colorToneCorrection + b)) * 255);
bi.setRGB(x, y, rgbToInteger(red, green, blue));
}
}
//MAKE THE RESULTING IMAGE A BIT BRIGHTER
float brightness = 1.35f;
float contrast = 0f;
RescaleOp rescaleOp = new RescaleOp(brightness, contrast, null);
rescaleOp.filter(bi, bi);
return bi;
}

glReadPixels returns more data than expected

I want to save a video of what I am showing with openGL using JOGL. To do this, I am writing my frames to pictures as follows and then, once I have saved all frames I'll use ffmpeg. I know that this is not the best approach but I still don't have much clear how to accelerate with tex2dimage and PBOs. Any help in that direction would be very useful.
Anyway, my problem is that if I run the opengl class it works but, if I call this class from another class, then I see that the glReadPixels is trhowing me an error. It always returns more data to buffer than memory has been allocated to my buffer "pixelsRGB". Does anyone know why?
As an example: width = 1042; height=998. Allocated=3.119.748 glPixels returned=3.121.742
public void display(GLAutoDrawable drawable) {
//Draw things.....
//bla bla bla
t++; //This is a time variable for the animation (it says to me the frame).
//Save frame
int width = drawable.getSurfaceWidth();
int height = drawable.getSurfaceHeight();
ByteBuffer pixelsRGB = Buffers.newDirectByteBuffer(width * height * 3);
gl.glReadPixels(0, 0, width,height, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, pixelsRGB);
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
int[] pixels = new int[width * height];
int firstByte = width * height * 3;
int sourceIndex;
int targetIndex = 0;
int rowBytesNumber = width * 3;
for (int row = 0; row < height; row++) {
firstByte -= rowBytesNumber;
sourceIndex = firstByte;
for (int col = 0; col < width; col++) {
int iR = pixelsRGB.get(sourceIndex++);
int iG = pixelsRGB.get(sourceIndex++);
int iB = pixelsRGB.get(sourceIndex++);
pixels[targetIndex++] = 0xFF000000
| ((iR & 0x000000FF) << 16)
| ((iG & 0x000000FF) << 8)
| (iB & 0x000000FF);
}
}
bufferedImage.setRGB(0, 0, width, height, pixels, 0, width);
File a = new File(t+".png");
ImageIO.write(bufferedImage, "PNG", a);
}
NOTE: With pleluron's answer now it works. The good code is:
public void display(GLAutoDrawable drawable) {
//Draw things.....
//bla bla bla
t++; //This is a time variable for the animation (it says to me the frame).
//Save frame
int width = drawable.getSurfaceWidth();
int height = drawable.getSurfaceHeight();
ByteBuffer pixelsRGB = Buffers.newDirectByteBuffer(width * height * 4);
gl.glReadPixels(0, 0, width,height, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, pixelsRGB);
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
int[] pixels = new int[width * height];
int firstByte = width * height * 4;
int sourceIndex;
int targetIndex = 0;
int rowBytesNumber = width * 4;
for (int row = 0; row < height; row++) {
firstByte -= rowBytesNumber;
sourceIndex = firstByte;
for (int col = 0; col < width; col++) {
int iR = pixelsRGB.get(sourceIndex++);
int iG = pixelsRGB.get(sourceIndex++);
int iB = pixelsRGB.get(sourceIndex++);
sourceIndex++;
pixels[targetIndex++] = 0xFF000000
| ((iR & 0x000000FF) << 16)
| ((iG & 0x000000FF) << 8)
| (iB & 0x000000FF);
}
}
bufferedImage.setRGB(0, 0, width, height, pixels, 0, width);
File a = new File(t+".png");
ImageIO.write(bufferedImage, "PNG", a);
}
The default value of GL_PACK_ALIGNMENT set with glPixelStore is 4. It means that each row of pixelsRGB should start at an address that is a multiple of 4, and the width of your buffer (1042) times the number of bytes in a pixel (3) isn't a multiple of 4. Adding a little padding so the next row starts at a multiple of 4 will make the total byte size of your buffer larger than what you expected.
To fix it, set GL_PACK_ALIGNMENT to 1. You could also read the pixels with GL_RGBA and use a larger buffer, since the data is most likely to be stored that way both on the GPU and in BufferedImage.
Edit: BufferedImage doesn't have a convenient 'setRGBA', too bad.

Why BufferedImage is not working well?!! Is it because I misused it?

I want to copy a gray image using BufferedImage from getRGB() to int[][] and then to setRGB(). The problem is that the size of image is different from the size of the one that the program outputs it. The original image has file size = 176 KB, whereas the output image has file size = 154 KB. I have to say that when you see the two image, all of the human-being would say it is the same, but in terms of binary bits, there are different in something that I would like to know.
Maybe some of you will say it doesn't matter, as long as image is the same when you look at it. In fact, during the processing of some noise project, this is a huge problem, and I suspect that this is the reason why I have the problem.
I just want to know if there are other method than BufferedImage to produce int[][] and then to create the output?
This is the code that I'm using:
public int[][] Read_Image(BufferedImage image)
{
width = image.getWidth();
height = image.getHeight();
int[][] result = new int[height][width];
for (int row = 0; row < height; row++)
for (int col = 0; col < width; col++)
result[row][col] = image.getRGB(row, col);
return result;
}
public BufferedImage Create_Gray_Image(int [][] pixels)
{
BufferedImage Ima = new BufferedImage(512,512, BufferedImage.TYPE_BYTE_GRAY);
for (int x = 0; x < 512; x++)
{
for (int y = 0; y < 512; y++)
{
int rgb = pixels[x][y];
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = (rgb & 0xFF);
int grayLevel = (r + g + b) / 3;
int gray = (grayLevel << 16) + (grayLevel << 8) + grayLevel;
Ima.setRGB(x, y, pixels[x][y]);
}
}
return Ima;
}
public void Write_Image(int [][] pixels) throws IOException
{
File outputfile;
outputfile = new File("Y0111.png");
BufferedImage BI = this.Create_Gray_Image(pixels);
ImageIO.write(BI, "png", outputfile);
System.out.println("We finished writing the file");
}
See the figure, you see file size = 176 KB (this is the original image) and file size = 154 KB (this is the output image).
The difference of size is not a problem. It's certainly because of different compression/encoding.
A BufferedImage is in fact a 1D array of size width * height * channel. getRGB is not the easiest/fastest way to manipulate a BufferedImage. You can use the Raster (faster than getRGB, not the fastest, but it takes care of the encoding for you). For a gray level image:
int[][] my array = new int[myimage.getHeight()][myimage.getWidth()] ;
for (int y=0 ; y < myimage.getHeight() ; y++)
for (int x=0 ; x < myimage.getWidth() ; x++)
myarray[y][x] = myimage.getRaster().getSample(x, y, 0) ;
The opposite way:
for (int y=0 ; y < myimage.getHeight() ; y++)
for (int x=0 ; x < myimage.getWidth() ; x++)
myimage.getRaster().setSample(x, y, 0, myarray[y][x]) ;
The fastest way to do it is to use the DataBuffer, but then you have to handle the image encoding.

Converting grayscale image pixels to defined scale

I'm looking to use a very crude heightmap I've created in Photoshop to define a tiled isometric grid for me:
Map:
http://i.imgur.com/jKM7AgI.png
I'm aiming to loop through every pixel in the image and convert the colour of that pixel to a scale of my choosing, for example 0-100.
At the moment I'm using the following code:
try
{
final File file = new File("D:\\clouds.png");
final BufferedImage image = ImageIO.read(file);
for (int x = 0; x < image.getWidth(); x++)
{
for (int y = 0; y < image.getHeight(); y++)
{
int clr = image.getRGB(x, y) / 99999;
if (clr <= 0)
clr = -clr;
System.out.println(clr);
}
}
}
catch (IOException ex)
{
// Deal with exception
}
This works to an extent; the black pixel at position 0 is 167 and the white pixel at position 999 is 0. However when I insert certain pixels into the image I get slightly odd results, for example a gray pixel that's very close to white returns over 100 when I would expect it to be in single digits.
Is there an alternate solution I could use that would yield more reliable results?
Many thanks.
Since it's a grayscale map, the RGB parts will all be the same value (with range 0 - 255), so just take one out of the packed integer and find out what percent of 255 it is:
int clr = (int) ((image.getRGB(x, y) & 0xFF) / 255.0 * 100);
System.out.println(clr);
getRGB returns all channels packed into one int so you shouldn't do arithmetic with it. Maybe use the norm of the RGB-vector instead?
for (int x = 0; x < image.getWidth(); ++x) {
for (int y = 0; y < image.getHeight(); ++y) {
final int rgb = image.getRGB(x, y);
final int red = ((rgb & 0xFF0000) >> 16);
final int green = ((rgb & 0x00FF00) >> 8);
final int blue = ((rgb & 0x0000FF) >> 0);
// Norm of RGB vector mapped to the unit interval.
final double intensity =
Math.sqrt(red * red + green * green + blue * blue)
/ Math.sqrt(3 * 255 * 255);
}
}
Note that there is also the java.awt.Color class that can be instantiated with the int returned by getRGB and provides getRed, getGreen and getBlue methods if you don't want to do the bit manipulations yourself.

java.lang.IllegalArgumentException: More than one component per pixel

I'm new to image processing in Java. I'm trying to compare two images with the code below and getting the message following the code. Any help is greatly appreciated. Thanks.
BufferedImage imgOrig = ImageIO.read(new URL(imgOrigUrl));
BufferedImage imgComp = ImageIO.read(new URL(imgCompUrl));
byte[] pixelsOrig = ((DataBufferByte) imgOrig.getRaster().getDataBuffer()).getData();
byte[] pixelsComp = ((DataBufferByte) imgComp.getRaster().getDataBuffer()).getData();
//System.out.println("Number of pixels orig:"+pixelsOrig.length);
//System.out.println("Number of pixels comp:"+pixelsComp.length);
ColorModel cmImgOrig = imgOrig.getColorModel();
ColorModel cmImgComp = imgComp.getColorModel();
int sum1 = 0;
int sum2 = 0;
for(int i:pixelsOrig){
System.out.println(cmImgOrig.getGreen(i)); //ERROR OCCURS HERE
//System.out.println(i);
}
ERROR:
Testcase: testCompareImages(com.myapp.img.compare.service.CompareServiceTest): Caused an ERROR
More than one component per pixel
java.lang.IllegalArgumentException: More than one component per pixel
at java.awt.image.ComponentColorModel.getRGBComponent(ComponentColorModel.java:594)
at java.awt.image.ComponentColorModel.getGreen(ComponentColorModel.java:675)
at com.scottmacri.img.compare.service.CompareService.compareImages(CompareService.java:42)
at com.scottmacri.img.compare.service.CompareServiceTest.testCompareImages(CompareServiceTest.java:45)
Like #Nathan Villaescusa said, the method you are using is expecting a single channel. Do you need the byte array or the color channel? If you only need color components you can do the following:
BufferedImage imgOrig = ImageIO.read(new URL(imgOrigUrl));
BufferedImage imgComp = ImageIO.read(new URL(imgCompUrl));
for (int y = 0; y < imgOrig.getHeight(); y++)
{
for (int x = 0; x < imgOrig.getWidth(); x++)
{
System.out.println(imgOrig.getRGB(x, y) >> 8 & 0xff);
}
}
where the int returned by getRGB(x, y) can be shifted to get the RGB and alpha components like so:
int a = rgb >> 32 & 0xff;
int r = rgb >> 16 & 0xff;
int g = rgb >> 8 & 0xff;
int b = rgb & 0xff;
It looks like that error is being thrown because your ColorSpace has more than 1 component, yet you are only passing in a single value to check.
You want to use the getGreen() method of ColorComponentModel that accepts a Object, not the one that accepts an int. I think the method one that accepts an int is for use with gray scale.
According to this answer, here is how to get pixel data using this method:
Raster r = imgOrig.getData();
SampleModel sm = r.getSampleModel();
int width = sm.getWidth();
int height = sm.getHeight();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
Object pixel = sm.getPixel(x, u, (int[])null, r.getDataBuffer());
System.out.println(cmImgOrig.getGreen(pixel));
}
}

Categories