RGB colors in java Vs VB.net - java

I'm no native English speaker, so please excuses any translation errors.
I'm not really having a coding problem. It's more of a conceptual question.
I wrote two times the same piece of code translating an image into a list of RGB values. (1 combination of 3 values for each pixel).
I wrote the code first in VB.net using:
Dim bmp As New Bitmap(File)
For x As Integer = 0 To w - 1
For y As Integer = 0 To h - 1
Dim c As Color = bmp.GetPixel(x, y)
Dim Red as integer = c.R
Dim Green as integer = c.G
Dim Blue as integer = c.B
Next y
next x
Afterwards I wrote the following in Java:
BufferedImage image = ImageIO.read(new File(File))
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
int pixel = image.getRGB(i,j);
int Red = ((pixel >> 16) & 0xff);
int Green = ((pixel >> 8) & 0xff);
int Blue = ((pixel) & 0xff);
}
}
My expectation would be to get the same values from both pieces of code, since they use the same image. I tried it on an image (270x320) which was a photograph (so a full spectrum of colors). To my surprise I saw there where small differences in the RGB values between the VB.net and Java codes.
If I compare the java(red)'s versus the VB.net(red)'s, the java(green)'s versus the VB.net(green)'s and the java(blue)'s versus the VB.net(blue)'s I compare 270x320x3 = 259.2k combinations. The differences between the integers gotten from the VB.net and from the Java code are as followed:
No difference: 250178 (96.5%)
One difference: 7426 (2.9%)
Two difference: 1582 (0.6%)
Three difference: 14 (0.0%)
Four or more diff.: 0 (0.0%)
Can anybody explain to me where this difference comes from? Has it to do with the way of reading the colors, the way of buffering the image, or with something like anti-aliasing?
Really curious what the reason is, thx in advance

As mentioned by others, the difference is caused by JPEG's lossy compression.
You should be testing these methods with a lossless format.

Related

Java - Decimal color to RGB color

I have a decimal (not hexadecimal) color code and, using Java, I need to convert it to the three RGB colors.
So for example, 16777215 (pure white) needs to get converted to Red: 255 Green: 255 Blue: 255.
65280 (pure green) needs to get converted to Red: 0 Green 255: Blue: 0 Here is a converter for more examples.
Just doing some small calculations and playing with the calculator on the page linked above, I have determined:
Red equals 65536 (256^2)
(255x65536 = 16711680, aka pure red)
Green equals 256 (256^1)
(255x256 = 65280, aka pure green)
Blue equals 1 (256^0)
(255x1 = 255, aka pure blue)
I can tell it obviously has something to do with bytes, but I am missing that last little bit. I am not the best with the whole concept of bits/bytes/etc and how it interacts with Java, so it is likely fairly simple.
So, anyone know the best way of going about this? What would be the best way to convert a single numerical decimal color into the three separate RGB values using java?
You where telling right: RGB values are encoded as bytes in a int. R is byte 2, G is byte 1 and B is byte 0, summing up to a 24bit color depth. Depending on the endianess, this could be a possible representation.
00000000 00000000 00000000 00000000 <-- 32bit int
^ ^ ^
| | |
+--red here | +--green here
8bit | 8bit
|
+--blue here
8bit
You can extract RGB values with some bit shift and masking:
int red = (color >> 16) & 0xff;
int green = (color >> 8) & 0xff;
int blue = color & 0xff;
You could do
Color color = new Color(16777215);
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
You can get the channels out by simple bitwise operations.
int r = (color >> 16) & 0xff;
int g = (color >> 8) & 0xff;
int b = color & 0xff;
From there, it should be easy to do whatever you want with the color information.
Decimal, hexadecimal: does not matter. Your computer uses binary representations internally.
You may have noticed that 0xFF00 == 65280.
Decimal and Hexadecimal are user representations.
I know I am a bit late, but...
int r = color / 65536;
int g = (color - r * 65536) / 256;
int b = color - r * 65536 - g * 256;
This is really doing the exact same thing as the binary shifts even though it doesn't use bitwise operators. This also only works when using valid RGB values (meaning each value is an integer between 0 and 255). For efficiency, however, I would use the binary shifts from the above answers.

Combine/compare colors in java

I would like to do 2 things:
Compare two colors (RGB), so that comparer gives me say 1 if the colors are the same and 0 if the compared colors are on the opposite sides of RGB like black and white, and some number from 0 to 1 if they are somehow equal (like both are reddish)
Mix 2 or more colors together to produce a new one: mixing blue and yellow should give me green, mixing red and green should give black etc.
Questions:
Is there any parts of java API which can do something like this for me? If not is there any 3rd part open source java libraries which can facilitate that?
Thanks.
1.
There are many different methods for comparing colors. If you want perceptual dissimilarity, check out the comments of other users. If you just want something simple that gives an indication: use this code:
int rgb1 = ...
int rgb2 = ...
int r, g, b;
r = (rgb1 >> 16) & 0x000000ff;
g = (rgb1 >> 8) & 0x000000ff;
b = rgb1 & 0x000000ff;
r -= (rgb2 >> 16) & 0x000000ff;
g -= (rgb2 >> 8) & 0x000000ff;
b -= rgb2 & 0x000000ff;
double answer = (r*r+g*g+b*b)/(255*255*3.0);
It calculates a euclidean distance and scales it to range from 0 to 1.
2.
Just as there are many different ways to compare colors, there are many ways to combine colors. It depends on what you want.
You could take the average but that's not what you want. As it would darken the resulting color.
EDIT: here is the code to take the average:
r = (rgb1 >> 16) & 0x000000ff;
g = (rgb1 >> 8) & 0x000000ff;
b = rgb1 & 0x000000ff;
r += (rgb2 >> 16) & 0x000000ff;
g += (rgb2 >> 8) & 0x000000ff;
b += rgb2 & 0x000000ff;
int average = ((r/2)<<16)+((g/2)<<8)+(b/2);
If you are somewhat mathematically inclined think about what behavior you actually look for, and try to put it in a formula (or rather 3 formulas, one for red, one for green, and one for blue)
It shouldn't be hard to convert the formula to java code.
You can put individual r, g and b values back into a single rgb value using
int rgb = (r<<16) | (g<<8) | b
Edit: notice that computers work with `red GREEN blue´, where yellow is a combination of green and blue, not the other way around. Conversion can be done, but I have not done it yet. Perhaps conversion between rgb and ryb color spaces may be of help.

Android: How to get pixels of a single channel 16bit PNG Bitmap

I have a png file created with python PIL that contains an height map (positive values).
The format is : single channel (grey level) at 16bit, thus 16 bit per pixel.
I read the file on android with BitmapFactory.decodeStream(<...data input stream...>);
I correctly get the size of the image through getWidth() and getHeight().
However when I loop though the pixels calling the getPixel(i,j) I obtain negative values, something like: -16776192 -16250872 -16250872 -16250872 -16250872 -16249848 ....
Instead I expect a positive value between 0 and 65535.
I discover that the value -16250872 in binary is 1111111111111111111111111111111111111111000010000000100000001000
This shows that the information relies on the first 16 least significant bits.
I tryed with getPixel(i,j)&0xffff and I obtain a plausible values, however I'm not sure about the endianess: should I to flip the 2 extracted bytes?
Is there a way to do it in more elegant and portable way this conversion ?
NOTE: the file is not a color (RGBA) PNG but a grey level PNG image with a single 16 bit value for each pixel.
I found a solution by myself using the consideration made in this post:
Android: loading an alpha mask bitmap
Basically, If you load directly a 16 bit greylevel PNG from bitmap factory the pixel format will be not correct. You need to use a RGBA 32 bit color format setting the pixel format to ARGB_8888. Then you have to get all the pixels with getPixel(i,j) and mask the integer value with 0xffff. In this way you will obtain the expected 16bit values.
This is a part of the code that I've used:
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig= Bitmap.Config.ARGB_8888;
Bitmap bmp=BitmapFactory.decodeStream(entity.getContent(),null,opt);
int W=bmp.getWidth();
int H=bmp.getHeight();
int px;
for (int i = 0; i < H; i++)
for (int j = 0; j < W; j++)
{
px= bmp.getPixel(j, i)&0x0000ffff;//in px you will find a value between 0 and 65535
...
}
I assume your trying to get the RGB for a pixel in an image. If that is correct then you will find the following helpful.
Returns the Color at the specified location. Throws an exception if x or y are out of bounds (negative or >= to the width or height respectively).
That is the quote for the Bitmap.getPixel();
What you need to do to print this off so that it is human readable. I have programed with android, but not done this with android. I used the following function for one my programs.
public static int[] getRGBValue(BufferedImage bi, int x, int y) {
int argb = bi.getRGB(x, y);
int rgb[] = new int[] {
(argb >> 16) & 0xff, // red
(argb >> 8) & 0xff, // green
(argb ) & 0xff, // blue
};
return rgb;
}
public static String getStringOfRGB(int[] value) {
return "Color: (" + value[0] + ", " + value[1] + ", " + value[2] + ")";
}
I don't kow what you trying to do exactly, so the code above won't answer your question... but should assist you in finding your answer on however you want to use the data of the pixel.
Hope this helps! :)

Get "XOR Mask" and "AND mask" from Icon

I need to get the XOR Mask and the AND Mask from an Icon(.ICO) file.
If anyone can suggest a way how I can do this from Java, this would be brilliant.
If not, do you know of any application which can get these two masks and allow you
to dump them?
This article, Enhance Java GUIs with Windows Icons, has a good explanation of the format, and there's a link to some source code.
The wikipedia article on Windows Icon Image File format, https://en.m.wikipedia.org/wiki/ICO_(file_format), is very straight forward.
There are three sections, header, entries, and image data. The header is 6 bytes. each entry is 16 bytes. Offset 8 of of each entry structure is a Java integer for the offset to the image data
The image data of 32 bitDepth bitmap has no AND mask. If it is a 24 bitDepth then it needs XOR color mask and 1 bitDepth AND mask.
bytes[] openFile(String fname) throws Exception
{
java.io.InputStream file = new java.io.FileInputStream(fname);
bytes []bytes = new bytes[file.available()];
file.read(bytes);
file.close();
return bytes;
}
//assumes 24 bitDepth
bytes[] getXorMask(bytes []ico)
{
int i = 6 + 8;
i = bytes[i+0] | (bytes[i+1]<<1) | (bytes[i+2]<<2) | (bytes[i+3]<<3);
i += sizeof(BITMAPINFOHEADER); // WOW! NOT JAVA
int width = bytes[6] == 0 ? 256 : bytes[6];
int height = bytes[7] == 0 ? 256 : bytes[7];
int sz = width * height * 3; // very presumptious
byte []bytes = new byte[sz];
for(int e=0; e<sz; ) bytes[e++] = ico[i++];
return bytes;
}
bytes[] getAndMask(bytes []ico)
{
int i = 6 + 8;
i = bytes[i+0] | (bytes[i+1]<<1) | (bytes[i+2]<<2) | (bytes[i+3]<<3);
i += sizeof(BITMAPINFOHEADER); // WOW! NOT JAVA
int width = bytes[6] == 0 ? 256 : bytes[6];
int height = bytes[7] == 0 ? 256 : bytes[7];
int sz = width * height * 3; // very presumptious
byte []bytes = new byte[sz];
i += sz; // seek to monochrome mask
// only works if bounds is multiple of 4
sz = width/8 * height;
for(int e=0; e<sz; ) bytes[e++] = ico[i++];
return bytes;
}
The preceding example always gets the first image entry. Also I thought this was simple implementation but Java knows nothing about BITMAPINFOHEADER and this structure is a variable size structure.
Edit:
Firstly big ups to Raymond Chen. He has a course on Icons (part 1 - part 4) on MSDN.
Further reading at, https://learn.microsoft.com/en-us/previous-versions/ms997538(v=msdn.10)?redirectedfrom=MSDN, shows that the BITMAPINFOHEADER structure should be of fixed size. Check out win32 definition of struct BITMAPINFOHEADER. Size of it is 40 bytes.
Edit:
BITMAPINFOHEADER structure's first DWORD is the size of the structure in little endian.

Get Bytes from an int to avoid bit shifting fun - Java (Median Filtering)

I'm trying to perform a Median filter on an image in Java but it's terribly slow. Firstly, if any of you know of a standalone implementation I could use it would be fantastic if you could let me know. I'm implementing on Android, trying to replicate a small part of the JAI.
In my method I take each pixel, extract the R,G & B values using
r = pixel >> 16 & 0xFF
Or similar, find the median for the kernel and finish with
pixel = a | r <<16 | g << 8 | b
Is there any way I can grab the bytes from an int in such a way that this would be faster?
Kind regards,
Gavin
EDIT: Full code to help diagnose my low performance upon request
For the actual source file please go here that's where my implementation of medianFilter can be found.
width and height variables are for the size of dest and are available as class member variables. The pixels are linearized into a one dimensional array.
private void medianFilterSquare(int[] source, int[] dest, int rWidth,
int rHeight, int radius) {
// Source has been reflected into a border of size radius
// This makes it radius * 2 pixels wider and taller than the dest
int r,g,b;
int destOffset, rOffset, kOffset;
// The first offset into the source to calculate a median for
// This corresponds to the first pixel in dest
int rFirst = radius + (rWidth*radius);
// We use a square kernel with the radius passed
int neighbours = (radius+radius+1)*(radius+radius+1);
int index;
// Arrays to accumulate the values for median calculation
int[] rs = new int[neighbours];
int[] gs = new int[neighbours];
int[] bs = new int[neighbours];
// Declaring outside the loop helps speed? I'm sure this is done for me
// by the compiler
int pixel;
// Iterate over the destination pixels
for(int x = 0; x < height; x++){
for(int y = 0; y < width; y++){
// Offset into destination
destOffset = x + (y * width);
// Offset into source with border size radius
rOffset = destOffset + rFirst + (y * (radius *2));
index = 0;
// Iterate over kernel
for(int xk = -radius; xk < radius ; xk ++){
for(int yk = -radius; yk < radius ; yk ++){
kOffset = rOffset + (xk + (rWidth*yk));
pixel = source[kOffset];
// Color.red is equivalent to (pixel>>16) & 0xFF
rs[index] = Color.red(pixel);
gs[index] = Color.green(pixel);
bs[index] = Color.blue(pixel);
index++;
}
}
r = medianFilter(rs);
g = medianFilter(gs);
b = medianFilter(bs);
dest[destOffset] = Color.rgb(r, g, b);
}
}
}
As others have said, it's possible that it's the bit in between which is causing the problem. One thing I would say (which may be obvious, but anyway) - don't just profile the application on a desktop VM and assume that the bottleneck will be in the same place. I wouldn't be at all surprised to find entirely different bottlenecks within Dalvik.
Is it possible for you to work with the values still shifted? For instance, if you were to just mask for different colours:
int r = pixel & 0xff0000;
int g = pixel & 0xff00;
int b = pixel & 0xff;
could you tweak your processing algorithm accordingly?
One final thought: I always find the precedence of shift operators confusing. I'd strongly recommend that from a readability point of view, you bracket them:
r = (pixel >> 16) & 0xFF;
pixel = a | (r <<16) | (g << 8) | b;
Irrelevant to performance, but if I were a maintainer I'd certainly appreciate it :)
The fastet way to get your r,g,b values should be
new byte[] {
(byte)(value >>> 24),
(byte)(value >>> 16),
(byte)(value >>> 8),
(byte)value
};
Concentrating on how to do the bit operations is a distraction. It only matters how you're doing these operations because you're needlessly processing the same pixel over and over.
You're calling median filter for every pixel three times, and you're getting multiple pixels around the pixel per pixel. Which means you're doing all that bit work for the same pixel multiple times. You have for loops nested four deep!
If your radius is 5, you're processing 121 pixels. Then you move down by one and process 121 pixels again, and you've already converted all but 11 of them! You do the same thing for every pixel down, then move to the right one pixel. For a radius of five, you're doing two orders of magnitude as many rgb conversions as you have to.
I'd suggest keeping your image in or converting your image to separate red, blue, and green arrays first.
If radius is large, you could keep the red, blue, and green sums as you move along, subtracting out the pixels from the top and adding in the pixels from the bottom as you crawl down the bitmap, but that would make the code a touch more complicated. Whether you add code to optimize further depends on your requirements.
Also, you have a bunch of little things that could be optimized. I'm not sure if the compiler is taking care of them or not. For instance, you could do some strength reduction. You don't need any of the multiplications you have in the lines that calculate neighbors, destOffset, rOffset, or kOffset. Addition is all you need for those if you refactor the code a bit.
You can occasionally get away with doing arithmetic on the red & blue components simultaneously in a single int:
int average(int rgb1, int rgb2)
{
int rb = (((rgb1 & 0xFF00FF) + (rgb2 & 0xFF00FF)) >> 1) & 0xFF00FF;
int g = (((rgb1 & 0xFF00) + (rgb2 & 0xFF00)) >> 1) & 0xFF00;
return (rb | g);
}
because the red and blue components are separated by 8 bits, they don't interfere with each other.
I've never seen a significant (more than 5-10%) speedup from this though.

Categories