Conflicting LAB Results from RGB

96 views Asked by At

I'm at a bit of a crossroads with my code and to be honest, i'm not quite sure what's going on....It's probably quite obvious but I'm failing to see where the problem lies.

I'm trying to convert RGB to XYZ to Lab and so far, everything seems to be working ok with the R(X) and G(Y) sections, but I can't pinpoint the error for B(Z) and it's driving me nuts.

Here is my code to start with, I'm using 2 Degree D50

void SearchColour()
{ 
    double[] xyz = new double[3];
    double[] lab = new double[3];
    double[] rgb = new double[3];
    rgb[0] = searchColourr / 255.0d;
    rgb[1] = searchColourg / 255.0d;
    rgb[2] = searchColourb / 255.0d;
    if (rgb[0] > .04045d)
    {
        rgb[0] = Math.Pow((rgb[0] + .055d) / 1.055d, 2.4d);
    }
    else
    {
        rgb[0] = rgb[0] / 12.92d;
    }
    if (rgb[1] > .04045d)
    {
        rgb[1] = Math.Pow((rgb[1] + .055d) / 1.055d, 2.4d);
    }
    else
    {
        rgb[1] = rgb[1] / 12.92d;
    }
    if (rgb[2] > .04045d)
    {
        rgb[2] = Math.Pow((rgb[2] + .055d) / 1.055d, 2.4d);
    }
    else
    {
        rgb[2] = rgb[2] / 12.92d;
    }
    //D50 sRGB working space
    xyz[0] = ((rgb[0] * 0.4360747d) + (rgb[1] * 0.3850649d) + (rgb[2] * 0.1430804d));
    xyz[1] = ((rgb[0] * 0.2225045d) + (rgb[1] * 0.7168786d) + (rgb[2] * 0.0606169d));
    xyz[2] = ((rgb[0] * 0.0139322d) + (rgb[1] * 0.0971045d) + (rgb[2] * 0.7141733d));
    xyz[0] = xyz[0] / 0.9642d;
    xyz[1] = xyz[1] / 1d;
    xyz[2] = xyz[2] / 0.8252d;
    
    if (xyz[0] > .008856d)
    {
        xyz[0] = Math.Pow(xyz[0], 0.3333333333333333);
    }
    else
    {
        xyz[0] = (7.787d * xyz[0]) + (16 / 116);
    }
    if (xyz[1] > .008856d)
    {
        xyz[1] = Math.Pow(xyz[1], 0.3333333333333333);
    }
    else
    {
        xyz[1] = (7.787d * xyz[1]) + (16 / 116);
    }
    if (xyz[2] > .008856d)
    {
        xyz[2] = Math.Pow(xyz[2], 0.33333333333333);
    }
    else
    {
        xyz[2] = (7.787d * xyz[2]) + (16 / 116);
    }
    lab[0] = (116 * xyz[1]) - 16;
    lab[1] = 500 * (xyz[0] - xyz[1]);
    lab[2] = 200 * (xyz[1] - xyz[2]);

    searchColourResultl = lab[0];
    searchColourResulta = lab[1];
    searchColourResultb1 = lab[2];
}

To test, I used the RGB code 113,75,41 which gives me

35.699016618103713, 13.845077110631209, 22.692020016283632

which is incorrect as when I cross reference it with the NiX converter it gives me for the same RGB code

35.70, 13.84, 26.81

And if I input it to an Excel Spreadsheet it gives me

35.69902, 13.84508, 26.81392

The excel and C# code are pretty much similar in every respect, but I can't see where the error lies in the C# code.

Tried changing doubles to floats, made situation worse and sent the B value in LAB to -145

Adjusted the Y illuminent from 0.8251 to 0.8252, made no difference

2/12/2023

Ok, this get's weirder. I just adjusted it (by breaking each section up into different functions and it worked.

void BreakdownRGB()
    {
        r = searchColourr / 255.0d;
        g = searchColourg / 255.0d;
        b = searchColourb / 255.0d;
        if (r > .04045d)
        {
            r = Math.Pow((r + .055d) / 1.055d, 2.4d);
        }
        else
        {
            r = r / 12.92d;
        }
        if (g > .04045d)
        {
            g = Math.Pow((g + .055d) / 1.055d, 2.4d);
        }
        else
        {
            g = g / 12.92d;
        }
        if (b > .04045d)
        {
            b = Math.Pow((b + .055d) / 1.055d, 2.4d);
        }
        else
        {
            b = b / 12.92d;
        }
    }
    
    void RGBtoXYZ()
    {
        //D50 sRGB working space
        x = ((r * 0.4360747d) + (g * 0.3850649d) + (b * 0.1430804d));
        y = ((r * 0.2225045d) + (g * 0.7168786d) + (b * 0.0606169d));
        z = ((r * 0.0139322d) + (g * 0.0971045d) + (b * 0.7141733d));
        x = x / 0.9642d;
        y = y / 1d;
        z = z / 0.8252d;
        if (x > .008856d)
        {
            x = Math.Pow(x, 0.3333333333333333);
        }
        else
        {
            x = (7.787d * x) + (16 / 116);
        }
        if (y > .008856d)
        {
            y = Math.Pow(y, 0.3333333333333333);
        }
        else
        {
            y = (7.787d * y) + (16 / 116);
        }
        if (z > .008856d)
        {
            z = Math.Pow(z, 0.3333333333333333);
        }
        else
        {
            z = (7.787d * z) + (16 / 116);
        }
    }
    
    void XYZtoLAB()
    { 
        l = (116 * y) - 16;
        a = 500 * (x - y);
        b2 = 200 * (y - z);
    
        searchColourResultl = Math.Round(l,5);
        searchColourResulta = Math.Round(a, 5);
        searchColourResultb1 = Math.Round(b2, 5);
    }

However if I changed to a different number (57,37,18) it goes very strange

If I run it step by step, the error occurs when calculating xyz[2]. In Excel it gives a result of 0.201027329, but in C# (Unity) it gives a result of 0.06309629423128113, xyz[0] and xyz[1] are correct.

The RGB values c# give are

    R = 0.040915196906853191
    G = 0.018500220128379697
    B = 0.0060488330228570539

Which is confirmed on the excel sheet

If I change

    if (z > .008856d)

to

    if (z < .008856d)

it works, but doesn't work with other RGB codes

1

There are 1 answers

0
Wacton On

If you're happy to use a library, I've built one in .NET called Unicolour that can do this. It's well-tested and gives the expected result:

var config = new Configuration(RgbConfiguration.StandardRgb, XyzConfiguration.D50);
var colour = new Unicolour(config, ColourSpace.Rgb255, 113, 75, 41);
Console.WriteLine(colour.Lab); // 35.70 +13.84 +26.81

The code is open source so feel free to take a look and compare.