Color: from spectrum to tristimulus

Introduction

Edit 30 Oct 2009:
This is the first post about an example on p. 160 of Wyszecki & Stiles (see the bibliography).

and find another edit about the components wrt the dual basis.
End edit.

As always, if my chatter is confusing, just start following the examples.

In a sense, what I am about to do now is one of the most important color calculations I will show you.

How do we get from a spectrum to tristimulus values?

I am going to get from a spectrum to XYZ values, and then to xyz values. Recall that XYZ come from xyz bar tables, which in turn come from rbar, gbar, bbar tables by a change of basis. Then xyz values come from XYZ by making the sum = 1.

For more about the CIE, see the previous post.

The question before us is to get from a spectrum to XYZ (and then to xyz).

What we are talking about is the perception of color, rather than the reproduction. Sort of. We wil use “the standard observer” rather than my eyes or your eyes, but we are going to translate an arbitrary spectrum into 3 scalar values, which can then be transformed to some RGB values in a color space. But not in this post.

This example comes from w&s (Wyszecki & Stiles, in the bibliography), page 160. Believe it or not, I am going to work it four ways — but I will give you the full details for only two of them, pictures for all four of them.

The original problem uses data at 10 nm intervals, and it uses the 1964 CIE. Working this example as they did showed me that there were 3 typos in their solution.

First, however, I will work the problem using the data at 20 nm intervals, with the 1964 CIE. This will show you explicitly how the calculations proceed. At 20 nm intervals, I can display the data itself.

Second, I will work the original problem as they posed it. I will not get their answers, so I will also show you their typos.

Third and fourth, I will again work the problem at both 10 nm and 20 nm intervals, but using the 1931 CIE. As will be my ongoing custom, I will show you all the numbers only for the 20 nm case. Ultimately, what choice we make isn’t going to matter a lot. And that’s worth knowing.

We are given a reflectance spectrum for a plastic object illuminated by daylight. In fact, we will use “standard illuminant D65” for “daylight”. The “reflectance spectrum” is the reflection from a perfectly uniform — perfectly equal energy — illuminant.

Let me say that another way. We are going to assume that the object is illuminated by one spectrum (“daylight” D65) and that the reflected spectrum which we perceive is the combination of the D65 spectrum and the so-called reflectance spectrum of the object.

The reflectance spectrum is a property of the object; by combining it with any arbitrary illumination spectrum, we get the reflected spectrum.

We are asked, in the given example, to find the XYZ tristimulus values, and the xy chromaticity coordinates, using the 1964 CIE color matching functions. That is, we will use the 1964 xyzbar tables.

(I didn’t plan to show this to you, but prudence dictated that I at least work it out. When it turned out that they had slightly wrong answers… well, I have to show it to you.)

Here’s what we will do. We will find the reflected spectrum, i.e. what is reflected from the object illuminated by D65 rather than by an equal energy illumninant. Then we will apply the xyzbar color matching functions to the reflected spectrum to get XYZ tristimulus values. Then we will get the normalized xyz values.

For more about the color matching functions, our matrices A for each problem, see the first Cohen post, and the second Cohen post.

CIE 1964 at 20 nm

Here is the reflectance spectrum \beta\ , from 380 to 780 at 20 nm intervals. As will be my custom, I am taking a subset of their values — I am not averaging the numbers. That is, their example provides data every 10 nm, but I am using half of that given data. For one thing, as I said, that gives us matrices of manageable size.

\beta = \begin{array}{c} 0.102 \\ 0.348 \\ 0.46 \\ 0.475 \\ 0.462 \\ 0.454 \\ 0.478 \\ 0.564 \\ 0.663 \\ 0.691 \\ 0.693 \\ 0.79 \\ 0.845 \\ 0.853 \\ 0.853 \\ 0.853 \\ 0.852 \\ 0.849 \\ 0.79 \\ 0.712 \\ 0.625\end{array}

Here’s a picture of that reflectance spectrum:

p64201

A word about scaling. It is appropriate that the reflectance spectrum be less than, or on the order of magnitude of, 1. At any given frequency, the amount reflected can range from significantly less to significantly more (since light absorbed at one frequency can be emitted at another) than the incident spectrum — but we dont want to be multiplying by a hundred. That would be bad.

Here is the D65 spectrum, from 380 to 780 nm at 20 nm intervals:

D65 = \begin{array}{c} 50. \\ 82.8 \\ 93.4 \\ 104.9 \\ 117.8 \\ 115.9 \\ 109.4 \\ 104.8 \\ 104.4 \\ 100. \\ 95.8 \\ 90. \\ 87.7 \\ 83.7 \\ 82.2 \\ 78.3 \\ 71.6 \\ 61.6 \\ 75.1 \\ 46.4 \\ 63.4\end{array}

Another word about scaling. The D65 spectrum ranges from 50 to more than 100. I have no idea what units it is measured in — but the reflected spectrum will be in the same units, whatever they are.

Here is a picture of that illumninant spectrum:

p64202

As I said, the scale is 100 times that of the reflectance spectrum. This doesn’t matter for the computations. If, however, I want to show both of these on the same axes, I will need to scale these values — but for the graph only, not for the computations.

Here are the 1964 xyzbar tables from 380 to 780 nm, at 20 nm intervals. This is the A matrix for this problem.

A = \left(\begin{array}{ccc} 0.0002 & 0. & 0.0007 \\ 0.0191 & 0.002 & 0.086 \\ 0.2045 & 0.0214 & 0.9725 \\ 0.3837 & 0.0621 & 1.9673 \\ 0.3023 & 0.1282 & 1.7454 \\ 0.0805 & 0.2536 & 0.7721 \\ 0.0038 & 0.4608 & 0.2185 \\ 0.1177 & 0.7618 & 0.0607 \\ 0.3768 & 0.962 & 0.0137 \\ 0.7052 & 0.9973 & 0. \\ 1.0142 & 0.8689 & 0. \\ 1.124 & 0.6583 & 0. \\ 0.8563 & 0.3981 & 0. \\ 0.4316 & 0.1798 & 0. \\ 0.1526 & 0.0603 & 0. \\ 0.0409 & 0.0159 & 0. \\ 0.0096 & 0.0037 & 0. \\ 0.0022 & 0.0008 & 0. \\ 0.0005 & 0.0002 & 0. \\ 0.0001 & 0. & 0. \\ 0. & 0. & 0.\end{array}\right)

And here is a picture of the color matching functions:

p64203

Those are our ingredients: the reflectance spectrum, the illiminant spectrum, and the color matching functions.

Now, let’s get the reflected spectrum. We multiply the values pointwise… i.e. at every wavelength, what is actually reflected is the product of the reflectance spectrum and the illuminant spectrum.

(Perhaps I should be calling this the perceived spectrum, since it is what our standard observer is seeing. I should not call it the incident spectrum, because that would be a sensible alternative to “the illimination spectrum”, in this case D65. Incident illumination on an object gives us a reflected spectrum, which our standard observer perceives.)

The result of multiplying the reflectance spectrum \beta and the illuminant spectrum D65 is

S = \begin{array}{c} 5.1 \\ 28.8144 \\ 42.964 \\ 49.8275 \\ 54.4236 \\ 52.6186 \\ 52.2932 \\ 59.1072 \\ 69.2172 \\ 69.1 \\ 66.3894 \\ 71.1 \\ 74.1065 \\ 71.3961 \\ 70.1166 \\ 66.7899 \\ 61.0032 \\ 52.2984 \\ 59.329 \\ 33.0368 \\ 39.625\end{array}

and the 3 spectra look like this (after I divide the D65 and S by 100, otherwise the reflectance spectrum is effectively the x-axis):

p64204

Let’s be clear: the reflectance spectrum is in green, the illuminant spectrum is in blue, and the product, at each wavelength, is the reflected spectrum, in black.

Before we deal with the reflected spectrum, however, I want to deal with the illumination itself. If we apply the color functions to the D65 spectrum, that is we compute

A^T\ D65\ ,

we get

{554.645, 582.565, 631.147}.

Are those our tristimulus values? No way. These numbers depend on the scale of the spectrum, and the number of components of the vectors. Somehow we have to normalize them, and we will do that for the total energy received.

As an aside, which I will try to remember to repeat in subsequent posts…. We have spectra and color-matching functions which live in vector spaces. The result of applying color matching functions to spectra is a set of 3 scalars, i.e. 3 real numbers. But our vector spaces are not bounded spaces, so those 3 numbers are not, in principle, bounded. They are simply the components of a spectrum wrt a basis. Edit: oops. Careful! They are components of the fundamental of the spectrum. End edit. OTOH, we have color spaces, such as RGB, in which the RGB values are bounded (whether between 0 and 1, or 0 and 100, or 0 and 255, they are limited).

We need to move from the unbounded results of vector operators to the bounded values of color spaces.

But how do we do that?

Well. Recall that ybar, the second column of A, is the photopic response (the total response of the cones) of the standard observer. This is incredibly useful: the second column of AT tells us the total energy perceived. If we were doing calculus, we would compute the areas under all three curves. But we’re doing linear algebra, and the appropriate number is the middle one.

We get XYZ values for the illumnination perceived by the observer by dividing those 3 numbers by the middle number, 582.565:

XYZ = {0.952075, 1., 1.08339}.

Let’s keep going with this. How do we get xyz coordinates from XYZ?

Normalize the sum of the XYZ to 1. The sum is 3.03547 … so we get that x,y,z are

xyz = {0.31365, 0.329438, 0.356911}.

Since all three values are close to 1/3, this is a plausible number for illumination.

Now we can do the same thing with the reflected spectrum. That is, we apply the matrix A^T to the reflected spectrum S:

{386.796, 381.417, 293.87}.

We are not going to divide by this middle number — this is the reflected spectrum, not the illimination spectrum. We need to divide by the same number as before, i.e. by the photopic response to the illumination, not the photopic response to the reflection. That number was 582.565, and now we divide our 3 values by that to get X,Y,Z…

XYZ = {0.663953, 0.654719, 0.504441}.

And, as before, we compute xyz values, too: we normalze the XYZ so that their sum is 1. Since the sum is 1.82311 … we get

xyz = {0.364186,0.359122,0.276692}.

This isn’t all that far from the illuminant.

p64205

Those dots are the outline of the 1964 chromaticity diagram. I got it the same way as the 1931, just with different data. Get the xyz tables — the ones where the rows add up to 1 — and just plot y versus x.

The red, green, and blue dots show the pure primaries for the 1964 standard. The white dot is (x,y) for D65, and the yellow dot is (x,y) for the reflected sprectrum. (Oh, I don’t think the reflection is yellow, but it’s rather closer to the light source than to much else.)

CIE 1964 at 10 nm

Now let me show you the pictures, and the answers, for 10 nm intervals. In fact, let me juxtapose the new pictures (on the right) with the old (on the left):

Here are the old and new reflectance spectra:

p64101

Here are the D65 spectra:

p64102

And here are the color matching functions (xyzbar tables) from 380 to 780 nm The data on the right is the A matrix for this problem.
p64103

We have the same ingredients: the reflectance spectrum, the illiminant spectrum, and the color matching functions.

Now, let’s get the reflected spectrum. We multiply the values pointwise… i.e. at every wavelength, what is actually reflected is the product of the reflectance spectrum and the illuminant spectrum.

The result of multiplying \beta and D65 together looks like this (after I divide the D65 and S by 100, otherwise the reflectance spectrum is effectively the x-axis):

p64104

As before, the reflectance spectrum is in green, the illuminant spectrum is in blue, and the product, at each wavelength, is the reflected spectrum, in black.

First, we apply the color functions to the D65 spectrum, that is we compute

A^T D65\ ,

and we get

{1102.11, 1162.02, 1247.78}.

Here we see quite clearly that these numbers depend on the scale of the spectrum and the number of values: they are about double what the previous values were. Oh, of course: our vectors are twice as long. (Even though the XYZ and xyz values will be comparable, these initial 3 values will not always be comparable. As I said, they depend on the dimension of our spectra and on the scaling of the illuminant spectrum.)

Once again, we need to scale those numbers, dividing each of them by the middle number, which is the total perceived illumination.

So we get XYZ values for the illumninant by dividing those 3 numbers by the middle number, 1162.02:

XYZ = {0.948445, 1., 1.0738}.

How do we get xy coordinates? Normalize the sum to 1. The sum is 3.02225 … so we get

xyz = {0.313821, 0.330879, 0.3553}.

As before, this is a plausible number for illumination.

Now we do the same thing with the reflected spectrum. That is, we apply the matrix A^T to the reflected spectrum S:

{769.263, 761.189, 581.056}.

Once again, we divide by the same number as before, i.e. by the photopic response to the illumination, not to the reflection. (That number was 1162.02.)

Now we divide our 3 values by that to get X,Y,Z…

XYZ = {0.662005, 0.655057, 0.50004}.

And, as before, we compute xy values, too: we normalze the XYZ so that their sum is 1. Since the sum is 1.8171 … we get

xyz = {0.364319, 0.360495, 0.275185}.

p64105

You can’t tell much from these two pictures; the final picture in this post will plot (almost) all of the xy answers. The numbers using 20 nm intervals were not much different: for the XYZ values of the reflected spectrum, we had:

XYZ = {0.663953, 0.654719, 0.504441}.

The corresponding answers using 10 nm intervals were:

XYZ = {0.662005, 0.655057, 0.50004}.

Pretty darn close. 10 nm or 20 nm, both using CIE 1964, not a whole lot of difference.

Now, what were their answers? They got sums of

{758.6, 760.8, 580.8}

… while I had

{769.263, 761.189, 581.056}.

We do not agree. What’s going on? I found that there is a problem in their work at 640 nm.

For a wavelength of 640 nm, we agree on the values of all the ingredients. We agree that the reflectance spectrum value is 0.853 … the D65 illuminant spectrum value is 83.7 … the values of that row of A are {0.4316, 0.1798, 0.}.

The pointwise products of these numbers are…

{30.8146, 12.837, 0.}

but what they show is

30.2 12.6 0.

Two typos. I have no idea why. And they appear to be the only typos pertaining to individual wavelengths. Of course, it is possible that the error is in the reflectance spectrum instead, but I have no other data-source to compare that to. In any event, it is clear that their entries (30.2 and 12.6) are not in fact the product of the inputs they show.

There’s another problem. I pasted the column that leads to the X-column-sum into a spreadsheet and computed the sum. For the data they have shown, the first sum is really 768.6 rather than 758.6 . That’s the third typo.

(In addition, they round off at each wavelength, before adding the columns: this leads to some round-off error. That’s why we differ on the sum that leads to Z; no typos there, they just rounded before adding.)

But, where they show X,Y,Z as (change of scale, decimals versus percentages; ignore it) …

XYZ = {65.3, 65.5, 50.}

the correct answers are

XYZ = {66.2005, 65.5057, 50.004}

and even using 20 nm intervals instead of 10, I got

XYZ = {66.3953, 65.4719, 50.4441}.

For x,y, they show

xy = {0.3612, 0.3623}

but correct (at 10 nm) is

xy = {0.364319, 0.360495}.

Just working at half the resolution (20 nm), I got

xy = {0.364186, 0.359122}.

I daresay their mistakes are worse than my cutting the resolution in half.

CIE 1931 10 nm

To switch from 1964 at 10 nm to 1931 at 10 nm requires only that I change the A matrix. (I have everything else in memory at 10 nm intervals. The reflectance, reflected, and D65 spectra do not change.)

Let me show 1931 on the right, for comparison with the 1964 color matching functions on the left. Note that the vertical scales are different.

p31101

Although they have not changed, I show again the reflectance spectrum and the illuminant spectrum for this case:

p31102

Go ahead, curse me out. After showing different cases side-by-side, I have just switched and shown side-by-side graphs for two different things for one and the same case.

Those are our ingredients: the reflectance spectrum, the illiminant spectrum, and the color matching functions. Only the color functions have changed, from 1964 to 1931.

Now, the pointwise product of the illuminant and reflectance spectra will be exactly the same reflected spectrum.

p31103

Let’s be clear: the reflectance spectrum is in green, the illumninant spectrum is in blue, and the product, at each wavelength, is the reflected spectrum — in black.

As before, we apply the color matching functions to the D65 spectrum, that is we compute

A^T\  D65\ ,

getting

{1004.46, 1056.89, 1150.02}.

These numbers are comparable to the previous case: we are using vectors of the same dimension.

Once again, we need to scale those numbers, dividing each of them by the middle number, which is the total perceived illumination.

So we get XYZ values or the illumninant by dividing those 3 numbers by the middle number:

XYZ = {0.950398, 1., 1.08812}.

To get xy coordinates, we normalize the sum to 1. The sum is 3.03852 … so we get

xyz = {0.312783, 0.329108, 0.358109}.

Now we do the same thing with the reflected spectrum. That is, we apply the matrix A^T to the reflected spectrum S:

{707.724, 707.564, 537.101}.

Once again, we divide by the same number as before, i.e. by the photopic response to the illumination, not to the reflection. That number was 1056.89 .

So we divide our 3 values by that to get X,Y,Z…

XYZ = {0.669631, 0.669479, 0.508191}.

And, as before, we compute xy values, too: we normalze the XYZ so that their sum is 1. Since the sum is 1.8473 … we get xyz as

xyz = {0.362491, 0.362409, 0.275099}.

Again, this isn’t all that far from the illuminant. We’re getting very similar answers.

p31104

The CIE boundaries look pretty similar, but the triangles — the gamut of the 3 primaries — are different. And recall that the line x + y = 1 is tangent to the right-side boundary; furthermore, for the 1931 CIE, one side of the triangle is very, very close to the tangent line.

CIE 1931 20 nm

Having shown you pictures with the 1931 CIE and 10 nm intervals, let me close by showing you the numerical data, as well as pictures, for the 1931 CIE 20 nm solution.

To switch to 20 nm intervals, I need to rebuild the color matching functions. Here’s the data:

A = \left(\begin{array}{ccc} 0.0014 & 0. & 0.0065 \\ 0.0143 & 0.0004 & 0.0679 \\ 0.1344 & 0.004 & 0.6456 \\ 0.3483 & 0.023 & 1.7471 \\ 0.2908 & 0.06 & 1.6692 \\ 0.0956 & 0.139 & 0.813 \\ 0.0049 & 0.323 & 0.272 \\ 0.0633 & 0.71 & 0.0782 \\ 0.2904 & 0.954 & 0.0203 \\ 0.5945 & 0.995 & 0.0039 \\ 0.9163 & 0.87 & 0.0017 \\ 1.0622 & 0.631 & 0.0008 \\ 0.8544 & 0.381 & 0.0002 \\ 0.4479 & 0.175 & 0. \\ 0.1649 & 0.061 & 0. \\ 0.0468 & 0.017 & 0. \\ 0.0114 & 0.0041 & 0. \\ 0.0029 & 0.001 & 0. \\ 0.0007 & 0.0003 & 0. \\ 0.0002 & 0.0001 & 0. \\ 0. & 0. & 0.\end{array}\right)

and here are the graphs (20 nm and 10 nm side by side):

p31201

I need to restore the reflectance spectrum \beta and the illuminant spectrum. As you recall, they were

\beta = \begin{array}{c} 0.102 \\ 0.348 \\ 0.46 \\ 0.475 \\ 0.462 \\ 0.454 \\ 0.478 \\ 0.564 \\ 0.663 \\ 0.691 \\ 0.693 \\ 0.79 \\ 0.845 \\ 0.853 \\ 0.853 \\ 0.853 \\ 0.852 \\ 0.849 \\ 0.79 \\ 0.712 \\ 0.625\end{array}

and

D65 = \begin{array}{c} 50. \\ 82.8 \\ 93.4 \\ 104.9 \\ 117.8 \\ 115.9 \\ 109.4 \\ 104.8 \\ 104.4 \\ 100. \\ 95.8 \\ 90. \\ 87.7 \\ 83.7 \\ 82.2 \\ 78.3 \\ 71.6 \\ 61.6 \\ 75.1 \\ 46.4 \\ 63.4\end{array}

p31202

The reflected spectrum was the pointwise product of those, namely

S = \begin{array}{c} 5.1 \\ 28.8144 \\ 42.964 \\ 49.8275 \\ 54.4236 \\ 52.6186 \\ 52.2932 \\ 59.1072 \\ 69.2172 \\ 69.1 \\ 66.3894 \\ 71.1 \\ 74.1065 \\ 71.3961 \\ 70.1166 \\ 66.7899 \\ 61.0032 \\ 52.2984 \\ 59.329 \\ 33.0368 \\ 39.625\end{array}

Those are our ingredients: the reflectance spectrum, the illuminant spectrum, and the color matching functions. Only the color functions have changed, from 1964 to 1931.

The product of illumninant spectrum and reflectance spectrum is the same as shown before for 20 nm intervals:

p64204

As before, the reflectance spectrum is in green, the illumninant spectrum is in blue, and the product, at each wavelength, is the reflected spectrum — in black.

As before, we apply the color matching functions to the D65 spectrum, that is we compute

A^T\ D65\ ,

getting

{506.693, 529.764, 581.089}.

These numbers are smaller than the previous case: we are using vectors of half the dimension.

Once again, we need to scale those numbers, dividing each of them by their middle number, which is the total perceived illumination.

So we get XYZ values or the illumninant by dividing those 3 numbers by the middle number, getting:

XYZ = {0.956451, 1., 1.09688}.

We get xy coordinates by normalizing the sum to 1. The sum is 3.05333 … so we get

xyz = {0.313248, 0.327511, 0.359241}.

Now we do the same thing with the reflected spectrum. That is, we apply the matrix A^T to the reflected spectrum S, getting:

{356.817,354.641,271.109}.

Once again, we divide by the same number as before, i.e. by the photopic response to the illumination, not to the reflection. That number was 529.764, so we get XYZ are

XYZ = {0.673539, 0.669432, 0.511754}.

And, as before, we normalze the XYZ so that their sum is 1. Since the sum is 1.85472 … we get

xyz = {0.363148 ,0.360933, 0.275919}.

p31203

OK, let’s compare.

Here are the unscaled sums (including their incorrect answers). Reading down, these are my 1964 at 20, then at 10, their answers, my 1931 at 10, then at 20.

\begin{array}{ccc} X & Y & Z \\ 386.796 & 381.417 & 293.87 \\ 769.263 & 761.189 & 581.056 \\ 758.6 & 760.8 & 580.8 \\ 707.724 & 707.564 & 537.101 \\ 356.817 & 354.641 & 271.109\end{array}

and the XYZ values scaled by the photopic response…

\begin{array}{ccc} X & Y & Z \\ 0.663953 & 0.654719 & 0.504441 \\ 0.662005 & 0.655057 & 0.50004 \\ 0.653 & 0.655 & 0.5 \\ 0.669631 & 0.669479 & 0.508191 \\ 0.673539 & 0.669432 & 0.511754\end{array}

and the xyz values

\begin{array}{ccc} x & y & z \\ 0.364186 & 0.359122 & 0.276692 \\ 0.364319 & 0.360495 & 0.275185 \\ 0.3612 & 0.3623 & 0.2765 \\ 0.362491 & 0.362409 & 0.275099 \\ 0.363148 & 0.360933 & 0.275919\end{array}

There’s not a whole lot of difference there, whether we use 1931 or 1964, or 10 nm or 20 nm, or make a few mistakes!

To look at it, here are my 4 computed xy, “white points”, for the illuminant, and the 5 xy points for the reflections.

p sum

(I didn’t grab their white point — the x,y coordinates — for D65, so only my 4 computed values are shown.)

The most important single computation in all this was to scale the values by the middle sum, i.e. by the total energy perceived by the observer. To be specific, we applied the ybar function to the illuminant spectrum.

I know, it looks just like the dot product of two vectors. But Cohen taught us that the color functions are really linear functionals. We want to visualize that computation as the matrix product of a row (the ybar) and a column (the D65 spectrum).

Of course, only such a trick as that division by the middle number could distract us from the marvelous computation of 3 numbers in the first place. We get from a spectrum to 3 scalars by applying 3 color matching functions (the transpose of our A matrix) to the reflected spectrum, which is also the spectrum incident on the eyes of the standard observer.

The first 3 numbers we get are, as we saw before and will see again, the (potentially unbounded) components of the incident spectrum wrt the dual basis spectra. The second set of 3 numbers are tristimulus values in XYZ space. The third set of numbers were xyz coordinates for the chromaticity chart.

The first 3 numbers depend on the dimensionality of our spectra, how many samples we have. In fact, we compute two such sets: one for the illuminant and one for the reflection. The middle number of the 3 for the illuminant sets the scale for white = (1,1,1).

The second set, XYZ, is found by dividing the first 3 numbers by that middle number. That is true whether we have XYZ for the illuminant or for the reflection: divide either by the total energy of the illuminant.

The third set, xyz, is found by dividing the XYZ numbers by their sum — instead of by their middle, or anybody’s middle.

I must emphasize that we have seen 4 different choices for the A matrix, and those were all “xyz bar” values! “The” A matrix is hardly unique.

More to come. You better believe it.

Posted in color. Tags: . 10 Comments »

10 Responses to “Color: from spectrum to tristimulus”

  1. greg aiken Says:

    dear rip, ive read this article of yours twice now, over a 2 day period. i am not having difficulty understanding the details of your calculations. rather i am struggling with trying to understand what the overall highest level problem is that you are setting out to solve.

    my confusion involves the date you refer to as ‘reflectance spectrum’. can you please describe exactly what the ‘reflectance spectrum’ actually means in a real world example?

    thanks,

    greg

  2. rip Says:

    Hi Greg,

    Thanks for asking. I hope I understand the questions:
    1- what is the highest level question I am answering? At this point, to find the XYZ tristimulus values of a spectrum seen by a standard observer.

    2- what is the reflectance spectrum?

    Let me quote Wyszecki & Stiles. “… an object-color stimulus is selected which involves an opaque plastic sample with given spectral reflectance factors \beta(\lambda)\ … and… CIE standard illuminnant D65.

    So we’re computing the XYZ values for a piece of plastic under standard illuminnant D65. What they call “spectral reflectance factors”, I call the reflectance spectrum. It is property of the piece of plastic. It specifies the ratio of the light reflected to the light incident, as a function of wavelength.

    Want to solve the very same problem for a piece of wood instead of a piece of plastic? Use the reflectance spectrum for that piece of wood instead of the one for the piece of plastic.

  3. greg aiken Says:

    so the first graph we see on this page would be a typical spectrophotometer plot of (in this case,) the reflectance of a piece of plastic.

    the second graph we see on this page would be a typical spectrophotometer plot of (in this case,) the CIE ‘Standard Illuminant D65’.

    the third graph represents the CIE 1931 2 degree ‘Standard Observer’.

    the overall concept here stated in english (to make it easy for me to understand):

    ‘if this piece of plastic (exhibiting this reflectance characteristic) is illuminated by a light source (Standard Illuminant D65, in this case), we can estimate the human visual response one might have (when looking at this piece of plastic) – by performing the calculations to follow…’

    we do this in 2 steps.

    we first calculate the (relative) ‘reflected spectrum’, by multiplying the 1×21 vector (beta) times the 1×21 vector (D65). the answer to this multiplication is a 1×21 vector ‘reflected spectrum’, expressed in relative units.

    question 1: Was there anything magical about the choice to divide this multiplied vector product by 100? in fact, if one looks at the 1×21 D65 vector value, we do see one value there thats ‘spot on’ = 100 is this coincidental – or is there some historical background that tells one what frequency value of a D65 spectral content represents 100%?

    question 2: i believe for the purpose of this article you chose to plot all 3 curves in the same graph, and you simply made an editorial decision to rescale the multiplied vector product – merely so all 3 curves could be seen in the graph. in my mind, im still comfortable that the multiplied vector producct does not have to exist within any contstrained value domain. what causes me to be lost here in my thinking about this is your statement “and the 3 spectra look like this (after I divide the D65 and S by 100, otherwise the reflectance spectrum is effectively the x-axis)” im lost by the part of the statement that says “otherwise the reflectance spectrum is effectively the x-axis”. might you possibly try to explain this to me?

    beyond my questions, it seems that to continue solving the problem, the second step is to convert the reflected spectrum into terms of the standard human visual response – we do this by multiplying the 1×21 vector ‘reflected spectrum’ times the 3×21 matrix CIE 1931 2deg xbar, ybar, zbar color matching functions.

    thats enough for my brain to deal with at this time. i am hoping you can confirm my understanding of this (up to this point), and possibly try to help with the 2 questions im still wrestling with.

    again, thanks for your kind help here and your patience…

    Greg

    we just basically multiply the theoretical light source (as D65 light sources do not exist in reality) is that when one multiplies the ‘spectral reflectance factor of the object’ (beta), times the ‘Standard Illuminant D65’, one reads is ISO 13655 (page 12)times the third

  4. greg aiken Says:

    ps – Yesterday, i just purchased the Wyszecki & Stiles book at Amazon, so once i receive this, i will review the pages you reference here… hopefully this will help as well…

  5. rip Says:

    Hi Greg,

    “so the first graph we see on this page would be a typical spectrophotometer plot of (in this case,) the reflectance of a piece of plastic.”

    Yes, but… the reflected spectrum of that piece of plastic illuminated by an equal-energy source, and you might choose to divide by 100, so that each number represents the fraction of incident light reflected at that wavelength.

    “question 1: Was there anything magical about the choice to divide this multiplied vector product by 100? in fact, if one looks at the 1×21 D65 vector value, we do see one value there thats ‘spot on’ = 100 is this coincidental – or is there some historical background that tells one what frequency value of a D65 spectral content represents 100%?”

    I recently saw… but cannot locate… a statement that the illuminant data is scaled to 100 at some specific frequency. If I find it again, I’ll post it.

    As for the division by 100, it was just to have all three graphs visible on the same scale. See below.

    “otherwise the reflectance spectrum is effectively the x-axis”. If you have two curves ranging from 0 to 100, and one curve ranging from 0 to 1, and you plot all three on a scale of 0 to 100, the 0-1 curve will be squished up against the x-axis.

    “the second step is to convert the reflected spectrum into terms of the standard human visual response – we do this by multiplying the 1×21 vector ‘reflected spectrum’ times the 3×21 matrix CIE 1931 2deg xbar, ybar, zbar color matching functions.”

    You got it.

    Thanks for asking. You’re probably not the only reader a bit overwhelmed.

  6. greg aiken Says:

    thanks for explaining your “effectively the x-axis” statement. now i understand what you were aiming for with that one!

    thanks for explaining that ‘divide by 100’ was done purely to show all sets of data in the same graph!

    as to the question ‘why in D65 standard illuminant did we find one value to be = 100?’, i read since my last post here that there is some notion of our human vision x_bar, y_bar, z_bar (short, medium, long cone response) – that the y_bar (medium cone response) also happens to be useable for overall luminosity when we view any given scene, and that if one were to graph the sensitivity of the y_bar sensitivity – the peak wavelength of our human visual sensitivity MIGHT possibly be the same corellated wavelength that would serve as the 100% mark within any ‘standard illuminant’ (this is my best guess). I believe this is around 560nm. (in fact, if you check your chart for D65 at 20nm steps, you’ll find the 100% mark is at 560nm).

    there is only one topic i am now hazy about (in your last post to me). if ok, perhaps we can discuss this one newly brought up topic. your second paragraph…

    “Yes, but… the reflected spectrum of that piece of plastic illuminated by an equal-energy source, and you might choose to divide by 100, so that each number represents the fraction of incident light reflected at that wavelength.”

    so that i can understand this clearly, i’ll restate this in a more literal way, in the way my brain sees, and understands this. if i make mistakes along the way, please interject to clear up any of my misunderstanding or confusion.

    if i look at your first graph (of this page), and i see (what appears in my mind to be) a typical spectral energy distribitution graph – as could be output from any spectrograph driver software being used in ‘relative count’ mode. for the purpose of your ‘downstream’ calculations presented in this page, it seems pretty irrelevent the actual scale used here for the y values (as in later calcs, you merely ‘normalize’ the scale of the multiplied terms). but as you eluded to in this paragraph, that if i wanted to get the y values, for smaller wavelength ‘steps’, i would need to divide the y values presently shown in the chart by ‘100’. im struggling with this statement. maybe you made this statement for brevity without going into lots of details to keep it easy to understand. and maybe youll hate me for being so literal here – but i am truly wanting to understand all aspects of this ‘color stuff’ – so thats why i ask here abou tthis. but technically, isnt the idea here that if we graph such a reflectance spectral ‘graph’. that with detail, the idea is that if one were to calculate the ‘area under this graph’ – this area would represent the ‘total’ of the graph (in this case the wavelength steps are at 20 nm). if we wanted to know how much each frequency ‘band’ contributed to the overall ‘total’ response – we would need to (in the case of 380 to 400 nm, for example). calculate the area under the graph (between 380 and 400 nm) and divide this smaller area by the ‘total’ area – the result would be the % contribution of the first band 380 to 400 nm. we could in fact, step through the entire graph for each 20 nm band to do this. we would then ‘double check’ our answer by adding up each % contribution for each band, and the total should = 100%.

    we could do this for ANY selection of lower frequency and upper frequency boundaries, using the exact same methodology.

    technically, if what i present here is correct, i dont think thats the same as merely dividing the y scale by 100,as you stated – but then perhaps i am not seeing this the same way that you are…

    again, thanks for your patience here with me.

    by the way, if i have not stated yet. i used to be a photographer (in film days). i used to undersatnd how to control film response pretty well, having access to a testing situation and color densitometer, I used to characterize each of the films that i used, and was able to master the use fo film. now that photography is largely digital – i am wanting to try to understand ALL that is color, so that i can apply that knowledge to trying to better understand and control color digital photography and eventually color management.

    Rip, what is your interest here in color? I’m thankful that you have this interest and are so willing to help others.

    Greg

    • rip Says:

      Hi Greg,

      Let me quote you: “if i wanted to get the y values, for smaller wavelength ‘steps’, i would need to divide the y values presently shown in the chart by ’100′.”

      No, that’s not what I meant. I wasn’t talking about changing the step size. Instead, If you measure a reflected spectrum under an equal intensity source, and if the numbers go up to about 100, you may want to divide them by 100 – so that what I call the reflectance spectrum goes up to about 1 — so that when you multiply a source spectrum by the reflectance spectrum you get numbers up to 100, instead of up to 10,000.

  7. greg aiken Says:

    i understand what you meant. again its a form of scaling. thanks.

  8. jf08056 Says:

    Great example. Just what I was looking for. Thanks. Only comment I can make is that it would be nicer if you could copy & paste your data. I had to type it all in to follow along in Excel to make sure I was getting it right. Thanks again.

  9. Hakanai Says:

    The answer to what units the D65 spectrum is in is: it’s arbitrary. They normalise all the values to 100 at 560nm, as with many of the other illuminants.

    Why I know: I’ve been looking for some spectral radiance data with a known correct XYZ result for a test I’d like to write, but reflectance * illuminant basically results in unknown units, so it gives me an XYZ that can’t be used.


Leave a reply to rip Cancel reply