HDK: RGB to XYZ/LAB conversion

   7511   6   0
User Avatar
Member
51 posts
Joined: Oct. 2006
Offline
Does anyone have any practical experience with using UT_Color and can comment on its usefulness for color space conversions? Is it documented somewhere how UT_Color::getXYZ does its conversion, what are the primaries and what's the white point?
I did some tests and the closest match I found is the NTSC primaries and CIE Illuminant C - see the code snippet below. I'm certainly not a color expert, but some of the UT_Color's conversion methods doesn't seem very useful without this additional information.
For example, what's the proper way to use UT_Color::getLAB? Apparently this method doesn't use any reference white point at all - let's say when doing RGB->LAB, the method supposedly performs RGB->XYZ internally, and this implies the white point mentioned above, but when it just assumes white at XYZ(1,1,1).

The test below compares UT_Color's RGB->XYZ conversion results with “NTSC” and HDTV/Rec709/sRGB matrices.
“HDK” & “NTSC” values are very close, while “HDTV” results are quite different, so I presume that Houdini's internal matrix is based on the NTSC values (maybe sRGB would be a better choice these days?).
Knowing the white point makes getLAB somewhat more useful I think, that is, I can do RGB->XYZ, divide by wp, then XYZ->LAB.
Thanks in advance for any comments on this.
void testColor(float r, float g, float b) {
float hxyz;
UT_Color c = UT_Color(UT_RGB, r, g, b);
cout << c;

c.getXYZ(&hxyz, &hxyz, &hxyz);
cout << “ HDK: ” << UT_Vector3(hxyz, hxyz, hxyz) << endl;

static const UT_Matrix3 primNTSC(
0.67f, 0.33f, 0.0f, // Rxyz
0.21f, 0.71f, 0.08f, // Gxyz
0.14f, 0.08f, 0.78f // Bxyz
);
static const UT_Vector3 whiteNTSC(0.31f, 0.316f, 0.374f);
UT_Matrix3 CI = UT_Matrix3(primNTSC);
CI.invert();
UT_Vector3 J = rowVecMult(whiteNTSC * (1.0f/whiteNTSC), CI);
UT_Matrix3 T;
T.identity();
T.scale(J, J, J);
T *= primNTSC;
cout << “NTSC: ” << rowVecMult(UT_Vector3(r, g, b), T) << endl;

static const UT_Matrix3 T709(
0.412453f, 0.212671f, 0.019334f,
0.357580f, 0.715160f, 0.119193f,
0.180423f, 0.072169f, 0.950227f
);
cout << “HDTV: ” << rowVecMult(UT_Vector3(r, g, b), T709) << endl;
}
User Avatar
Member
7729 posts
Joined: July 2005
Online
You're fully correct, so I don't think UT_Color is useful for practical color space conversions. It's likely never been used for the XYZ/Lab conversions given such problems. This type of code shouldn't be replicated in every project anyhow, might as well use some well-known colour management library like LittleCMS [littlecms.com].
User Avatar
Member
51 posts
Joined: Oct. 2006
Offline
Thanks for the info!
In my case I need to perform these conversions on GPU in a real-time rendering system, so I was looking if I can use some of these HDK functions in my Houdini-side tools. But UT_Color seems too problematic indeed.
Well, HDK conversion matrix can be obtained like this:
float m;
UT_Color(UT_RGB, 1, 0, 0).getXYZ(&m, &m, &m);
UT_Color(UT_RGB, 0, 1, 0).getXYZ(&m, &m, &m);
UT_Color(UT_RGB, 0, 0, 1).getXYZ(&m, &m, &m);
cout << UT_Matrix3(m) << endl;

And, in principle, this matrix can be used for my needs, but it seems wrong to use it if it's not documented (it's slightly different from the one computed from NTSC values).

PS:
I think that more accurate RGB->LAB conversion can be done like this:
float xyzv;
float labv;
UT_Color(UT_RGB, 1, 1, 1).getXYZ(&xyzv, &xyzv, &xyzv);
UT_Vector3 xyzIN = UT_Vector3(1.0f/xyzv, 1.0f/xyzv, 1.0f/xyzv);
UT_Color(UT_RGB, r, g, b).getXYZ(&xyzv, &xyzv, &xyzv);
UT_Vector3 xyz = UT_Vector3(xyzv) * xyzIN;
UT_Color(UT_XYZ, xyz, xyz, xyz).getLAB(&labv, &labv, &labv);
UT_Color Lab = UT_Color(UT_LAB, labv, labv, labv);

For example, by default, RGB(1,1,1) is converted to LAB(100, -3.30344, -11.4058) - that's too far off center in , while the fragment above produces LAB(100, 0, 0).
User Avatar
Member
7729 posts
Joined: July 2005
Online
axebeak
I think that more accurate RGB->LAB conversion can be done like this

I don't think it's an accuracy issue as opposed to a correctness issue. Isn't the conversion out right wrong if it's not using the same reference white as the RGB -> XYZ conversion?
User Avatar
Member
7729 posts
Joined: July 2005
Online
axebeak
(it's slightly different from the one computed from NTSC values)

This is part is puzzling to me as well. I wonder if it could be due to (lack of) chromatic adaptation.
User Avatar
Member
51 posts
Joined: Oct. 2006
Offline
Oh yes - correctness is the right word here.
The conversion chain above is correct in the sense that it accounts for the white point in the original RGB->XYZ transformation. I just can't believe UT_Color was intended to be used this way!

Can't really comment on the matrix difference yet - after all maybe there is some imprecision in my own calculations. HDK matrix is probably precomputed or taken directly from some reference documentation (however I can't seem to find a precomputed NTSC/Ill.C matrix anywhere).
User Avatar
Member
7729 posts
Joined: July 2005
Online
It's probably worth logging a bug for it.
  • Quick Links