Browse Source

updated chromatic integration stimulus function get return stimulus order

MHK 3 years ago
parent
commit
1da5ee61d5

+ 221 - 0
Stimulation/Chromatic_Integration_Contrast_Order.m

@@ -0,0 +1,221 @@
+
+
+function varargout = Chromatic_Integration_Contrast_Order(pulsenumbers, varargin)
+%
+%
+%%% Chromatic_Integration_Contrast_Order %%%
+%
+%
+% This function returns the stimulus order and contrast values for the defined
+% numbers of input pluses. It uses Fisher-Yates random permutation to set the
+% stimulus order. For more info, see the function fisher_Yates_shuffle_order
+% below.
+%
+% ===============================Inputs====================================
+%
+%   pulsenumbers    :   number of stimulus pulses.
+%
+% =========================Optional Inputs=================================
+%
+% 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
+% help                  false       Option to check the list of available parameters
+%
+%================================Output====================================
+%
+%   varargout{1}    :   stimulus order, green contrasts and blue contrasts.
+%   varargout{2}    :   stimulus order.
+%
+% written by Mohammad, 12.02.2021
+
+para            =   read_stimulus_parameters(varargin{:});
+if para.help, return; end
+
+offcontrasts    =   fliplr(0:-para.contrastdiff:para.mincontrast);
+oncontrasts     =   0:para.contrastdiff:para.maxcontrast;
+numcontrasts    =   length(oncontrasts)*2;
+% generating contrast values
+if numel(pulsenumbers) > 1
+    para.numtrials      =       floor( length( pulsenumbers(2 : 2 : numel(pulsenumbers))) / numcontrasts );
+else
+    para.numtrials      =       floor( pulsenumbers/2 / numcontrasts ); % 1 pulse for stimulus, one for preframe
+end
+seed                    =       para.seed;
+stimorder               =       nan(para.numtrials, numcontrasts);
+
+% getting the order and location of each contrast from the psudo random sequence
+for ii      =   1 : para.numtrials
+    [stimorder(ii,:),seed,~]    =   fisher_Yates_shuffle_order(seed, 1:numcontrasts);
+end
+
+% setting up output to get the stimulus order contrast orders
+stimorder     =   stimorder';     % transposing to make vectorization (:) easier
+stimorder     =   stimorder(:);
+% out.numberContrastsShown = floor((framecounter-1)/(para.stimduration+para.preframes));
+% stimorder     =   stimorder(1:out.numberContrastsShown);
+out.stimulusOrder   =   transpose(stimorder);
+% to get contrasts between input values
+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);
+% set the contrast output
+out.greenContrasts  =   greencontrasts(stimorder);
+out.blueContrasts   =   bluecontrasts(stimorder);
+out.trialnumbers    =   para.numtrials;
+
+varargout{1}        =   out;
+varargout{2}        =   stimorder;
+
+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('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
+
+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 

+ 20 - 3
Stimulation/Chromatic_Integration_Stimulus.m

@@ -1,6 +1,6 @@
 
 
-function Chromatic_Integration_Stimulus(varargin)
+function varargout = Chromatic_Integration_Stimulus(varargin)
 
 %===================================================================================
 % Parameter             Default                     Usage
@@ -60,6 +60,7 @@ 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)
     
@@ -73,14 +74,15 @@ while ishandle(fh)
     
     % resest the color order using Fisher-Yates random permutations
     if colorindexMod == 0 && frameMod == 0
-        [colorder, seed]    =   fisher_Yates_shuffle_order(seed,1:numcontrasts);
+        [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(colorder(colorindexMod+1)), bluecontrasts(colorder(colorindexMod+1))];
+        r.FaceColor =   [0, greencontrasts(col_order(colorindexMod+1)), bluecontrasts(col_order(colorindexMod+1))];
     end
     
     % this is to draw the frames relatively accurately.
@@ -90,6 +92,21 @@ while ishandle(fh)
     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
 
 %--------------------------------------------------------------------------------------------------%