I have the following java code, which takes three double values (between 0 and 1) of the colors RGB and converts them to decimal format. I understood how the first 8 bit save color x, the second 8 bit color y ... and also how to get the decimal value from the resulting binary. what i dont understand 100% is why we multiply with 255 (i know 128+64+32+16+8+4+2+1). What exactly do we get from multiplying the double value with 255. is it a value which can be stored in 8 bit? And why dont we use 256 (possible amount of one color)?
public final double getR() {
return (1 - cyan);
}
public final double getG() {
return (1 - magenta);
}
public final double getB() {
return (1 - yellow);
}
/**
* Gets the rgb color in one integer.
*
* #return an integer containing the red component in bits 16-23, the green component in bits 8-15
* and the blue component in bits 0-7. Bits 24-32 are zero.
*/
public int getRGB() {
int r = (int) Math.round(getB() * 255);
r |= (int) Math.round(getR() * 255) << 16;
r |= (int) Math.round(getG() * 255) << 8;
return r;
}
Thanks
You need the conversion from double because you cannot store the double value into 8 bit.
Your double values are between 0.0 and 1.0. You can see them as the proportion of color used (e.g. 0.33333333 in yellow means that one-third of the possible yellow is used). As you can see such a double can have many decimal places, which means we need a lot of memory (64-bit) to store such a color.
Your function now tries to store the double value into only 8 bit (256 values). As we said the double value can be seen as a portion (between 0 and 1) and the function calculates the same for 8 bit (between 0 and 255). This can simply be done by multiplying the double value with 255. For the example with yellow (0.33333333 of yellow is used) it is: 0.33333333 * 255 = 84,99999915. The meaning is still the same 84,99999915 yellow parts of 255 yellow parts are used, which is still a third.
In order to get a compressed version of this number, it is rounded to the next integer value. In our example, this is 85, which is really close to the actual portion, but we save a lot of memory.
It makes also sense for the lowest double value 0.0, which is converted to the lowest int value 0. The highest double value 1.0 is converted to 255 (highest 8-bit number).
In conclusion, we convert a double (64-bit) into an only 8-bit number, which has the same proportion of the color, but it is not as accurate.
Edit: As there is also a confusion with the 255: 8 bit can store 256 values (0 to 255). If you can choose 256 as a color value somewhere they use the range 1-256 without 0. Essentially it is the same one shifted by 1.
Related
I'm trying to convert a rgb color to hsl in Java, i have searched for many codes that explain how you convert rgb to hsl, i now have saturation and lightness working, but the hue value is incorrect
I am now trying to convert rgb to hsl and then back.
the rgb values i am using are
red: 54
green: 43
blue: 21
The hsl values i get are
hue: 260
saturation: 44
lightness: 15
I tried to convert the rgb values to hsl at https://www.rapidtables.com/convert/color/rgb-to-hsl.html
The values i get there are
hue: 40
saturation: 44.0
lightness: 14.7
Does anyone know what i'm doing wrong in converting rgb to hsl?
Here is my code
public static Map<String, Integer> rgbToHsl(Integer red, Integer green, Integer blue){
Float redDouble = ((float)red) / 255.0f;
Float greenDouble = ((float)green) / 255.0f;
Float blueDouble = ((float)blue) / 255.0f;
Float max = Math.max(Math.max(redDouble, greenDouble), blueDouble);
Float min = Math.min(Math.min(redDouble, greenDouble), blueDouble);
Float chroma = max - min;
Float hue = chroma == 0.0f ? 0.0f :
(max == redDouble ? (greenDouble - blueDouble) / chroma :
(max == greenDouble ? 2f + (blueDouble - redDouble) / chroma :
4f + (redDouble - greenDouble) / chroma));
Float lightness = (max + min) * 0.5f;
Float saturation = chroma == 0.0f ? 0.0f : (lightness > 0.5f ? chroma / (2.0f - max - min) : chroma / (max + min));
return Map.ofEntries(
Map.entry("hue", (int) Math.round(hue * 60)),
Map.entry("saturation", (int) Math.round(saturation * 100)),
Map.entry("lightness", (int) Math.round(lightness * 100))
);
}
When you use boxed Floats everywhere, the Math.max(Math.max(a, b), c) will unbox the arguments a, b and c, then perform the computation, then box them back into a Float.
The result will be a new object, unequal to all three a, b and c.
Therefore, the identity comparisons max == redDouble and max == greenDouble will always be false.
Eliminate the boxed types, use floats everywhere, it's both faster and clearer.
Even better: never use either == or equals on any kind of floating-point values. See, for example, how here additional boolean flags were used. Booleans are not susceptible to tiny rounding errors.
For the hue value if red is the maximum, you forgot to convert it by modulus 6.
According to what IntelliJ is showing me, it doesn't believe max == redDouble even though their printed values look identical. So, your nested logic for the hue is calculating the wrong part. I would suggest you write some logic to figure out whether you're looking for white, red, green or blue as a string, then use a switch block with the new colour string as your trigger to decide which value to return. This will be longer, but probably more readable as well. Although I am a fan of the ternary operator, nesting them can be muddling.
i have a code like this :
String hex = String.format("0x%02x%02x%02x", r * 0.5, green * 0.6, blue * 0.7));
0.5 and 0.6 and 0.7 are variables
and i want to set background color of a view from variable hex :
v.setBackgroundColor(Integer.parseInt(hex, 16));
When i try to convert it to Hexadecimal integer it throws exceptions like
java.lang.NumberFormatException
how can i do this?
0x causes NumberFormatException. try Integer.decode instead:
v.setBackgroundColor(Integer.decode(hex));
Read:
https://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html#decode(java.lang.String)
There are potentially three problems with this code:
As #shiftpsh notes: Integer.parseInt does not understand the 0x prefix. There are two ways to solve this:
Use Integer.decode rather that Integer.parseInt.
Don't include the 0x prefix in the format.
The second potential problem is that %02x means hexadecimal, zero-padded with a minimum of two digits. But if any of r, green or blue were large enough, the resulting color value would be greater than 255, and you would get more than 2 hex digits. That would result in an incorrect RGB value when the string is decoded.
I am rather surprised that %02x works for an argument like r * 0.5. The latter is a double and according to my reading of the javadoc the x conversion is not applicable.
However, if the sole point of this code is to create and RGB value from r, green and blue, there is a significantly more efficient way to do it.
int r0 = ((int)(r * 0.5)) & 0xff;
int g0 = ((int)(green * 0.6)) & 0xff;
int b0 = ((int)(blue * 0.7)) & 0xff;
v.setBackgroundColor((r0 << 16) | (g0 << 8) | b0);
Alternatively, using android.graphics.Color.argb to do the RGB conversion should be just as efficient .... though that is an Android API rather than a Java API. (I could not find a directly equivalent Java SE library method that is as efficient.)
I want to take a range of float values (subject) that change and map them on to a static range of colours 0 - 255 for displaying on the screen.
The subject range of values start with the float being a minimum of 0.056582272 to the largest 0.34371486
when running calculation changes overtime to a minimum of 0.0791025 to the largest 4.5757337
If I map them using
number / 255 * largest
which in code is
thecol=elements[x][y][subject]*255/largest;
You see the colours at the beginning when I pop them on the screen, then at the end but nothing in between.
You did not factor in the minimum of the range for your calculation.
float rangeSize = largest - smallest;
float mappedTo0to1 = (value - smallest) / rangeSize; // Map to range 0 - 1.
int mappedResult = (int) (mappedTo0to1 * 255); // Map to range you want. (0 - 255).
The trick with verifying these kind of functions is to look at the lowest and highest input values possible.
I was just trying to convert the following methods that I wrote in C/C++ to Java. In short, the code provides a very efficient way of calculating the indices of the left-most and right-most bits of a number that are set to one. The two methods are based off of code in Knuth's Art of Computer programming, volume 4.
// Returns index of the left-most bit of x that is one in the binary
// expansion of x. Assumes x > 0 since otherwise lambda(x) is undefined.
// Can be used to calculate floor(log(x, 2)), the number of binary digits
// of x, minus one.
int lambda(unsigned long x) {
double y = (double) x;
// Excuse the monstrocity below. I need to have a long that has the raw
// bits of x in data. Simply (long)y would yield x back since C would cast
// the double to a long. So we need to cast it to a (void *) so that C
// "forgets" what kind of data we are dealing with, and then cast it to
// long.
unsigned long xx = *((long *)((void*)&y));
// The first 52 bits are the the significant. The rest are the sign and
// exponent. Since the number is assumed to be positive, we don't have to
// worry about the sign bit being 1 and can simply extract the exponent by
// shifting right 52 bits. The exponent is in "excess-1023" format so we
// must subtract 1023 after.
return (int)(xx >> 52) - 1023;
}
// Returns the index of the right-most one bit in the binary expansion of x
int rho(unsigned long x) {
return lambda(x & -x);
}
As you can see, I need to have a long that has the same bits of a double, but without a void* cast, I am not sure how to do this in Java. Any thoughts? Is it even possible?
There's a static function, doubleToLongBits(), to perform the type conversion.
long xx = Double.doubleToLongBits(y);
return (int) (xx >>> 52) - 1023;
Note the >>> treats the long as an unsigned value when shifting right.
Reading the commentary, though, it sounds like what you want is a simple function of the number of leading zeros.
return 63 - Long.numberOfLeadingZeros(x);
I would guess this is more efficient on most current architectures, but you'd have to profile it to be sure. There's a similar "trailing zeros" method to compute your rho() function.
I'm confused with converting the RGB values to YCbCr color scheme. I used this equation:
int R, G, b;
double Y = 0.229 * R + 0.587 * G + 0.144 * B;
double Cb = -0.168 * R - 0.3313 * G + 0.5 * B + 128;
double Cr = 0.5 * R - 0.4187 * G - 0.0813 * B + 128;
The expected output of YCbCr is normalized between 0-255, I'm confused because one of my source says it is normalized within the range of 0-1.
And it is going well, But I am having problem when getting the LipMap to isolate/detect the lips of the face, I implemented this:
double LipMap = Cr*Cr*(Cr*Cr-n*(Cr/Cb))*(Cr*Cr-n*(Cr/Cb));
n returns 0-255, the equation for n is: n=0.95*(summation(Cr*Cr)/summation(Cr/Cb))
but another sources says: n = 0.95*(((1/k)*summation(Cr*Cr))/((1/k)*summation(Cr/Cb)))
where k is equal to the number of pixels in the face image.
It say's from my sources that it will return a result of 0-255, but in my program it always returns large numbers always, not even giving me 0-255.
So can anyone help me implement this and solve my problem?
From the sources you linked in your comments, it looks like either the equations or the descriptions in the first source are wrong:
If you use RGB values in the Range [0,255] and the given conversion (your Cb conversion differs from that btw.) you should get Cr and Cb values in the same range.
Now if you calculate n = 0.95 * (ΣCr2/Σ(Cr/Cb)) you'll notice that the values for Cr2 range from [0,65025] whereas Cr/Cb is in the range [0,255] (assuming Cb=0 is not possible and thus the highest value would be 255/1 = 255).
If you further assume an image with quite high red and low blue components, you'll get way higher values for n than what is stated in that paper:
Constant η fits final value in range 0..255
The second paper states this, which makes much more sense IMHO (although I don't know whether they normalize Cr and Cb to range [0,1] before the calculation or if they normalize the result which might result in a higher difference between Cr2 and Cr/Cb):
Where (Cr) 2,(Cr/Cb) all are normalized to the
range [0 1].
Note that in order to normalize Cr and Cb to range [0,1] you'd either need to divide the result of your equations by 255 or simply use RGB in range [0,1] and add 0.5 instead of 128:
//assumes RGB are in range [0,1]
double Cb = -0.168 * R - 0.3313 * G + 0.5 * B + 0.5;
double Cr = 0.5 * R - 0.4187 * G - 0.0813 * B + 0.5;