color rescaling in java of buffered image - java

First of all, thanks for reading.
I am writing a class which returns an image from 2D double array(kDEvalue from the code).
However, since so many values(256*256) vary in a very small range(-1 to 1), i cannot use the default color scaling(it is all black if i use the value from the array).
BufferedImage image = new BufferedImage(kDEvalues.length, kDEvalues[0].length, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < 256; x++) {
for (int y = 0; y < 256; y++) {
image.setRGB(x, y, (255*(int)Math.round(kDEvalues[x][y]))/100);
}
}
try {
ImageIO.write(image, "jpg", new File("CustomImage.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
In this case, how I rescale the color in grey scale so that I can see the varience of the value? Thanks in advance

You almost got it right here:
image.setRGB(x, y, (255*(int)Math.round(kDEvalues[x][y]))/100);
There are three things wrong with your expression for the RGB value. First, you round and then multiply. This drops any fractions of your small values, it should be the other way around. Second you only create a value for one color channel (the blue one) and thrid, you do not check for overflow.
Try something like this:
int gray = (int) Math.round((kDEvalues[x][y] - (-1)) * 127.5);
// limit range to 0-255
gray = Math.min(gray, 255);
gray = Math.max(gray, 0);
// copy gray value to r, g and b channel
int rgb = (gray << 16) | (gray << 8) | gray;
image.setRGB(x, y, rgb);
First operation is to scale the value to 0-255. Since you say your values are in range -1 to 1, adjust the values so the lower bound falls on zero (by subtracting the lower bound). Then multiply by a factor that the highes value becomes 255 (thats (1 - (-1)) * 127.5 = 255).
Just to be safe, clip the resulting value to 0-255 (Math.min/max expressions).
Then replicate the gray value in RGB by setting the gray value to all 3 color channels. The format is documented in the javadoc and the bit shifts just place the gray value at the correct position within the int.

Related

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 BufferedImage get single pixel brightness

I want to convert coloured image to a monochrome, i thought to loop all pixel, but I don't know how to test if they are bright or dark.
for(int y=0;y<image.getHeight();y++){
for(int x=0;x<image.getWidth();x++){
int color=image.getRGB(x, y);
// ???how to test if its is bright or dark?
}
}
int color = image.getRGB(x, y);
// extract each color component
int red = (color >>> 16) & 0xFF;
int green = (color >>> 8) & 0xFF;
int blue = (color >>> 0) & 0xFF;
// calc luminance in range 0.0 to 1.0; using SRGB luminance constants
float luminance = (red * 0.2126f + green * 0.7152f + blue * 0.0722f) / 255;
// choose brightness threshold as appropriate:
if (luminance >= 0.5f) {
// bright color
} else {
// dark color
}
I suggest first converting the pixel to grayscale, then applying a threshold for converting it pure black&white.
There are libraries that will do this for you, but if you want to learn how images are processed, here you are:
Colour to grayscale
There are various formulas for converting (see a nice article here), I prefer the "luminosity" one. So:
int grayscalePixel = (0.21 * pRed) + (0.71 * pGreen) + (0.07 * pBlue)
I cannot tell what API you are using to manipulate the image, so I left the formula above in general terms. pRed, pGreen and pBlue are the red, green and blue levels (values) for the pixel.
Grayscale to b/w
Now, you can apply a threshold with:
int bw = grayscalePixel > THRESHOLD? 1: 0;
or even:
boolean bw = grayscalePixel > THRESHOLD;
Pixel will be white if above threshold, black if below. Find the right THRESHOLD by experimenting a bit.

how to display picture in java with giving R,G,B

the value of R or G or B is store in int from 0~255.
I already have all the rgb value of every pixel of the picuture, I want to display the pics base on the r,g,b I already know.
BufferedImage imgnew = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
//I can get access to the rgb of every pixel by R[x][y],G[x][y],B[x][y]
//how to calculate the rgb of every pixel?
imgnew.setRGB(x, y, rgb);
}
}
JFrame frame = new JFrame();
JLabel labelnew = new JLabel(new ImageIcon(imgnew));
frame.getContentPane().add(labelnew, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
my question is how to calculate the right pix of every pixel, as the rgb is store as int, should I transfer it to byte? if it is, how to do it, if not, is there any other way to calculate pix?
I know someone use
int rgb = 0xff000000 | ((R[x][y] & 0xff) << 16) | (((G[x][y] & 0xff)<< 8) | ((B[x][y] & 0xff);//the way I calcualte the pix is wrong, which lead to the wrong color of pics
to calculate rgb, but here the R[x][y] G,B is store in type bytes
The BufferedReader class returns one or list of pixel by getRGB() method, and I have to mention that you don't get it as a 2-demition array like int[width][height], for example if you request pixels from 0,0 to 10,20, then you will get it as a 200-length int[] array.
then you need to break up each int value into 4 byte which represents (argb) of each pixel, so you would do it with ByteBuffer class.
here a simple example
int imgWidth=1920,imgHeight=1080;
int[] row=new int[imgWidth];//for storing a line of pixels
for(int i=0;i<imgHeight;i++){
row=img.getRGB(0,i,imgWidth,1,null,0,imgWidth);//get pixel from the current row
for(int k=0;k<row.length;k++){
byte[] argb=ByteBuffer.allocate(4).putInt(4).array();//break up int(color) to 4 byte (argb)
//doing some business with pixel....
}
//setting the processed pixel
//////////////////////////////////////////UPDATED!
//Preparing each pixel using ByteBuffer class, make an int(pixel) using a 4-lenght byte array
int rgb=ByteBuffer.wrap(new byte[]{0xff,R[x][y]&0xff,G[x][y]&0xff,B[x][y]&0xff}).getInt();
imgnew.setRGB(x,y,rgb);//this is bettrer to buffer some pixel then set it to the image, instead of set one-by-one
//////////////////////////////////////////
//img.setRGB(0,i,imgWidth,1,row,0,imgWidth)
}
also check this example too

Gradient color lookup in Java

I have a list of values from 0-1. I want to convert this list to an image by using a gradient that converts these floating point values to RGB values. Are there any tools in Java that provide you with this functionality?
0 should be mapped 0
1 should be mapped 255
keep in mind that you need 3 of them to make a color
so multiply by 255 the floating number and cast it to int.
Perhaps GradientPaint can do what you want. It's unclear how you want a list of floating point values to be converted into a gradient. Normally a gradient consists of two colors and some mechanism that interpolates between those colors. GradientPaint implements a linear gradient.
Say you have an array made of 64 000 triples corresponding to RGB values, like this:
final Random rand = new Random();
final float[] f = new float[320*200*3];
for (int i = 0; i < f.length; i++) {
f[i] = rand.nextFloat(); // <-- generates a float between [0...1.0[
}
And say you have a BufferedImage that has a size of 320x200 (64 000 pixels) of type INT_ARGB (8 bits per value + 8 bits for the alpha level):
final BufferedImage bi = new BufferedImage( 320, 200, BufferedImage.TYPE_INT_ARGB );
Then you can convert you float array to RGB value and fill the image doing this:
for (int x = 0; x < 320; x++) {
for (int y = 0; y < 200; y++) {
final int r = (int) (f[x+y*200*3] * 255.0);
final int g = (int) (f[x+y*200*3+1] * 255.0);
final int b = (int) (f[x+y*200*3+2] * 255.0);
bi.setRGB( x, y, 0xFF000000 | (r << 16) | (g << 8) | b );
}
}
Note that would you display this image it would appear gray but if you zoom in it you'll see it's actually made of perfectly random colorful pixels. It's just that the random number generator is so good that it all looks gray on screen :)

Change the alpha value of a BufferedImage?

How do I change the global alpha value of a BufferedImage in Java? (I.E. make every pixel in the image that has a alpha value of 100 have a alpha value of 80)
#Neil Coffey:
Thanks, I've been looking for this too; however, Your code didn't work very well for me (white background became black).
I coded something like this and it works perfectly:
public void setAlpha(byte alpha) {
alpha %= 0xff;
for (int cx=0;cx<obj_img.getWidth();cx++) {
for (int cy=0;cy<obj_img.getHeight();cy++) {
int color = obj_img.getRGB(cx, cy);
int mc = (alpha << 24) | 0x00ffffff;
int newcolor = color & mc;
obj_img.setRGB(cx, cy, newcolor);
}
}
}
Where obj_img is BufferedImage.TYPE_INT_ARGB.
I change alpha with setAlpha((byte)125); alpha range is now 0-255.
Hope someone finds this useful.
I don't believe there's a single simple command to do this. A few options:
copy into another image with an AlphaComposite specified (downside: not converted in place)
directly manipulate the raster (downside: can lead to unmanaged images)
use a filter or BufferedImageOp
The first is the simplest to implement, IMO.
This is an old question, so I'm not answering for the sake of the OP, but for those like me who find this question later.
AlphaComposite
As #Michael's excellent outline mentioned, an AlphaComposite operation can modify the alpha channel. But only in certain ways, which to me are somewhat difficult to understand:
is the formula for how the "over" operation affects the alpha channel. Moreover, this affects the RGB channels too, so if you have color data that needs to be unchanged, AlphaComposite is not the answer.
BufferedImageOps
LookupOp
There are several varieties of BufferedImageOp (see 4.10.6 here). In the more general case, the OP's task could be met by a LookupOp, which requires building lookup arrays. To modify only the alpha channel, supply an identity array (an array where table[i] = i) for the RGB channels, and a separate array for the alpha channel. Populate the latter array with table[i] = f(i), where f() is the function by which you want to map from old alpha value to new. E.g. if you want to "make every pixel in the image that has a alpha value of 100 have a alpha value of 80", set table[100] = 80. (The full range is 0 to 255.) See how to increase opacity in gaussian blur for a code sample.
RescaleOp
But for a subset of these cases, there is a simpler way to do it, that doesn't require setting up a lookup table. If f() is a simple, linear function, use a RescaleOp. For example, if you want to set newAlpha = oldAlpha - 20, use a RescaleOp with a scaleFactor of 1 and an offset of -20. If you want to set newAlpha = oldAlpha * 0.8, use a scaleFactor of 0.8 and an offset of 0. In either case, you again have to provide dummy scaleFactors and offsets for the RGB channels:
new RescaleOp({1.0f, 1.0f, 1.0f, /* alpha scaleFactor */ 0.8f},
{0f, 0f, 0f, /* alpha offset */ -20f}, null)
Again see 4.10.6 here for some examples that illustrate the principles well, but are not specific to the alpha channel.
Both RescaleOp and LookupOp allow modifying a BufferedImage in-place.
for a nicer looking alpha change effect, you can use relative alpha change per pixel (rather than static set, or clipping linear)
public static void modAlpha(BufferedImage modMe, double modAmount) {
//
for (int x = 0; x < modMe.getWidth(); x++) {
for (int y = 0; y < modMe.getHeight(); y++) {
//
int argb = modMe.getRGB(x, y); //always returns TYPE_INT_ARGB
int alpha = (argb >> 24) & 0xff; //isolate alpha
alpha *= modAmount; //similar distortion to tape saturation (has scrunching effect, eliminates clipping)
alpha &= 0xff; //keeps alpha in 0-255 range
argb &= 0x00ffffff; //remove old alpha info
argb |= (alpha << 24); //add new alpha info
modMe.setRGB(x, y, argb);
}
}
}
I'm 99% sure the methods that claim to deal with an "RGB" value packed into an int actually deal with ARGB. So you ought to be able to do something like:
for (all x,y values of image) {
int argb = img.getRGB(x, y);
int oldAlpha = (argb >>> 24);
if (oldAlpha == 100) {
argb = (80 << 24) | (argb & 0xffffff);
img.setRGB(x, y, argb);
}
}
For speed, you could maybe use the methods to retrieve blocks of pixel values.
You may need to first copy your BufferedImage to an image of type BufferedImage.TYPE_INT_ARGB. If your image is of type, say, BufferedImage.TYPE_INT_RGB, then the alpha component won't be set correctly. If your BufferedImage is of type BufferedImage.TYPE_INT_ARGB, then the code below works.
/**
* Modifies each pixel of the BufferedImage so that the selected component (R, G, B, or A)
* is adjusted by delta. Note: the BufferedImage must be of type BufferedImage.TYPE_INT_ARGB.
* #param src BufferedImage of type BufferedImage.TYPE_INT_ARGB.
* #param colorIndex 0=red, 1=green, 2=blue, 3= alpha
* #param delta amount to change component
* #return
*/
public static BufferedImage adjustAColor(BufferedImage src,int colorIndex, int delta) {
int w = src.getWidth();
int h = src.getHeight();
assert(src.getType()==BufferedImage.TYPE_INT_ARGB);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
int rgb = src.getRGB(x,y);
java.awt.Color color= new java.awt.Color(rgb,true);
int red=color.getRed();
int green=color.getGreen();
int blue=color.getBlue();
int alpha=color.getAlpha();
switch (colorIndex) {
case 0: red=adjustColor(red,delta); break;
case 1: green=adjustColor(green,delta); break;
case 2: blue=adjustColor(blue,delta); break;
case 3: alpha=adjustColor(alpha,delta); break;
default: throw new IllegalStateException();
}
java.awt.Color adjustedColor=new java.awt.Color(red,green,blue,alpha);
src.setRGB(x,y,adjustedColor.getRGB());
int gottenColorInt=src.getRGB(x,y);
java.awt.Color gottenColor=new java.awt.Color(gottenColorInt,true);
assert(gottenColor.getRed()== red);
assert(gottenColor.getGreen()== green);
assert(gottenColor.getBlue()== blue);
assert(gottenColor.getAlpha()== alpha);
}
return src;
}
private static int adjustColor(int value255, int delta) {
value255+= delta;
if (value255<0) {
value255=0;
} else if (value255>255) {
value255=255;
}
return value255;
}

Categories