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);
|