If we take, say, 32-bits per pixel pictures using ARGB and ARGB_PRE (ARGB but with premultiplied alpha), are the values identical when the alpha is fully on (that is: no transparency at all)?
For example if I have an ARGB pixel with the following value: 0xFF808080 (which is a shade gray, without any transparency because the alpha is at its max value: 255), what would this become in ARGB_PRE?
How can I find this out by myself? Is it enough to instanciate one buffered image using ARGB and the other ARGB_PRE and using setRGB(...) on both and then comparing the int I'd get back?
For example if I do this:
final BufferedImage bi1 = new BufferedImage(10,10,BufferedImage.TYPE_INT_ARGB);
final BufferedImage bi2 = new BufferedImage(10,10,BufferedImage.TYPE_INT_ARGB_PRE);
bi1.setRGB(0,0,0xFF808080);
bi2.setRGB(0,0,0xFF808080);
System.out.println("bi1: " + Integer.toHexString(bi1.getRGB(0, 0)));
System.out.println("bi2: " + Integer.toHexString(bi2.getRGB(0, 0)));
I get back the same value for both, but it's normal it's the very value I gave.
Basically my question boils down to this: if pictures doesn't have a single pixel being
transparent, can I generate the exact same pictures by using the exact same values both in ARGB and ARGB_PRE modes?
Or formulated this way: if I don't have any transparent pixel, are ARGB and ARGB_PRE basically identical?
Premultiplied alpha means that colors stored in the image data are multiplied by alpha, so they dont need to be multiplied when composing(drawing).
this doesnt change how image looks when drawed, but how it's data is stored...
BTW if your image only has alpha values of 255 (255 in composing means 1.0f) then resulted color will always be 1 * color = color (not changed)
Yes. Your understanding is correct.
Related
Is there a way to add aplha to color that i can get from a color string.
Because now i can draw my color without the alpha by using the following code
CircleOptions options = new CircleOptions().center(new LatLng(car.getLatitude(), car.getLongitude()))
.radius(car.getRadius())
.strokeColor(Color.argb(50,232,245,248))
.strokeWidth(2)
.fillColor(Color.parseColor(car.getColorString()));
This works fine. But i want to make the color more transparent. Is there a good way to add alpha do this color because i can only get out a "ColorString"
Color is represented as a int and you can represent it with hex notation like 0xAARRGGBB, which means Alpha, Red, Green, Blue. So each value can be 0 to 255.
Thus, what you want can be achieved with some bit operations.
If you want reset alpha value then,
int newColor = 0x10000000 | (Color.parseColor(car.getColorString()) & 0xFFFFFF);
(0x10000000 means new alpha value - 16 which is about 6.274% alpha because 100% is 255)
(0xFFFFFF means we take only rgb value from a color)
See also
You can change the alpha of a colour in android with two extra digits on hex code of the colour.
Take a look at the official colour documentation
All you need is applying the following rules :
The value always begins with a pound (#) character and then followed
by the Alpha-Red-Green-Blue information in one of the following
formats:
RGB
ARGB
RRGGBB
AARRGGBB
After I set a pixel of a java.awt.image.BufferedImage to a value using setRGB, a subsequent call to getRGB returns a different value than I set.
Code:
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY);
int color1 = -16711423; // corresponds to RGB(1, 1, 1)
image.setRGB(0, 0, color1);
int color2 = image.getRGB(0, 0);
System.out.println(color1);
System.out.println(color2);
It produces the following output
-16711423
-16777216
I think it has to do something with gamma correction, but I couldn't find anything about it in the documentation.
Ideally, I want to change this behavior to return the same value as I set. Is that possible?
The BufferedImage.getRGB() method, always returns a color (as an int in "packed format") in the non-linear sRGB color space (ColorSpace.CS_sRGB). It will do so, regardless of what color space and bits per pixel etc. your image has. Thus, conversion and possible precision loss may occur.
From the JavaDoc:
Returns an integer pixel in the default RGB color model (TYPE_INT_ARGB) and default sRGB colorspace. Color conversion takes place if this default model does not match the image ColorModel.
Your TYPE_BYTE_GRAY image internally uses a linear gray color space (ColorSpace.CS_GRAY), which does not map one-to-one with sRGB.
Also, I suggest using hexadecimal notation for (A)RGB colors, it makes the colors and difference much easier to see:
-16711423 == 0xff010101
-16777216 == 0xff000000
So, there is a minor precision loss here, but nothing unexpected.
If you want direct access to the pixel data, look into the Raster, SampleModel and DataBuffer classes (and their respective subclasses).
You set a color specified with an int which stores RGB components as bytes (in the range of 0..255 inclusive).
But the color model of your image is not RGB but BYTE_GRAY. Obviously you may suffer precision losing. This explains the different colors. Should you have used image type TYPE_INT_RGB you would've ended up with the same color.
I would like to compute the Normalized Cross Correlation between two images using Java.
My program works fine but when I tried to verify my results in MATLAB, I unfortunately did not get the same results as I did with my Java implementation.
This is the code that I performed in MATLAB:
Img1 = rgb2gray(imread('image1.png'));
Img2 = rgb2gray(imread('image2.png'));
corr2(Img1, Img2);
This is part of my Java implementation. Some of the classes have been removed for better understanding:
I am not sure what is wrong with my Java implementation.
I also have one other question. In MATLAB, I had to convert the image to grayscale before using corr2. Do I need to do the same in Java?
The reason why it isn't the same is because you didn't account for the headers in the PNG file.
If you take a look at your Java code, you are reading in the image as a byte stream in your readImage method. For PNG, there are headers involved such as the size of the image and how many bits of colour per pixel there are. Not only are you grabbing image data (which by the way is compressed using a version of LZW so you're not even reading in raw image data), but you are also grabbing in extra information which is being collected in your correlation code.
What's confusing is that you are reading in the image fine using the BufferedImage type at the beginning of your correlation code to obtain the rows and columns. Why did you switch to using a byte stream in your readImage method?
As such, you need to change your readImage method to take in the BufferedImage object, or reread the data like you did in the correlation method in your readImage method. Once you do that, use the BufferedImage methods to access the RGB pixels. FWIW, if you are reading in the image as grayscale, then every channel should give you the same intensity so you can operate on one channel alone. Doesn't matter which.... but make sure that you're doing correlation on grayscale images. It is ambiguous when you go to colour, as there is currently no set standard on how to do this.
Using BufferedImage, you can use the getRGB method to obtain the pixel you want at column x and row y. x traverses from left to right, while y traverses from top to bottom. When you call getRGB it returns a single 32-bit integer in ARGB format. Each channel is 8 bits. As such, the first 8 bits (MSB) are the alpha value, the second 8 bits are for red, the third 8 bits are for green, and the final 8 are for blue. Depending on what channel you want, you need to bitshift and mask out the bits you don't need to get the value you want.
As an example:
int rgb = img.getRGB(x, y);
int alpha = rgb >> 24 & 0xFF;
int red = rgb >> 16 & 0xFF;
int green = rgb >> 8 & 0xFF;
int blue = rgb & 0xFF;
For the alpha value, you need to shift to the right by 24 bits to get it down to the LSB positions, then mask with 0xFF to obtain only the 8 bits that represent the alpha value. Similarly you would have do the same for the red, green and blue channels. Because correlation is rather ill-posed for colour images, let's convert the image to grayscale within your readImage method. As such, there is no need to convert the image before you run this method. We will do that within the method itself to save you some hassle.
If you take a look at how MATLAB performs rgb2gray, it performs a weighted sum, weighting the channels differently. The weights are defined by the SMPTE Rec. 601 standard (for those of you that want to figure out how I know this, you can take a look at the source of rgb2gray and read off the first row of their transformation matrix. These coefficients are essentially the definition of the 601 standard).
Previous versions of MATLAB simply added up all of the channels, divided by 3 and took the floor. I don't know which version of MATLAB you are using, but to be safe I'm going to use the most up to date conversion.
public static void readImage(BufferedImage img, int array[][], int nrows, int ncols) {
for (int i = 0; i < nrows; i++)
for (int j = 0; j < ncols; j++) {
int rgb = img.getRGB(j, i);
int red = rgb >> 16 & 0xFF;
int green = rgb >> 8 & 0xFF;
int blue = rgb & 0xFF;
array[i][j] = (int) (0.299*((double)red) + 0.587*((double)green) +
0.114*((double)blue) );
}
}
This should hopefully give you what you want!
I've heard that the data in gray-scale images with 8-bits color depth is stored in the first 7 bits of a byte of each pixel and the last bit keep intact! So we can store some information using the last bit of all pixels, is it true?
If so, how the data could be interpreted in individual pixels? I mean there is no Red, Blue and Green! so what do those bits mean?
And How can I calculate the average value of all pixels of an image?
I prefer to use pure java classes not JAI or other third parties.
Update 1
BufferedImage image = ...; // loading image
image.getRGB(i, j);
getRGB method always return an int which is bigger than one byte!!!
What should I do?
My understanding is that 8-bits colour depth means there is 8-bits per pixel (i.e. one byte) and that Red, Gren and Blue are all this value. e.g. greyscale=192 means Red=192, Green=192, Blue=192. There is no 7 bits plus another 1 bit.
AFAIK, you can just use a normal average. However I would use long for the sum and make sure each byte is unsigned i.e. `b & 0xff
EDIT: If the grey scale is say 128 (or 0x80), I would expect the RGB to be 128,128,128 or 0x808080.
I have a buffered image in java and I want to record how similar each pixel is to another based on the color value. so the pixels with 'similar' colors will have a higher similarity value. for example red and pink will have a similarity value 1000 but red and blue will have something like 300 or less.
how can I do this. when I get the RGB from a buffered Image pixel it returns a negative integer I am not sure how to implement this with that.
First, how are you getting the integer value?
Once you get the RGB values, you could try
((r2 - r1)2 + (g2 - g1)2 + (b2 - b1)2)1/2
This would give you the distance in 3D space from the two points, each designated by (r1,g1,b1) and (r2,g2,b2).
Or there are more sophisticated ways using the HSV value of the color.
HSL is a bad move. L*a*b is a color space designed to represent how color is actually percieved, and is based on data from hundreds of experiments involving people with real eyes looking at different colors and saying "I can tell the difference between those two. But not those two".
Distance in L*a*b space represents actual percieved distance according to the predictions derived from those experiments.
Once you convert into L*a*b you just need to measure linear distance in a 3D space.
I suggest you start reading here
Color difference formulas if you want to do this right. It explains the ΔE*ab, ΔE*94, ΔE*00 and ΔE*CMC formulas for calculating color difference.
If you are going to use HSV you need to realize that HSV are not points in a three dimensional space but rather the angle, magnitude, and distance-from-top of a cone. To calculate the distance of an HSV value you either need to determine your points in 3d space by transforming.
X = Cos(H)*S*V
Y = Sin(H)*S*V
Z = V
For both points and then taking the Euclidian distance between them:
Sqrt((X0 - X1)*(X0 - X1) + (Y0 - Y1)*(Y0 - Y1) + (Z0 - Z1)*(Z0 - Z1))
At a cost of 2 Cos, 2 Sin, and a square root.
Alternatively you can actually calculate distance a bit more easily if you're so inclined by realizing that when flattened to 2D space you simply have two vectors from the origin, and applying the law of cosign to find the distance in XY space:
C² = A² + B² + 2*A*B*Cos(Theta)
Where A = S*V of the first value, and B = S*V of the second and cosign is the difference theta or H0-H1
Then you factor in Z, to expand the 2D space into 3D space.
A = S0*V0
B = S1*V1
dTheta = H1-H0
dZ = V0-V1
distance = sqrt(dZ*dZ + A*A + B*B + 2*A*B*Cos(dTheta);
Note that because the law of cosigns gives us C² we just plug it right in there with the change in Z. Which costs 1 Cos and 1 Sqrt. HSV is plenty useful, you just need to know what type of color space it's describing. You can't just slap them into a euclidian function and get something coherent out of it.
The easiest is to convert both colours to HSV value and find the difference in H values. Minimal changes means the colours are similar. It's up to you to define a threshold though.
You're probably calling getRGB() on each pixel which is returning the color as 4 8 bits bytes, the high byte alpha, the next byte red, the next byte green, the next byte blue. You need to separate out the channels. Even then, color similarity in RGB space is not so great - you might get much better results using HSL or HSV space. See here for conversion code.
In other words:
int a = (argb >> 24) & 0xff;
int r = (argb >> 16) & 0xff;
int g = (argb >> 8) & 0xff;
int b = argb & 0xff;
I don't know the specific byte ordering in java buffered images, but I think that's right.
You could get the separate bytes as follows:
int rgb = bufferedImage.getRGB(x, y); // Returns by default ARGB.
int alpha = (rgb >>> 24) & 0xFF;
int red = (rgb >>> 16) & 0xFF;
int green = (rgb >>> 8) & 0xFF;
int blue = (rgb >>> 0) & 0xFF;
I find HSL values easier to understand. HSL Color explains how they work and provides the conversion routines. Like the other answer you would need to determine what similiar means to you.
There's an interesting paper on exactly this problem:
A New Perceptually Uniform Color Space with Associated Color Similarity Measure for Content-Based Image and Video Retrieval
by M. Sarifuddin and Rokia Missaoui
You can find this easily using Google or in particular [Google Scholar.][1]
To summarise, some color spaces (e.g. RGB, HSV, Lab) and distance measures (such as Geometric mean and Euclidean distance) are better representations of human perception of color similarity than others. The paper talks about a new color space, which is better than the rest, but it also provides a good comparison of the common existing color spaces and distance measures. Qualitatively*, it seems the best measure for perceptual distance using commonly available color spaces is : the HSV color space and a cylindrical distance measure.
*At least, according to Figure 15 in the referenced paper.
The cylindrical distance measure is (in Latex notation):
D_{cyl} = \sqrt{\Delta V^{2}+S_1^{2}+S_2^{2}-2S_1S_2cos(\Delta H)}
This is a similar question to #1634206.
If you're looking for the distance in RGB space, the Euclidean distance will work, assuming you treat red, green, and blue values all equally.
If you want to weight them differently, as is commonly done when converting color/RGB to grayscale, you need to weight each component by a different amount. For example, using the popular conversion from RGB to grayscale of 30% red + 59% green + 11% blue:
d2 = (30*(r1-r2))**2 + (59*(g1-g2))**2 + (11*(b1-b2))**2;
The smaller the value of d2, the closer the colors (r1,g1,b1)and(r2,g2,b2) are to each other.
But there are other color spaces to choose from than just RGB, which may be better suited to your problem.
Color perception is not linear because the human eye is more sensitive to certain colors than others.
So jitter answered correctly
i tried it out. the HSL/HSV value is definitely not useful. for instance:
all colors with L=0 are 'black' (RGB 000000), though their HSL difference may implicate a high color distance.
all colors with S=0 are a shade of 'gray', though their HSL difference may implicate a high color distance.
the H (hue) range begins and ends with a shade of 'red', so H=0 and H=[max] (360° or 100% or 240, depending on the application) are both red and relatively similar to each other, but the Euclidean HSL distance is close to maximum.
so my recommendation is to use the Euclidean RGB distance (r2-r1)² + (g2-g1)² + (b2-b1)² without root. the (subjective) threshold of 1000 then works fine for similar colors. colors with differences > 1000 are well distinguishable by the human eye. additionally it can be helful to weight the components differently (see prev. post).