/******************************************************************/ /* XYZSpectrum.h */ /* ------------- */ /* */ /* This file defines the spectral matching curves for the XYZ */ /* color-space. The curves are sampled from 375 to 780 nm, in */ /* increments of 5 nm per bin (82 total bins). */ /* */ /* Note the class only has static methods. */ /* */ /* You may use this file in one of two ways: */ /* 1) Without thinking about the underlying bin sampling (useful */ /* if you decide to later change it). You can do this by */ /* accessing the methods: */ /* RGBColor XYZSpectrum::GetXYZStimulus(float wavelen) */ /* and RGBColor XYZSpectrum::ConvertXYZToRGB(RGBColor &xyzColor) */ /* */ /* 2) With an understanding that there are 82 bins sampled in */ /* increments of 5 nm. 5 nm should be enough for nearly all */ /* applications. This approach allows you direct access to */ /* the underlying data. In addition, the normalization */ /* factors for the x, y, and z stimuli have already been */ /* computed. (This can be a speed advantage, but ties you */ /* into using 82 bins, which can be hard to change later, and */ /* [probably] is overkill for most applications). */ /* */ /* Also note that the use of the RGBColor class is on purpose! */ /* Functions taking (or returning) them explicitly handle */ /* 3-component colors, and should not change, even if the */ /* rest of your code uses an arbitrarily different color */ /* representation. Ideally, this code will allow you to write */ /* a function converting from an arbitrary spectrum to RGB. */ /* */ /* Chris Wyman (10/26/2006) */ /******************************************************************/ #ifndef XYZSPECTRUM_H #define XYZSPECTRUM_H #include "DataTypes/RGBColor.h" class XYZSpectrum { private: static const int spectrumStart = 375; static const int spectrumEnd = 780; static const int numBins = 82; static const int wavelengthSampling = 5; static const float xStimulus[numBins]; static const float yStimulus[numBins]; static const float zStimulus[numBins]; public: // These shouldn't be necessary. You should be able to call all class methods directly XYZSpectrum() {} ~XYZSpectrum() {} ///////////////////////////////////////////////////////////////////////////////////// // The following two functions do not require you to think about the internal data // // representation. The first gives you a 3-component color XYZ value from a // // specified wavelength. The second converts a XYZ color to a RGB color. // ///////////////////////////////////////////////////////////////////////////////////// // Return the X, Y, or Z stimuli for the specified wavelength. static RGBColor GetXYZStimulus( float atWavelength ) { if ( atWavelength < spectrumStart || atWavelength > spectrumEnd ) return RGBColor::Black(); int bin = (int)((atWavelength - spectrumStart) / wavelengthSampling); float binWeight = ((atWavelength - spectrumStart) / wavelengthSampling) - bin; return RGBColor( xStimulus[bin], yStimulus[bin], zStimulus[bin] ) * (1-binWeight) + RGBColor( xStimulus[bin+1], yStimulus[bin+1], zStimulus[bin+1] ) * binWeight ; } // Convert an color in XYZ format to RGB. NOTE: This is NOT clamped to [0..1]! static RGBColor ConvertXYZToRGB( const RGBColor &xyzColor ) { return RGBColor( 3.240479f * xyzColor.Red() - 1.537250f * xyzColor.Green() - 0.498535f * xyzColor.Blue(), -0.969256f * xyzColor.Red() + 1.875991f * xyzColor.Green() + 0.041556f * xyzColor.Blue(), 0.055648f * xyzColor.Red() - 0.204043f * xyzColor.Green() + 1.057311f * xyzColor.Blue() ); } ///////////////////////////////////////////////////////////////////////////////////// // The following functions are usable if you are aware of the data representation! // // (82 bins in each spectra. Sectra sampled at 5 nm resolution.) // // (Start wavelengh 375 nm. Last sampled wavelength 780 nm.) // ///////////////////////////////////////////////////////////////////////////////////// // Returns the wavelength for a given bin number. inline static int BinWavelength( int binNum ) { return spectrumStart + wavelengthSampling*binNum; } // Return the X, Y, or Z stimuli for the given bin number. // Bin #i has a wavelength of 375 + i*5 nanometers, for 0 <= i < 82 inline static float XStimulus( int binNum ) { return xStimulus[binNum]; } inline static float YStimulus( int binNum ) { return yStimulus[binNum]; } inline static float ZStimulus( int binNum ) { return zStimulus[binNum]; } // Normalization factors. These are the sums of the 82 entries in each // of the X, Y, and Z stimuli tables (respectively). // Please note: These normalization factors WILL BE WRONG if you use // different sampling rates (the tables assume 5 nm sampling) inline static float XNormalizationFactor( void ) { return 21.3714f; } inline static float YNormalizationFactor( void ) { return 21.3711f; } inline static float ZNormalizationFactor( void ) { return 21.3895f; } }; const float XYZSpectrum::xStimulus[numBins] = { 0.0000f, 0.0014f, 0.0022f, 0.0042f, 0.0076f, 0.0143f, 0.0232f, 0.0435f, 0.0776f, 0.1344f, 0.2148f, 0.2839f, 0.3285f, 0.3483f, 0.3481f, 0.3362f, 0.3187f, 0.2908f, 0.2511f, 0.1954f, 0.1421f, 0.0956f, 0.0580f, 0.0320f, 0.0147f, 0.0049f, 0.0024f, 0.0093f, 0.0291f, 0.0633f, 0.1096f, 0.1655f, 0.2257f, 0.2904f, 0.3597f, 0.4334f, 0.5121f, 0.5945f, 0.6784f, 0.7621f, 0.8425f, 0.9163f, 0.9786f, 1.0263f, 1.0567f, 1.0622f, 1.0456f, 1.0026f, 0.9384f, 0.8544f, 0.7514f, 0.6424f, 0.5419f, 0.4479f, 0.3608f, 0.2835f, 0.2187f, 0.1649f, 0.1212f, 0.0874f, 0.0636f, 0.0468f, 0.0329f, 0.0227f, 0.0158f, 0.0114f, 0.0081f, 0.0058f, 0.0041f, 0.0029f, 0.0020f, 0.0014f, 0.0010f, 0.0007f, 0.0005f, 0.0003f, 0.0002f, 0.0002f, 0.0001f, 0.0001f, 0.0001f, 0.0000f }; const float XYZSpectrum::yStimulus[numBins] = { 0.0000f, 0.0000f, 0.0001f, 0.0001f, 0.0002f, 0.0004f, 0.0006f, 0.0012f, 0.0022f, 0.0040f, 0.0073f, 0.0116f, 0.0168f, 0.0230f, 0.0298f, 0.0380f, 0.0480f, 0.0600f, 0.0739f, 0.0910f, 0.1126f, 0.1390f, 0.1693f, 0.2080f, 0.2586f, 0.3230f, 0.4073f, 0.5030f, 0.6082f, 0.7100f, 0.7932f, 0.8620f, 0.9149f, 0.9540f, 0.9803f, 0.9950f, 1.0000f, 0.9950f, 0.9786f, 0.9520f, 0.9154f, 0.8700f, 0.8163f, 0.7570f, 0.6949f, 0.6310f, 0.5668f, 0.5030f, 0.4412f, 0.3810f, 0.3210f, 0.2650f, 0.2170f, 0.1750f, 0.1382f, 0.1070f, 0.0816f, 0.0610f, 0.0446f, 0.0320f, 0.0232f, 0.0170f, 0.0119f, 0.0082f, 0.0057f, 0.0041f, 0.0029f, 0.0021f, 0.0015f, 0.0010f, 0.0007f, 0.0005f, 0.0004f, 0.0002f, 0.0002f, 0.0001f, 0.0001f, 0.0001f, 0.0000f, 0.0000f, 0.0000f, 0.0000f }; const float XYZSpectrum::zStimulus[numBins] = { 0.0000f, 0.0065f, 0.0105f, 0.0201f, 0.0362f, 0.0679f, 0.1102f, 0.2074f, 0.3713f, 0.6456f, 1.0391f, 1.3856f, 1.6230f, 1.7471f, 1.7826f, 1.7721f, 1.7441f, 1.6692f, 1.5281f, 1.2876f, 1.0419f, 0.8310f, 0.6162f, 0.4652f, 0.3533f, 0.2720f, 0.2123f, 0.1582f, 0.1117f, 0.0782f, 0.0573f, 0.0422f, 0.0298f, 0.0203f, 0.0134f, 0.0087f, 0.0057f, 0.0039f, 0.0027f, 0.0021f, 0.0018f, 0.0017f, 0.0014f, 0.0011f, 0.0010f, 0.0008f, 0.0006f, 0.0003f, 0.0002f, 0.0002f, 0.0001f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f }; #endif