Color quantization diluting pure whites - java

I have a simple quantization function
public static int quantize(int oldpixel) {
int r = (oldpixel >> 16) & 0xff;
int g = (oldpixel >> 8) & 0xff;
int b = (oldpixel >> 0) & 0xff;
int color = 0xff << 24 | (((int) ((r) / 32) * 32) & 0xff) << 16 |
(((int) ((g) / 32) * 32) & 0xff) << 8 |
(((int) ((b) / 32) * 32)& 0xff) << 0;
return color;
}
What it does is reduce a color to a lower detail color, then expands it, this artificially limits the pallet and I'll use it for a dither filter, an image through the function produces this
In:
Unquantized hue wheel
Out:
Quantized hue wheel
This is almost perfect as the result, except the whites are reduced to a gray, I understand the cause is my flooring of divided colors in the algorithm, but I do not know how to fix this, any suggestions would be appreciated

After dividing each component by 32, you have an integer between 0 and 7. You are trying to map this back to the range 0 to 255, so that 0 is 0 and 7 is 255.
You can do this by multiplying it by 255/7, which happens to be about 36.428.
You could use something like (int)((r / 32) * (255.0 / 7.0)), but the cast is ugly in Java. To improve that you could wrap it in a function, and have quantizeChannel(r), quantizeChannel(g) and quantizeChannel(b). Or you could swap the order and use integer arithmetic: r / 32 * 255 / 7.

Related

What does this java bitshift do?

I found some bitshift code in the Java implementation of Hough Transform on Rosetta code, I understand in general what the code does, except this part:
rgbValue = (int)(((rgbValue & 0xFF0000) >>> 16) * 0.30 + ((rgbValue & 0xFF00) >>> 8) * 0.59 + (rgbValue & 0xFF) * 0.11);
I think it takes the average of all 3 pixels, that is at least what it looks like when I output the result. But how does this work? What are these magic numbers?
Method in which this function is used, pasted for convenience:
public static ArrayData getArrayDataFromImage(String filename) throws IOException
{
BufferedImage inputImage = ImageIO.read(new File(filename));
int width = inputImage.getWidth();
int height = inputImage.getHeight();
int[] rgbData = inputImage.getRGB(0, 0, width, height, null, 0, width);
ArrayData arrayData = new ArrayData(width, height);
// Flip y axis when reading image
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int rgbValue = rgbData[y * width + x];
// What does this do?
rgbValue = (int)(((rgbValue & 0xFF0000) >>> 16) * 0.30 + ((rgbValue & 0xFF00) >>> 8) * 0.59 + (rgbValue & 0xFF) * 0.11);
arrayData.set(x, height - 1 - y, rgbValue);
}
}
return arrayData;
}
This is the trick that converts a 24-bit RGB value to a grayscale value using coefficients 0.3, 0.59, and 0.11 (note that these values add up to 1).
This operation (rgbValue & 0xFF0000) >>> 16 cuts out bits 17..24, and shifts them right to position 0..7, producing a value between 0 and 255, inclusive. Similarly, (rgbValue & 0xFF00) >>> 8 cuts out bits 8..16, and shifts them to position 0..7.
This Q&A talks about the coefficients, and discusses other alternatives.

Getting a grey level image back from a color image in android after bitwise extraction

The following code shows a part of my code.
When I delete the part between '// start of color extraction & composing'
and '// end of color extraction & composing' I always get the same color image back
in tmpBmp. But if I insert the part, then I always get a grey-level image
displayed (whereas I'm expecting the same color image).
Is there any reason that the code between '// start of color extraction & composing' and '// end of color extraction & composing' returns a grey-scaled image?
int width = captureBmp.getWidth();
int height = captureBmp.getHeight();
int red, green, blue, alpha;
Bitmap tmpBmp = captureBmp.copy(Bitmap.Config.ARGB_8888, true);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int value = captureBmp.getPixel(x, y);
// start of color extraction & composing
alpha = value & 0xFF000000 >> 24;
red = value & 0x00FF0000 >> 16;
green = value & 0x0000FF00 >> 8;
blue = value & 0x000000FF;
value = ( (alpha << 24) & 0xFF000000) |
( (red << 16) & 0x00FF0000) |
((green << 8) & 0x0000FF00) |
(blue & 0x000000FF);
// end of color extraction & composing
tmpBmp.setPixel(x, y, value);
}
}
ImageView imgView = (ImageView) findViewById(R.id.imageview);
imgView.setImageBitmap(tmpBmp);
The line
red = value & 0x00FF0000 >> 16
is the same as
red = value & (0x00FF0000 >> 16)
Note that (0x00FF0000 >> 16) is just 0x000000FF. The same happens for green and blue, so you get the same value three times. Hence it's grey.
The alpha value is different because 0xFF000000 >> 24 is 0xFFFFFFFF due to the way the signed right shift works.
The solution is to rebracket. (I'd also use >>> for this, but it actually makes no difference because alpha gets shifted back 24 places left anyway).
alpha = (value & 0xFF000000) >>> 24;
red = (value & 0x00FF0000) >>> 16;
green = (value & 0x0000FF00) >>> 8;
blue = value & 0x000000FF;
The problem is that this code:
alpha = value & 0xFF000000 >> 24;
red = value & 0x00FF0000 >> 16;
green = value & 0x0000FF00 >> 8;
blue = value & 0x000000FF;
First masks with bitwise mask and then shifts, try the opposite. Shift then, bitmask with 0xFF.
It probabbly has to do with byte formats and unsigned integers and unsigned right shift in java.
As already noted by #Paul Boddington operator precedence plays a major part.

Hosting a Color.argb(a,r,g,b) inside a byte without losing information

The question is pretty much inside the title.
I want to host inside a byte ,a color without losing any sort of information, keeping in mind that the Color.argb is actually an int.
Byte colorToSave;
colorToSave = Color.argb(255, 255,0, 100);
As far as I know this is pretty much impossible, but perhaps someone found a nice workaround.
No its not possible - the core of Color.argb is a number of bitwise operations -
alpha << 24) | (red << 16) | (green << 8) | blue;
So if we break it down with your values (255,255,0,100) you can see that you can see the precision loss -
int alpha = 255;
System.out.println(Integer.toBinaryString(alpha));
int red = 255;
System.out.println(Integer.toBinaryString(red));
int green = 0;
System.out.println(Integer.toBinaryString(green));
int blue = 100;
System.out.println(Integer.toBinaryString(blue));
int argb = 255<<24 | 255 << 16 | 0 << 8 | 100;
System.out.println(Integer.toBinaryString(argb));
byte argbbyte = (byte) argb;
System.out.println(Integer.toBinaryString(argbbyte));
Producing an output -
11111111
11111111
0
1100100
11111111111111110000000001100100
1100100
The final part of the output is casting the int to a byte which shows we return the color for blue

Any one tell me why we write this 0xff000000 in filters?

Here is my code for GrayScale filter want to know about this
class GrayScale extends RGBImageFilter {
#Override
public int filterRGB(int x, int y, int rgb) {
int a = rgb & 0xff000000;
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
rgb = (r * 77 + g * 151 + b * 28) >> 8;
return a | (rgb << 16) | (rgb << 8) | rgb;
}
}
The format of your input is 0xAARRGGBB where AA is the alpha (transparency), RR is the red, GG is the green, and BB is the blue component. This is hexadecimal, so the values range from 00 to FF (255).
Your question is about the extraction of the alpha value. This line:
int a = rgb & 0xFF000000
If you consider a value like 0xFFFFFFFF (white), AND will return whatever bits are set in both the original colour and the mask; so you'll get a value of 0xFF, or 255 (correctly).
The
int a = rgb & 0xff000000;
...
return a | ...;
simply preserves the top 8 bits of rgb (which contain the alpha component).
Beside Ashes999 and NPE answers, if you want to know how it's treated internally in java, check out this post

signed int to unsigned byte and vice versa

I have a signed value e.g. -7368817
when I cast it to byte it will be something like: -113
I convert it to unsigned byte bye & 0xff and it will be something like 143
now I manipulate this byte value and after that I want to whole way back! to get the signed integer of the new byte. :)
Update
The whole story:
I have an image which is 8-bit depth and gray scale! it means that all pixels are presented using 1 byte
BufferedImage image = ImageIO.read(new File("my_grayscal_8bit_photo.jpg"));
int intPixel = image.getRGB(1, 1);
now I need some bit manipulation in this pixel but since it is int I must convert it to byte first:
byte bytePixel = (byte) intPixel;
and to make it unsigned:
int intPixel2 = bytePixel & 0xff;
now I do my bit manipulation and want to convert it to int and do:
image.setRGB(1, 1, neworiginalint);
The getRGB(int x, int y) method always returns an int pixel in the TYPE_INT_ARGB color model. To manually extract the red, green, blue and alpha values for the pixel you can do this:
int pixel = image.getRGB(1, 1);
int a = (pixel >> 24) & 0xFF;
int r = (pixel >> 16) & 0xFF;
int g = (pixel >> 8) & 0xFF;
int b = pixel & 0xFF;
Or use the Color(int rgba, boolean hasalpha) constructor for convenience (at the cost of performance). Once you've manipulated the red, green, blue and alpha values (in the range of 0 to 255) you can recombine them back into an int for setting pixels:
int newPixel = (a << 24) | (r << 16) | (g << 8) | b;
Using the -7368817 pixel you mentioned with this code, the alpha is 255 (so no transparency) and the red, green and blue values are all 143. Since you're dealing with grayscale you could just pick any of red, green or blue to get the gray value. However on setting the pixel you're going to have set all three to maintain the grayscale since it's RGB. You could shortcut it a bit like so:
int pixel = image.getRGB(1, 1);
// extract your gray value from blue, assume red and green are same
int gray = pixel & 0xFF;
// this method does your manipulation on the gray value, 0 to 255
gray = manipulate(gray);
// recombine back into int, preserving the original alpha
int newPixel = (pixel & 0xFF000000) | (gray << 16) | (gray << 8) | gray;
// now you can set your new pixel
image.setRGB(1, 1, nexPixel);
Basically the trick is to use int as your unsigned byte. Just make sure you keep the values from 0 to 255 and everything should work fine.
Are you asking how to manipulate the least-significant byte in an signed int, without touching the more significant byte?
This can be done like this:
int i = -7368817; // 0xFF8F 8F8F
int b = i & 0xFF; // "signed byte", = 0x8F = 143
b += 0x2C; // your manipulation, result = 0xBB
i = (i & 0xFFFFFF00) | (b & 0xFF); // result of LSB modification = 0xFF8F8FBB
or in one step: i = (i & 0xFFFFFF00) | ((i + 0x2C) & 0xFF); if it is a simple manipulation.
If the manipulation can't ever produce an overflow, you can simply do it on the whole int:
i ^= 0x34; // i = 0xFF8F8FBB

Categories