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
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:
The code is open source so feel free to take a look and compare.