introduction & setup

I want to play with five colors. Four of them are perfectly straight-forward: red, green, blue, and white. The fifth color, looking purple to me, is close to one of the colors on the Gretag Macbeth color checker; we’ll be seeing this color again in a subsequent post.

I have two software tools which came with my Mac: the DigitalColor Meter and the ColorSync Utility. If you are not on a Mac, perhaps you have, or can get, something similar.

I want to know the relationship between RGB and XYZ on my monitor. First I will establish that the relationship is not linear; then I will show how what it is and how I found it.

Let’s try finding a linear relationship by looking at the relationship between RGB and XYZ for my five chosen colors, but especially for red, green, and blue.

Here is a red disk, defined by RGB = (1,0,0). If you’re new to my blog, the following commands are Mathematica®.

I can ask the DigitalColorMeter for the RGB values of those red pixels… not surprisingly, I get 1, 0, 0. (Actually, 100, 0, 0.) Having confirmed this once, I won’t waste time on it again. (Well, one time more.)

More interesingly, I can ask it for the XYZ (tristimulus) values of those red pixels:

XYZred = {35.733, 20.605, 2.618}/100 = {0.35733, 0.20605, 0.02618}.

I can also ask for the 1931 xyY values:

xyYred = {0.606, 0.350, 20.605}.

We observe that Y is the same, as it should be… and we know how to compute xy from XYZ: take the XYZ, sum them, and then divide them by that sum (and round off).

The last two lines are not in perfect agreement, but close enough to be convincing. And, having confirmed this once, I won’t bother with it again.

Let’s do green.

On my monitor, the XYZ values are

45.224 69.995 12.091

and I will again divide by 100:

XYZgrn = {45.224, 69.995, 12.091}/100 = {0.45224, 0.69995, 0.12091}.

And blue?

The XYZ values are:

XYZblu={15.463, 9.399, 67.780}/100 = {0.15463, 0.09399, 0.6778}.

White?

The XYZ values are:

XYZwht = {96.420, 100.003, 82.489}/100 = {0.9642, 1.00003, 0.82489}.

And finally the mystery color… I specify the following RGB components:

RGB = {.5666, .3877, .6864}.

The DigitalColor Meter says that the tristimulus values are

XYZpur = {28.891, 24.930, 37.567}/100 = {0.28891, 0.2493, 0.37567}.

In this case I should confirm the RGB values — one last time because this isn’t one of the primary colors. The color meter says

RGBpur = {56.5, 38.8, 68.6}/100 = {0.565, 0.388, 0.686}.

They are not exactly what I specified; I’m not sure why. Just to be clear, the spec was:

RGB = {.5666, .3877, .6864}.

As far as I know, these are close enough.

trying a linear relationship

Considering just the red, green and blue disks, we have three pairs of components: RGB and XYZ.

We do not, however, have two sets of basis vectors. Instead, we have an active transformation that maps RGB to XYZ. Call it M. Its columns — the columns of any linear transformation — are the images of the basis vectors, so I can start by entering each XYZ set as a row…

MT={XYZred,XYZgrn,XYZblu}

$M^T = \left(\begin{array}{ccc} 0.35733 & 0.20605 & 0.02618 \\ 0.45224 & 0.69995 & 0.12091 \\ 0.15463 & 0.09399 & 0.6778\end{array}\right)$

… and then transpose to get them in columns:

$M = \left(\begin{array}{ccc} 0.35733 & 0.45224 & 0.15463 \\ 0.20605 & 0.69995 & 0.09399 \\ 0.02618 & 0.12091 & 0.6778\end{array}\right)$

Check one column: M applied to (1,0,0) should give me XYZred… and it does.

That is where it breaks down.

the relationship is not linear

The relationship between (color meter) RGB and XYZ is not linear. If it were, we’d have gotten the same answers. That is, M applied to RGB would have given us the XYZ we measured. It didn’t, so the linear transformation M does not describe the relationship. It correctly relates the RGB and XYZ values for the primaries red, green, blue, and for white — but it does not describe the relationship between RGB and XYZ for the purple color.

I’ve known this for a while, but I didn’t know what the true relationship was.

Let me just show you the relationship first. We take our RGB for purple:

RGBpur = {0.565, 0.388, 0.686}

raise each number to the 1.801:

$\rho \gamma \beta = \{0.357634,\ 0.181755,\ 0.507247\}$

Now apply the matrix M to the vector $\rho \gamma \beta\$, getting

XYZ = {0.288426, 0.248586, 0.375151}

and compare that to our measured XYZpur:

XYZpur = {0.28891, 0.2493, 0.37567}.

Pretty close.

Now, where did that relationship come from? Specifically, where did that power of 1.801 come from?

The clue I needed came from the “Digital Color Management” by Giorgianni & Madden (see my bibliographies page). Here’s a rough sketch based on one of their figures (page 40):

So all I have to do is find a curve that looks something like that. It helps that I know that the word “gamma” is usually associated with such a curve.

the ColorSync Utility

Now, and only now, do I run my ColorSync utility. (If you’re not on a Mac, you should have or get something that looks at ICC device profiles.)

Here’s the opening screen.

We see that it has a couple of menus for “profiles”, plus “devices”, “filters”, and a “calculator”.

Let’s open profiles.

We see “Color LCD” and that, I hope, is my monitor. I open it, and I find, among other things, a set of graphs, one of which is:

That is the red response curve; there are two other curves, for green and blue, and they look the same, and they have the same gamma = 1.801 .

That’s it. That’s how we get from color meter values of XYZ to RGB. I find it rather curious that the values are reported from different sides of the nonlinearity, but there we are. (If the color meter were reporting $\rho \gamma \beta\$, the relationship would be linear: XYZ = M.$\rho \gamma \beta\$ .)

I suppose I should write out the nonlinear transformation. In one step, it is:

XYZ = M.(RGB^1.801).

But the two steps corresponding to the sketch are:

$\rho \gamma \beta = \text{RGB}^{1.801}\$,

$XYZ = M.\rho \gamma \beta\$.

I should also remark that the reason M works is that the nonlinear part of the transformation has no effect on 1 or 0, so for red, green, blue, and white, we have

$\rho \gamma \beta = \text{RGB}\$,

and that’s how I found M. The nine values that I put into the M matrix are not only RGB but simultaneously $\rho \gamma \beta\$, because 0 or 1 raised to 1.801 is still 0 or 1, respectively.

In particular, if the transformation does not preserve 0 and 1 (i.e. if $\rho \gamma \beta \ne \text{RGB}\$ for the RGB basis), we would have wanted to construct M as a transformation between $\rho \gamma \beta\$ and XYZ.

I should be explicit. A more general transformation would have an offset:

$\rho \gamma \beta = k_1\ \text{RGB}^{\gamma} + k_2\$

and that would map 0 to $k_2\$ instead of to 0.

I could stop here, but once we’ve opened the ColorSync Utility, there’s at least one other thing we should look at right away. There are three things called “colorant tristimulus” and all three look like this one:

Those XYZ values should should look familiar; they are almost exactly the XYZ values I measured and saved in the matrix M; in fact, rounded to 3 places, the M matrix matches the displayed values.

$M = \left(\begin{array}{ccc} 0.35733 & 0.45224 & 0.15463 \\ 0.20605 & 0.69995 & 0.09399 \\ 0.02618 & 0.12091 & 0.6778\end{array}\right)$

Good: I could have gotten those numbers from the ColorSync Utility — but this way, I know exactly what they are, what they mean.

To be very clear: the ColorSync Utility gives me both the linear part (M) and the nonlinear part (that 1.801 power) of the transformation between the RGB and XYZ reported by the DigitalColor meter.

I had found the linear part myself by looking at the relationship for red, green, and blue.

You may be wondering why I have spent so much space showing the exact details of what I did. Well, by putting it all out here, I don’t have to go fishing through Mathematica® looking for the details, or trying to reconstruct a calculation. This really is a little too close to a physics lab for my comfort, so I increase my comfort by putting all the pieces together.

There are a couple of other things I want to point out in the ColorSync Utility, but they belong in a separate post.

The fundamental purpose of this post was

1. to demonstrate the nonlinearity (matrix M applied to purple RGB does not produce purple XYZ);
2. to find and verify the actual nonlinear transformation ($\text{RGB}^{1.801}\$).