Calculating colors
In my previous post I presented Planck's law which relates the temperature of an object to the spectrum of electromagnetic radiation that it emits. Here I show how I've used this to represent the colors corresponding to each temperature.
The colors displayed on a computer monitor are commonly encoded in the RGB format, which is a collection of three numbers designating the intensity of red (R), green (G) and blue (B) components in the color in question. Converting physical properties such as temperature or spectrum to this format is done by first converting the spectrum (which may be obtained from temperature using Planck's law) into the XYZ format, which can then be mapped to the RGB color.
Another representation of colors is the chromaticity, or xy representation. While both XYZ and RGB encode information about both the luminance and chromaticity of each color, the xy format describes only the chromaticity. This reduces the number of dimensions in the color representation from three to two, allowing depiction of the color space (or rather, chromaticity space) in a two-dimensional diagram.
Such a diagram is shown below. The chromaticity coordinates are x and y, and the bended curve bounding the upper side of the colored area represents the spectral colors, that is, colors corresponding to monochromatic light; selected wavelengths are marked along this curve. Every color in the chromaticity diagram is displayed at full luminance, i.e., the strongest version of each hue available.
The dashed line inside the colored area is the Planckian locus, which represents colors of black body radiation at various temperatures, as described by Planck's law.The xy coordinates are normalized version of the XYZ coordinates: \begin{align} x &= \frac{X}{X+Y+Z}\,, \\ y &= \frac{Y}{X+Y+Z}\,, \\ z &= \frac{Z}{X+Y+Z} = 1 - x - y\,, \end{align} where X, Y and Z represent the visual nerves' response to light.
Given xy coordiates, the XYZ coordinates can be found by setting Y=1 and inverting the above equations: \begin{align} X &= \frac{x}{y}\\ Y &= 1\\ Z &= \frac{1-x-y}{y}\,. \end{align} Letting Y=1 corresponds to full luminance, because Y represents the response of the cone sensitive to the widest range of wavelengths, as explained in the following.
Color vision is facilitated by cones, sensory cells in the eye, which are activated by light. There are three different types of cones, each sensitive to different parts of the visible spectrum. The sensitivity of each cell as a function of wavelength is described by the color matching functions \bar{x}(\lambda), \bar{y}(\lambda) and \bar{z}(\lambda), each corresponding to one type of cone. Plots of these functions vs wavelength (in nanometers) are shown here along with the colors of the visual spectrum:
The color matching functions plotted above, which are the ones I've used for this project, can be obtained as cvs files here.Given light with a certain spectrum L(\lambda), i.e., a certain distribution of wavelengths in the visual spectrum, the response is found by integrating the spectrum weighed with each of the color matching functions: \begin{align} X &= \int L(\lambda) \bar{x}(\lambda)\,\mathrm{d}\lambda\,,\\ Y &= \int L(\lambda) \bar{y}(\lambda)\,\mathrm{d}\lambda\,,\\ Z &= \int L(\lambda) \bar{z}(\lambda)\,\mathrm{d}\lambda\,. \end{align} The XYZ coordinates thus describe the response of each type of cone in the eye, and the chromaticity xy gives this information normalized with respect to the total luminance. Because of this the colors in the spectrum don't exactly match those along the edge of the chromaticity diagram, since the latter are shown with full luminance, whereas the spectral colors take into account the varying sensitivity of cones to light of different colors.
The practical conversion of spectral data to XYZ coordiates is done by calculating the inner products of a spectral vector, whose entries are the intensities of each integer-nanometer wavelength, and a similar vector containing the values of the color matching functions. The three resulting scalar values are X, Y and Z.
The Planckian locus in the chromaticity diagram above is computed by obtaining XYZ values in this manner from the black body spectrum given by Planck's law for a series of temperatures, converting these to xy coordinates, and fitting a cubic spline to the points in the chromaticity plane. For the spectral colors bounding the chromaticity diagram, a spectrum is defined with a single non-zero value at the wavelength in question, and the same method is applied.
The final piece is the conversion to RGB colors, which has two steps: a linear conversion and a non-linear \gamma correction. The linear part is done by forming an [XYZ] vector which is transformed using a matrix M, \begin{align} \left[\begin{array}{c} R \\ G\\ B \end{array}\right]_L &= \left[\begin{array}{ccc} 3.2406255 & -1.537208 & -0.4986286 \\ -0.9689307 & 1.8757561 & 0.0415175 \\ 0.0557101 & -0.20400211 & 1.0569959 \end{array}\right] \left[\begin{array}{c} X \\ Y\\ Z \end{array}\right]\,. \end{align} The entires of M are those specified in the standard IEC 61966-2-1:1999, found here.
The \gamma correction is then applied to each entry in the [RGB]_L vector: \begin{align} C_{\gamma} &= \left\{ \begin{array}{cc} 12.92 \cdot C_L\,, & C_L \leq 0.0031308\\ (1.055\cdot C_L)^{1/2.4} - 0.055\,, & C_L > 0.0031308 \end{array} \right.\,, \quad C \in \left\{R, G, B\right\}\,. \end{align} The resulting vector [RGB]_{\gamma} is finally clipped to the interval [0; 1], and it is then used to encode colors in the Python scripts used to produce the figures found in this blog.
The methods described here are available through modules such as python-colormath. However, as a challenge and as an exercise to familiarize myself with the concepts presented I opted instead to implement the functions myself for the purpose of illustrating this blog. This approach cannot be recommended outside the scope of leisure projects, or in any setting where time is premium.
Comments
Post a Comment