% In this tutorial, we will introduce some basic spectral calculations that % we can use to understand color vision, surface reflectances, illuminants, % photoreceptor encoding, and trichromacy. % You will notice that much of the work below involves matrix algebra. I % suggest that you pull out a pad of paper and draw matrix tableaus as we % proceed to help you follow the calculations. % Running the tutorial: % - Run matlab % - Change directory to the folder containing the tutorial using 'cd ' at % the matlab prompt. % - Type 'edit colorMatchingTutorial' at the matlab prompt to open this % file. % - Click on Cell in the menubar and select Enable Cell Mode. % - The tutorial is subdivided into sections called cells. Each cell % contains some comments and explanations along with some code. Click the % mouse in the first cell (sampling the visible spectrum) and use Evaluate % Current Cell from the Cell menu to evaluate the code segment in that % cell. Or use the keyboard shortcut (command-enter or apple-return on the % Mac). Continue reading the comments for each successive cell and then % evaluating it. % - Click on File in the menubar and select Save and then select Publish To % HTML to produce a version of this file with the results inserted at each % step, that you can view with a web browser. By default, the output is % saved in a subfolder called html. % - For more information about cell mode, choose Matlab Help from the Help % menu and search for "Rapid Code Iteration Using Cells"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SAMPLING THE VISIBLE SPECTRUM % Throughout, we will be sampling wavelenghts in 10nm steps from 400 to 700 % so define the spectrum as follows: spectrum = linspace(400,700,31);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SURFACE REFLECTANCE % This loads in the macbeth color checker and the munsell % standard surfaces load surfaces % macbeth, munsell size(macbeth) size(munsell) % The Macbeth Color Checker is a set of 24 surfaces commonly used to % evaluate color balancing systems. The materials in the Color Checker % were selected to have the same surface reflectance functions as various % important surfaces that are commonly used in television and film images. % We will read in a matrix of data. Each row of the matrix is the % reflectance function of one of the Macbeth surfaces, measured at each of % 31 samples in the visible spectrum. (Most of the visible spectrum is in % the 400-700 nanometers wavelength region.) Thus the variable 'macbeth' % is a 24x31 matrix of surface spectra. Each row is the reflectance % function for one surface. Likewise, the 'munsell' variable is a 359x31 % matrix in which each row is the reflectance function of a surface.
ans = 24 31 ans = 359 31
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % LOOK AT THE SURFACE REFLECTANCE SPECTRA % The 8th row of the macbeth is a surface that typically looks greenish. We % plot the fractional reflectance (as a function of wavelength) for this % surface. figure(1);clf plot(spectrum,macbeth(8,:),'g'); xlabel('Wavelength (nm)'); ylabel('Surface Reflectance'); title('Reflectance of Macbeth Surfaces'); % For example, about 28% of the light at wavelength 500 nm is % reflected by the "Green" surface. green_500 = macbeth(8,find(spectrum==500)) % Here is the fractional reflectance of red surface: hold on plot(spectrum,macbeth(9,:),'r'); % And here it is for a gray surface: plot(spectrum,macbeth(20,:),'b'); hold off
green_500 = 0.2779

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % TYPICAL DAYLIGHT SPECTRUM % Now, load in the illuminants. Each illuminant vector represents the % amount of light present at each wavelength. Again, the light is sampled % at 31 points in the visible spectrum, so we get vector of 31 numbers % representing the spectral power distribution (SPD) for that light. load illuminants %cie_a,daylight_65,floursecent,illuminant_C,xenon_flash % Make a plot of daylight_65. Daylight_65 is the standard illuminant which % represents a mix of blue sky and clouds. Note: the energy units are % arbitrary. figure(1);clf plot(spectrum,daylight_65,'k'); xlabel('Wavelength (nm)'); ylabel('Energy'); title('Energy spectrum of daylight 65 Illimunant');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % MORE ILLUMINANTS % Here are the spectral power distributions of some other illuminants. % Several of these are used as international standards for illuminants, as % specified by the CIE (an international standards committee for color % printing and technology). figure(1);clf plot(spectrum,[cie_a',daylight_65',flourescent',illuminant_C',xenon_flash']); xlabel('Wavelength (nm)'); ylabel('Energy'); title('Energy Spectrum of Illuminants'); legend('cie a','daylight 65','flourescent','illuminant C','xenon flash');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % CALCULATE THE SPECTRAL SIGNAL RESULTING FROM A LIGHT REFLECTING OFF A % MACBETH SURFACE % The spectral signal reaching the eye is the pointwise product of the % incident light and the reflectance of the surface at the each wavelength. % This can be computed using .* in matlab. Or it can be expressed as a % matrix product, where the surface reflectance is multiplied by a big % diagonal matrix with the light intensities at each wavelength along the % diagonal. As an example, plot the spectral signal coming off the green % Macbeth surface for daylight illumination. spectralSignal = macbeth(8,:)' .* daylight_65'; figure(1);clf plot(spectrum,spectralSignal,'g'); xlabel('Wavelength (nm)'); ylabel('Reflected Energy'); title('Reflected energy of surfaces under daylight_65')

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % HUMAN CONE SPECTRAL SENSITIVITIES % Now we want to measure the photoresponses of the human cones to these % stimuli. The human cone spectral sensitivities have been measured and % correspond closely with behavioral color matching data known for many % years. We now read in a matrix of the human cone sensitivities. There are % three classes of cone, the so-called L,M, and S cones with (L)ong-, % (M)iddle-, and (S)hort-wavelength peak sensitivities. We represent each % cone by its sensitivity at each of 31 sample wavelengths in the spectrum. % Thus, we have a 3x31 matrix representing the human cone spectral % sensitivities. load cones %cones % Look at the three cone classes superimposed. Notice how close % the L and M cones are in terms of their peak sensitivities. figure(1) clf plot(spectrum,cones(1,:),'r'); hold on plot(spectrum,cones(2,:),'g'); plot(spectrum,cones(3,:),'b'); xlabel('Wavelength (nm)'); ylabel('Relative Sensitivity'); title('Cone Spectral Sensitivity Functions'); ylim([0 1]);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % HUMAN CONE RESPONSES TO THE SPECTRAL SIGNAL % We can describe the photoreceptor responses as a matrix multiplication. The % matrix product of the receptor sensitivites and the spectral % signal gives the photoisomerizations in each receptor class due to each % of the surfaces. The cone spectral sensitivity is a 3x31 matrix and the % spectralSignal is a 31x1 vector so the product is a vector of 3 numbers, % one for each of the cones (L, M, S). coneSignals = cones * spectralSignal % So these three numbers represent the relative responses of the cones in % your retina to a real surface illuminated by a real light source. Note % that the units are arbitrary because both the reflectance function and % the cone sensitivities were scaled between 0-1. We could redo these % calculations, keeping track of the actual intensity of the illuminant % such that the coneSignals could be expressed in terms of the number of % photopigment isomerizations but that's not worth the effort for our % purposes here.
coneSignals = 497.7669 429.1217 72.9683
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % MONITOR PHOSPHORS % We know from the color matching experiment that we can match any test % light (including the above spectralSignal from the Macbeth surface) by an % appropriate combination of 3 primary lights. An example of the % application of this principle is in color TV, for which you can display % any of a large number of colors using the 3 lights in the display. For a % CRT (if you can still buy one) the 3 primaries are generated by an % electron beam exciting 3 types of phosphors (a phosphor is a substance % that exhibits phosphorescence or sustained glowing after exposure to % electrons). % Read in the spectra of a typical set of color monitor phosphors. Each row % in this matrix corresponds to one phosphor, giving its relative output % energy as a function of wavelength. These are probably similar to the 3 % primaries on your screen, but not quite right because you are probably % using a flat panel display. We could measure the spectra for the 3 % primaries on your screen, a procedure called color calibration. load phosphors %phosphors % Look at the three phosphor spectra together figure(1);clf plot(spectrum,phosphors(1,:),'r'); hold on plot(spectrum,phosphors(2,:),'g'); plot(spectrum,phosphors(3,:),'b'); xlabel('Wavelength (nm)'); ylabel('Relative Energy'); title('Spectral power distributions of typical monitor phosphors');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % COLOR MATCHING % Here we calculate the monitor phosophor intensities required to % generate the *same* receptor responses in your eye that the Macbeth % surface generates under daylight. To do so, we first find the % linear tranform that gives the cone responses due to the different % monitor spectra. To understand this calculation, try pulling out a % piece of paper and convincing yourself that this is the way to get % cone responses from the phosphor intensities. The variable cones is a % 3x31 matrix and phosphors' (transpose of phosphors) is a 31x3 matrix. The % product is a 3x3 matrix that takes 3 numbers corresponding to the % intensities of each of the 3 monitor primaries and converts it into 3 % numbers corresponding to the 3 cone responses. monitor_to_cones = cones * phosphors' % Now, the inverse of this matrix tells us how to set the phosphors to % achieve any desired cone response: cones_to_monitor = inv(monitor_to_cones) % We apply this transformation to the desired cone responses and % obtain the necessary monitor intensities for rendering a color % on the monitor that matches the Macbeth surface reflectance under % daylight. monitorSignals = cones_to_monitor * coneSignals
monitor_to_cones = 0.0908 0.1556 0.0174 0.0378 0.1623 0.0256 0.0051 0.0162 0.1379 cones_to_monitor = 18.3038 -17.6384 0.9637 -4.2301 10.3529 -1.3891 -0.1753 -0.5663 7.3776 monitorSignals = 1.0e+03 * 1.6123 2.2357 0.2081
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % RENDERING % We have a function, 'renderSurfaces' to do the above work for us and % display an image of all of the Macbeth surfaces under a particular % illuminant. This function starts with % spectral representations of a set of surfaces, a light, the human cones, % and the monitor phosphors. It produces a color image that is % displayed as a true-color picture. If we were using the calibrated SPDs % of the primaries in your screen then the rendered image would be an exact match. scaleFactor = 155; renderSurfaces(macbeth,daylight_65,cones,phosphors,scaleFactor,... 'macbeth surfaces under daylight 65') % You may get a warning such as: % Warning, image(s) 13 out of range % meaning that there are some colors that are not properly represented. % The color-image is made up of float rgb values that are rescaled to 8bit % values In this particular case, there is one color with rgb values that % are negative. This means that this color can not be rendered on this % monitor. It is out of the monitor's gamut. The blue-ish square near the % lower left corner, for example, is the color that is out of range. % Also, we used an arbitrary scale factor to display these colors. Change % the scaleFactor to 255 and re-evaluate to display the same surfaces under % "dim" daylight conditions. % If it weren't for this gamut problem (arbitrary scale factor and clipping % negative values), and if the phosphor spectra accurately reflected your % screen, the Macbeth chart you see would look like it would look under % normal daylight. % Try rendering the Macbeth Color Checker under the xenon_flash % and cie_a illuminants. Does it look the same or different? Why?
Warning, image(s) 13 out of range.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % MONITOR SPECTRUM WITH ALL 3 PRIMARIES AT 1/2 MAX % In color vision experiments, it is frequently useful to control the % stimuli in terms of the cone responses that they evoke. As an example of % this and as another example of how to apply the principles of color % matching and trichromacy, we will calculte how to change the monitor % display intensities so as to affect only the S-cones. % Start with a light which is one-half the maximum intensity for all three % monitor primaries (each set to 128 assuming that each primary can be set % to range from 0 to 255). spectralSignalMean = phosphors' * [128 128 128]'; figure(1);clf plot(spectrum,spectralSignalMean); xlabel('Wavelength (nm)'); ylabel('Relative Energy'); title('Spectral power distribution of mean display');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % S-CONE ISOLATING STIMULUS % First we compute the cone responses to the monitor spectrum above: coneSignalsMean = cones * spectralSignalMean % Next we add a small amount to the S-cone response: coneSignalsDeltaS = coneSignalsMean + [0 0 10]' % Next we convert back from cone responses to monitor intensities: monitorSignalsDeltaS = cones_to_monitor * coneSignalsDeltaS % Note that all 3 of the monitor intensities are now different from 128 % (the value we started with). The 3rd one was increased a lot which makes % sense because this is the monitor phosphor that has most of its energy at % short wavelengths, and we wanted to increase the responses of the S cones % which are most sensitive to short wavelengths. The other two primaries % were decreased in intensity so as to maintain the L and M cone responses % as they were. The intensity of each primary has a differential affect on % each cone class (e.g., the 3rd primary having the greatest effect on the % S cones). But because of the broad spectrum of each of the monitor % primaries and because of the cone spectral sensitivities are also broad, % changing the intensity of any one primary will affect the responses of % all 3 cones to some extent. Turning down the intensities of the first two % primaries compensate (just right) for this. % Superimpose the two spectral signals corresponding to the mean and S cone % increment, and note the increase at short wavelenghts along with the % slight decrease at middle wavelengths. spectralSignalDeltaS = phosphors' * monitorSignalsDeltaS; figure(1);clf plot(spectrum,[spectralSignalMean spectralSignalDeltaS]); xlabel('Wavelength (nm)'); ylabel('Relative Energy'); title('Spectral power distribution of S cone increment'); % Recompute this for a decrease, rather than an increase, in the S cone % responses. What happens if you try to make a much larger decrease (or % increase) in the S cone responses (-100 or 100 instead of -10 or 10)?
coneSignalsMean = 33.7620 28.8917 20.3697 coneSignalsDeltaS = 33.7620 28.8917 30.3697 monitorSignalsDeltaS = 137.6374 114.1088 201.7764
