File: PTBAndIsetbioColorimetryTest.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 (307 lines) | stat: -rw-r--r-- 10,718 bytes parent folder | download | duplicates (3)
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
% PTBAndIsetbioColorimetryTest
% 
% This script compares values calculated by PTB and routines in isetbio (developed by
% Wandell and colleague) for various colorimetric functions.
%
% For information on isetbio, see https://github.com/isetbio/isetbio/wiki.
% It contains its own implementation of many of the colorimetric computations 
% implemented in PTB.  Note that Brainard and Wandell are not completely
% independent sources.
%
% Make sure isetbio is on your path to run these comparisons.  The tests should
% also work if you have the proprietary iset on your path instead.
% isetbio is available on gitHub as https://github.com/isetbio/isetbio.git.
%
% The checks are grouped into cells that check one thing at a time.
%
% Because DHB and BAW have too much time on their hands -
%  "A man with one watch always knows what time it is, a man with two is never sure."
%  "A man with SPM knows how to blur and average his watches so that he can't tell the time"
%
% 7/7/10  dhb  Wrote it.
% x/xx/10 baw  Checked with ISET-4.0 revision 351 (BW)
% 2/28/13 dhb  Updated to work with vset rather than proprietary iset.
% 8/5/13  dhb  Updated to work with isetbio, vset's successor.
% 7/31/14 dhb, ncp Put a cd around call to isetbio xyz2lab to deal with
%              fact that 2014b has added a routine with this name that
%              doesn't work quite the same.
% 2/09/21 mk   Fix for renamed xyz2lab -> ieXYZ2LAB() and ieLAB2XYZ().

if IsOctave
    % Suppress false Octave path warnings:
    warning('off', 'Octave:data-file-in-path', 'local');
end

%% Clear and close
clear; close all;
if (exist('isetbioRootPath','file'))
    fprintf('\nComparing PTB and isetbio Colorimetry\n');
else
    error('Need isetbio on your path to run this test');
end

%% Play the namespace game as necessary.
% This sets up what we need.
isetbioPath = fileparts(which('colorTransformMatrix'));
curDir = pwd;

%% XYZ-related colorimetry
%
% xy
fprintf('\n***** Basic XYZ *****\n');
testXYZs = [[1 2 1]' [2 1 0.5]' [1 1 1]' [0.6 2.3 4]'];
ptbxyYs = XYZToxyY(testXYZs);
ptbxys = ptbxyYs(1:2,:);
isetxys = chromaticity(testXYZs')';
if (any(abs(ptbxys-isetxys) > 1e-10))
    fprintf('PTB-ISET DIFFERENCE for XYZ to xy\n');
else
    fprintf('PTB-ISET AGREE for XYZ to xy\n');
end

%% xyY
ptbXYZs = xyYToXYZ(ptbxyYs);
if (any(abs(testXYZs-ptbXYZs) > 1e-10))
    fprintf('PTB FAILS XYZ to xyY to XYZ\n');
else
    fprintf('PTB PASSES XYZ to xyY to XYZ\n');
end
isetXYZs = xyy2xyz(ptbxyYs')';
if (any(abs(testXYZs-isetXYZs) > 1e-10))
    fprintf('PTB-ISET DIFFERENCE for xyY to XYZ\n');
else
    fprintf('PTB-ISET AGREE for xyY to XYZ\n');
end 

%% CIE uv chromaticity
ptbuvYs = XYZTouvY(testXYZs);
ptbuvs = ptbuvYs(1:2,:);
[isetus,isetvs] = xyz2uv(testXYZs');
isetuvs = [isetus' ; isetvs'];
if (any(abs(ptbuvs-isetuvs) > 1e-10))
    fprintf('PTB-ISET DIFFERENCE for XYZ to uv\n');
    fprintf('\tI think this is because ISET implements an obsolete version of the standard\n');
else
    fprintf('PTB-ISET AGREE for XYZ to uv\n');
end

%% CIELUV
whiteXYZ = [3,4,3]';
ptbLuvs = XYZToLuv(testXYZs,whiteXYZ);
isetLuvs = xyz2luv(testXYZs',whiteXYZ')';
if (any(abs(ptbLuvs-isetLuvs) > 1e-10))
    fprintf('PTB-ISET DIFFERENCE for XYZ to Luv\n');
    fprintf('\tPresumably because the uv transformation differs.\n');
else
    fprintf('PTB-ISET AGREE for XYZ to Luv\n');
end

%% CIELAB
whiteXYZ = [3,4,3]';
ptbLabs = XYZToLab(testXYZs,whiteXYZ);
cd(isetbioPath);
isetLabs = ieXYZ2LAB(testXYZs',whiteXYZ')';
cd(curDir);
if (any(abs(ptbLabs-isetLabs) > 1e-10))
    fprintf('PTB-ISET DIFFERENCE for XYZ to Lab\n');
else
    fprintf('PTB-ISET AGREE for XYZ to Lab\n');
end
ptbXYZCheck = LabToXYZ(ptbLabs,whiteXYZ);
isetXYZCheck = ieLAB2XYZ(isetLabs',whiteXYZ')';
if (any(abs(testXYZs-ptbXYZCheck) > 1e-10))
    fprintf('PTB FAILS XYZ to Lab to XYZ\n');
else
    fprintf('PTB PASSES XYZ to Lab to XYZ\n');
end
if (any(abs(testXYZs-isetXYZCheck) > 1e-10))
    fprintf('ISET FAILS XYZ to Lab to XYZ\n');
else
    fprintf('ISET PASSES XYZ to Lab to XYZ\n');
end

%% sRGB
%
% The iset routines seem to use a matrix that is 1/100 of the standard
% definition.  Or, at least, 1/100 of what the PTB routines use.  To
% account for this, I multiply XYZ values by 100 before passing them
% to the iset routines.
%
% The iset routines take an exponent argument for the sRGB gamma transform.
% At http://www.w3.org/Graphics/Color/sRGB this is specified as 2.4.  The
% iset routine xyz2srgb uses 2.2, with a comment that an update at
% www.srgb.com changed this from 2.4 to 2.2.  I think this is wrong,
% though.  The text I can find on the web says that the 2.4 exponent, plus
% the linear initial region, is designed to approximate a gamma of 2.2.
% That is, you want 2.4 in the formulae to approximate the 2.2 industry
% standard gamma.  Site www.srgb.com now appears gone, by the way, but all
% the other sites I find seem to be the same in this regard.
% 
% Also note that if you change the exponent in the iset sRGB formulae, you
% also should probably change the cutoff used at the low-end, where the
% sRGB standard special cases the functional form of the gamma curve.  Here
% the test is set for 2.4.
%
% Finally,the default gamma used by iset lrgb2srgb and by xzy2srgb
% currently differ, so you really want to be careful using these.  The
% inverse routine srgb2lrgb doesn't allow passing of the exponent, and it
% is hard coded as 2.4.  This causes a failure of the iset sRGB gamma
% routines to self-invert for gamma other than 2.4, and with their
% defaults.
%
% One other convention difference is that the PTB routine rounds to
% integers for the settings, while the iset routine leaves the rounding up
% to the caller.
fprintf('\n***** sRGB *****\n');

% Create some test sRGB values and convert them in the PTB framework
ptbSRGBs = [[188 188 188]' [124 218 89]' [255 149 203]' [255 3 203]'];
ptbSRGBPrimary = SRGBGammaUncorrect(ptbSRGBs);
ptbXYZs = SRGBPrimaryToXYZ(ptbSRGBPrimary);

% The ISET form takes the frame buffer values in the [0,1] regime
isetSRGBs = ptbSRGBs/255;
isetSRGBs = XW2RGBFormat(isetSRGBs',4,1);
isetXYZ   = srgb2xyz(isetSRGBs);
isetXYZs  = RGB2XWFormat(isetXYZ)';

if (any(abs(isetXYZs-ptbXYZs) > 1e-10))
    d = isetXYZs - ptbXYZs;
    fprintf('PTB-ISET DIFFERENCE for XYZ to sRGB: %f\n',max(abs(d(:))));
    d = d ./ptbXYZs;
    fprintf('PTB-ISET Percent XYZ DIFFERENCE: %f\n',max(abs(d(:))));
else
    fprintf('PTB-ISET AGREE for XYZ to sRGB\n');
end

% PTB testing of inversion
if (any(abs(XYZToSRGBPrimary(ptbXYZs)-ptbSRGBPrimary) > 1e-10))
    fprintf('PTB FAILS linear sRGB to XYZ to linear sRGB\n');
else
    fprintf('PTB PASSES linear sRGB to XYZ to linear sRGB\n');
end
if (any(abs(SRGBGammaCorrect(ptbSRGBPrimary)-ptbSRGBs) > 1e-10))
    fprintf('PTB FAILS sRGB to linear sRGB to sRGB\n');
else
    fprintf('PTB PASSES sRGB to linear sRGB to sRGB\n');
end

% Compare sRGB matrices
[nil,ptbSRGBMatrix] = XYZToSRGBPrimary([]);
isetSRGBMatrix = colorTransformMatrix('xyz2srgb')';

if (any(abs(ptbSRGBMatrix-isetSRGBMatrix) > 1e-10))
    fprintf('PTB-ISET DIFFERENCE for sRGB transform matrix\n');
else
    fprintf('PTB-ISET AGREE for sRGB transform matrix\n');
end

% XYZ -> lRGB 
% Reformat shape
ptbXYZsImage = CalFormatToImage(ptbXYZs,1,size(ptbXYZs,2));

% ISET convert 
[isetSRGBImage,isetSRGBPrimaryImage] = xyz2srgb(ptbXYZsImage);

% Reformat shape
isetSRGBs = ImageToCalFormat(isetSRGBImage); 
isetSRGBPrimary = ImageToCalFormat(isetSRGBPrimaryImage);

if (any(abs(ptbSRGBPrimary-isetSRGBPrimary) > 1e-10))
    d = ptbSRGBPrimary - isetSRGBPrimary;
    fprintf('PTB-ISET DIFFERENCE for XYZ to sRGB: %f\n',max(abs(d(:))));
    d = d ./isetSRGBPrimary;
    fprintf('PTB-ISET Percent RGB DIFFERENCE: %f\n',max(abs(d(:))));
else
    fprintf('PTB-ISET AGREE for XYZ to linear sRGB\n');
end

% ISET/PTB sRGB comparison in integer gamma corrected space
if (any(abs(round(isetSRGBs*255)-ptbSRGBs) > 1e-10))
    fprintf('PTB-ISET DIFFERENCE for XYZ to sRGB\n');
else
    fprintf('PTB-ISET AGREE for XYZ to sRGB\n');
end

% lrgb -> srgb -> lrgb in ISET
isetSRGBPrimaryCheckImage = srgb2lrgb(isetSRGBImage);
isetSRGBPrimaryCheck = ImageToCalFormat(isetSRGBPrimaryCheckImage);
if (any(abs(isetSRGBPrimaryCheck-isetSRGBPrimary) > 1e-10))
    fprintf('ISET FAILS linear sRGB to sRGB to linear sRGB\n');
else
    fprintf('ISET PASSES linear sRGB to sRGB to linear sRGB\n');
end

%% Quanta/Energy
%
% The ISET routines define c and h to more places than the PTB, so the
% agreement is only good to about 5 significant places.  Seems OK to me.
fprintf('\n***** Energy/Quanta *****\n');
load spd_D65
spdEnergyTest = spd_D65;
wlsTest = SToWls(S_D65);
testPlaces = 5;
ptbQuanta = EnergyToQuanta(wlsTest,spdEnergyTest);
isetQuanta = Energy2Quanta(wlsTest,spdEnergyTest);
if (any(abs(ptbQuanta-isetQuanta) > (10^-testPlaces)*min(ptbQuanta)))
    fprintf('PTB-ISET DIFFERENCE for energy to quanta conversion at %d significant places\n',testPlaces);
else
    fprintf('PTB-ISET AGREE for energy to quanta conversion to %d significant places\n',testPlaces);
end
if (any(abs(QuantaToEnergy(wlsTest,ptbQuanta)-spdEnergyTest) > 1e-10))
    fprintf('PTB FAILS energy to quanta to energy\n');
else
    fprintf('PTB PASSES energy to quanta to energy\n');
end
if (any(abs(Quanta2Energy(wlsTest,isetQuanta')'- spdEnergyTest) > 1e-10))
    fprintf('ISET FAILS energy to quanta to energy\n');
else
    fprintf('ISET PASSES energy to quanta to energy\n');
end

%% CIE daylights
% 
% These routines are now running in ISET and everything agrees.
fprintf('\n***** CIE Daylights *****\n');
load B_cieday
testWls = SToWls(S_cieday);
testTemp = 4987;
ptbDaySpd = GenerateCIEDay(testTemp,B_cieday);
ptbDaySpd = ptbDaySpd/max(ptbDaySpd(:));

% Iset version of normalized daylight
isetDaySpd = daylight(testWls,testTemp);
isetDaySpd = isetDaySpd/max(isetDaySpd(:));
if (any(abs(isetDaySpd-ptbDaySpd) > 1e-10))
    fprintf('PTB-ISET DIFFERENCE for daylight');
else
    fprintf('PTB-ISET AGREE for daylight\n');
end

%% Not yet really implemented
% The code below calls some isetbio routines that
% either don't yet have PTB counterparts, or for which
% we have not yet written the comparison code.

% Calculate correlated color temperature
isetColorTemp = cct([.31,.32]');

% Calculate spd of a daylight with some color temperature
wave = 400:700;
Tc = 6500; spd = cct2sun(wave, Tc, 'energy'); % plot(wave,spd);

% deltaE94.m
% deltaE2000.m
% deltaEab.m
% deltaEuv.m

% ieResponsivityConvert.m
% ieScotopicLuminanceFromEnergy.m
bb = blackbody(wave,6500,'watts');
chromaticity(ieXYZFromEnergy(bb',wave));
ieLuminanceFromEnergy(bb',wave);

bb = blackbody(wave,6500,'quanta');
chromaticity(ieXYZFromPhotons(bb',wave));
ieLuminanceFromPhotons(bb',wave);