Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's wrong with this RGB to XYZ color space conversion algorithm?

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:


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)...

like image 406
SecretService - not really Avatar asked Jul 08 '11 19:07

SecretService - not really


People also ask

How do I convert RGB to XYZ?

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.

What is XYZ in the XYZ color 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 .

How do you convert RGB to LAB color space?

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.

What are color space conversions used for?

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.


1 Answers

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.

like image 146
Vinicius Kamakura Avatar answered Sep 21 '22 15:09

Vinicius Kamakura