How to get the pixel value in a grayscale image - java

I have a BufferedImage which is of TYPE_BYTE_GRAY and I need to get the pixel value at x,y. I know I can't use getRGB as it returns the wrong color model so how do I go about it? Many thanks!

Get java.awt.image.Raster from BufferedImage by invoking getData() method.
Then use
int getSample(int x, int y, int b)
on received object, where b is the color channel (where each color is represented by 8 bits).
For gray scale
b = 0.
For RGB image
b = 0 ==>> R channel,
b = 1 ==>> G channel,
b = 2 ==>> B channel.

I guess what you looking for is the math to get a one number to represent the Gray scale in that RGB, there are few diff ways, follow some of them:
The lightness method averages the most prominent and least prominent
colors: (max(R, G, B) + min(R, G, B)) / 2.
The average method simply averages the values: (R + G + B) / 3.
The luminosity method is a more sophisticated version of the average
method. It also averages the values, but it forms a weighted average
to account for human perception. We’re more sensitive to green than
other colors, so green is weighted most heavily. The formula for
luminosity is 0.21 R + 0.71 G + 0.07 B.
Reference : http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/

Provided that you have a BufferedImage named grayImg whose type is TYPE_BYTE_GRAY
int width = grayImg.getWidth();
int height = grayImg.getHeight();
byte[] dstBuff = ((DataBufferByte) grayImg.getRaster().getDataBuffer()).getData();
Then the gray value at (x,y) would simply be:
dstBuff[x+y*width] & 0xFF;

Related

Get just one pixel value without allocating whole array of pixels

I just need to check three pixel values on the whole image (Image instance). I'd really like to do this without allocating an array of pixels.
Is that possible? Something like BufferedImage's getRGB()?
Yes. You are on the right track with the getRGB method, as it would return a single int with three values (RGB). To convert the single int to three ints, you can do two things:
1. Use the Color class's built-in constructor:
int rgb = img.getGraphics().getRGB(0,0);//Get color of pixel 0,0
Color c = new Color(rgb); //c now contains the r, g, and b values.
2. Build the decoder yourself:
int rgb = img.getGraphics().getRGB(0,0) //Get color of pixel 0,0
int r = rgb >> 24;
int g = 0 >> 16;
int b = 0 >> 8;
Both methods will return a r, g, and b value, which can be used.

What type of array required in WritableRaster method setPixels()?

I don't understand how WritableRaster class of Java works. I tried looking at the documentation but don't understand how it takes values from an array of pixels. Plus, I am not sure what the array of pixels consists.
Here I explain.
What I want to do is : Shamir's Secret Sharing on images. For that I need to fetch an image in BuferedImage image. I take a secret image. Create shares by running a 'function' on each pixel of the image. (basically changing the pixel values by something)
Snippet:
int w = image.getWidth();
int h = image.getHeight();
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
int pixel = image.getRGB(j, i);
int red = (pixel >> 16) & 0xFF;
int green = (pixel >> 8) & 0xFF;
int blue = (pixel) & 0xFF;
pixels[j][i] = share1(red, green, blue);
// Now taking those rgb values. I change them using some function and return an int value. Something like this:
public int share1 (r, g, b)
{
a1 = rand.nextInt(primeNumber);
total1 = r+g+b+a1;
new_pixel = total1 % primeNumber;
return new_pixel;
}
// This 2d array pixels has all the new color values, right? But now I want to build an image using this new values. So what I did is.
First converted this pixels array to a list.
Now this list has pixel values of the new image. But to build an image using RasterObj.setPixels() method, I need an array with RGB values [I MIGHT BE WRONG HERE!]
So I take individual values of a list and find rgb values and put it consecutively in a new 1D array
pixelvector..something like this (r1,g1,b1,r2,g2,b2,r3,g3,b3...)
Size of the list is wh because it contains single pixel value of each pixel.
BUT, Size of the new array pixelvector will become wh*3 since it contains r,g,b values of each pixel..
Then to form image I do this: Snippet
BufferedImage image_share1 = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
WritableRaster rast = (WritableRaster) image_share1.getData();
rast.setPixels(0, 0, w, h, pixelvector);
image_share1.setData(rast);
ImageIO.write(image_share1,"JPG",new File("share1.jpg"));
If I put an array with just single pixel values in setPixels() method, it does not return from that function! But if I put an array with separate r,g,b values, it returns from the function. But doing the same thing for share1 , share 2 etc.. I am getting nothing but shades of blue. So, I am not even sure I will be able to reconstruct the image..
PS - This might look like a very foolish code I know. But I had just one day to do this and learn about images in Java. So I am doing the best I can.
Thanks..
A Raster (like WriteableRaster and its subclasses) consists of a SampleModel and a DataBuffer. The SampleModel describes the sample layout (is it pixel packed, pixel interleaved, band interleaved? how many bands? etc...) and dimensions, while the DataBuffer describes the actual storage (are the samples bytes, short, ints, signed or unsigned? single array or array per band? etc...).
For BufferedImage.TYPE_INT_RGB the samples will be pixel packed (all 3 R, G and B samples packed into a single int for each pixel), and data/transfer type DataBuffer.TYPE_INT.
Sorry for not answering your question regarding WritableRaster.setPixels(...) directly, but I don't think it's the method you are looking for (in most cases, it's not). :-)
For your goal, I think what you should do is something like:
// Pixels in TYPE_INT_RGB format
// (ie. 0xFFrrggbb, where rr is two bytes red, gg two bytes green etc)
int[] pixelvector = new int[w * h];
BufferedImage image_share1 = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
WritableRaster rast = image_share1.getRaster(); // Faster! No copy, and live updated
rast.setDataElements(0, 0, w, h, pixelvector);
// No need to call setData, as we modified image_share1 via it's raster
ImageIO.write(image_share1,"JPG",new File("share1.jpg"));
I'm assuming the rest of your code for modifying each pixel value is correct. :-)
But just a tip: You'll make it easier for yourself (and faster due to less conversion) if you use a 1D array instead of a 2D array. I.e.:
int[] pixels = new int[w * h]; // instead of int[][] pixels = new int[w][h];
// ...
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
// ...
pixels[y * w + x] = share1(red, green, blue); // instead of pixels[x][y];
}
}

What is the meaning of a negative number when using getRGB()?

I am new to color stuff, rendering etc. and watching a couple tutorial videos about rendering etc. My question is, when I call a getRGB method on a pixel, it returns a negative int. what is the meaning of this negative number? For example, when i call getRGB on a color with r: 186, g: 186, b: 186, it returns -4539718. How is this number related to its rgb value? I've made a couple of google search but was not successful.
The getRGB method returns an int whose 4 bytes are the alpha, red, green, and blue components in that order. Assuming that the pixel is not transparent, the alpha is 255 (0xFF). It's the most significant byte in the int, and the first bit is set in that value. Because in Java int values are signed according to Two's Complement, the value is actually negative because that first bit is on.
To get the color of a pixel:
Color c = new Color(image.getRGB(10,10));
int red = c.getRed();
int green = c.getGreen();
int blue = c.getBlue();

Remove the alpha from a colour but retain its texture

I have a colour #6A8F6509
I wish to remove the Alpha part of that colour and be left with only RGB components (i.e. #RRGGBB).
The resulting colour must look identical to the initial one without the transparency.
How do I go about this on Android's Java?
Update: The initial background is always white (#FFF)
int newColor = oldColor | 0xFF000000;
If you want to do it with the code you can try following code:
static int stripAlpha(int color){
return Color.rgb(Color.red(color), Color.green(color), Color.blue(color));
}
For each color:
C' = C(a/255) + 255(1-(a/255))
So for #6A8F6509:
R' = 143(106/255) + 255(1-(106/255) = (approx) 208
G' = 101(106/255) + 255(1-(106/255) = (approx) 191
B' = 9(106/255) + 255(1-(106/255) = (approx) 153
So your answer should be: #D0BF99, if my math is correct. This only applies to an all white background as well - for a non-white background, the second addend should have the appropriate corresponding color value of the background instead of 255.
-- EDIT --
Looking at the formula again, I'm not entirely sure whether the formula gets applied to each color or the entire color (a quick visual test should be able to tell you - I'm guessing per color). I should point out that this is the formula direct from the Alpha Compositing wiki page:
http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
try
#FF8F6509
the first two digits indicate the alpha value of any color.
Calculate the way a semi-transparent color will look when placed on top of another color.
Kotlin color extension:
#ColorInt
fun #receiver:ColorInt Int.removeAlpha(#ColorInt underlyingColor: Int): Int {
return ColorUtils.blendARGB(this, underlyingColor, alpha / 255F)
}
Usage:
Color.parseColor("#6A8F6509").removeAlpha(underlyingColor = Color.WHITE)
Jetpack Compose version is
Color(0x6A8F6509).compositeOver(Color.White)
I'll admit to not knowing Android's Java, but can't you just change the color from #6A8F6509 to #FF8F6509? The in most modern programming languages, color is typically encoded as ##AARRGGBB.
AA = Hex of the Alpha, ranging from 0 (hex 00), full transparent, to 255 (hex FF), no transparency.
RR = Hex of the red part of the color. Similar to above, 0 is no red, 255 is full red.
GG = Hex of green.
BB = Hex of blue.
-Edit-
Based on your update, you can do this to get the color:
For each of the red, green, and blue:
get the difference between the transparent color and the background color and multiply that by the alpha value. Divide that by 255 (hex FF, if you prefer). Finally, add that number to the transparent color's value.
In your case:
int transparentR = 0x8F;
int transparentG = 0x65;
int transparentB = 0x09;
int backgroundR = 0xFF;
int backgroundG = 0xFF;
int backgroundB = 0xFF;
int alpha = 0x6A;
int newR = transparentR + ( ( (backgroundR - transparentR) * alpha ) / 255 );
int newG = transparentR + ( ( (backgroundG - transparentG) * alpha ) / 255 );
int newB = transparentR + ( ( (backgroundB - transparentB) * alpha ) / 255 );
-Edit, again-
Ok, I've redone the formula based on LJ2's answer using Wikipedia's Alpha Blending function. and had a colleague confirm this with Paint.Net. This is the version where the background Color is fully opaque, and it's in C#, sorry:
int transparentColor2(int intFgColor, int intBgColor, int intAlpha)
{
double dAlpha = (Convert.ToDouble(intAlpha)/255.0);
double dFirst = Convert.ToDouble(intFgColor) * dAlpha;
double dSecond = Convert.ToDouble(intBgColor) * (1 - dAlpha);
return Convert.ToInt32(dFirst + dSecond);
}
do this for each R, G, and B value, and you should get the right answer. Incase Android's Java has different, double could be Double/Single/Float, and int could be Int32/Int16/Int64/Byte. I don't remember how to Convert between the two in Java, unfortunately.

java.awt.image.BufferedImage 24-bit RGB to 8-bit Grayscale conversion using custom ColorSpace

I want to do a simple color to grayscale conversion using java.awt.image.BufferedImage. I'm a beginner in the field of image processing, so please forgive if I confused something.
My input image is an RGB 24-bit image (no alpha), I'd like to obtain a 8-bit grayscale BufferedImage on the output, which means I have a class like this (details omitted for clarity):
public class GrayscaleFilter {
private BufferedImage colorFrame;
private BufferedImage grayFrame =
new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
I've succesfully tried out 2 conversion methods until now, first being:
private BufferedImageOp grayscaleConv =
new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
protected void filter() {
grayscaleConv.filter(colorFrame, grayFrame);
}
And the second being:
protected void filter() {
WritableRaster raster = grayFrame.getRaster();
for(int x = 0; x < raster.getWidth(); x++) {
for(int y = 0; y < raster.getHeight(); y++){
int argb = colorFrame.getRGB(x,y);
int r = (argb >> 16) & 0xff;
int g = (argb >> 8) & 0xff;
int b = (argb ) & 0xff;
int l = (int) (.299 * r + .587 * g + .114 * b);
raster.setSample(x, y, 0, l);
}
}
}
The first method works much faster but the image produced is very dark, which means I'm losing bandwidth which is unacceptable (there is some color conversion mapping used between grayscale and sRGB ColorModel called tosRGB8LUT which doesn't work well for me, as far as I can tell but I'm not sure, I just suppose those values are used). The second method works slower, but the effect is very nice.
Is there a method of combining those two, eg. using a custom indexed ColorSpace for ColorConvertOp? If yes, could you please give me an example?
Thanks in advance.
public BufferedImage getGrayScale(BufferedImage inputImage){
BufferedImage img = new BufferedImage(inputImage.getWidth(), inputImage.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
Graphics g = img.getGraphics();
g.drawImage(inputImage, 0, 0, null);
g.dispose();
return img;
}
There's an example here which differs from your first example in one small aspect, the parameters to ColorConvertOp. Try this:
protected void filter() {
BufferedImageOp grayscaleConv =
new ColorConvertOp(colorFrame.getColorModel().getColorSpace(),
grayFrame.getColorModel().getColorSpace(), null);
grayscaleConv.filter(colorFrame, grayFrame);
}
Try modifying your second approach. Instead of working on a single pixel, retrieve an array of argb int values, convert that and set it back.
The second method is based on pixel's luminance therefore it obtains more favorable visual results. It could be sped a little bit by optimizing the expensive floating point arithmetic operation when calculate l using lookup array or hash table.
Here is a solution that has worked for me in some situations.
Take image height y, image width x, the image color depth m, and the integer bit size n. Only works if (2^m)/(x*y*2^n) >= 1.
Keep a n bit integer total for each color channel as you process the initial gray scale values. Divide each total by the (x*y) for the average value avr[channel] of each channel. Add (192 - avr[channel]) to each pixel for each channel.
Keep in mind that this approach probably won't have the same level of quality as standard luminance approaches, but if you're looking for a compromise between speed and quality, and don't want to deal with expensive floating point operations, it may work for you.

Categories