File: LMSToMacBoyn.m

package info (click to toggle)
psychtoolbox-3 3.0.19.14.dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 86,796 kB
  • sloc: ansic: 176,245; cpp: 20,103; objc: 5,393; sh: 2,753; python: 1,397; php: 384; makefile: 193; java: 113
file content (138 lines) | stat: -rw-r--r-- 6,345 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
function ls = LMSToMacBoyn(LMS,T_cones,T_lum)
% ls = LMSToMacBoyn(LMS,[T_cones,T_lum])
%
% Compute MacLeod-Boynton chromaticity from cone exciation coordinates.
% This is L/Lum and S/Lum, with appropriate normalization as described
% below. Recommended usage yields MB chromacities according to CIE 170-2:2015.
%
% ** Recommended Usage: Compute LMS with respect to some specified T_cones,
% and pass both T_cones as well as the cooresponding T_lum (the photopic
% luminosity function.) T_lum should be a linear combination of the L and M
% cone fundamentals.
% 
% This routine will then scale passed L and M values so that they sum to
% the best linear approximation of luminance and then normalize the L
% excitation by luminance (as computed by the linear combination) to obtain
% the l chromaticity. It will normalize the S excitaiton by luminance to
% obtain s chromaticity, with an overall scaling so that the maximum value
% of this chromaticity is 1 taken over the visible spectrum.
%
% Note that the s cone scaling can vary a bit depending on the wavelength
% sampling of the passed T_cones and T_lum, since the max is taken over
% these. If you use the T_cones_ss2/T_cones_ss10 and T_CIE_Y2/T_CIE_Y10
% files provided in PTB, the default sampling is at 1 nm and this is fine.
% If you use subsampled wavelength spacing, the computation of the s cone
% scaling will begin to deviate from the standard.  But so will your
% computation of LMS values, so this isn't really an issue specific to this
% routine.
%
% When you use the CIE cone fundamentals and corresponding luminance
% functions, this procedure yields the MacLeod-Boynton chromaticity
% diagrams as specified in CIE 170-2:2015.
%
% ** Legacy Usage: Just pass LMS values. In this case, we assume that the
% passed LMS values were computed with respect to the Smith-Pokorny
% fundamentals normalized to a peak of 1 and Judd-Vos luminance (more or
% less).  That is, this usage assumes LMS was computed using the
% fundamentals stored in PTB's T_cones_sp. This is old usage and preserved
% for backwards compatibility, but the three argument usage as described
% above is preferred for clarity. Moreover, in this case, the s
% chromaticity is not further normalized.  This leads to S chromaticities
% considerably larger than those obtained with the new usage.
%
% ** A Backwards Incompatibility. The scaling for s chromaticity to match
% CIE 170-2:2015 was introduced in Janurary 2019 and is not backwards
% compatible with previous behavior when T_cones and T_lum are passed.
% Preserving such compatibility did not seem important enough relative to
% the gains of having this work as now specified in the CIE standard.
%
% 10/30/97  dhb  Wrote it.
% 7/9/02    dhb  T_cones_sp -> T_cones on line 20.  Thanks to Eiji Kimura.
% 1/23/19   dhb  Scale s chromaticity value to be consistent with CIE
%                170-2:2015, when T_cones and T_lum are passed.  This is
%                not backwards compatible with previous scaling, but it
%                seems good to match the standard. Thanks to Danny Garside
%                for pointing out the scaling specified in the 2015
%                standard.

% Examples:
%{
    % Recreate the spectrum locus and equal energy white shown in Figure 8.2
    % of CIE 170-2:2015. Also performs a regression check.
    load T_cones_ss2
    load T_CIE_Y2
    lsSpectrumLocus = LMSToMacBoyn(T_cones_ss2,T_cones_ss2,T_CIE_Y2);
 
    % Compute the sum of the ls values in the spectrum locus, and compare
    % to the value that this example computed in February 2019, entered
    % here to four places as 412.2608.  This comparison provides a
    % check that this routine still works the way it did when we put in the
    % check.
    check = round(sum(lsSpectrumLocus(:)),4);
    if (abs(check-412.2608) > 1e-4)
        error('No longer get same check value as we used to');
    end

    % Compute ls for equal energy white
    LMSEEWhite = sum(T_cones_ss2,2);
    lsEEWhite = LMSToMacBoyn(LMSEEWhite,T_cones_ss2,T_CIE_Y2); 

    % Plot
    figure; hold on;
    plot(lsSpectrumLocus(1,:)',lsSpectrumLocus(2,:)','r','LineWidth',3);
    plot(lsEEWhite(1),lsEEWhite(2),'bs','MarkerFaceColor','b','MarkerSize',12);
    xlim([0.4 1]); ylim([0,1]);
    xlabel('l chromaticity'); ylabel('s chromaticity');
    title('CIE 170-2:2015 Figure 8.2');
%}
%{
    % Demonstrate invariance of ls after scaling of cones and luminance, as
    % long as LMS valued are computed with respect to passed cones and
    % luminance.
    load T_cones_ss2
    load T_CIE_Y2

    % Spectrum locus ls chromaticity with no scaling.
    lsSpectrumLocus = LMSToMacBoyn(T_cones_ss2,T_cones_ss2,T_CIE_Y2); 


    % Do some scaling and recompute spectrum locus.
    % The choices of 1.8, 3, 0.05 as scaling are
    % just three numbers I made up. You can use any three numbers and it
    % should still work, modulo any numerical issues with very big or
    % very small numbers.
    T_CIE_Y2_scaled = 1.8*T_CIE_Y2;
    T_cones_ss2_scaled = T_cones_ss2;
    T_cones_ss2_scaled(1,:) = 3*T_cones_ss2(1,:);
    T_cones_ss2_scaled(3,:) = 0.05*T_cones_ss2(3,:);
    lsSpectrumLocusScaled = LMSToMacBoyn(T_cones_ss2_scaled,T_cones_ss2_scaled,T_CIE_Y2_scaled); 

    % Make sure the difference is very small numerically.
    diff = max(abs(lsSpectrumLocus(:)-lsSpectrumLocusScaled(:)));
    fprintf('Difference in spectrum locus chromaticity after scaling is %0.5f (should be small)\n',diff);

    % Plot the locus computed both ways to compare visually.
    figure; hold on;
    plot(lsSpectrumLocus(1,:)',lsSpectrumLocus(2,:)','r','LineWidth',3);
    plot(lsSpectrumLocusScaled(1,:)',lsSpectrumLocusScaled(2,:)','g','LineWidth',2);
    xlim([0.4 1]); ylim([0,1]);
    xlabel('l chromaticity'); ylabel('s chromaticity');
%}

% Scale LMS so that L+M = luminance and S cone value corresponds to a
% fundamental with a max of 1.
if (nargin == 1)
	LMS = diag([0.6373 0.3924 1]')*LMS;
elseif (nargin == 3 )
	factorsLM = (T_cones(1:2,:)'\T_lum');
    factorS = 1/max(T_cones(3,:)./(factorsLM(1)*T_cones(1,:) + factorsLM(2)*T_cones(2,:)));
	LMS = diag([factorsLM ; factorS])*LMS;
else
    error('Number of input arguments should be either 1 or 3');
end

% Compute ls coordinates from LMS
n = size(LMS,2);
ls = zeros(2,n);
denom = [1 1 0]*LMS;
ls = LMS([1 3],:) ./ ([1 1]'*denom);