|
@@ -1,265 +0,0 @@
|
|
-
|
|
|
|
-
|
|
|
|
-function varargout = Chromatic_Integration_Stimulus(varargin)
|
|
|
|
-
|
|
|
|
-%===================================================================================
|
|
|
|
-% Parameter Default Usage
|
|
|
|
-%===================================================================================
|
|
|
|
-
|
|
|
|
-% selecttextfile false Browse prompt to select the parameters text file
|
|
|
|
-% stimduration 30 Stimulus presentation duration screen refresh rate unit (30 = 0.5 sec)
|
|
|
|
-% preframes 120 Duration of background light presented in between the stimulus flashes
|
|
|
|
-% maxcontrast 0.2 Maximum Weber contrast value (ranges from -1 to 1)
|
|
|
|
-% mincontrast -0.2 Minimum Weber contrast value (ranges from -1 to 1)
|
|
|
|
-% contrastdiff 0.02 Contrast steps from mincontrast to maxcontrast e.g. -20 : 2 : 20
|
|
|
|
-% seed -1000 Starting value for random number generator (should be always negative)
|
|
|
|
-% redmeanintensity 0 Mean intensity for red gun of the screen
|
|
|
|
-% greenmeanintensity 0.5 Mean intensity for green gun of the screen
|
|
|
|
-% bluemeanintensity 0.5 Mean intensity for blue gun of the screen
|
|
|
|
-% screensize 864 x 480 Screen resolution, default value is the resolution of the lightcrafter projector
|
|
|
|
-% refreshrate 60 Screen refresh rate in Hz
|
|
|
|
-% fullscreen false Option to display the stimulus in full-screen mode
|
|
|
|
-% help false Option to check the list of available parameters
|
|
|
|
-% lmargin 0 Margins from left side of the screen (not available for this stimulus)
|
|
|
|
-% rmagin 0 Margins from right side of the screen (not available for this stimulus)
|
|
|
|
-% tmargin 0 Margins from top of the screen (not available for this stimulus)
|
|
|
|
-% bmargin 0 Margins from bottom of the screen (not available for this stimulus)
|
|
|
|
-% coneisolating false Option to activate opsin-isolation (excluded for simplicity)
|
|
|
|
-%
|
|
|
|
-%===================================================================================
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-para = read_stimulus_parameters(varargin{:});
|
|
|
|
-if para.help, return; end
|
|
|
|
-
|
|
|
|
-offcontrasts = fliplr(0:-para.contrastdiff:para.mincontrast);
|
|
|
|
-oncontrasts = 0:para.contrastdiff:para.maxcontrast;
|
|
|
|
-
|
|
|
|
-greencontrasts = [offcontrasts,oncontrasts];
|
|
|
|
-bluecontrasts = [oncontrasts,offcontrasts];
|
|
|
|
-
|
|
|
|
-% convert to weber contrast (mean+(contrast*mean))
|
|
|
|
-greencontrasts = para.greenmeanintensity + (greencontrasts * para.greenmeanintensity);
|
|
|
|
-bluecontrasts = para.bluemeanintensity + (bluecontrasts * para.bluemeanintensity);
|
|
|
|
-
|
|
|
|
-numcontrasts = size(greencontrasts,2);
|
|
|
|
-
|
|
|
|
-% The darwing of the screen goes here
|
|
|
|
-monitorsize = get(0,'ScreenSize');
|
|
|
|
-scpos = [monitorsize(3)/2-(para.screensize(1)/2), ...
|
|
|
|
- monitorsize(4)/2-(para.screensize(2)/2), para.screensize];
|
|
|
|
-% make an screen like figure
|
|
|
|
-fh = figure('Menu','none','ToolBar','none','Position',scpos,'Color',0.5*[1 1 1]);
|
|
|
|
-fh.Name = 'Example of Chromatic Integration Stimulus, from Khani and Gollisch (2021)';
|
|
|
|
-fh.Colormap = gray;
|
|
|
|
-ah = axes('Units','Normalize','Position',[0 0 1 1]);
|
|
|
|
- axis(ah,[0, para.screensize(1),0, para.screensize(2)]);
|
|
|
|
- axis(ah,'off');
|
|
|
|
-
|
|
|
|
-stimsize = [0+para.bmargin, 0+para.lmargin, para.screensize(1)-para.rmargin,...
|
|
|
|
- para.screensize(2)-para.tmargin];
|
|
|
|
-framecounter= 0;
|
|
|
|
-seed = para.seed;
|
|
|
|
-collistorderout = [];
|
|
|
|
-
|
|
|
|
-while ishandle(fh)
|
|
|
|
-
|
|
|
|
- if framecounter == 0
|
|
|
|
- % start with gray screen
|
|
|
|
- r= rectangle('pos',stimsize,'facecolor',[0.5 0.5 0.5],'edgecolor','none');
|
|
|
|
- end
|
|
|
|
-
|
|
|
|
- frameMod = mod(framecounter,para.stimduration + para.preframes);
|
|
|
|
- colorindexMod = floor(mod((framecounter)/(para.stimduration+para.preframes),numcontrasts));
|
|
|
|
-
|
|
|
|
- % resest the color order using Fisher-Yates random permutations
|
|
|
|
- if colorindexMod == 0 && frameMod == 0
|
|
|
|
- [col_order, seed] = fisher_Yates_shuffle_order(seed,1:numcontrasts);
|
|
|
|
- collistorderout = [collistorderout; col_order]; %#ok, not the most efficient way!
|
|
|
|
- end
|
|
|
|
-
|
|
|
|
- % show gray screen in between stimulus frames
|
|
|
|
- if frameMod < (para.preframes)
|
|
|
|
- r.FaceColor = [0.5 0.5 0.5]; % gray screen during preframes
|
|
|
|
- else
|
|
|
|
- r.FaceColor = [0, greencontrasts(col_order(colorindexMod+1)), bluecontrasts(col_order(colorindexMod+1))];
|
|
|
|
- end
|
|
|
|
-
|
|
|
|
- % this is to draw the frames relatively accurately.
|
|
|
|
- drawnow;
|
|
|
|
- java.lang.Thread.sleep(1/para.refreshrate*1e3);
|
|
|
|
- %pause(1/para.refreshrate);
|
|
|
|
- framecounter = framecounter+1;
|
|
|
|
-end
|
|
|
|
-
|
|
|
|
-% setting up output to get the stimulus order contrast orders
|
|
|
|
-collistorderout = collistorderout'; % transposing to make vectorization (:) easier
|
|
|
|
-collistorderout = collistorderout(:);
|
|
|
|
-out.numberContrastsShown = floor((framecounter-1)/(para.stimduration+para.preframes));
|
|
|
|
-collistorderout = collistorderout(1:out.numberContrastsShown);
|
|
|
|
-out.stimulusOrder = transpose(collistorderout);
|
|
|
|
-% to get contrasts between input values
|
|
|
|
-greencontrasts = [offcontrasts,oncontrasts];
|
|
|
|
-bluecontrasts = [oncontrasts,offcontrasts];
|
|
|
|
-out.greenContrasts = greencontrasts(collistorderout);
|
|
|
|
-out.blueContrasts = bluecontrasts(collistorderout);
|
|
|
|
-out.frameCounter = framecounter -1 ; % -1 for the last +1
|
|
|
|
-
|
|
|
|
-varargout{1} = out;
|
|
|
|
-
|
|
|
|
-end
|
|
|
|
-
|
|
|
|
-%--------------------------------------------------------------------------------------------------%
|
|
|
|
-%---------- sub-functions ----------%
|
|
|
|
-%--------------------------------------------------------------------------------------------------%
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-function varargout = fisher_Yates_shuffle_order(seed,inputVec,varargin)
|
|
|
|
-%
|
|
|
|
-%%% fisher_Yates_shuffle_order %%%
|
|
|
|
-%
|
|
|
|
-%
|
|
|
|
-% This function generate psudorandom permution similar to randperm in MATLAB
|
|
|
|
-% but works with psudorandom number generator ran1. It also gives back the
|
|
|
|
-% most recent seed value to continue the permuation in case of repeated trials.
|
|
|
|
-% note that the direction of permutation is along x-axix or for columns of
|
|
|
|
-% MATALB not for the rows.
|
|
|
|
-%
|
|
|
|
-%
|
|
|
|
-% ===============================Inputs====================================
|
|
|
|
-%
|
|
|
|
-% seed : seed value for random number generation.
|
|
|
|
-% inputVec : input vector used for permutation.
|
|
|
|
-%
|
|
|
|
-%================================Output====================================
|
|
|
|
-%
|
|
|
|
-% testOrder : vector of permuted indices for the inputVec.
|
|
|
|
-% newSeed : the recent seed that used in the ran1 function.
|
|
|
|
-% outputVec : the permuted input vector along x-axis
|
|
|
|
-%
|
|
|
|
-% Note that the permution algorithem is based on Fisher-Yates shuffle
|
|
|
|
-% algorithem identical to what is used in the stimulus program.
|
|
|
|
-% for more info check :
|
|
|
|
-% https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
|
|
|
|
-%
|
|
|
|
-% written by Mohammad, 01.02.2016
|
|
|
|
-
|
|
|
|
-newSeed = seed;
|
|
|
|
-testOrder = zeros(1,size(inputVec,2));
|
|
|
|
-testOrder(1) = 1;
|
|
|
|
-
|
|
|
|
-for i = 2:length(inputVec)-1
|
|
|
|
-
|
|
|
|
- [randVal,newSeed] = ran1(newSeed);
|
|
|
|
-
|
|
|
|
- j = ceil(i*randVal); % based on Fischer-Yates algorithem
|
|
|
|
- testOrder(i) = testOrder(j);
|
|
|
|
- testOrder(j) = i;
|
|
|
|
-
|
|
|
|
-end
|
|
|
|
-
|
|
|
|
-testOrder = [testOrder(end),testOrder(1:end-1)]+1; % to match MATLAB indexing
|
|
|
|
-varargout{1} = testOrder;
|
|
|
|
-varargout{2} = newSeed;
|
|
|
|
-for j = 1:size(inputVec,1)
|
|
|
|
- varargout{3}(j,:) = inputVec(j,testOrder);
|
|
|
|
-end
|
|
|
|
-
|
|
|
|
-end
|
|
|
|
-
|
|
|
|
-%--------------------------------------------------------------------------------------------------%
|
|
|
|
-
|
|
|
|
-function paraout = read_stimulus_parameters(varargin)
|
|
|
|
-
|
|
|
|
-% first parse the user inputs
|
|
|
|
-p = inputParser(); % check the user options.
|
|
|
|
-p.addParameter('selecttextfile', false, @(x) islogical(x) || (isnumeric(x) && ismember(x,[0,1])));
|
|
|
|
-p.addParameter('stimduration', 30, @isnumeric);
|
|
|
|
-p.addParameter('preframes', 120, @isnumeric);
|
|
|
|
-p.addParameter('contrastdiff', 0.02, @isnumeric);
|
|
|
|
-p.addParameter('mincontrast',-0.2, @isnumeric);
|
|
|
|
-p.addParameter('maxcontrast', 0.2, @isnumeric);
|
|
|
|
-p.addParameter('seed', -1000, @isnumeric);
|
|
|
|
-p.addParameter('redmeanintensity', 0, @isnumeric);
|
|
|
|
-p.addParameter('greenmeanintensity', 0.5, @isnumeric);
|
|
|
|
-p.addParameter('bluemeanintensity', 0.5, @isnumeric);
|
|
|
|
-p.addParameter('lmargin',0, @isnumeric);
|
|
|
|
-p.addParameter('rmargin', 0, @isnumeric);
|
|
|
|
-p.addParameter('bmargin', 0, @isnumeric);
|
|
|
|
-p.addParameter('tmargin', 0, @isnumeric);
|
|
|
|
-p.addParameter('coneisolating', true, @(x) islogical(x) || (isnumeric(x) && ismember(x,[0,1])));
|
|
|
|
-p.addParameter('screensize', [864 480], @isnumeric);
|
|
|
|
-p.addParameter('refreshrate', 60, @isnumeric);
|
|
|
|
-p.addParameter('fullscreen', false, @(x) islogical(x) || (isnumeric(x) && ismember(x,[0,1])));
|
|
|
|
-p.addParameter('help', false, @(x) islogical(x) || (isnumeric(x) && ismember(x,[0,1])));
|
|
|
|
-
|
|
|
|
-p.parse(varargin{:});
|
|
|
|
-% defualt parameters
|
|
|
|
-defpara = p.Results;
|
|
|
|
-
|
|
|
|
-if defpara.help
|
|
|
|
- help_info_for_parameters(defpara);
|
|
|
|
- paraout = defpara;
|
|
|
|
- return;
|
|
|
|
-end
|
|
|
|
-
|
|
|
|
-if defpara.selecttextfile
|
|
|
|
- % now read the text files
|
|
|
|
- [stimfile, stimpath] = uigetfile('*.txt','Select a chromatic integration stimulus parameter file','chromatic_integration.txt');
|
|
|
|
-
|
|
|
|
- fid = fopen([stimpath,filesep,stimfile]);
|
|
|
|
- tline = fgetl(fid);
|
|
|
|
- while ischar(tline)
|
|
|
|
- tline = fgetl(fid);
|
|
|
|
- if tline == -1, break; end
|
|
|
|
- fn = extractBefore(tline,' = ');
|
|
|
|
- if isempty(fn), continue; end
|
|
|
|
- val = extractAfter(tline,' = ');
|
|
|
|
- % to convert to double
|
|
|
|
- if ~isnan(str2double(val))
|
|
|
|
- val = str2double(val);
|
|
|
|
- end
|
|
|
|
- % to convert to logical
|
|
|
|
- if strcmp(val,'true'), val = true; end
|
|
|
|
- if strcmp(val,'false'), val = false; end
|
|
|
|
- % store the values in a structure
|
|
|
|
- stimpara.(fn) = val;
|
|
|
|
- end
|
|
|
|
- fclose(fid);
|
|
|
|
-
|
|
|
|
- % compare the text file to the defualt values and fill the missing parameters
|
|
|
|
- fn = fieldnames(defpara);
|
|
|
|
- for ii = 1:numel(fn)
|
|
|
|
- if isfield(stimpara,fn{ii})
|
|
|
|
- paraout.(fn{ii}) = stimpara.(fn{ii});
|
|
|
|
- else
|
|
|
|
- paraout.(fn{ii}) = defpara.(fn{ii});
|
|
|
|
- end
|
|
|
|
- end
|
|
|
|
-else
|
|
|
|
- paraout = defpara;
|
|
|
|
-end
|
|
|
|
-
|
|
|
|
-if paraout.fullscreen
|
|
|
|
- monitorsize = get(0,'ScreenSize');
|
|
|
|
- paraout.screensize = monitorsize(3:4) ;
|
|
|
|
-end
|
|
|
|
-
|
|
|
|
-end
|
|
|
|
-
|
|
|
|
-%--------------------------------------------------------------------------------------------------%
|
|
|
|
-
|
|
|
|
-function help_info_for_parameters(defpara)
|
|
|
|
-
|
|
|
|
-fn = fieldnames(defpara);
|
|
|
|
-fprintf(['\n\n',repmat('==',1,50),'\r\n']);
|
|
|
|
-fprintf([repmat(' ',1,35),'List of Stimulus Parameters\r\n']);
|
|
|
|
-fprintf([repmat('==',1,50),'\r\n']);
|
|
|
|
-maxtxtlen = max(cellfun(@numel,fn))+10;
|
|
|
|
-for ii = 1:numel(fn)
|
|
|
|
- g = repmat(' ',1,maxtxtlen - numel(fn{ii}));
|
|
|
|
- fprintf(['\t-- \t%s',g,':',repmat(' ',1,10),'%s\n'],fn{ii},num2str(defpara.(fn{ii})));
|
|
|
|
-end
|
|
|
|
-fprintf(['\n',repmat('==',1,50),'\r\n']);
|
|
|
|
-
|
|
|
|
-end
|
|
|