My goal is to convert an RGB pixel into CIELab color space for some special calculations only possible in CIELab. For this, I must convert RGB to XYZ first, which is the really hard part.
I tried to implement this algorithm in Objective-C (mostly using plain C though), but the results are wrong.
My code is based on the pseudo-implementation provided by easyrgb.com. They have an online-color-converter which works great. They say that their pseudo-code is the same one used in their converter.
This is their Pseudo-Code:
var_R = ( R / 255 ) //R from 0 to 255
var_G = ( G / 255 ) //G from 0 to 255
var_B = ( B / 255 ) //B from 0 to 255
if ( var_R > 0.04045 ) var_R = ( ( var_R + 0.055 ) / 1.055 ) ^ 2.4
else var_R = var_R / 12.92
if ( var_G > 0.04045 ) var_G = ( ( var_G + 0.055 ) / 1.055 ) ^ 2.4
else var_G = var_G / 12.92
if ( var_B > 0.04045 ) var_B = ( ( var_B + 0.055 ) / 1.055 ) ^ 2.4
else var_B = var_B / 12.92
var_R = var_R * 100
var_G = var_G * 100
var_B = var_B * 100
//Observer. = 2°, Illuminant = D65
X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805
Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505
This is my attempt to implement it in Objective-C / C:
void convertRGBtoXYZ(NSInteger * inR, NSInteger * inG, NSInteger * inB, CGFloat * outX, CGFloat * outY, CGFloat * outZ) {
// http://www.easyrgb.com/index.php?X=MATH&H=02#text2
CGFloat var_R = (*inR / 255); //R from 0 to 255
CGFloat var_G = (*inG / 255); //G from 0 to 255
CGFloat var_B = (*inB / 255); //B from 0 to 255
if (var_R > 0.04045f) {
var_R = powf(( (var_R + 0.055f) / 1.055f), 2.4f);
} else {
var_R = var_R / 12.92f;
}
if (var_G > 0.04045) {
var_G = powf(( (var_G + 0.055f) / 1.055f), 2.4f);
} else {
var_G = var_G / 12.92f;
}
if (var_B > 0.04045f) {
var_B = powf(( (var_B + 0.055f) / 1.055f), 2.4f);
} else {
var_B = var_B / 12.92f;
}
var_R = var_R * 100;
var_G = var_G * 100;
var_B = var_B * 100;
//Observer. = 2°, Illuminant = D65
*outX = var_R * 0.4124f + var_G * 0.3576f + var_B * 0.1805f;
*outY = var_R * 0.2126f + var_G * 0.7152f + var_B * 0.0722f;
*outZ = var_R * 0.0193f + var_G * 0.1192f + var_B * 0.9505f;
}
However, I don't get the same results as their tool (with same Observer and Illuminant setting).
In my test, I entered these values into their tool and got this result for XYZ which is far off from what my implementation produces for that RGB value. Please see screenshot:
The resulting Lab color values are pretty close to what Photoshop tells me, so the converter works great.
The C-code above gives me this results though:
X = 35.76... // should be 42.282
Y = 71.52... // should be 74.129
Z = 11.92... // should be 46.262
Any idea what's the cause for this failure? Did I do a mistake in my implementation, or do I need other constants?
If you know some tested RGB to XYZ, XYZ to CIELab or RGB to CIELab, XYZ to Lab or RGB to Lab implementations, please don't hesitate to post them here.
Basically, all I want to do is calculate the deviation between two colors, also known as Delta-E. That's why I need to convert from RGB to XYZ to Lab (or CIELab)...
Linear RGB to XYZ For example, if your values are in the range [0, 255], you must first divide each by 255.0. The output XYZ values are in the nominal range [0.0, 1.0]. The XYZ values will be relative to the same reference white as the RGB system.
The XYZ coordinates for the lightest light is called the color space's white point . The XYZ coordinates for the reddest possible red, greenest possible green and bluest possible blue are called the color space's Red, Green, and Blue primaries .
lab = rgb2lab( RGB ) converts sRGB values to CIE 1976 L*a*b* values. lab = rgb2lab( RGB , Name,Value ) specifies additional conversion options, such as the color space of the RGB image, using one or more name-value pair arguments.
Color space conversion is the translation of the representation of a color from one basis to another. This typically occurs in the context of converting an image that is represented in one color space to another color space, the goal being to make the translated image look as similar as possible to the original.
I believe here is your problem, this is truncating to an integer:
CGFloat var_R = (*inR / 255); //R from 0 to 255
CGFloat var_G = (*inG / 255); //G from 0 to 255
CGFloat var_B = (*inB / 255); //B from 0 to 255
Try this:
CGFloat var_R = (*inR / 255.0f); //R from 0 to 255
CGFloat var_G = (*inG / 255.0f); //G from 0 to 255
CGFloat var_B = (*inB / 255.0f); //B from 0 to 255
I haven't checked the rest of the code for other problems.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With