What's new

Math Help Needed!

Steve Mariotti

Kavapithecus Krunkarensis
Review Maestro
For the final step, we need to do some 2d math using the chromaticity coordinates and the white point to find the intersection point for the dominant wavelength. I have the reference at home. It shouldn't be much more than a little slope/intercept work to arrive at the final wavelength value in nm.

@verticity, I think you're right. I'm getting percentage values as the transmittance and not normalizing it, just pumping it through. It could account for some of the variance from the reference CIE values. I'll take a look tonight, and also post my code for computing dominant wavelength.
 

verticity

I'm interested in things
noble: x 0.4030, y 0.4252, domWL 574.7
two day: x 0.5133, y 0.4546, domWL 583.0

Illuminant is D65: x 0.3127, y 0.3290
OK, here are my latest results:

Noble:
xyY: x = 0.3929, y = 0.4453

Two day:
xyY: x = 0.4979, y = 0.4699

Now I am interpolating the CMF and D65 values linearly.

The CMF data is from here:
http://cvrl.ioo.ucl.ac.uk/

D65 Illuminant data from here:
http://www.cis.rit.edu/research/mcsl2/online/CIE/all_1nm_data.xls

I'm using the data set for "CIE 1931 2-deg, XYZ CMFs modified by Judd (1951) and Vos (1978)" because more recent is better, right?

And that brings up the question of what CMF data set are you using for the above results? Clearly it is close, but something is a little bit different. (That site also has "New CIE physiologically-relevant XYZ functions (proposed)"; should we use those to get the newest of the new?)
 

verticity

I'm interested in things
That reference contains the "old" or original I guess CIE 1931 numbers

noble: x 0.4030, y 0.4252, domWL 574.7
two day: x 0.5133, y 0.4546, domWL 583.0

Illuminant is D65: x 0.3127, y 0.3290
If I use those numbers I get:

Noble:
CMF: CIE_1931 2 deg
xyY: x = 0.3878, y = 0.4343

Two day:
CMF: CIE_1931 2 deg
xyY: x = 0.4986, y = 0.4674

So I'll try the 10-degree numbers I guess
 

verticity

I'm interested in things
@verticity - Here's your results and mine plotted on the CIE chart. Relative position is fine, it's just a matter of fine tuning the xy to domWL algorithm. A line drawn from the illuminant through the data point indicates the domWL on the perimeter. It's not entirely accurate (because of pic scaling) but the relationships are.

View attachment 4092
Well, hey, my nobles are yellower than your nobles. :D
Speaking of Dom WL, do you have open source software to do that part?
 

CactusKava

Phoenix, AZ
Kava Vendor
Oh man, I feel bad. I haven't had time to even touch this. I'm also so far behind you two that I'm not sure if I should even try to jump in. Maybe when you're all done doing the hard parts, I can figure out a web app version? @verticity I'm not too sure how easily a standard C# project can be converted to maybe .Net Web API or something? Might be easier than porting to an entirely different language.
 

verticity

I'm interested in things
Oh man, I feel bad. I haven't had time to even touch this. I'm also so far behind you two that I'm not sure if I should even try to jump in. Maybe when you're all done doing the hard parts, I can figure out a web app version? @verticity I'm not too sure how easily a standard C# project can be converted to maybe .Net Web API or something? Might be easier than porting to an entirely different language.
Well, it would be easier to convert it to ASP.NET, actually, if your hosting provider can do that. I'm very familiar with ASP.NET... I've put the code up on a free Subversion repository, so I can give you access if you PM me your email... but as you can see from Deleted User's comments, it is not quite finished; I'm still working out some bugs...
 

CactusKava

Phoenix, AZ
Kava Vendor
Well, it would be easier to convert it to ASP.NET, actually, if your hosting provider can do that. I'm very familiar with ASP.NET... I've put the code up on a free Subversion repository, so I can give you access if you PM me your email... but as you can see from Deleted User's comments, it is not quite finished; I'm still working out some bugs...
I have multiple hosts, so that shouldn't be a worry. If I were to use CactusKava.com (which is running on a linux VPS), I think I could use Apache's mod_mono. I've been successful in the past with using it. If that doesn't work, I've got a few other places I can throw it.
 

verticity

I'm interested in things
noble: x 0.4030, y 0.4252, domWL 574.7
two day: x 0.5133, y 0.4546, domWL 583.0
Here's what I get for the CIE 1964 (10 degree) data

Noble:
xyY: x = 0.3968, y = 0.4354

Two day:
xyY: x = 0.5101, y = 0.4604


So, getting closer, but still no cigar... what is different?
Maybe my illuminant data?
 

verticity

I'm interested in things
No my illuminant data appears to be the same as Table T.1 of your reference: "Standard Illuminant D65".. So I dunno
 

verticity

I'm interested in things
Actually, not quite the same. In that reference the illuminant data ends at 780 nm. Mine is extrapolated out somehow.
 

verticity

I'm interested in things
I have multiple hosts, so that shouldn't be a worry. If I were to use CactusKava.com (which is running on a linux VPS), I think I could use Apache's mod_mono. I've been successful in the past with using it. If that doesn't work, I've got a few other places I can throw it.
Mono can be iffy for ASP.NET apps... The best thing really is an IIS server on Windows
 

Steve Mariotti

Kavapithecus Krunkarensis
Review Maestro
Ok, here's the remainder of the algorithm to get to nanometers.

I couldn't find an implementation of this ANYWHERE, so I winged it.

The goal is to get the wavelength of the dominant color in the spectral data. You've got the color now (x, y) and the white point (wX, wY) so you draw a line starting at the data color (x, y) and passing through the white point (wX, wY) and determine where it intersects the boundary of the so-called "spectral locus" or the space where the colors exist.

Turns out its actually easy to draw the boundary line of the spectral locus by just iterating over the wavelengths we care about and using the same color space functions as before, except using 1.0 as the transmission value. This time, rather than pooping out the exact coordinate of the color that was matched, it poops out the edge point of the CIE diagram along which the wavelengths are labelled in nanometers for reference (see CIE 1931 diagram for example).


So my strategy was to just intersect a line segment with the current and previous point along the curve as I iterated through the wavelengths. As soon as something intersected, I had my wavelength;

NOTE: precision could be improved greatly as before by interpolating between wavelengths based on the exact intersection point's distance from the start of the wavelength range (ie. 500 and 501). I also used a 2d line segment vs. line segment function that I found on the interwebs (and cited) to perform the test. I believe the accuracy once I fix a few things should be plenty for our application once it's properly dialed in.

Seems to work. Haven't verified results yet, but gave me a reasonably yellow color for Kaolik so I'm posting it as-is.

I'll post everything on a git hub soon so that hopefully others can improve it and I can con someone in color science to check my math.

Here's the code tack this onto the previous code I posted, where I got x2d and y2d and the white point:


double diffX = x2d - xWhitePt;
double diffY = y2d - yWhitePt;

double normX = diffX / sqrt(diffX * diffX + diffY * diffY);
double normY = diffY / sqrt(diffX * diffX + diffY * diffY);

// create line segment to collide against spectral locus below
double segmentLength = 2.0;

double segX = x2d + normX * segmentLength;
double segY = y2d + normY * segmentLength;

// Iterate over spectral locus and collide with line segment S
// as given by [(x2d, y2d), (segX, segY)]

Point2d segStart = { x2d, y2d };
Point2d segEnd = { segX, segY };

double lambdaX = 0.0;
double lambdaY = 0.0;
double lambdaZ = 0.0;
double lambdaN = 0.0;

double dominantWavelength = 0.0;
Point2d pointOnLocus = { 0.0, 0.0 };

Point2d lastPoint = { 0.0, 0.0 };

for (double wave = 360.0; wave <= 830.0; wave += 1.0)
{
double transmittance = 1.0;

double refIllum = refIlluminant(wave);
CMFData matching = cmf(wave);

lambdaX = matching.m_xBar * transmittance * refIllum;
lambdaY = matching.m_yBar * transmittance * refIllum;
lambdaZ = matching.m_zBar * transmittance * refIllum;
lambdaN = matching.m_yBar * refIllum;

double locusN = sumN;

// Tristimulus values
double locusX = (1.0 / locusN) * lambdaX;
double locusY = (1.0 / locusN) * lambdaY;
double locusZ = (1.0 / locusN) * lambdaZ;

double locus_x2d = locusX / (locusX + locusY + locusZ);
double locus_y2d = locusY / (locusX + locusY + locusZ);
double locus_z = locusZ / (locusX + locusY + locusZ);

Point2d point;

point.m_x = locus_x2d;
point.m_y = locus_y2d;

// Intersect [lastPoint, point] with [segStart, segEnd]
if (lineSegmentIntersection(
lastPoint.m_x, lastPoint.m_y,
point.m_x, point.m_x,
segStart.m_x, segStart.m_y,
segEnd.m_x, segEnd.m_y,
&pointOnLocus.m_x, &pointOnLocus.m_y))
{
dominantWavelength = wave;
}

lastPoint = point;
}

if (dominantWavelength > 0.0)
{
char s[1024];
sprintf_s(s, 1024, "DomWavelen: %.2lf nm, Locus Coord (%.4lf, %.4lf)\n", dominantWavelength, pointOnLocus.m_x, pointOnLocus.m_y);
OutputDebugStringA(s);
}
 
Last edited:

Steve Mariotti

Kavapithecus Krunkarensis
Review Maestro
Here's what I'm getting:

Tudei (V)
ChromPt: (0.4966, 0.4676)
DomWavelen: 582.65 nm

Noble (V)
ChromPt: (0.3851, 0.4305)
DomWavelen: 562.85 nm

D65 illuminant, 2 degree CMF, D65 White Point
 
Top