|
1 | 1 | % Make a figure showing our monitor gamut in the LS contrast plane |
2 | 2 |
|
3 | 3 | %% Initialize |
4 | | -clear; %close all; |
| 4 | +clear; close all; |
5 | 5 |
|
6 | 6 | %% Where to write figure |
7 | 7 | figureDir = getpref('ColorTracking','figureSavePath'); |
8 | 8 |
|
9 | 9 | %% Load typical calibration file from the experiment |
10 | | -whichExperiment = 'detection'; |
| 10 | +whichExperiment = 'tracking'; |
11 | 11 | switch (whichExperiment) |
12 | 12 | case 'tracking' |
13 | 13 | whichCalFile = 'ViewSonicG220fb.mat'; |
|
39 | 39 | % Make the bit depth correct as far as the calibration file goes. |
40 | 40 | nDeviceLevels = 2^nDeviceBits; |
41 | 41 | CalibrateFitGamma(calObjCones, nDeviceLevels); |
| 42 | +CalibrateFitGamma(calObjCones1, nDeviceLevels); |
42 | 43 | nPrimaries = calObjCones.get('nDevices'); |
43 | 44 |
|
44 | 45 | % Set gamma mode. A value of 2 was used in the experiment |
|
77 | 78 | T_cones1 = T_cones; |
78 | 79 | end |
79 | 80 | SetSensorColorSpace(calObjCones,T_cones,Scolor); |
80 | | -SetSensorColorSpace(calObjCones1,T_cones,Scolor); |
| 81 | +SetSensorColorSpace(calObjCones1,T_cones1,Scolor); |
81 | 82 |
|
82 | 83 | %% XYZ cal object |
83 | 84 | calObjXYZ = ObjectToHandleCalOrCalStruct(cal); |
84 | 85 | calObjXYZ1 = ObjectToHandleCalOrCalStruct(cal); |
85 | 86 |
|
86 | 87 | % Get gamma correct |
87 | 88 | CalibrateFitGamma(calObjXYZ, nDeviceLevels); |
| 89 | +CalibrateFitGamma(calObjXYZ1, nDeviceLevels); |
88 | 90 | SetGammaMethod(calObjXYZ,gammaMode); |
89 | 91 | SetGammaMethod(calObjXYZ1,gammaMode); |
90 | 92 | if (NOAMBIENT) |
|
313 | 315 | % angular directions |
314 | 316 | specificVectorLengthContrast; |
315 | 317 | theSpecificAngles; |
316 | | -clear theSpecficAngles |
317 | 318 |
|
318 | 319 | %% Convert cone contrasts with respect to first calibration to second. |
319 | 320 | % |
320 | 321 | % Angles from paper Figure 5 labels, contrasts read off of those graphs |
321 | 322 | % roughly by eye. If we go to 16 bit depth, the match would have been very |
322 | 323 | % good, had the detection calibration been used the way it should have |
323 | 324 | % been. |
324 | | -theDetectionSpecificAngles = [-86.25 -82.5 -78.75 -75 -45 0 45 75 78.75 82.5 86.25 90]; |
325 | | -theVectorLengthContrasts = [ 0.05 0.03 0.02 0.017 0.005 0.003 0.004 0.015 0.02 0.03 0.04 0.05]; |
326 | | -for aa = 1:length(theDetectionSpecificAngles) |
327 | | - fprintf('Angle %0.1f, vector length contrast %0.1f\n',theDetectionSpecificAngles(aa),100*theVectorLengthContrasts(aa)); |
328 | | - |
329 | | - % Convert from cone contrast to cone excitation direction. |
330 | | - % Don't care about length here as that is handled by the contrast |
331 | | - % maximization code below. |
332 | | - targetContrastDir = [cosd(theDetectionSpecificAngles(aa)) 0 sind(theDetectionSpecificAngles(aa))]'; |
333 | | - targetConeContrast(:,aa) = (theVectorLengthContrasts(aa)*targetContrastDir); |
334 | | - |
335 | | - % Convert from cone contrast to cone excitation direction. |
336 | | - % Don't care about length here as that is handled by the contrast |
337 | | - % maximization code below. |
338 | | - targetCones = (targetConeContrast(:,aa).* bgCones) + bgCones; |
339 | | - |
340 | | - % Compute settings we would have used using first calibration |
341 | | - [theSettings,badIndex] = SensorToSettings(calObjCones,targetCones); |
342 | | - if (any(badIndex)) |
343 | | - fprintf(' Out of gamut\n'); |
| 325 | +% |
| 326 | +% Use this code to correct the detection stimuli for further analysis |
| 327 | +subjID = 'MAB'; |
| 328 | +switch (whichExperiment) |
| 329 | + case 'tracking' |
| 330 | + contrastLim = 1.0; |
| 331 | + contrastDevLim = 0.06; |
| 332 | + angleDevLim = 4; |
| 333 | + expNameCell = { 'Experiment1-Pos' 'Experiment2-Pos' ['Experiment3-' subjID '-Pos']}; |
| 334 | + expContrastLMS = []; |
| 335 | + for ii = 1:length(expNameCell) |
| 336 | + expContrastLMS = [expContrastLMS ; LMSstimulusContrast('experiment',expNameCell{ii})]; |
| 337 | + end |
| 338 | + for ii = 1:size(expContrastLMS,1) |
| 339 | + targetAngle(ii) = atand(expContrastLMS(ii,2),expContrastLMS(ii,1)); |
| 340 | + targetContrast(ii) = norm([expContrastLMS(ii,1) expContrastLMS(ii,2)]); |
| 341 | + end |
| 342 | + |
| 343 | + targetAngleRaw = theSpecificAngles; |
| 344 | + targetContrast = specificVectorLengthContrast; |
| 345 | + case {'detection', 'detectionRaw'}; |
| 346 | + % Options are 'MAB', 'BMC', 'KAS' |
| 347 | + contrastLim = 0.3; |
| 348 | + contrastDevLim = 0.015; |
| 349 | + angleDevLim = 2; |
| 350 | + |
| 351 | + [targetContrast,targetAngleRaw] = getContrastLSD(subjID,'combined'); |
| 352 | + targetAngleRaw = targetAngleRaw'; |
| 353 | +end |
| 354 | +for cc = 1:size(targetContrast,1) |
| 355 | + targetAngle(cc,:) = targetAngleRaw; |
| 356 | + for aa = 1:length(targetAngleRaw) |
| 357 | + fprintf('Angle %0.1f, vector length contrast %0.1f\n',targetAngle(cc,aa),100*targetContrast(cc,aa)); |
| 358 | + |
| 359 | + % Convert from cone contrast to cone excitation direction. |
| 360 | + % Don't care about length here as that is handled by the contrast |
| 361 | + % maximization code below. |
| 362 | + targetContrastDir = [cosd(targetAngle(cc,aa)) 0 sind(targetAngle(cc,aa))]'; |
| 363 | + targetConeContrast(:,cc,aa) = (targetContrast(cc,aa)*targetContrastDir); |
| 364 | + |
| 365 | + % Convert from cone contrast to cone excitation direction. |
| 366 | + % Don't care about length here as that is handled by the contrast |
| 367 | + % maximization code below. |
| 368 | + targetCones = (targetConeContrast(:,cc,aa).* bgCones) + bgCones; |
| 369 | + |
| 370 | + % Compute settings we would have used using first calibration |
| 371 | + [theSettings,badIndex] = SensorToSettings(calObjCones,targetCones); |
| 372 | + if (any(badIndex)) |
| 373 | + fprintf(' Out of gamut\n'); |
| 374 | + end |
| 375 | + |
| 376 | + % Go back to contrast with both calibrations |
| 377 | + obtainedCones(:,cc,aa) = SettingsToSensor(calObjCones,theSettings); |
| 378 | + obtainedCones1(:,cc,aa) = SettingsToSensor(calObjCones1,theSettings); |
| 379 | + obtainedConeContrast(:,cc,aa) = ((obtainedCones(:,cc,aa)-bgCones) ./ bgCones); |
| 380 | + obtainedConeContrast1(:,cc,aa) = ((obtainedCones1(:,cc,aa)-bgCones1) ./ bgCones1); |
| 381 | + |
| 382 | + % Need to deal with angle flipping which can happen for small |
| 383 | + % contrasts. |
| 384 | + obtainedAngle(cc,aa) = atand(obtainedConeContrast(3,cc,aa)/obtainedConeContrast(1,cc,aa)); |
| 385 | + if (targetAngle(cc,aa) > 0 && obtainedAngle(cc,aa) < 0) |
| 386 | + obtainedAngle(cc,aa) = obtainedAngle(cc,aa) + 180; |
| 387 | + end |
| 388 | + if (targetAngle(cc,aa) < 0 && obtainedAngle(cc,aa) > 0) |
| 389 | + obtainedAngle(cc,aa) = obtainedAngle(cc,aa) - 180; |
| 390 | + end |
| 391 | + obtainedAngle1(cc,aa) = atand(obtainedConeContrast1(3,cc,aa)/obtainedConeContrast1(1,cc,aa)); |
| 392 | + if (targetAngle(cc,aa) > 0 && obtainedAngle1(cc,aa) < 0) |
| 393 | + obtainedAngle1(cc,aa) = obtainedAngle1(cc,aa) + 180; |
| 394 | + end |
| 395 | + if (targetAngle(cc,aa) < 0 && obtainedAngle1(cc,aa) > 0) |
| 396 | + obtainedAngle1(cc,aa) = obtainedAngle1(cc,aa) - 180; |
| 397 | + end |
| 398 | + obtainedContrast(cc,aa) = norm(obtainedConeContrast(:,cc,aa)); |
| 399 | + obtainedContrast1(cc,aa) = norm(obtainedConeContrast1(:,cc,aa)); |
| 400 | + angleDeviation(cc,aa) = obtainedAngle(cc,aa)-targetAngle(cc,aa); |
| 401 | + angleDeviation1(cc,aa) = obtainedAngle1(cc,aa)-targetAngle(cc,aa); |
| 402 | + contrastDeviation(cc,aa) = obtainedContrast(cc,aa) - targetContrast(cc,aa); |
| 403 | + contrastDeviation1(cc,aa) = obtainedContrast1(cc,aa) - targetContrast(cc,aa); |
| 404 | + |
| 405 | + % Figure out the cone excitations for the settings we computed, and |
| 406 | + % then convert to contrast as our maximum contrast in this direction. |
| 407 | + % |
| 408 | + % Dividing by imageScaleFactor handles the sine phase of the Gabor |
| 409 | + fprintf(' Target contrasts: L cone contrast %7.3f%%, M, %7.3f%%, S %7.3f%%, angle %7.1f, vector length %0.1f%%\n', ... |
| 410 | + 100*targetConeContrast(1,cc,aa),100*targetConeContrast(2,cc,aa),100*targetConeContrast(3,cc,aa), ... |
| 411 | + targetAngle(cc,aa),100*targetContrast(cc,aa)); |
| 412 | + fprintf(' Had ambient/cones used been right: L cone contrast %7.3f%%, M, %7.3f%%, S %7.3f%%, angle %7.1f, vector length %0.1f%%\n', ... |
| 413 | + 100*obtainedConeContrast(1,cc,aa),100*obtainedConeContrast(2,cc,aa),100*obtainedConeContrast(3,cc,aa), ... |
| 414 | + obtainedAngle(cc,aa),100*obtainedContrast(cc,aa)); |
| 415 | + fprintf(' What we actually got: L cone contrast %7.3f%%, M, %7.3f%%, S %7.3f%%, angle %7.1f, vector length %0.1f%%\n', ... |
| 416 | + 100*obtainedConeContrast1(1,cc,aa),100*obtainedConeContrast1(2,cc,aa),100*obtainedConeContrast1(3,cc,aa), ... |
| 417 | + obtainedAngle1(cc,aa),100*obtainedContrast1(aa)); |
344 | 418 | end |
| 419 | +end |
345 | 420 |
|
346 | | - % Go back to contrast with both calibrations |
347 | | - obtainedCones(:,aa) = SettingsToSensor(calObjCones,theSettings); |
348 | | - obtainedCones1(:,aa) = SettingsToSensor(calObjCones1,theSettings); |
349 | | - obtainedConeContrast(:,aa) = ((obtainedCones(:,aa)-bgCones) ./ bgCones); |
350 | | - obtainedConeContrast1(:,aa) = ((obtainedCones1(:,aa)-bgCones1) ./ bgCones1); |
351 | | - obtainedAngle(aa) = atand(obtainedConeContrast(3,aa)/obtainedConeContrast(1,aa)); |
352 | | - obtainedAngle1(aa) = atand(obtainedConeContrast1(3,aa)/obtainedConeContrast1(1,aa)); |
353 | | - obtainedVectorLength(aa) = norm(obtainedConeContrast(:,aa)); |
354 | | - obtainedVectorLength1(aa) = norm(obtainedConeContrast1(:,aa)); |
355 | | - angleDeviation(aa) = obtainedAngle(aa)-theDetectionSpecificAngles(aa); |
356 | | - angleDeviation1(aa) = obtainedAngle1(aa)-theDetectionSpecificAngles(aa); |
357 | | - vectorLengthDeviation(aa) = obtainedVectorLength(aa) - theVectorLengthContrasts(aa); |
358 | | - vectorLengthDeviation1(aa) = obtainedVectorLength1(aa) - theVectorLengthContrasts(aa); |
| 421 | +% Compute values to use |
| 422 | +targetAngleToUse = mean(obtainedAngle1,1); |
| 423 | +targetContrastToUse = obtainedContrast1; |
| 424 | +figure; clf; hold on; |
| 425 | +subplot(2,2,1); hold on; |
| 426 | +plot(targetAngleRaw,targetAngleToUse,'ro','MarkerFaceColor','r','MarkerSize',10); |
| 427 | +plot([-100 100],[-100 100],'k'); |
| 428 | +xlim([-100 100]); ylim([-100 100]); |
| 429 | +axis('square'); |
| 430 | +xlabel('Target Angle (deg)'); ylabel('Obtained Angle (deg)'); |
| 431 | +subplot(2,2,2); hold on; |
| 432 | +plot(100*targetContrast(:),100*targetContrastToUse(:),'ro','MarkerFaceColor','r','MarkerSize',10); |
| 433 | +plot([0 100*contrastLim],[0 100*contrastLim],'k'); |
| 434 | +xlim([0 100*contrastLim]); ylim([0 100*contrastLim]); |
| 435 | +axis('square'); |
| 436 | +xlabel('Target Contrast (%)'); ylabel('Obtained Contrast (%)'); |
| 437 | +subplot(2,2,3); hold on; |
| 438 | +plot(targetAngleRaw,targetAngleToUse-targetAngleRaw,'ro','MarkerFaceColor','r','MarkerSize',10); |
| 439 | +plot([-100 100],[0 0],'k'); |
| 440 | +xlim([-100 100]); ylim([-angleDevLim angleDevLim]); |
| 441 | +axis('square'); |
| 442 | +xlabel('Target Angle (deg)'); ylabel('Obtained Angle Deviation (deg)'); |
| 443 | +subplot(2,2,4); hold on; |
| 444 | +plot(100*targetContrast(:),100*targetContrastToUse(:)-100*targetContrast(:),'ro','MarkerFaceColor','r','MarkerSize',10); |
| 445 | +plot([0 100*contrastLim],[0 0],'k'); |
| 446 | +xlim([0 100*contrastLim]); ylim([100*-contrastDevLim 100*contrastDevLim]); |
| 447 | +axis('square'); |
| 448 | +xlabel('Target Contrast (%)'); ylabel('Obtained Contrast Deviation (%)'); |
359 | 449 |
|
360 | | - % Figure out the cone excitations for the settings we computed, and |
361 | | - % then convert to contrast as our maximum contrast in this direction. |
362 | | - % |
363 | | - % Dividing by imageScaleFactor handles the sine phase of the Gabor |
364 | | - fprintf(' Target contrasts: L cone contrast %7.3f%%, M, %7.3f%%, S %7.3f%%, angle %7.1f, vector length %0.1f%%\n', ... |
365 | | - 100*targetConeContrast(1,aa),100*targetConeContrast(2,aa),100*targetConeContrast(3,aa), ... |
366 | | - theDetectionSpecificAngles(aa),100*theVectorLengthContrasts(aa)); |
367 | | - fprintf(' Had ambient/cones used been right: L cone contrast %7.3f%%, M, %7.3f%%, S %7.3f%%, angle %7.1f, vector length %0.1f%%\n', ... |
368 | | - 100*obtainedConeContrast(1,aa),100*obtainedConeContrast(2,aa),100*obtainedConeContrast(3,aa), ... |
369 | | - obtainedAngle(aa),100*obtainedVectorLength(aa)); |
370 | | - fprintf(' What we actually got: L cone contrast %7.3f%%, M, %7.3f%%, S %7.3f%%, angle %7.1f, vector length %0.1f%%\n', ... |
371 | | - 100*obtainedConeContrast1(1,aa),100*obtainedConeContrast1(2,aa),100*obtainedConeContrast1(3,aa), ... |
372 | | - obtainedAngle1(aa),100*obtainedVectorLength1(aa)); |
373 | | -end |
374 | | -fprintf('\n Had ambient/cones used been right: Max abs angle deviation %0.4f, max abs vector length deviation %0.4f\n',max(abs(angleDeviation)),max(abs(vectorLengthDeviation))); |
375 | | -fprintf('\n What we actually got: Max abs angle deviation %0.4f, max abs vector length deviation %0.4f\n',max(abs(angleDeviation1)),max(abs(vectorLengthDeviation1))); |
| 450 | +% The angular deviations start to lose meaning as the contrast gets very |
| 451 | +% small |
| 452 | +fprintf('\n Had ambient/cones used been right: Max abs angle deviation %0.4f, max abs vector length deviation %0.4f\n',max(abs(angleDeviation(:))),max(abs(contrastDeviation(:)))); |
| 453 | +fprintf('\n What we actually got: Max abs angle deviation %0.4f, max abs vector length deviation %0.4f\n',max(abs(angleDeviation1(:))),max(abs(contrastDeviation1(:)))); |
376 | 454 |
|
377 | 455 | %% Gamut chromaticity plot for comparisons |
378 | 456 | % |
|
0 commit comments