Obtain float[] or int[] from BufferedImage - java

I am trying to use the tone mapping code here. It has a function:
public void toneMap(final float[] image, final byte[] rgbOut) {
for (int i = 0; i < image.length; i++) {
final float clamped = Math.max(0, Math.min(1f, image[i]));
rgbOut[i] = (byte) (0xFF & (short) (clamped * 255.0));
}
}
It takes in a float array and a byte array of images and clearly applies a simple clamping to the pixel values.
I tried:
int[]rbg=((DataBufferInt) image.getRaster().getDataBuffer()).getData();
But I got the following exception:
java.lang.ClassCastException: java.awt.image.DataBufferByte cannot be cast to java.awt.image.DataBufferInt
Other stackoverflow answers have clearly explained as to why it was so.
I have an image file want to apply this function to that file. I know that I can read the image into a BufferedImage using ImageIO.read(). But how do I get a float[] rgb from it?
Is it possible? Or are there any other libraries available which can help me do this conversion from image to a float[] rgb array of pixels?
EDIT:
As few people have mentioned in their comments that the code above would do nothing to the pixel values as they are in range of 0..255. But I want to apply above code to HDR images, like the .exr format image , which have an infinite range.

Related

Multiplying Pixel Values in BufferedImage results in strange Behaviour

I am currently working on a program to help photographers with the creation of timelapses.
It calculates an decline or rise in brightness over a series of images. So the change in Exposure and iso for example dont affect the overall decline in brightness.
For this i use a simple Swing-based Interface which displays the first and last image. Under them are sliders to adjust the Brightness of the image.
This is applied via a direct manipulation of the BufferedImages underlying DataBuffer.
Mostly this works but i encountered some images which seem to have kind of a problem.
Do you have an idea why this is happening?
public BufferedImage getImage(float mult){
BufferedImage retim;
retim = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
Graphics g = retim.getGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
DataBufferByte db = (DataBufferByte) retim.getRaster().getDataBuffer();
byte[] bts = db.getData();
for(int i=0;i<bts.length;i++){
float n = bts[i]*mult;
if(n > 255){
bts[i]= (byte) 255;
}else{
bts[i] = (byte) n;
}
}
return retim;
}
This is the method which takes an float and multiplies every pixel in the image with it. (And some code to prevent the byte values from overflowing).
This is the unwanted behaviour (on the left) and the expected on the right.
Your problem is this line, and it occurs due to the fact that Java bytes are signed (in the range [-128...127]):
float n = bts[i] * mult;
After the multiplication, your n variable may be negative, thus causing the overflow to occur.
To fix it, use a bit mask to get the value as an unsigned integer (in the range [0...255]), before multiplying with the constant:
float n = (bts[i] & 0xff) * mult;
A better fix yet, is probably to use the RescaleOp, which is built to do brightness adjustments on BufferedImages.
Something like:
public BufferedImage getImage(float mult) {
return new RescaleOp(mult, 0, null).filter(img, null);
}
This is due to the capping of the value in certain bytes in the image.
For example (assuming RGB simple colour space):
The pixel starts at (125,255,0), if you multiply by factor 2.0, the result is (255,255,0). This is a different hue than the original.
This is also why the strange results only occur on pixels that already have high brightness to start with.
This link may help with better algorithm for adjusting brightness.
You could also refer to this related question.

How to use ScriptIntrinsic3DLUT with a .cube file?

first, I'm new to image processing in Android. I have a .cube file that was "Generated by Resolve" that is LUT_3D_SIZE 33. I'm trying to use android.support.v8.renderscript.ScriptIntrinsic3DLUT to apply the lookup table to process an image. I assume that I should use ScriptIntrinsic3DLUT and NOT android.support.v8.renderscript.ScriptIntrinsicLUT, correct?
I'm having problems finding sample code to do this so this is what I've pieced together so far. The issue I'm having is how to create an Allocation based on my .cube file?
...
final RenderScript renderScript = RenderScript.create(getApplicationContext());
final ScriptIntrinsic3DLUT scriptIntrinsic3DLUT = ScriptIntrinsic3DLUT.create(renderScript, Element.U8_4(renderScript));
// How to create an Allocation from .cube file?
//final Allocation allocationLut = Allocation.createXXX();
scriptIntrinsic3DLUT.setLUT(allocationLut);
Bitmap bitmapIn = selectedImage;
Bitmap bitmapOut = selectedImage.copy(bitmapIn.getConfig(),true);
Allocation aIn = Allocation.createFromBitmap(renderScript, bitmapIn);
Allocation aOut = Allocation.createTyped(renderScript, aIn.getType());
aOut.copyTo(bitmapOut);
imageView.setImageBitmap(bitmapOut);
...
Any thoughts?
Parsing the .cube file
First, what you should do is to parse the .cube file.
OpenColorIO shows how to do this in C++. It has some ways to parse the LUT files like .cube, .lut, etc.
For example, FileFormatIridasCube.cpp shows how to
process a .cube file.
You can easily get the size through
LUT_3D_SIZE. I have contacted an image processing algorithm engineer.
This is what he said:
Generally in the industry a 17^3 cube is considered preview, 33^3 normal and 65^3 for highest quality output.
Note that in a .cube file, we can get 3*LUT_3D_SIZE^3 floats.
The key point is what to do with the float array. We cannot set this array to the cube in ScriptIntrinsic3DLUT with the Allocation.
Before doing this we need to handle the float array.
Handle the data in .cube file
As we know, each RGB component is an 8-bit int if it is 8-bit depth.
R is in the high 8-bit, G is in the middle, and B is in the low 8-bit. In this way, a 24-bit int can contain these
three components at the same time.
In a .cube file, each data line contains 3 floats.
Please note: the blue component goes first!!!
I get this conclusion from trial and error. (Or someone can give a more accurate explanation.)
Each float represents the coefficient of the component according to 255. Therefore, we need to calculate the real
value with these three components:
int getRGBColorValue(float b, float g, float r) {
int bcol = (int) (255 * clamp(b, 0.f, 1.f));
int gcol = (int) (255 * clamp(g, 0.f, 1.f));
int rcol = (int) (255 * clamp(r, 0.f, 1.f));
return bcol | (gcol << 8) | (rcol << 16);
}
So we can get an integer from each data line, which contains 3 floats.
And finally, we get the integer array, the length of which is LUT_3D_SIZE^3. This array is expected to be
applied to the cube.
ScriptIntrinsic3DLUT
RsLutDemo shows how to apply ScriptIntrinsic3DLUT.
RenderScript mRs;
Bitmap mBitmap;
Bitmap mLutBitmap;
ScriptIntrinsic3DLUT mScriptlut;
Bitmap mOutputBitmap;
Allocation mAllocIn;
Allocation mAllocOut;
Allocation mAllocCube;
...
int redDim, greenDim, blueDim;
int[] lut;
if (mScriptlut == null) {
mScriptlut = ScriptIntrinsic3DLUT.create(mRs, Element.U8_4(mRs));
}
if (mBitmap == null) {
mBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.bugs);
mOutputBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), mBitmap.getConfig());
mAllocIn = Allocation.createFromBitmap(mRs, mBitmap);
mAllocOut = Allocation.createFromBitmap(mRs, mOutputBitmap);
}
...
// get the expected lut[] from .cube file.
...
Type.Builder tb = new Type.Builder(mRs, Element.U8_4(mRs));
tb.setX(redDim).setY(greenDim).setZ(blueDim);
Type t = tb.create();
mAllocCube = Allocation.createTyped(mRs, t);
mAllocCube.copyFromUnchecked(lut);
mScriptlut.setLUT(mAllocCube);
mScriptlut.forEach(mAllocIn, mAllocOut);
mAllocOut.copyTo(mOutputBitmap);
Demo
I have finished a demo to show the work.
You can view it on Github.
Thanks.
With a 3D LUT yes, you have to use the core framework version as there is no support library version of 3D LUT at this time. Your 3D LUT allocation would have to be created by parsing the file appropriately, there is no built in support for .cube files (or any other 3D LUT format).

java - how to make an image using setRGB?

i have 2D array to keep color component value :
p[pixel_value][red]
p[pixel_value][green]
p[pixel_value][blue]
i just dont know how to use them to make an image.
i read about setRGB, what i understand is i should mix all of them to become a RGBArray. then how to make it?
is it any better way to make an image without setRGB ? i need some explanation.
The method setRGB() can be used to set the color of a pixel of an already existing image. You can open an already existing image and set all the pixels of it, using the values stored in your 2D array.
You can do it like this:
BufferedImage img = ImageIO.read(new File("image which you want to change the pixels"));
for(int width=0; width < img.getWidth(); width++)
{
for(int height=0; height < img.getHeight(); height++)
{
Color temp = new Color(p[pixel_value][red], p[pixel_value][green], p[pixel_value][blue]);
img.setRGB(width, height, temp.getRGB());
}
}
ImageIO.write(img, "jpg", new File("where you want to store this new image"));
Like this, you can iterate over all the pixels and set their color accordingly.
NOTE: By doing this, you will lose your original image. This is just a way which I know.
What you need is a BufferedImage. Create a BufferedImage of type TYPE_3BYTE_BGR, if RGB is what you want, with a specified width and height.
QuickFact:
The BufferedImage subclass describes an Image with an accessible
buffer of image data.
Then, call the getRaster() method to get a WritableRaster
QuickFact:
This class extends Raster to provide pixel writing capabilities.
Then, use the setPixel(int x, int y, double[] dArray) method to set the pixels.
Update:
If all you want is to read an image, use the ImageIO.read(File f) method. It will allow you to read an image file in just one method call.
Somewhat SSCCE:
BufferedImage img = null;
try {
img = ImageIO.read(new File("strawberry.jpg"));
} catch (IOException e) {
}
You want to manually set RGB values?
You need to know that since an int is 32bit it contains all 4 rgb values (1 for the transparency).
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
^Alpha ^red ^green ^blue
You can accomplish using 4 int values by the use of binary arithmetic:
int rgb = (red << 16) && () (green << 8) & (blue);
bufferedImage.setRGB(x, y, rgb);
IN the above you can add Alpha as well if needed. You just "push" the binary codes into the right places.

Is there a function to sum the rasters of two buffered images?

I'm doing 2D filteing and want to do element by element addition on grayscale BufferedImages. Is there an existing function that will complete this for me or do i need to make one from scrach?
Is there some sort of matrix class that converts a raster to a matrix to simplyfy this problem?
Edit: here is the general gist of it
BufferedImageOp opX = new ConvolveOp(new Kernel(3,3, kernelX));
BufferedImageOp opY = new ConvolveOp(new Kernel(3,3, kernelY));
BufferedImage filtImageX = opX.filter(sourceImage, null);
BufferedImage filtImageY = opY.filter(sourceImage, null);
BufferedImage outputImage = addBufferedImages(filtImageX, filtImageY);
Grayscale Conversion:
public void toGrayscale() {
BufferedImageOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
sourceImage = op.filter(sourceImage, null);
}
I am not familiar with any java libs that do that for you.
You can get pixel at [i,j] with: image.getRGB(i, j);
BufferedImage image = ...;
BufferedImage resultImage = ...
int rgb= image.getRGB(i, j);
resultImage.setRGB(i, j, rgb);
You can also convert a buffered image to a byte array [ https://stackoverflow.com/a/7388025/1007845 ].
See this thread: how to convert image to byte array in java? to get a WritableRaster
EDIT:
It seems that WritableRaster might be useful in this case: http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/image/WritableRaster.html
WritableRaster raster = image.getRaster();
for(int h=0;h<height;h++) {
for(int w=0;w<width;w++) {
int colour = 127;
raster.setSample(w,h,0,colour);
}
}
I don't know of a direct way to do this.
But i can suggest a slightly underhanded approach. First, take your two images, and combine them into a single image with two bands. I'm hazy on the details of how to do this. I suspect you will want to create a Raster with a BandedSampleModel, and then blit the contents of the other two images into its DataBuffer. Although it looks like you should be able to create a two-bank DataBuffer which uses the arrays of the source images' (one-banked) DataBuffers as banks, which would avoid copying.
Once you have a two-band image, simply apply a BandCombineOp which sums the bands. You will need to express the summation as a matrix, but that shouldn't be hard. I think it would be [1.0, 1.0],or [0.5, 0.5] if you want to rescale the result.

How can I get the RGB values of a 'TYPE_3BYTE_BGR' image?

I have an image with TYPE_3BYTE_BGR and I want to convert it to a TYPE_INT_RGB.
Though I have searched, I have not found a method to do this. I want to convert the image pixel by pixel. However, it seems that BufferedImage.getRGB(i, j) doesn't work.
How can I get the RGB values in an image of type TYPE_3BYTE_BGR?
I'm not sure what you mean by "getRGB(i,j) doesn't work". getRGB returns a packed int; you need to decode it.
int color = image.getRGB(i,j);
int r = (argb)&0xFF;
int g = (argb>>8)&0xFF;
int b = (argb>>16)&0xFF;
int a = (argb>>24)&0xFF;
See How to convert get.rgb(x,y) integer pixel to Color(r,g,b,a) in Java?

Categories

Resources