PsychToolBox colour difference assessment project

1 view (last 30 days)
Hello. I'm currently working on my Bachelor thesis. In its' practical part I'd like to conduct an experiment where a set of colours would be presented as a number of pairs, where one side of colour pair would be fixed whilst the second one may be changed by the observer (using keyboard) until it, from his viewpoint, matches the fixed colour. The testing shall cover multiple colour centres (to fill the whole gamut of a WCG display). The experiment shall be conducted under non-standard conditions (my BP aims to investigate the ability to distinguish colours under mesopic, quasi-scotopic conditions, but that, I assume, will hardly affect the code itself).
Here is a draft of my code (using PTB), which doesnt allow me to perform the mentioned adjustments of the colour using keyboard and also. I'd also like to know, wheter there are some built in functions that allow to draw McAdam (JND distinguishing) ellipses. And if there's no such a funtion, I'm sure that wise men of this forum, who are much more experienced than I am, will give me some advice on how to do that only using native PTB.
% Initialize Psychtoolbox
addpath(genpath('Psychtoolbox'));
Screen('Preference','VisualDebugLevel', 0);
Screen('Preference', 'SkipSyncTests', 1);
PsychDefaultSetup(2);
screenNumber = max(Screen('Screens'));
[window, windowRect] = PsychImaging('OpenWindow', screenNumber, 0);
[xCenter, yCenter] = RectCenter(windowRect);
% Define colour centres in CIELAB space
colorCenters = [50, 20, 20; 70, -15, -15; 60, 30, 30]; % Example LAB values
% Convert LAB to RGB (assuming D65 illuminant)
RGBColors = lab2rgb(colorCenters, 'ColorSpace', 'srgb', 'WhitePoint', 'd65') * 255;
% Setup trial
numTrials = size(RGBColors, 1);
results = zeros(numTrials, 3); % To store matched colors
for i = 1:numTrials
% Display the color center
fixedColor = RGBColors(i, :);
Screen('FillRect', window, fixedColor, [xCenter - 100, yCenter - 100, xCenter, yCenter]);
% Initial match color is the same as the center color
matchColor = fixedColor;
matchLAB = colorCenters(i, :);
% Allow user to adjust the second color
keepAdjusting = true;
while keepAdjusting
% Display the match color next to the center color
Screen('FillRect', window, matchColor, [xCenter, yCenter - 100, xCenter + 100, yCenter]);
Screen('Flip', window);
% Check keyboard input to adjust color
[keyIsDown, ~, keyCode, ~] = KbCheck;
if keyIsDown
if keyCode(KbName('RightArrow'))
matchLAB(2) = matchLAB(2) + 1; % Increment a* for simplicity
elseif keyCode(KbName('LeftArrow'))
matchLAB(2) = matchLAB(2) - 1; % Decrement a*
elseif keyCode(KbName('Return'))
keepAdjusting = false; % Confirm color match
end
matchColor = lab2rgb(matchLAB, 'ColorSpace', 'srgb', 'WhitePoint', 'd65') * 255;
WaitSecs(0.1); % Limit speed of adjustments
end
end
% Log results
results(i, :) = matchLAB;
end
% Close window
sca;

Answers (1)

Ayush
Ayush on 21 May 2024
Hello Stepan,
The direction that you have taken for implementing your experiment is a good starting point. I would be building on top of this implementation to further your request and giving a brief about my rationale for the change.
Extended Color Adjustment Functionality:
The provided code is missing a key part that resets after processing the “keyIsDown” flag which could lead to issues with the loop not properly waiting for new input and thus lead to an improper color adjustment algorithm. Furthermore, your code adjusts only the “a*” value in the CIELAB color space. To extend the functionality, you would need to allow adjustments across different dimensions such as “L*”, “b*”, etc. You can refer to the below code snippet for your reference:
% Check keyboard input to adjust color
[keyIsDown, ~, keyCode] = KbCheck;
if keyIsDown
if keyCode(KbName('RightArrow'))
matchLAB(2) = matchLAB(2) + 1; % Increment a*
elseif keyCode(KbName('LeftArrow'))
matchLAB(2) = matchLAB(2) - 1; % Decrement a*
elseif keyCode(KbName('UpArrow'))
matchLAB(1) = matchLAB(1) + 1; % Increment L*
elseif keyCode(KbName('DownArrow'))
matchLAB(1) = matchLAB(1) - 1; % Decrement L*
elseif keyCode(KbName('w'))
matchLAB(3) = matchLAB(3) + 1; % Increment b*
elseif keyCode(KbName('s'))
matchLAB(3) = matchLAB(3) - 1; % Decrement b*
elseif keyCode(KbName('Return'))
keepAdjusting = false; % Confirm color match
end
matchColor = lab2rgb(matchLAB, 'ColorSpace', 'srgb', 'WhitePoint', 'd65') * 255;
WaitSecs(0.1); % Limit speed of adjustments
% Reset keyIsDown to avoid multiple detections
KbReleaseWait;
end
Handling McAdam Ellipses:
Although there is no direct support for drawing McAdam Ellipses using the Psychtoolbox, you can manually implement a supplementary function which defines the ellipses based on the standard deviations in the “L*”, “a*” and “b*” dimensions. You can refer to the below example code snippet for your reference.
% example function to draw an ellipse representing a JND in CIELAB space
function drawMcAdamEllipse(window, centerLAB, stdLAB, colorRGB)
% Convert ellipse center from LAB to RGB
centerRGB = lab2rgb(centerLAB, 'ColorSpace', 'srgb', 'WhitePoint', 'd65') * 255;
% Define ellipse parameters (highly simplified and not accurate)
% This part would require actual McAdam ellipse data or a model to calculate
ellipseWidth = stdLAB(2) * 10; % Assuming some conversion from stdLAB to pixels
ellipseHeight = stdLAB(3) * 10; % Ditto
% Draw the ellipse (placeholder function, you'd have to implement drawing based on these parameters)
Screen('FillOval', window, colorRGB, [centerRGB(1) - ellipseWidth / 2, centerRGB(2) - ellipseHeight / 2, centerRGB(1) + ellipseWidth / 2, centerRGB(2) + ellipseHeight / 2]);
end
For more information and updates on the Psychtoolbox, you can refer to the following documentation and even reach out to the respective file exchange community for feature requests and discussions:
Hope this helps!

Products

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!