diff --git a/@file_array/private/file2mat.mexa64 b/@file_array/private/file2mat.mexa64 index 829b08b..e0f7665 100644 Binary files a/@file_array/private/file2mat.mexa64 and b/@file_array/private/file2mat.mexa64 differ diff --git a/@file_array/private/file2mat.mexglx b/@file_array/private/file2mat.mexglx index ffb6237..745b686 100644 Binary files a/@file_array/private/file2mat.mexglx and b/@file_array/private/file2mat.mexglx differ diff --git a/@file_array/private/file2mat.mexmaci b/@file_array/private/file2mat.mexmaci index bb3fc60..b932e4f 100644 Binary files a/@file_array/private/file2mat.mexmaci and b/@file_array/private/file2mat.mexmaci differ diff --git a/@file_array/private/file2mat.mexmaci64 b/@file_array/private/file2mat.mexmaci64 index f6f2092..d5a41f9 100755 Binary files a/@file_array/private/file2mat.mexmaci64 and b/@file_array/private/file2mat.mexmaci64 differ diff --git a/@file_array/private/file2mat.mexw32 b/@file_array/private/file2mat.mexw32 index 67cb8e3..1eb71ec 100755 Binary files a/@file_array/private/file2mat.mexw32 and b/@file_array/private/file2mat.mexw32 differ diff --git a/@file_array/private/file2mat.mexw64 b/@file_array/private/file2mat.mexw64 index 5dd5269..9f06847 100644 Binary files a/@file_array/private/file2mat.mexw64 and b/@file_array/private/file2mat.mexw64 differ diff --git a/@file_array/private/mat2file.mexmaci b/@file_array/private/mat2file.mexmaci index 2afe703..56a9a2f 100644 Binary files a/@file_array/private/mat2file.mexmaci and b/@file_array/private/mat2file.mexmaci differ diff --git a/@gifti/export.m b/@gifti/export.m index a59d42f..f87f303 100644 --- a/@gifti/export.m +++ b/@gifti/export.m @@ -8,7 +8,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: export.m 3081 2009-04-22 20:15:38Z guillaume $ +% $Id: export.m 3332 2009-08-25 13:50:08Z guillaume $ if numel(this) > 1, warning('Only handle scalar objects yet.'); end @@ -20,7 +20,7 @@ case 'patch' if isfield(this,'vertices') - s.vertices = subsref(this, substruct('.', 'vertices')); + s.vertices = double(subsref(this, substruct('.', 'vertices'))); end if isfield(this,'faces') s.faces = subsref(this, substruct('.', 'faces')); diff --git a/@gifti/save.m b/@gifti/save.m index 93a4192..73a8981 100644 --- a/@gifti/save.m +++ b/@gifti/save.m @@ -4,32 +4,60 @@ function save(this,filename,encoding) % this - GIfTI object % filename - name of GIfTI file that will be created % encoding - optional argument to specify encoding format, among -% ASCII, Base64Binary, GZipBase64Binary, ExternalFileBinary +% ASCII, Base64Binary, GZipBase64Binary, ExternalFileBinary, +% Collada (.dae) %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: save.m 2076 2008-09-10 12:34:08Z guillaume $ +% $Id: save.m 3290 2009-07-27 18:11:52Z guillaume $ error(nargchk(1,3,nargin)); -% Open file for writing +% Check filename and file format %-------------------------------------------------------------------------- +ext = '.gii'; if nargin == 1 filename = 'untitled.gii'; else + if nargin == 3 && strcmpi(encoding,'collada') + ext = '.dae'; + end [p,f,e] = fileparts(filename); - if ~ismember(lower(e),{'.gii'}) - e = '.gii'; + if ~ismember(lower(e),{ext}) + e = ext; end filename = fullfile(p,[f e]); end +% Open file for writing +%-------------------------------------------------------------------------- fid = fopen(filename,'wt'); if fid == -1 error('Unable to write file %s: permission denied.',filename); end +% Write file +%-------------------------------------------------------------------------- +switch ext + case '.gii' + if nargin < 3, encoding = 'GZipBase64Binary'; end + fid = save_gii(fid,this,encoding); + case '.dae' + fid = save_dae(fid,this); + otherwise + error('Unknown file format.'); +end + +% Close file +%-------------------------------------------------------------------------- +fclose(fid); + +%========================================================================== +% function fid = save_gii(fid,this,encoding) +%========================================================================== +function fid = save_gii(fid,this,encoding) + % Defaults for DataArray's attributes %-------------------------------------------------------------------------- [unused,unused,mach] = fopen(fid); @@ -40,11 +68,7 @@ function save(this,filename,encoding) else error('[GIFTI] Unknown byte order "%s".',mach); end -if nargin > 2 - def.Encoding = encoding; -else - def.Encoding = 'GZipBase64Binary'; -end +def.Encoding = encoding; def.Intent = 'NIFTI_INTENT_NONE'; def.DataType = 'NIFTI_TYPE_FLOAT32'; def.ExternalFileName = ''; @@ -214,4 +238,144 @@ function save(this,filename,encoding) end fprintf(fid,'\n'); -fclose(fid); \ No newline at end of file + +%========================================================================== +% function fid = save_dae(fid,this) +%========================================================================== +function fid = save_dae(fid,this) + +o = inline('blanks(x*3)'); + +% Split the mesh into connected components +%-------------------------------------------------------------------------- +s = struct(this); +try + C = spm_mesh_label(s.faces); + d = []; + for i=1:numel(unique(C)) + d(i).faces = s.faces(C==i,:); + u = unique(d(i).faces); + d(i).vertices = s.vertices(u,:); + a = 1:max(d(i).faces(:)); + a(u) = 1:size(d(i).vertices,1); + %a = sparse(1,double(u),1:1:size(d(i).vertices,1)); + d(i).faces = a(d(i).faces); + end + s = d; +end + +% Prolog & root of the Collada XML file +%-------------------------------------------------------------------------- +fprintf(fid,'\n'); +fprintf(fid,'\n'); + +% Assets +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(1)); +fprintf(fid,'%s\n',o(2)); +fprintf(fid,'%s%s\n',o(3),... + 'http://www.fil.ion.ucl.ac.uk/spm/'); +fprintf(fid,'%s%s\n',o(3),spm('Ver')); +fprintf(fid,'%s\n',o(2)); +fprintf(fid,'%s%s\n',o(2),datestr(now,'yyyy-mm-ddTHH:MM:SSZ')); +fprintf(fid,'%s%s\n',o(2),datestr(now,'yyyy-mm-ddTHH:MM:SSZ')); +fprintf(fid,'%s\n',o(2)); +fprintf(fid,'%sZ_UP\n',o(2)); +fprintf(fid,'%s\n',o(1)); + +% Image, Materials, Effects +%-------------------------------------------------------------------------- +%fprintf(fid,'%s\n',o(1)); + +fprintf(fid,'%s\n',o(1)); +for i=1:numel(s) + fprintf(fid,'%s\n',o(2),i,i); + fprintf(fid,'%s\n',o(3),i); + fprintf(fid,'%s\n',o(2)); +end +fprintf(fid,'%s\n',o(1)); + +fprintf(fid,'%s\n',o(1)); +for i=1:numel(s) + fprintf(fid,'%s\n',o(2),i,i); + fprintf(fid,'%s\n',o(3)); + fprintf(fid,'%s\n',o(4)); + fprintf(fid,'%s\n',o(5)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s%f %f %f %d\n',o(7),[0 0 0 1]); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s%f %f %f %d\n',o(7),[0 0 0 1]); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s%f %f %f %d\n',o(7),[0.5 0.5 0.5 1]); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s%d %d %d %d\n',o(7),[1 1 1 1]); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s%f\n',o(7),0); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(5)); + fprintf(fid,'%s\n',o(4)); + fprintf(fid,'%s\n',o(3)); + fprintf(fid,'%s\n',o(2)); +end +fprintf(fid,'%s\n',o(1)); + +% Geometry +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(1)); +for i=1:numel(s) + fprintf(fid,'%s\n',o(2),i,i); + fprintf(fid,'%s\n',o(3)); + fprintf(fid,'%s\n',o(4),i); + fprintf(fid,'%s',o(5),i,numel(s(i).vertices)); + fprintf(fid,'%f ',repmat(s(i).vertices',1,[])); + fprintf(fid,'\n'); + fprintf(fid,'%s\n',o(5)); + fprintf(fid,'%s\n',o(6),size(s(i).vertices,1),i); + fprintf(fid,'%s\n',o(7)); + fprintf(fid,'%s\n',o(7)); + fprintf(fid,'%s\n',o(7)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(5)); + fprintf(fid,'%s\n',o(4)); + fprintf(fid,'%s\n',o(4),i); + fprintf(fid,'%s\n',o(5),i); + fprintf(fid,'%s\n',o(4)); + fprintf(fid,'%s\n',o(4),i,size(s(i).faces,1)); + fprintf(fid,'%s\n',o(5),i); + fprintf(fid,'%s

',o(5)); + fprintf(fid,'%d ',repmat(s(i).faces',1,[])-1); + fprintf(fid,'

\n'); + fprintf(fid,'%s
\n',o(4)); + fprintf(fid,'%s
\n',o(3)); + fprintf(fid,'%s
\n',o(2)); +end +fprintf(fid,'%s
\n',o(1)); + +% Scene +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(1)); +fprintf(fid,'%s\n',o(2)); +for i=1:numel(s) + fprintf(fid,'%s\n',o(3),i); + fprintf(fid,'%s\n',o(4),i); + fprintf(fid,'%s\n',o(5)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(7),i,i); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(5)); + fprintf(fid,'%s\n',o(4)); + fprintf(fid,'%s\n',o(3)); +end +fprintf(fid,'%s\n',o(2)); +fprintf(fid,'%s\n',o(1)); +fprintf(fid,'%s\n',o(1)); +fprintf(fid,'%s\n',o(2)); +fprintf(fid,'%s\n',o(1)); + +% End of XML +%-------------------------------------------------------------------------- +fprintf(fid,'
\n'); diff --git a/@meeg/check.m b/@meeg/check.m index 6517b7d..9ac2063 100644 --- a/@meeg/check.m +++ b/@meeg/check.m @@ -11,7 +11,11 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: check.m 2720 2009-02-09 19:50:46Z vladimir $ +% $Id: check.m 3196 2009-06-11 12:54:47Z vladimir $ + +if nargin == 1 + option = 'basic'; +end [ok, this] = checkmeeg(struct(this), option); diff --git a/@meeg/clone.m b/@meeg/clone.m index 9e4936a..94b4a7f 100644 --- a/@meeg/clone.m +++ b/@meeg/clone.m @@ -1,7 +1,9 @@ -function new = clone(this, fnamedat, dim) +function new = clone(this, fnamedat, dim, reset) % Creates a copy of the object with a new, empty data file, % possibly changing dimensions -% FORMAT new = clone(this, fnamedat, dim) +% FORMAT new = clone(this, fnamedat, dim, reset) +% reset - 0 (default) do not reset channel or trial info unless dimensions +% change, 1 - reset channels only, 2 - trials only, 3 both % Note that when fnamedat comes with a path, the cloned meeg object uses % it. Otherwise, its path is by definition that of the meeg object to be % cloned. @@ -9,7 +11,11 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel, Vladimir Litvak -% $Id: clone.m 2476 2008-11-18 12:58:57Z christophe $ +% $Id: clone.m 3350 2009-09-03 13:19:20Z vladimir $ + +if nargin < 4 + reset = 0; +end if nargin < 3 if ~strcmp(transformtype(this), 'TF') @@ -39,6 +45,10 @@ d(end, end, end, end) = 0; nsampl = dim(3); ntrial = dim(4); + + if ~strncmpi(transformtype(new), 'TF',2) + new = transformtype(new, 'TF'); + end else error('Dimensions different from 3 or 4 are not supported.'); end @@ -52,14 +62,14 @@ new.path = pth; % ensure consistency -if dim(1) ~= nchannels(this) +if (dim(1) ~= nchannels(this)) || ismember(reset, [1 3]) new.channels = []; for i = 1:dim(1) new.channels(i).label = ['Ch' num2str(i)]; end end -if ntrial ~= ntrials(this) +if ntrial ~= ntrials(this) || ismember(reset, [2 3]) new.trials = repmat(struct('label', 'Undefined'), 1, ntrial); end diff --git a/@meeg/condlist.m b/@meeg/condlist.m index 09d87d0..bdbddab 100644 --- a/@meeg/condlist.m +++ b/@meeg/condlist.m @@ -6,7 +6,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: condlist.m 3046 2009-04-02 14:28:31Z vladimir $ +% $Id: condlist.m 3254 2009-07-07 15:18:54Z vladimir $ res = getset(this, 'trials', 'label'); if ~iscell(res) @@ -23,7 +23,7 @@ if numel(res)>1 && isfield(this.other, 'condlist') &&... iscell(this.other.condlist) && ~isempty(this.other.condlist) - [sel1, sel2] = spm_match_str(this.other.condlist, res); + [sel1, sel2] = match_str(this.other.condlist, res); res = res([sel2(:)' setdiff(1:numel(res), sel2)]); end else diff --git a/@meeg/coor2D.m b/@meeg/coor2D.m index bafa148..8b9ae99 100644 --- a/@meeg/coor2D.m +++ b/@meeg/coor2D.m @@ -1,11 +1,11 @@ -function [res, plotind] = coor2D(this, ind, val) +function [res, plotind] = coor2D(this, ind, val, mindist) % returns x and y coordinates of channels in 2D plane % FORMAT coor2D(this) % _______________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging -% Vladimir Litvak -% $Id: coor2D.m 2720 2009-02-09 19:50:46Z vladimir $ +% Vladimir Litvak, Laurence Hunt +% $Id: coor2D.m 3335 2009-08-25 16:37:02Z vladimir $ megind = strmatch('MEG', chantype(this)); @@ -31,7 +31,7 @@ end end -if nargin < 3 +if nargin < 3 || isempty(val) if ~isempty(intersect(ind, megind)) if ~any(cellfun('isempty', {this.channels(megind).X_plot2D})) meg_xy = [this.channels(megind).X_plot2D; this.channels(megind).Y_plot2D]; @@ -77,7 +77,11 @@ end end end - + + if nargin > 3 && ~isempty(mindist) + xy = shiftxy(xy,mindist); + end + res = xy; else this = getset(this, 'channels', 'X_plot2D', ind, val(1, :)); @@ -86,6 +90,7 @@ res = this; end + function xy = grid(n) ncol = ceil(sqrt(n)); @@ -95,3 +100,37 @@ y = fliplr(x); [X, Y] = meshgrid(x, y); xy = [X(1:n); Y(1:n)]; + + +function xy = shiftxy(xy,mindist) + +x = xy(1,:); +y = xy(2,:); + +l=1; +i=1; %filler +mindist = mindist/0.999; % limits the number of loops +while (~isempty(i) && l<50) + xdiff = repmat(x,length(x),1) - repmat(x',1,length(x)); + ydiff = repmat(y,length(y),1) - repmat(y',1,length(y)); + xydist= sqrt(xdiff.^2 + ydiff.^2); %euclidean distance between all sensor pairs + + [i,j] = find(xydistj + + for m = 1:length(i); + if (xydist(i(m),j(m)) == 0) + diffvec = [mindist./sqrt(2) mindist./sqrt(2)]; + else + xydiff = [xdiff(i(m),j(m)) ydiff(i(m),j(m))]; + diffvec = xydiff.*mindist./xydist(i(m),j(m)) - xydiff; + end + x(i(m)) = x(i(m)) - diffvec(1)/2; + y(i(m)) = y(i(m)) - diffvec(2)/2; + x(j(m)) = x(j(m)) + diffvec(1)/2; + y(j(m)) = y(j(m)) + diffvec(2)/2; + end + l = l+1; +end + +xy = [x; y]; diff --git a/@meeg/delete.m b/@meeg/delete.m new file mode 100644 index 0000000..a1d15d4 --- /dev/null +++ b/@meeg/delete.m @@ -0,0 +1,17 @@ +function res = delete(this) +% Delete the files of M/EEG dataset from the disk +% FORMAT res = delete(this) +%_______________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: delete.m 3378 2009-09-09 16:47:16Z guillaume $ + +res = 1; + +try + delete(fullfile(path(this), fnamedat(this))); + delete(fullfile(path(this), fname(this))); +catch + res = 0; +end diff --git a/@meeg/display.m b/@meeg/display.m index 33764cb..929168d 100644 --- a/@meeg/display.m +++ b/@meeg/display.m @@ -5,20 +5,35 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: display.m 2113 2008-09-18 11:14:46Z vladimir $ +% $Id: display.m 3219 2009-06-25 10:15:36Z vladimir $ str = ['SPM M/EEG data object\n'... 'Type: ' type(this) '\n'... + 'Transform: ' transformtype(this) '\n'... num2str(nconditions(this)), ' conditions\n'... - num2str(nchannels(this)), ' channels\n'... + num2str(nchannels(this)), ' channels\n' + ]; + +if strncmpi(transformtype(this),'TF',2) + str = [str num2str(nfrequencies(this)), ' frequencies\n']; +end + +str = [str ... num2str(nsamples(this)), ' samples/trial\n'... num2str(ntrials(this)), ' trials\n'... 'Sampling frequency: ' num2str(fsample(this)) ' Hz\n'... - 'Loaded from file %s\n\n'... - 'Use the syntax D(channels, samples, trials) to access the data\n'... - 'Type "methods(''meeg'')" for the list of methods performing other operations with the object\n'... + 'Loaded from file %s\n\n' ]; +if strncmpi(transformtype(this),'TF',2) + str = [str 'Use the syntax D(channels, frequencies, samples, trials) to access the data\n']; +else + str = [str 'Use the syntax D(channels, samples, trials) to access the data\n']; +end + +str = [str 'Type "methods(''meeg'')" for the list of methods performing other operations with the object\n'... + 'Type "help meeg/method_name" to get help about methods\n']; + str = sprintf(str, fullfile(this.path, this.fname)); disp(str); \ No newline at end of file diff --git a/@meeg/frequencies.m b/@meeg/frequencies.m index 2be6398..fc360ca 100644 --- a/@meeg/frequencies.m +++ b/@meeg/frequencies.m @@ -1,31 +1,40 @@ -function res = frequencies(this, varargin) +function res = frequencies(this, ind, f) % Method for getting/setting frequencies of TF data -% FORMAT res = frequencies(this, varargin) +% FORMAT res = frequencies(this, ind, values) % _________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: frequencies.m 2857 2009-03-11 13:21:04Z guillaume $ +% $Id: frequencies.m 3350 2009-09-03 13:19:20Z vladimir $ -if isempty(varargin) +if nargin < 3 if strncmpi(transformtype(this), 'TF',2) - res = getset(this, 'transform', 'frequencies'); + res = this.transform.frequencies; else res = []; + return end -else - f = varargin{1}; - + if exist('ind', 'var') == 1 && ~isempty(ind) + res = res(ind); + end +else + if ~strncmpi(transformtype(this), 'TF',2) + error('Frequencies can only be assigned to a TF dataset'); + end + if any(f) <= 0 || any(~isnumeric(f)) - error('Frequencies must be positive numbers'); res = []; return + error('Frequencies must be positive numbers'); + end + + if isempty(ind) || isequal(ind, ':') + ind = 1:size(this, 2); end - - % can't use getset because both information must be set at the same - % time - sD = struct(this); - sD.transform.ID = 'TF'; % could it be TFphase? - sD.transform.frequencies = f; - res = meeg(sD); + if length(ind)~=length(f) || max(ind)>size(this, 2) + error('Wrong frequency axis or indices'); + end + + this.transform.frequencies(ind) = f; + res = this; end diff --git a/@meeg/fsample.m b/@meeg/fsample.m index 1d6d92e..3635a22 100644 --- a/@meeg/fsample.m +++ b/@meeg/fsample.m @@ -1,10 +1,15 @@ -function res = fsample(this) -% Method for getting the sampling rate +function res = fsample(this, value) +% Method for getting and setting the sampling rate % FORMAT res = fsample(this) % _______________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: fsample.m 1373 2008-04-11 14:24:03Z spm $ +% $Id: fsample.m 3200 2009-06-12 17:29:40Z vladimir $ -res = this.Fsample; \ No newline at end of file +if nargin == 1 + res = this.Fsample; +else + this.Fsample = value; + res = this; +end \ No newline at end of file diff --git a/@meeg/fttimelock.m b/@meeg/fttimelock.m new file mode 100644 index 0000000..dbb3704 --- /dev/null +++ b/@meeg/fttimelock.m @@ -0,0 +1,26 @@ +function timelock = fttimelock(this) +% Method for converting meeg object to Fieldtrip timelock/freq struct +% FORMAT timelock = fttimelock(this) +% _______________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: fttimelock.m 3210 2009-06-17 13:46:25Z vladimir $ + +timelock = []; +timelock.label = chanlabels(this); +timelock.label = timelock.label(:); +if isequal(transformtype(this), 'time') + timelock.dimord = 'rpt_chan_time'; + timelock.fsample = fsample(this); + timelock.trial = permute(this.data.y(:, :, :), [3 1 2]); + timelock.dimord = 'rpt_chan_time'; +elseif strncmpi(transformtype(this),'TF',2) + timelock.dimord = 'rpt_chan_freq_time'; + timelock.powspctrm = permute(this.data.y(:, :, :, :), [4 1 2 3]); + timelock.freq = frequencies(this); +else + error('Unknown transform type.'); +end + +timelock.time = time(this); \ No newline at end of file diff --git a/@meeg/getfield.m b/@meeg/getfield.m new file mode 100644 index 0000000..06b3477 --- /dev/null +++ b/@meeg/getfield.m @@ -0,0 +1,12 @@ +function res = getfield(this, varargin) +% Returns fields in .other +% FORMAT res = getfield(this, varargin) +% +% An overloaded function... +% _______________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: getfield.m 3228 2009-06-26 17:43:19Z vladimir $ + +res = getfield(this.other, varargin{:}); \ No newline at end of file diff --git a/@meeg/history.m b/@meeg/history.m index af78657..d6e2989 100644 --- a/@meeg/history.m +++ b/@meeg/history.m @@ -6,7 +6,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: history.m 2348 2008-10-16 16:51:25Z vladimir $ +% $Id: history.m 3196 2009-06-11 12:54:47Z vladimir $ if isempty(varargin) @@ -19,13 +19,20 @@ else nh = length(this.history); end - this.history(nh+1).fun = varargin{1}; - - if isstruct(varargin{2}) && isfield(varargin{2}, 'D') && ... - isa(varargin{2}.D, 'meeg') - varargin{2}.D = fullfile(varargin{2}.D.path, varargin{2}.D.fname); + + if ischar(varargin{1}) + this.history(nh+1).fun = varargin{1}; + + if isstruct(varargin{2}) && isfield(varargin{2}, 'D') && ... + isa(varargin{2}.D, 'meeg') + varargin{2}.D = fullfile(varargin{2}.D.path, varargin{2}.D.fname); + end + + this.history(nh+1).args = varargin{2}; + + elseif isstruct(varargin{1}) + this.history = varargin{1}; end - - this.history(nh+1).args = varargin{2}; + res = this; end \ No newline at end of file diff --git a/@meeg/indchannel.m b/@meeg/indchannel.m new file mode 100644 index 0000000..172c1b2 --- /dev/null +++ b/@meeg/indchannel.m @@ -0,0 +1,18 @@ +function res = indchannel(this, label) +% Method for getting channel indices based on channel labels +% FORMAT res = indchannel(this, label) +% this - MEEG object +% label - string or cell array of labels +% +% res - vector of channel indices matching labels +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: indchannel.m 3254 2009-07-07 15:18:54Z vladimir $ + +if ischar(label) + label = {label}; +end + +[junk, res] = match_str(label, chanlabels(this)); \ No newline at end of file diff --git a/@meeg/indfrequency.m b/@meeg/indfrequency.m new file mode 100644 index 0000000..67b5c4c --- /dev/null +++ b/@meeg/indfrequency.m @@ -0,0 +1,29 @@ +function res = indfrequency(this, f) +% Method for getting the index closest to given frequency +% FORMAT res = indfrequency(this, f) +% this - MEEG object +% f - vector of frequencies (in Hz) +% +% res - vector of sample indices matching indices +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Stefan Kiebel +% $Id: indfrequency.m 3254 2009-07-07 15:18:54Z vladimir $ + +if ~strncmpi(transformtype(this),'TF',2) + error('Only TF datasets are supported'); +end + +res = NaN(1,length(f)); +fdiff = mean(diff(frequencies(this))); +if nsamples(this) > 0 + F = frequencies(this); + for i = 1:length(f) + [m, res(i)] = min(abs(F-f(i))); + if m > fdiff + warning('Could not find an index matching the requested frequency %d Hz', f(i)); + res(i) = NaN; + end + end +end diff --git a/@meeg/indtrial.m b/@meeg/indtrial.m new file mode 100644 index 0000000..6169b97 --- /dev/null +++ b/@meeg/indtrial.m @@ -0,0 +1,18 @@ +function res = indtrial(this, label) +% Method for getting channel indices based on channel labels +% FORMAT res = indtrial(this, label) +% this - MEEG object +% label - string or cell array of labels +% +% res - vector of trial indices matching condition labels +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: indtrial.m 3254 2009-07-07 15:18:54Z vladimir $ + +if ischar(label) + label = {label}; +end + +[junk, res] = match_str(label, conditions(this)); \ No newline at end of file diff --git a/@meeg/meegchannels.m b/@meeg/meegchannels.m index a2a90d2..f3ba4b7 100644 --- a/@meeg/meegchannels.m +++ b/@meeg/meegchannels.m @@ -12,7 +12,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: meegchannels.m 2886 2009-03-16 20:54:17Z guillaume $ +% $Id: meegchannels.m 3228 2009-06-26 17:43:19Z vladimir $ type = chantype(this); @@ -29,6 +29,8 @@ ind = find(ismember(upper(type), {'MEG', 'MEGMAG', 'MEGGRAD'})); case 'MEGPLANAR' ind = find(ismember(upper(type), {'MEGPLANAR'})); + case 'MEEG' + ind = find(ismember(upper(type), {'EEG', 'MEG', 'MEGMAG', 'MEGGRAD'})); otherwise error('Unsupported modality.'); end diff --git a/@meeg/private/checkmeeg.m b/@meeg/private/checkmeeg.m index 03d524c..73c1c26 100644 --- a/@meeg/private/checkmeeg.m +++ b/@meeg/private/checkmeeg.m @@ -9,7 +9,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: checkmeeg.m 3068 2009-04-20 17:21:57Z vladimir $ +% $Id: checkmeeg.m 3394 2009-09-11 16:04:10Z vladimir $ if nargin==1 option = 'basic'; @@ -55,6 +55,45 @@ if ~isfield(meegstruct.trials, 'events') [meegstruct.trials.events] = deal([]); end + + for k = 1:Ntrials + if length(meegstruct.trials(k).bad)>1 || ~ismember(meegstruct.trials(k).bad, [0, 1]) + disp(['checkmeeg: illegal value for bad flag in trial ' num2str(k) ', setting to zero.']); + meegstruct.trials(k).bad = 0; + end + + event = meegstruct.trials(k).events; + + if ~isempty(event) + % make sure that all required elements are present + if ~isfield(event, 'type'), error('type field not defined for each event'); end + if ~isfield(event, 'time'), error('time field not defined for each event'); end + if ~isfield(event, 'value'), [event.value] = deal([]); end + if ~isfield(event, 'offset'), [event.offset] = deal(0); end + if ~isfield(event, 'duration'), [event.duration] = deal([]); end + + + % make sure that all numeric values are double + for i=1:length(event) + if isnumeric(event(i).value) + event(i).value = double(event(i).value); + end + event(i).time = double(event(i).time); + event(i).offset = double(event(i).offset); + event(i).duration = double(event(i).duration); + end + + if ~isempty(event) + % sort the events on the sample on which they occur + % this has the side effect that events without time are discarded + [dum, indx] = sort([event.time]); + event = event(indx); + end + + meegstruct.trials(k).events = event; + end + end + if ~isfield(meegstruct.trials, 'onset') [meegstruct.trials.onset] = deal([]); end @@ -83,12 +122,12 @@ disp('checkmeeg: no channel type, assigning default'); [meegstruct.channels.type] = deal('Other'); end - + % This is for backward compatibility with early SPM8b. Can be removed % after a while. ind = strmatch('MEGREF', {meegstruct.channels.type}, 'exact'); [meegstruct.channels(ind).type] = deal('REF'); - + if ~isfield(meegstruct.channels, 'X_plot2D') [meegstruct.channels.X_plot2D] = deal([]); [meegstruct.channels.Y_plot2D] = deal([]); @@ -125,8 +164,9 @@ if ~isfield(meegstruct.data, 'fnamedat') disp('checkmeeg: data file name missing'); return; - else - meegstruct.data.fnamedat = spm_str_manip(meegstruct.data.fnamedat, 't'); + else + [junk, fnamedat] = fileparts(meegstruct.data.fnamedat); + meegstruct.data.fnamedat = [fnamedat '.dat']; end if ~isfield(meegstruct.data, 'datatype') disp('checkmeeg: data type missing, assigning default'); @@ -174,7 +214,7 @@ % the use of copied (raw) integer data files case 'time' meegstruct.data.y = file_array(fullfile(filepath, meegstruct.data.fnamedat), ... - [Nchannels Nsamples Ntrials], meegstruct.data.datatype); + [Nchannels Nsamples Ntrials], meegstruct.data.datatype); case {'TF', 'TFphase'} meegstruct.data.y = file_array(fullfile(filepath, meegstruct.data.fnamedat), ... @@ -193,8 +233,8 @@ meegstruct.data.y.scl_slope = sav_sc; end - end - + end + switch(meegstruct.transform.ID) case 'time' if Ntrials>1 @@ -209,7 +249,7 @@ expected_size = [Nchannels Nfrequencies Nsamples]; end - + otherwise error('Unknown transform type'); end @@ -222,12 +262,14 @@ if ~isfield(meegstruct, 'type') ||... (strcmp(meegstruct.type, 'continuous') && Ntrials>1) ||... - strcmp(meegstruct.type, 'evoked') && (numel(unique({meegstruct.trials.label})) ~= Ntrials) + strcmp(meegstruct.type, 'evoked') && (numel(unique({meegstruct.trials.label})) ~= Ntrials) ||... + (strcmp(meegstruct.type, 'continuous') && strncmp(meegstruct.transform.ID, 'TF', 2)) disp('checkmeeg: data type is missing or incorrect, assigning default'); % rule of thumb - 10 sec if Nsamples == 0 meegstruct.type = 'continuous'; - elseif Ntrials==1 && (Nsamples/meegstruct.Fsample) > 10 + elseif Ntrials==1 && (Nsamples/meegstruct.Fsample) > 10 &&... + ~strncmp(meegstruct.transform.ID, 'TF', 2) meegstruct.type = 'continuous'; elseif numel(unique({meegstruct.trials.label})) == Ntrials meegstruct.type = 'evoked'; @@ -336,7 +378,7 @@ 'history' 'cache'}; -[sel1, sel2] = spm_match_str(fieldnames_order, fieldnames(meegstruct)); +[sel1, sel2] = match_str(fieldnames_order, fieldnames(meegstruct)); tempcell = struct2cell(meegstruct); meegstruct = cell2struct(tempcell(sel2), fieldnames_order, 1); @@ -348,7 +390,7 @@ chantypes = getset(meegstruct, 'channels', 'type'); eegind = strmatch('EEG', chantypes, 'exact'); megind = strmatch('MEG', chantypes); -lfpind = strmatch('LFP', chantypes, 'exact'); +lfpind = strmatch('LFP', chantypes, 'exact'); % Allow DCM on a pure LFP dataset if strcmp(option, 'dcm') && isempty([eegind megind]) && ~isempty(lfpind) @@ -357,11 +399,11 @@ end if strcmp(option, 'sensfid') || strcmp(option, '3d') ||... - (strcmp(option, 'dcm') && ~isempty([eegind megind])) + (strcmp(option, 'dcm') && ~isempty([eegind megind])) if isempty(meegstruct.sensors) disp('checkmeeg: no sensor positions are defined'); return; - end + end if ~isempty(eegind) if ~isfield(meegstruct.sensors, 'eeg') || isempty(meegstruct.sensors.eeg) @@ -413,31 +455,31 @@ lelbl = {'fidle', 'fidt9', 'lpa', 'lear', 'earl' 'le', 't9', 'spmlpa'}; relbl = {'fidre', 'fidt10', 'rpa', 'rear', 'earr', 're', 't10', 'spmrpa'}; - [sel1, nzind] = spm_match_str(nzlbl, lower(meegstruct.fiducials.fid.label)); + [sel1, nzind] = match_str(nzlbl, lower(meegstruct.fiducials.fid.label)); if isempty(nzind) disp('checkmeeg: could not find the nasion fiducial'); else nzind = nzind(1); end - - [sel1, leind] = spm_match_str(lelbl, lower(meegstruct.fiducials.fid.label)); + + [sel1, leind] = match_str(lelbl, lower(meegstruct.fiducials.fid.label)); if isempty(leind) disp('checkmeeg: could not find the left fiducial'); else leind = leind(1); end - [sel1, reind] = spm_match_str(relbl, lower(meegstruct.fiducials.fid.label)); + [sel1, reind] = match_str(relbl, lower(meegstruct.fiducials.fid.label)); if isempty(reind) disp('checkmeeg: could not find the right fiducial'); else reind = reind(1); end - - restind = setdiff(1:length(meegstruct.fiducials.fid.label), [nzind, leind, reind]); - meegstruct.fiducials.fid.label = meegstruct.fiducials.fid.label([nzind, leind, reind, restind]); - meegstruct.fiducials.fid.pnt = meegstruct.fiducials.fid.pnt([nzind, leind, reind, restind], :); + restind = setdiff(1:length(meegstruct.fiducials.fid.label), [nzind(:)', leind(:)', reind(:)']); + + meegstruct.fiducials.fid.label = meegstruct.fiducials.fid.label([nzind(:)', leind(:)', reind(:)', restind(:)']); + meegstruct.fiducials.fid.pnt = meegstruct.fiducials.fid.pnt([nzind(:)', leind(:)', reind(:)', restind(:)'], :); result = 1; end diff --git a/@meeg/private/getset.m b/@meeg/private/getset.m index b38a5d1..1c58f23 100644 --- a/@meeg/private/getset.m +++ b/@meeg/private/getset.m @@ -5,11 +5,11 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: getset.m 2105 2008-09-17 15:34:09Z vladimir $ +% $Id: getset.m 3368 2009-09-07 22:50:35Z vladimir $ this = struct(this); -if nargin == 3 || isempty(ind) +if nargin == 3 || isempty(ind) || (ischar(ind) && isequal(ind, ':')) try ind = 1:numel(getfield(this, parent)); catch diff --git a/@meeg/private/match_str.m b/@meeg/private/match_str.m new file mode 100644 index 0000000..d32ac7a --- /dev/null +++ b/@meeg/private/match_str.m @@ -0,0 +1,76 @@ +function [sel1, sel2] = match_str(a, b) + +% MATCH_STR looks for matching labels in two listst of strings +% and returns the indices into both the 1st and 2nd list of the matches. +% They will be ordered according to the first input argument. +% +% [sel1, sel2] = match_str(strlist1, strlist2) +% +% The strings can be stored as a char matrix or as an vertical array of +% cells, the matching is done for each row. + +% Copyright (C) 2000, Robert Oostenveld +% +% $Log: match_str.m,v $ +% Revision 1.6 2006/11/06 21:11:45 roboos +% also deal with empty [] input +% +% Revision 1.5 2004/11/10 17:11:40 roboos +% reverted to original implementation and reimplemented the speed up +% from scratch. The previous two revisions both were incompatible +% with the original implementation. +% +% Revision 1.4 2004/11/09 15:28:57 roboos +% fixed incompatibility that was introduced by previous speed increase: +% the original version gave back double occurences, and other fieldtrip +% functions (sourceanalysis) rely on this. The previously commited +% version only gave back one occurence of each hit, this is fixed by jansch +% in this version +% +% Revision 1.3 2004/10/22 15:59:41 roboos +% large speed increase by replacing 2 nested for loops by a standard matlab function (intersect) +% +% Revision 1.2 2003/03/17 10:37:28 roberto +% improved general help comments and added copyrights + +% ensure that both are cell-arrays +if isempty(a) + a = {}; +elseif ~iscell(a) + a = cellstr(a); +end +if isempty(b) + b = {}; +elseif ~iscell(b) + b = cellstr(b); +end + +% ensure that both are column vectors +a = a(:); +b = b(:); + +% regardless of what optimizations are implemented, the code should remain +% functionally compatible to the original, which is +% for i=1:length(a) +% for j=1:length(b) +% if strcmp(a(i),b(j)) +% sel1 = [sel1; i]; +% sel2 = [sel2; j]; +% end +% end +% end + +% replace all unique strings by a unique number and use the fact that +% numeric comparisons are much faster than string comparisons +[dum1, dum2, c] = unique([a; b]); +a = c(1:length(a)); +b = c((length(a)+1):end); + +sel1 = []; +sel2 = []; +for i=1:length(a) + % s = find(strcmp(a(i), b)); % for string comparison + s = find(a(i)==b); % for numeric comparison + sel1 = [sel1; repmat(i, size(s))]; + sel2 = [sel2; s]; +end diff --git a/@meeg/putfsample.m b/@meeg/putfsample.m deleted file mode 100644 index 2b97c61..0000000 --- a/@meeg/putfsample.m +++ /dev/null @@ -1,10 +0,0 @@ -function this = putfsample(this, fsample) -% Method for changing the sampling rate -% FORMAT res = putfsample(this) -% _______________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging - -% Stefan Kiebel -% $Id: putfsample.m 1373 2008-04-11 14:24:03Z spm $ - -this.Fsample = fsample; \ No newline at end of file diff --git a/@meeg/putnsamples.m b/@meeg/putnsamples.m deleted file mode 100644 index 177ef7f..0000000 --- a/@meeg/putnsamples.m +++ /dev/null @@ -1,10 +0,0 @@ -function this = putnsamples(this, nsamples) -% Method for changing the number of samples -% FORMAT this = putnsample(this, nsamples) -% _______________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging - -% Stefan Kiebel -% $Id: putnsamples.m 1373 2008-04-11 14:24:03Z spm $ - -this.Nsamples = nsamples; diff --git a/@meeg/sconfounds.m b/@meeg/sconfounds.m index c8a7080..bfa662c 100644 --- a/@meeg/sconfounds.m +++ b/@meeg/sconfounds.m @@ -5,10 +5,10 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: sconfounds.m 2419 2008-10-30 19:40:32Z vladimir $ +% $Id: sconfounds.m 3317 2009-08-10 12:39:52Z vladimir $ if nargin == 2 - [sel1, sel2] = spm_match_str(chanlabels(this), newsconfounds.label); + [sel1, sel2] = match_str(chanlabels(this, meegchannels(this)), newsconfounds.label); if length(sel1)=timeborders(1))); - timeEnd = max(find(timeAxis<=timeborders(2))); - timeind = timeStart:timeEnd; + timeind = indsample(this, timeborders(1)):indsample(this, timeborders(2)); end - if isempty(condition) trialind = 1:ntrials(this); else - if ischar(condition) - condition = {condition}; - end - [junk, trialind] = spm_match_str(condition, {this.trials.label}); + trialind = indtrial(this, condition); end -res = double(this.data.y(chanind, timeind, trialind)); \ No newline at end of file +if isequal(transformtype(this), 'time') + res = double(this.data.y(chanind, timeind, trialind)); +else + res = double(this.data.y(chanind, freqind, timeind, trialind)); +end \ No newline at end of file diff --git a/@meeg/size.m b/@meeg/size.m new file mode 100644 index 0000000..1d1ee8b --- /dev/null +++ b/@meeg/size.m @@ -0,0 +1,14 @@ +function res = size(this, varargin) +% returns the dimensions of the data matrix +% FORMAT res = size(this, dim)) +% _______________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: size.m 3350 2009-09-03 13:19:20Z vladimir $ + +res = size(this.data.y, varargin{:}); + +if ntrials(this) == 1 + res = [res 1]; +end diff --git a/@meeg/transformtype.m b/@meeg/transformtype.m index 6404c89..afa9ead 100644 --- a/@meeg/transformtype.m +++ b/@meeg/transformtype.m @@ -5,11 +5,15 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: transformtype.m 2081 2008-09-11 13:04:24Z vladimir $ +% $Id: transformtype.m 3350 2009-09-03 13:19:20Z vladimir $ if nargin == 1 res = this.transform.ID; else + if strncmpi(this, 'TF', 2) && length(size(this))~=4 + error('TF transformtype can only be assigned to 4D dataset'); + end + this.transform.ID = newtype; res = this; end diff --git a/Contents.m b/Contents.m index 2303430..33e377b 100644 --- a/Contents.m +++ b/Contents.m @@ -1,5 +1,5 @@ % Statistical Parametric Mapping -% Version 3164 (SPM8) 29-May-09 +% Version 8.2 (SPM8) 16-Sep-2009 %__________________________________________________________________________ % ___ ____ __ __ % / __)( _ \( \/ ) @@ -42,7 +42,7 @@ %__________________________________________________________________________ % Copyright (C) 1991,1994-2003,2005-2009 Wellcome Trust Centre for Neuroimaging -% $Id: Contents.m 3163 2009-05-29 14:04:55Z guillaume $ +% $Id: Contents.m 3407 2009-09-16 13:02:36Z guillaume $ %========================================================================== % PROGRAMMERS NOTE: diff --git a/EEGtemplates/FIF306_setup.mat b/EEGtemplates/FIF306_setup.mat deleted file mode 100644 index ea8c4fa..0000000 Binary files a/EEGtemplates/FIF306_setup.mat and /dev/null differ diff --git a/EEGtemplates/Neuromag306_EEG70.mat b/EEGtemplates/Neuromag306_EEG70.mat new file mode 100644 index 0000000..e95cb02 Binary files /dev/null and b/EEGtemplates/Neuromag306_EEG70.mat differ diff --git a/EEGtemplates/biosemi64.sfp b/EEGtemplates/biosemi64.sfp new file mode 100644 index 0000000..d472626 --- /dev/null +++ b/EEGtemplates/biosemi64.sfp @@ -0,0 +1,67 @@ +spmnas 1 85 -41 +spmlpa -83 -20 -65 +spmrpa 83 -20 -65 +A1 -29.2644 82.1011 -0.1681 +A2 -54.9914 67.5738 -5.8804 +A3 -32.8795 71.8452 30.1284 +A4 -25.7835 47.4288 69.8291 +A5 -49.1339 46.0206 49.8831 +A6 -64.1055 44.0763 21.4442 +A7 -70.6916 41.8958 -10.8395 +A8 -81.4433 13.7802 -15.1615 +A9 -76.9277 14.1172 25.097 +A10 -58.9898 14.3544 59.7085 +A11 -32.0939 14.4686 86.4236 +A12 -34.1983 -22.4568 90.9388 +A13 -64.2027 -20.782 63.5856 +A14 -80.1105 -18.6349 24.6594 +A15 -85.0072 -16.3778 -18.4196 +A16 -85.8268 -46.4935 -21.2067 +A17 -79.5982 -51.4848 20.7615 +A18 -62.6018 -56.1594 58.5984 +A19 -33.7659 -59.7753 85.984 +A20 -27.4707 -91.1008 62.1009 +A21 -52.4644 -86.8018 41.7174 +A22 -67.4857 -80.9442 12.0896 +A23 -73.4404 -74.4816 -21.5311 +A24 -75.0014 -70.3299 -64.4026 +A25 -55.7888 -99.2792 -20.6777 +A26 -36.5153 -106.8019 16.2198 +A27 -30.1827 -115.1564 -17.6531 +A28 -1.4725 -117.9297 -55.4479 +A29 -0.3825 -118.6416 -12.82 +A30 0.7288 -110.1495 29.4389 +A31 1.798 -92.892 68.8025 +A32 2.5193 -61.2034 93.5467 +B1 0.5966 85.4186 5.2724 +B2 30.3458 82.3208 -2.5084 +B3 56.1483 67.2462 -10.3733 +B4 36.9056 71.7503 28.2715 +B5 1.6107 73.6809 45.2343 +B6 2.3304 47.9355 75.787 +B7 31.496 47.454 66.7353 +B8 53.4301 46.0515 44.4067 +B9 68.9352 44.219 15.7579 +B10 73.3302 42.0529 -16.9723 +B11 81.9645 12.9962 -21.7677 +B12 80.6141 13.3845 18.9204 +B13 64.1073 13.7668 54.9967 +B14 37.0696 14.142 82.3849 +B15 2.747 14.3924 94.9242 +B16 2.8165 -23.3292 101.2672 +B17 39.9696 -22.8958 86.5061 +B18 68.9216 -21.6725 57.4563 +B19 84.453 -19.7733 18.2209 +B20 85.084 -17.5501 -25.2931 +B21 85.4063 -48.208 -28.142 +B22 84.1409 -53.1682 14.4935 +B23 68.2191 -57.4632 53.3152 +B24 40.4866 -60.4388 82.3243 +B25 33.4052 -91.992 61.0662 +B26 56.7709 -88.0486 38.0235 +B27 68.346 -82.269 6.331 +B28 72.7791 -75.9692 -27.446 +B29 72.628 -72.8203 -70.7433 +B30 55.269 -100.7939 -25.2641 +B31 37.125 -107.6542 12.3793 +B32 29.3709 -115.6245 -20.0571 diff --git a/MIP.mat b/MIP.mat index 667eaf5..a85d0f5 100644 Binary files a/MIP.mat and b/MIP.mat differ diff --git a/canonical/scalp_2562.surf.gii b/canonical/scalp_2562.surf.gii index 920ad4f..c3a534f 100644 --- a/canonical/scalp_2562.surf.gii +++ b/canonical/scalp_2562.surf.gii @@ -34,6 +34,6 @@ 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 - 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 diff --git a/config/spm_cfg.m b/config/spm_cfg.m index dd9b0ae..9d67e1d 100644 --- a/config/spm_cfg.m +++ b/config/spm_cfg.m @@ -4,7 +4,7 @@ %_______________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging -% $Id: spm_cfg.m 2988 2009-03-28 16:47:21Z volkmar $ +% $Id: spm_cfg.m 3250 2009-07-06 09:31:13Z vladimir $ %_______________________________________________________________________ % temporal Temporal @@ -37,7 +37,7 @@ meeg.tag = 'meeg'; meeg.name = 'M/EEG'; meeg.help = {'M/EEG functions.'}; -meeg.values = { spm_cfg_eeg_convert spm_cfg_eeg_montage spm_cfg_eeg_epochs spm_cfg_eeg_filter spm_cfg_eeg_artefact spm_cfg_eeg_average spm_cfg_eeg_downsample spm_cfg_eeg_merge spm_cfg_eeg_contrast spm_cfg_eeg_grandmean spm_cfg_eeg_convert2images}; +meeg.values = { spm_cfg_eeg_convert spm_cfg_eeg_montage spm_cfg_eeg_epochs spm_cfg_eeg_filter spm_cfg_eeg_bc spm_cfg_eeg_artefact spm_cfg_eeg_average spm_cfg_eeg_downsample spm_cfg_eeg_merge spm_cfg_eeg_contrast spm_cfg_eeg_grandmean spm_cfg_eeg_tf_rescale spm_cfg_eeg_convert2images}; % --------------------------------------------------------------------- % util Util @@ -64,65 +64,68 @@ ['See spm_cfg.m or MATLABBATCH documentation ' ... 'for information about the form of SPM''s configuration ' ... 'files.']}; -%-Toolbox autodetection -% Disable warnings when converting SPM5 toolboxes - set this to 'on' to -% debug problems with SPM5 toolboxes -warning('off','matlabbatch:cfg_struct2cfg:verb'); -%-Get the list of toolbox directories -tbxdir = fullfile(spm('Dir'),'toolbox'); -d = dir(tbxdir); d = {d([d.isdir]).name}; -dd = regexp(d,'^\.'); -%(Beware, regexp returns an array if input cell array is of dim 0 or 1) -if ~iscell(dd), dd = {dd}; end -d = {'' d{cellfun('isempty',dd)}}; -ft = {}; dt = {}; -ftc = {}; dtc = {}; -%-Look for '*_cfg_*.m' or '*_config_*.m' files in these directories -for i=1:length(d) - d2 = fullfile(tbxdir,d{i}); - di = dir(d2); di = {di(~[di.isdir]).name}; - f2 = regexp(di,'.*_cfg_.*\.m$'); - if ~iscell(f2), f2 = {f2}; end - fi = {di{~cellfun('isempty',f2)}}; - if ~isempty(fi) - ft = {ft{:} fi{:}}; - dt(end+1:end+length(fi)) = deal({d2}); - else - % try *_config_*.m files, if toolbox does not have '*_cfg_*.m' files - f2 = regexp(di,'.*_config_.*\.m$'); +if ~isdeployed + %-Toolbox autodetection + % In compiled mode, cfg_master will take care of this + % Disable warnings when converting SPM5 toolboxes - set this to 'on' to + % debug problems with SPM5 toolboxes + warning('off','matlabbatch:cfg_struct2cfg:verb'); + %-Get the list of toolbox directories + tbxdir = fullfile(spm('Dir'),'toolbox'); + d = dir(tbxdir); d = {d([d.isdir]).name}; + dd = regexp(d,'^\.'); + %(Beware, regexp returns an array if input cell array is of dim 0 or 1) + if ~iscell(dd), dd = {dd}; end + d = {'' d{cellfun('isempty',dd)}}; + ft = {}; dt = {}; + ftc = {}; dtc = {}; + %-Look for '*_cfg_*.m' or '*_config_*.m' files in these directories + for i=1:length(d) + d2 = fullfile(tbxdir,d{i}); + di = dir(d2); di = {di(~[di.isdir]).name}; + f2 = regexp(di,'.*_cfg_.*\.m$'); if ~iscell(f2), f2 = {f2}; end fi = {di{~cellfun('isempty',f2)}}; - ftc = {ftc{:} fi{:}}; - dtc(end+1:end+length(fi)) = deal({d2}); - end; -end -if ~isempty(ft)||~isempty(ftc) - % The toolbox developer MUST add path to his/her toolbox in his/her 'prog' - % function, with a command line like: - % >> addpath(fullfile(spm('Dir'),'toolbox','mytoolbox'),'-end'); - cwd = pwd; - j = 1; - for i=1:length(ft) - try - cd(dt{i}); - tools.values{j} = feval(strtok(ft{i},'.')); - j = j + 1; - catch - disp(['Loading of toolbox ' fullfile(dt{i},ft{i}) ' failed.']); - end + if ~isempty(fi) + ft = {ft{:} fi{:}}; + dt(end+1:end+length(fi)) = deal({d2}); + else + % try *_config_*.m files, if toolbox does not have '*_cfg_*.m' files + f2 = regexp(di,'.*_config_.*\.m$'); + if ~iscell(f2), f2 = {f2}; end + fi = {di{~cellfun('isempty',f2)}}; + ftc = {ftc{:} fi{:}}; + dtc(end+1:end+length(fi)) = deal({d2}); + end; end - for i=1:length(ftc) - try - cd(dtc{i}); - % use cfg_struct2cfg to convert from SPM5 to matlabbatch - % configuration tree - tools.values{j} = cfg_struct2cfg(feval(strtok(ftc{i},'.'))); - j = j + 1; - catch - disp(['Loading of toolbox ' fullfile(dtc{i},ftc{i}) ' failed.']); + if ~isempty(ft)||~isempty(ftc) + % The toolbox developer MUST add path to his/her toolbox in his/her 'prog' + % function, with a command line like: + % >> addpath(fullfile(spm('Dir'),'toolbox','mytoolbox'),'-end'); + cwd = pwd; + j = 1; + for i=1:length(ft) + try + cd(dt{i}); + tools.values{j} = feval(strtok(ft{i},'.')); + j = j + 1; + catch + disp(['Loading of toolbox ' fullfile(dt{i},ft{i}) ' failed.']); + end + end + for i=1:length(ftc) + try + cd(dtc{i}); + % use cfg_struct2cfg to convert from SPM5 to matlabbatch + % configuration tree + tools.values{j} = cfg_struct2cfg(feval(strtok(ftc{i},'.'))); + j = j + 1; + catch + disp(['Loading of toolbox ' fullfile(dtc{i},ftc{i}) ' failed.']); + end end + cd(cwd); end - cd(cwd); end %_______________________________________________________________________ % spmjobs SPM diff --git a/config/spm_cfg_bms.m b/config/spm_cfg_bms.m index c501dd2..516388b 100644 --- a/config/spm_cfg_bms.m +++ b/config/spm_cfg_bms.m @@ -4,7 +4,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Maria Joao Rosa -% $Id: spm_cfg_bms.m 2915 2009-03-20 19:15:44Z maria $ +% $Id: spm_cfg_bms.m 3288 2009-07-27 09:23:54Z maria $ % --------------------------------------------------------------------- % dir Directory @@ -94,6 +94,27 @@ map.values = {subj_map }; map.num = [1 Inf]; +% --------------------------------------------------------------------- +% mod_name Name +% --------------------------------------------------------------------- +mod_name = cfg_entry; +mod_name.tag = 'mod_name'; +mod_name.name = 'Name'; +mod_name.help = {'Specify name for each model (optional).'}; +mod_name.strtype = 's'; +mod_name.num = [0 Inf]; +mod_name.val = {''}; + +% --------------------------------------------------------------------- +% name_mod Name models +% --------------------------------------------------------------------- +name_mod = cfg_repeat; +name_mod.tag = 'name_mod'; +name_mod.name = 'Name models'; +name_mod.help = {'Specify name for each model (optional).'}'; +name_mod.values = {mod_name }; +name_mod.num = [0 Inf]; + % --------------------------------------------------------------------- % file BMS.mat % --------------------------------------------------------------------- @@ -127,6 +148,24 @@ 'RFX' }'; +% --------------------------------------------------------------------- +% verify_id Verify data ID +% --------------------------------------------------------------------- +verify_id = cfg_menu; +verify_id.tag = 'verify_id'; +verify_id.name = 'Verify data identity'; +verify_id.help = {['Verify whether the model comparison is valid '... + 'i.e. whether the models have been fitted to the same data.']}; +verify_id.labels = { + 'yes' + 'no' +}'; +verify_id.values = { + 1 + 0 +}'; +verify_id.val = {0}; + % --------------------------------------------------------------------- % out_file Output files % --------------------------------------------------------------------- @@ -137,20 +176,26 @@ 'RFX analyses). ']... ''... ['Default option (and faster option): '... - 'Alpha + PPM = alpha.img (Dirichlet '... - 'parameters) + xppm.img (Posterior Probability '... - 'Maps) for each model.. ']... + 'PPM = xppm.img (Posterior Probability Maps) '... + 'for each model.']... ''... - ['Second option: Alpha + PPM + EPM = alpha.img + '... - 'xppm.img + epm.img (Exceedance Probability '... - ' Maps) for each model.']}; + ['Second option: PPM + EPM = xppm.img + '... + 'epm.img (Exceedance Probability '... + 'Maps + Exceedance Probability Maps) for each model.']... + ''... + ['Third option: PPM + EPM + Alpha = xppm.img + '... + 'epm.img + alpha.img (PPM, EPM and Map of Dirichlet '... + 'Parameters) for each model.']}; out_file.labels = { - 'Alpha + PPM' - 'Alpha + PPM + EPM' + 'PPM' + 'PPM + EPM' + 'PPM + EPM + Alpha' + }'; out_file.values = { 0 1 + 2 }'; out_file.val = {0}; @@ -229,6 +274,18 @@ thres.num = [0 Inf]; thres.val = {[]}; +% --------------------------------------------------------------------- +% k Extent threshold +% --------------------------------------------------------------------- +k = cfg_entry; +k.tag = 'k'; +k.name = 'Extent threshold'; +k.help = {['Specify extent threshold (minimum number of voxels '... + 'per cluster).']}; +k.strtype = 'e'; +k.num = [0 Inf]; +k.val = {[]}; + % --------------------------------------------------------------------- % scale Map Scale % --------------------------------------------------------------------- @@ -259,7 +316,7 @@ bms_dcm = cfg_exbranch; bms_dcm.tag = 'bms_dcm'; bms_dcm.name = 'BMS: DCM'; -bms_dcm.val = {dir dcm load_f method }; +bms_dcm.val = {dir dcm load_f method verify_id}; bms_dcm.help = {['Bayesian Model Selection for Dynamic Causal Modelling '... '(DCM) for fMRI or MEEG.']... ''... @@ -299,7 +356,7 @@ bms_map_inf = cfg_exbranch; bms_map_inf.tag = 'bms_map_inf'; bms_map_inf.name = 'BMS: Maps'; -bms_map_inf.val = {dir map method out_file mask nsamp }; +bms_map_inf.val = {dir map name_mod method out_file mask nsamp }; bms_map_inf.help = {'Bayesian Model Selection for Log-Evidence Maps. '... ''... ['Input: log-evidence maps (.img) for each model, session and '... @@ -325,7 +382,7 @@ bms_map_vis = cfg_exbranch; bms_map_vis.tag = 'bms_map_vis'; bms_map_vis.name = 'BMS: Maps (Results)'; -bms_map_vis.val = {file img thres scale }; +bms_map_vis.val = {file img thres k scale}; bms_map_vis.help = {['Bayesian Model Selection Maps (Results). '... 'Show results from BMS Maps (Inference).']}; bms_map_vis.prog = @spm_run_bms_vis; diff --git a/config/spm_cfg_defs.m b/config/spm_cfg_defs.m index 7be2884..1cce4b8 100644 --- a/config/spm_cfg_defs.m +++ b/config/spm_cfg_defs.m @@ -4,7 +4,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_cfg_defs.m 2500 2008-11-28 16:11:15Z john $ +% $Id: spm_cfg_defs.m 3339 2009-08-28 19:14:37Z john $ hsummary = {[... 'This is a utility for working with deformation fields. ',... @@ -177,6 +177,40 @@ applyto.val = {''}; applyto.help = happly; +savepwd = cfg_const; +savepwd.name = 'Current directory'; +savepwd.tag = 'savepwd'; +savepwd.val = {1}; +savepwd.help = {['All created files (deformation fields and warped images) ' ... + 'are written to the current directory.']}; + +savesrc = cfg_const; +savesrc.name = 'Source directories'; +savesrc.tag = 'savesrc'; +savesrc.val = {1}; +savesrc.help = {['The combined deformation field is written into the ' ... + 'directory of the first deformation field, warped images ' ... + 'are written to the same directories as the source ' ... + 'images.']}; + +savedef = cfg_const; +savedef.name = 'Source directory (deformation)'; +savedef.tag = 'savedef'; +savedef.val = {1}; +savedef.help = {['The combined deformation field and the warped images ' ... + 'are written into the directory of the first deformation ' ... + 'field.']}; + +saveusr = files('Output directory','saveusr','dir',[1 1]); +saveusr.help = {['The combined deformation field and the warped images ' ... + 'are written into the specified directory.']}; + +savedir = cfg_choice; +savedir.name = 'Output destination'; +savedir.tag = 'savedir'; +savedir.values = {savepwd savesrc savedef saveusr}; +savedir.val = {savepwd}; + interp = cfg_menu; interp.name = 'Interpolation'; interp.tag = 'interp'; @@ -199,27 +233,29 @@ }; -conf = exbranch('Deformations','defs',{comp,saveas,applyto,interp}); +conf = exbranch('Deformations','defs',{comp,saveas,applyto,savedir,interp}); conf.prog = @spm_defs; -conf.vfiles = @vfiles; +conf.vout = @vout; conf.help = hsummary; return; %_______________________________________________________________________ %_______________________________________________________________________ -function vf = vfiles(job) -vf = {}; -s = strvcat(job.ofname); -if ~isempty(s), - vf = {vf{:}, fullfile(pwd,['y_' s '.nii,1'])}; -end; - -s = strvcat(job.fnames); -for i=1:size(s,1), - [pth,nam,ext,num] = spm_fileparts(s(i,:)); - vf = {vf{:}, fullfile(pwd,['w',nam,ext,num])}; -end; +function vo = vout(job) +vo = []; +if ~strcmp(job.ofname,'') && ~isempty(job.ofname) + vo = cfg_dep; + vo.sname = 'Combined deformation'; + vo.src_output = substruct('.','def'); + vo.tgt_spec = cfg_findspec({{'filter','image','filter','nifti'}}); +end +if iscellstr(job.fnames) && ~isempty(job.fnames) + if isempty(vo), vo = cfg_dep; else vo(end+1) = cfg_dep; end + vo(end).sname = 'Warped images'; + vo(end).src_output = substruct('.','warped'); + vo(end).tgt_spec = cfg_findspec({{'filter','image'}}); +end return; %_______________________________________________________________________ diff --git a/config/spm_cfg_eeg_artefact.m b/config/spm_cfg_eeg_artefact.m index b893b5f..a54c981 100644 --- a/config/spm_cfg_eeg_artefact.m +++ b/config/spm_cfg_eeg_artefact.m @@ -3,10 +3,11 @@ %_______________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging -% Stefan Kiebel -% $Id: spm_cfg_eeg_artefact.m 2225 2008-09-29 12:25:27Z stefan $ +% Vladimir Litvak +% $Id: spm_cfg_eeg_artefact.m 3258 2009-07-08 17:46:54Z vladimir $ + +rev = '$Rev: 3258 $'; -rev = '$Rev: 2225 $'; D = cfg_files; D.tag = 'D'; D.name = 'File Name'; @@ -14,111 +15,94 @@ D.num = [1 1]; D.help = {'Select the EEG mat file.'}; -nothing = cfg_const; -nothing.tag = 'nothing'; -nothing.name = 'No lists'; -nothing.val = {1}; - -out_list = cfg_entry; -out_list.tag = 'out_list'; -out_list.name = 'Artefactual trial list'; -out_list.strtype = 'r'; -out_list.num = [0 inf]; -out_list.help = {'List artefactual trials (0 for none)'}; - -in_list = cfg_entry; -in_list.tag = 'in_list'; -in_list.name = 'Clean trial list'; -in_list.strtype = 'r'; -in_list.num = [0 inf]; -in_list.help = {'List clean trials (0 for none)'}; - -list = cfg_branch; -list.tag = 'list'; -list.name = 'Trial lists'; -list.val = {out_list in_list}; - -External_list = cfg_choice; -External_list.tag = 'External_list'; -External_list.name = 'Use own artefact list'; -External_list.val = {nothing}; -External_list.help = {'Choose this option if you want to specify your own artefact list'}'; -External_list.values = {list nothing}; - -Weightingfunction = cfg_entry; -Weightingfunction.tag = 'Weightingfunction'; -Weightingfunction.name = 'Weightingfunction'; -Weightingfunction.strtype = 'r'; -Weightingfunction.num = [1 1]; -Weightingfunction.val = {3}; -Weightingfunction.help = {'Input offset of weighting function'}; - -Smoothing = cfg_entry; -Smoothing.tag = 'Smoothing'; -Smoothing.name = 'Smoothing'; -Smoothing.strtype = 'r'; -Smoothing.num = [1 1]; -Smoothing.val = {20}; -Smoothing.help = {'FWHM for residual smoothing (ms)'}; - -weighted_arg = cfg_branch; -weighted_arg.tag = 'weighted_arg'; -weighted_arg.name = 'Robust averaging'; -weighted_arg.val = {Weightingfunction Smoothing}; - -nothing = cfg_const; -nothing.tag = 'nothing'; -nothing.name = 'No robust averaging'; -nothing.val = {1}; - -weighted = cfg_choice; -weighted.tag = 'weighted'; -weighted.name = 'Use robust averaging'; -weighted.val = {nothing}; -weighted.help = {'Choose this option if you want to apply robust averaging'}'; -weighted.values = {weighted_arg nothing}; - -nothing = cfg_const; -nothing.tag = 'nothing'; -nothing.name = 'No thresholding'; -nothing.val = {1}; - -channels_threshold = cfg_entry; -channels_threshold.tag = 'channels_threshold'; -channels_threshold.name = 'Channels to threshold'; -channels_threshold.strtype = 'r'; -channels_threshold.num = [1 inf]; -channels_threshold.help = {'Choose channel indices of channels which you want to threshold.'}; - -thresholdval = cfg_entry; -thresholdval.tag = 'thresholdval'; -thresholdval.name = 'Thresholds'; -thresholdval.strtype = 'r'; -thresholdval.num = [1 inf]; -thresholdval.help = {'Channel-wise thresholds. Use single threshold to apply the same threshold to all channels'}; -thresholdval.val = {80}; - -threshold = cfg_branch; -threshold.tag = 'threshold'; -threshold.name = 'Thresholding'; -threshold.val = {channels_threshold thresholdval}; - -Check_Threshold = cfg_choice; -Check_Threshold.tag = 'Check_Threshold'; -Check_Threshold.name = 'Threshold channels'; -Check_Threshold.val = {threshold}; -Check_Threshold.help = {'Choose this option if you want to threshold the data'}'; -Check_Threshold.values = {threshold nothing}; - -artefact = cfg_branch; -artefact.tag = 'artefact'; -artefact.name = 'Artefact'; -artefact.val = {External_list weighted Check_Threshold}; +badchanthresh = cfg_entry; +badchanthresh.tag = 'badchanthresh'; +badchanthresh.name = 'Bad channel threshold'; +badchanthresh.strtype = 'r'; +badchanthresh.num = [1 1]; +badchanthresh.val = {0.2}; +badchanthresh.help = {'Fraction of trials with artefacts ', ... + 'above which an M/EEG channel is declared as bad.'}; + +chanall = cfg_const; +chanall.tag = 'type'; +chanall.name = 'All'; +chanall.val = {'all'}; + +chanmeg = cfg_const; +chanmeg.tag = 'type'; +chanmeg.name = 'MEG'; +chanmeg.val = {'MEG'}; + +chanmegplanar = cfg_const; +chanmegplanar.tag = 'type'; +chanmegplanar.name = 'MEGPLANAR'; +chanmegplanar.val = {'MEGPLANAR'}; + +chaneeg = cfg_const; +chaneeg.tag = 'type'; +chaneeg.name = 'EEG'; +chaneeg.val = {'EEG'}; + +chaneog = cfg_const; +chaneog.tag = 'type'; +chaneog.name = 'EOG'; +chaneog.val = {'EOG'}; + +chanecg = cfg_const; +chanecg.tag = 'type'; +chanecg.name = 'ECG'; +chanecg.val = {'ECG'}; + +chanemg = cfg_const; +chanemg.tag = 'type'; +chanemg.name = 'EMG'; +chanemg.val = {'EMG'}; + +chanlfp = cfg_const; +chanlfp.tag = 'type'; +chanlfp.name = 'LFP'; +chanlfp.val = {'LFP'}; + +chanfile = cfg_files; +chanfile.tag = 'file'; +chanfile.name = 'Channel file'; +chanfile.filter = 'mat'; +chanfile.num = [1 1]; + +channels = cfg_choice; +channels.tag = 'channels'; +channels.name = 'Channel selection'; +channels.values = {chanall, chanmeg, chanmegplanar, chaneeg, chaneog, chanecg, chanemg, chanlfp, chanfile}; +channels.val = {chanall}; + +artefact_funs = dir(fullfile(spm('dir'), 'spm_eeg_artefact_*.m')); +artefact_funs = {artefact_funs(:).name}; + +fun = cfg_choice; +fun.tag = 'fun'; +fun.name = 'Detection algorithm'; +for i = 1:numel(artefact_funs) + fun.values{i} = feval(spm_str_manip(artefact_funs{i}, 'r')); +end + +methods = cfg_branch; +methods.tag = 'methods'; +methods.name = 'Method'; +methods.val = {channels, fun}; + +methodsrep = cfg_repeat; +methodsrep.tag = 'methodsrep'; +methodsrep.name = 'How to look for artefacts'; +methodsrep.help = {'Choose channels and methods for artefact detection'}; +methodsrep.values = {methods}; +methodsrep.num = [1 Inf]; + S = cfg_exbranch; S.tag = 'eeg_artefact'; S.name = 'M/EEG Artefact detection'; -S.val = {D artefact}; +S.val = {D, badchanthresh, methodsrep}; S.help = {'Detect artefacts in epoched M/EEG data.'}; S.prog = @eeg_artefact; S.vout = @vout_eeg_artefact; @@ -128,35 +112,22 @@ function out = eeg_artefact(job) % construct the S struct S.D = job.D{1}; -S.artefact = job.artefact; - -if isfield(S.artefact.External_list, 'nothing') - S.artefact.External_list = 0; -else - S.artefact.External_list = 1; - S.artefact.in_list = job.artefact.External_list.in_list; - S.artefact.out_list = job.artefact.External_list.out_list; - +S.badchanthresh = job.badchanthresh; + +for i = 1:numel(job.methods) + if isfield(job.methods(i).channels, 'type') + S.methods(i).channels = job.methods(i).channels.type; + else + S.methods(i).channels = getfield(load(job.methods(i).channels.file{1}), 'label'); + end + + fun = fieldnames(job.methods(i).fun); + fun = fun{1}; + + S.methods(i).fun = fun; + S.methods(i).settings = getfield(job.methods(i).fun, fun); end - -if isfield(S.artefact.weighted, 'nothing') - S.artefact.Weighted = 0; -else - S.artefact.Weighted = 1; - S.artefact.Weightingfunction = job.artefact.weighted.weighted_arg.Weightingfunction; - S.artefact.Smoothing = job.artefact.weighted.weighted_arg.Smoothing; - -end - -if isfield(S.artefact.Check_Threshold, 'nothing') - S.artefact.Check_Threshold = 0; -else - S.artefact.Check_Threshold = 1; - S.artefact.threshold = job.artefact.Check_Threshold.threshold.thresholdval; - S.artefact.channels_threshold = job.artefact.Check_Threshold.threshold.channels_threshold; - -end - + out.D = spm_eeg_artefact(S); out.Dfname = {out.D.fname}; diff --git a/config/spm_cfg_eeg_average.m b/config/spm_cfg_eeg_average.m index 50cffe1..769ef97 100644 --- a/config/spm_cfg_eeg_average.m +++ b/config/spm_cfg_eeg_average.m @@ -4,9 +4,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_cfg_eeg_average.m 2870 2009-03-12 13:43:35Z guillaume $ +% $Id: spm_cfg_eeg_average.m 3258 2009-07-08 17:46:54Z vladimir $ -rev = '$Rev: 2870 $'; +rev = '$Rev: 3258 $'; D = cfg_files; D.tag = 'D'; D.name = 'File Name'; @@ -14,19 +14,65 @@ D.num = [1 1]; D.help = {'Select the M/EEG mat file.'}; +standard = cfg_const; +standard.tag = 'standard'; +standard.name = 'Standard'; +standard.val = {false}; + +ks = cfg_entry; +ks.tag = 'ks'; +ks.name = 'Offset of the weighting function'; +ks.strtype = 'r'; +ks.val = {3}; +ks.num = [1 1]; +ks.help = {'Parameter determining the how far the values should be from the median, '... + 'to be considered outliers (the larger, the farther).'}; + +bycondition = cfg_menu; +bycondition.tag = 'bycondition'; +bycondition.name = 'Compute weights by condition'; +bycondition.help = {'Compute weights for each condition separately or for all conditions together.'}; +bycondition.labels = {'Yes', 'No'}; +bycondition.values = {true, false}; +bycondition.val = {true}; + +savew = cfg_menu; +savew.tag = 'savew'; +savew.name = 'Save weights'; +savew.help = {'Save weights in a separate dataset for quality control.'}; +savew.labels = {'Yes', 'No'}; +savew.values = {true, false}; +savew.val = {true}; + +robust = cfg_branch; +robust.tag = 'robust'; +robust.name = 'Robust'; +robust.val = {ks, bycondition, savew}; + +userobust = cfg_choice; +userobust.tag = 'userobust'; +userobust.name = 'Averaging type'; +userobust.help = {'choose between using standard and robust averaging'}; +userobust.values = {standard, robust}; +userobust.val = {standard}; + S = cfg_exbranch; S.tag = 'eeg_average'; -S.name = 'M/EEG averaging'; -S.val = {D}; +S.name = 'M/EEG Averaging'; +S.val = {D, userobust}; S.help = {'Average epoched EEG/MEG data.'}; S.prog = @eeg_average; S.vout = @vout_eeg_average; S.modality = {'EEG'}; - function out = eeg_average(job) % construct the S struct S.D = job.D{1}; +if isfield(job.userobust, 'robust') + S.robust = job.userobust.robust; +else + S.robust = false; +end S.review = false; out.D = spm_eeg_average(S); diff --git a/config/spm_cfg_eeg_bc.m b/config/spm_cfg_eeg_bc.m new file mode 100644 index 0000000..02bb456 --- /dev/null +++ b/config/spm_cfg_eeg_bc.m @@ -0,0 +1,61 @@ +function S = spm_cfg_eeg_bc +% configuration file for baseline correction +%__________________________________________________________________________ +% Copyright (C) 2009 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: spm_cfg_eeg_bc.m 3250 2009-07-06 09:31:13Z vladimir $ + +%-------------------------------------------------------------------------- +% D +%-------------------------------------------------------------------------- +D = cfg_files; +D.tag = 'D'; +D.name = 'File Name'; +D.filter = 'mat'; +D.num = [1 1]; +D.help = {'Select the M/EEG mat file.'}; + +%-------------------------------------------------------------------------- +% time +%-------------------------------------------------------------------------- +time = cfg_entry; +time.tag = 'time'; +time.name = 'Baseline'; +time.help = {'Start and stop of baseline [ms].'}; +time.strtype = 'e'; +time.num = [1 2]; + +%-------------------------------------------------------------------------- +% S +%-------------------------------------------------------------------------- +S = cfg_exbranch; +S.tag = 'eeg_bc'; +S.name = 'M/EEG Baseline correction'; +S.val = {D, time}; +S.help = {'Baseline correction of M/EEG time data'}'; +S.prog = @eeg_bc; +S.vout = @vout_eeg_bc; +S.modality = {'EEG'}; + +%========================================================================== +function out = eeg_bc(job) +% construct the S struct +S.D = job.D{1}; +S.time = job.time; + +out.D = spm_eeg_bc(S); +out.Dfname = {fullfile(out.D.path,out.D.fname)}; + +%========================================================================== +function dep = vout_eeg_bc(job) +% return dependencies +dep(1) = cfg_dep; +dep(1).sname = 'Baseline corrected M/EEG data'; +dep(1).src_output = substruct('.','D'); +dep(1).tgt_spec = cfg_findspec({{'strtype','e'}}); + +dep(2) = cfg_dep; +dep(2).sname = 'Baseline corrected M/EEG datafile'; +dep(2).src_output = substruct('.','Dfname'); +dep(2).tgt_spec = cfg_findspec({{'filter','mat'}}); diff --git a/config/spm_cfg_eeg_convert.m b/config/spm_cfg_eeg_convert.m index e087760..84eabe3 100644 --- a/config/spm_cfg_eeg_convert.m +++ b/config/spm_cfg_eeg_convert.m @@ -4,7 +4,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_cfg_eeg_convert.m 3059 2009-04-15 18:09:13Z guillaume $ +% $Id: spm_cfg_eeg_convert.m 3383 2009-09-10 17:53:49Z vladimir $ dataset = cfg_files; dataset.tag = 'dataset'; @@ -18,7 +18,7 @@ timewindow.name = 'Timing'; timewindow.strtype = 'r'; timewindow.num = [1 2]; -timewindow.help = {'start and end of epoch [s]'}; +timewindow.help = {'start and end of epoch [ms]'}; readall = cfg_const; readall.tag = 'readall'; @@ -34,7 +34,7 @@ usetrials = cfg_const; usetrials.tag = 'usetrials'; usetrials.name = 'Trials defined in data'; -usetrials.val = {0}; +usetrials.val = {1}; trlfile = cfg_files; trlfile.tag = 'trlfile'; @@ -197,14 +197,16 @@ S.usetrials = S.continuous.trials.usetrials; end if isfield(S.continuous.trials, 'trlfile') - S.trlfile = S.continuous.trials.trlfile; + S.trlfile = char(S.continuous.trials.trlfile); + S.usetrials = 0; end if isfield(S.continuous.trials, 'define') S.trialdef = S.continuous.trials.define.trialdef; S.pretrig = S.continuous.trials.define.timing(1); S.posttrig = S.continuous.trials.define.timing(2); - + S.reviewtrials = 0; + S.save = 0; S.usetrials = 0; [S.trl, S.conditionlabel] = spm_eeg_definetrial(S); end diff --git a/config/spm_cfg_eeg_merge.m b/config/spm_cfg_eeg_merge.m index ce4c1b3..86dfd4c 100644 --- a/config/spm_cfg_eeg_merge.m +++ b/config/spm_cfg_eeg_merge.m @@ -4,56 +4,69 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel, Volkmar Glauche -% $Id: spm_cfg_eeg_merge.m 2225 2008-09-29 12:25:27Z stefan $ +% $Id: spm_cfg_eeg_merge.m 3188 2009-06-08 08:47:46Z vladimir $ -rev = '$Rev: 2225 $'; +rev = '$Rev: 3188 $'; D = cfg_files; D.tag = 'D'; D.name = 'File Names'; D.filter = 'mat'; -D.num = [1 1]; -D.help = {'Select the M/EEG mat file.'}; +D.num = [2 Inf]; +D.help = {'Select the M/EEG mat files.'}; -string = cfg_entry; -string.tag = 'str'; -string.name = 'Label'; -string.strtype = 's'; -string.num = [1 Inf]; +file = cfg_entry; +file.tag = 'file'; +file.name = 'Files to which the rule applies'; +file.strtype = 's'; +file.val = {'.*'}; +file.help = {'Regular expression to match the files to which the rule applies (default - all)'}; -strings = cfg_repeat; -strings.tag = 'unused'; -strings.name = 'Labels'; -strings.values = {string}; +labelorg = cfg_entry; +labelorg.tag = 'labelorg'; +labelorg.name = 'Original labels to which the rule applies'; +labelorg.strtype = 's'; +labelorg.val = {'.*'}; +labelorg.help = {'Regular expression to match the original condition labels to which the rule applies (default - all)'}; -file = cfg_branch; -file.tag = 'file'; -file.name = 'File info'; -file.val = {D strings}; +labelnew = cfg_entry; +labelnew.tag = 'labelnew'; +labelnew.name = 'New label for the merged file'; +labelnew.strtype = 's'; +labelnew.val = {'#labelorg#'}; +labelnew.help = {['New condition label for the merged file. Special tokens can be used as part of the name. '... + '#file# will be replaced by the name of the original file, #labelorg# will be replaced by the original '... + 'condition labels.']}; + +rule = cfg_branch; +rule.tag = 'rule'; +rule.name = 'Recoding rule'; +rule.val = {file, labelorg, labelnew}; +rule.help = {'Recoding rule. The default means that all trials will keep their original label.'}; -files = cfg_repeat; -files.tag = 'unused'; -files.name = 'Files'; -files.num = [2 inf]; -files.values = {file}; + +rules = cfg_repeat; +rules.tag = 'unused'; +rules.name = 'Condition label recoding rules'; +rules.values = {rule}; +rules.num = [1 Inf]; +rules.val = {rule}; +rules.help = {['Specify the rules for translating condition labels from ' ... + 'the original files to the merged file. Multiple rules can be specified. The later ' ... + 'rules have precedence. Trials not matched by any of the rules will keep their original labels.']}; S = cfg_exbranch; S.tag = 'eeg_merge'; S.name = 'M/EEG Merging'; -S.val = {files}; +S.val = {D, rules}; S.help = {'Merge EEG/MEG data.'}; S.prog = @eeg_merge; S.vout = @vout_eeg_merge; S.modality = {'EEG'}; - function out = eeg_merge(job) % construct the S struct -S.D = strvcat(cat(1,job.file(:).D)); -for i = 1:length(job.file) - for j = 1:numel(job.file(i).str) - S.recode{i}{j} = job.file(i).str{j}; - end -end +S.D = strvcat(job.D{:}); +S.recode = job.rule; out.D = spm_eeg_merge(S); out.Dfname = {out.D.fname}; diff --git a/config/spm_cfg_eeg_tf_rescale.m b/config/spm_cfg_eeg_tf_rescale.m index 54baf04..357c166 100644 --- a/config/spm_cfg_eeg_tf_rescale.m +++ b/config/spm_cfg_eeg_tf_rescale.m @@ -1,30 +1,128 @@ function S = spm_cfg_eeg_tf_rescale % configuration file for rescaling spectrograms -%_______________________________________________________________________ +%__________________________________________________________________________ % Copyright (C) 2009 Wellcome Trust Centre for Neuroimaging % Will Penny -% $Id: spm_cfg_eeg_tf_rescale.m 3090 2009-04-29 17:30:05Z will $ - -Fname = cfg_files; -Fname.tag = 'Fname'; -Fname.name = 'File Names'; -Fname.filter = 'mat'; -Fname.num = [1 inf]; -Fname.help = {'Select the M/EEG mat file.'}; - - -S = cfg_exbranch; -S.tag = 'eeg_tf_rescale'; -S.name = 'M/EEG TF Rescale'; -S.val = {Fname}; -S.help = {'Rescale spectrogram using eg Log Ratio operator'}; -S.prog = @eeg_tf_rescale; -S.modality = {'EEG'}; +% $Id: spm_cfg_eeg_tf_rescale.m 3204 2009-06-15 14:45:25Z guillaume $ + +%-------------------------------------------------------------------------- +% D +%-------------------------------------------------------------------------- +D = cfg_files; +D.tag = 'D'; +D.name = 'File Name'; +D.filter = 'mat'; +D.num = [1 1]; +D.help = {'Select the M/EEG mat file.'}; + +%-------------------------------------------------------------------------- +% Sbaseline +%-------------------------------------------------------------------------- +Sbaseline = cfg_entry; +Sbaseline.tag = 'Sbaseline'; +Sbaseline.name = 'Baseline'; +Sbaseline.help = {'Start and stop of baseline [ms].'}; +Sbaseline.strtype = 'e'; +Sbaseline.num = [1 2]; + +%-------------------------------------------------------------------------- +% method_logr +%-------------------------------------------------------------------------- +method_logr = cfg_branch; +method_logr.tag = 'LogR'; +method_logr.name = 'Log Ratio'; +method_logr.val = {Sbaseline}; +method_logr.help = {'Log Ratio.'}; + +%-------------------------------------------------------------------------- +% method_diff +%-------------------------------------------------------------------------- +method_diff = cfg_branch; +method_diff.tag = 'Diff'; +method_diff.name = 'Difference'; +method_diff.val = {Sbaseline}; +method_diff.help = {'Difference.'}; + +%-------------------------------------------------------------------------- +% method_rel +%-------------------------------------------------------------------------- +method_rel = cfg_branch; +method_rel.tag = 'Rel'; +method_rel.name = 'Relative'; +method_rel.val = {Sbaseline}; +method_rel.help = {'Relative.'}; +%-------------------------------------------------------------------------- +% method_log +%-------------------------------------------------------------------------- +method_log = cfg_const; +method_log.tag = 'Log'; +method_log.name = 'Log'; +method_log.val = {1}; +method_log.help = {'Log.'}; +%-------------------------------------------------------------------------- +% method_sqrt +%-------------------------------------------------------------------------- +method_sqrt = cfg_const; +method_sqrt.tag = 'Sqrt'; +method_sqrt.name = 'Sqrt'; +method_sqrt.val = {1}; +method_sqrt.help = {'Square Root.'}; + +%-------------------------------------------------------------------------- +% method +%-------------------------------------------------------------------------- +method = cfg_choice; +method.tag = 'method'; +method.name = 'Rescale method'; +method.val = {method_logr}; +method.help = {'Select the rescale method.'}; +method.values = {method_logr method_diff method_rel method_log method_sqrt}; + +%-------------------------------------------------------------------------- +% S +%-------------------------------------------------------------------------- +S = cfg_exbranch; +S.tag = 'eeg_tf_rescale'; +S.name = 'M/EEG Time-Frequency Rescale'; +S.val = {D, method}; +S.help = {'Rescale (avg) spectrogram with nonlinear and/or difference operator.' + 'For ''Log'' and ''Sqrt'', these functions are applied to spectrogram.' + 'For ''LogR'', ''Rel'' and ''Diff'' this function computes power in the baseline.' + 'p_b and outputs:' + '(i) p-p_b for ''Diff''' + '(ii) 100*(p-p_b)/p_b for ''Rel''' + '(iii) log (p/p_b) for ''LogR'''}'; +S.prog = @eeg_tf_rescale; +S.vout = @vout_eeg_tf_rescale; +S.modality = {'EEG'}; + +%========================================================================== function out = eeg_tf_rescale(job) % construct the S struct -S = job; -S.Fname = strvcat(job.Fname); +S.D = job.D{1}; +S.tf.method = fieldnames(job.method); +S.tf.method = S.tf.method{1}; +switch lower(S.tf.method) + case {'logr','diff', 'rel'} + S.tf.Sbaseline = job.method.(S.tf.method).Sbaseline; + case {'log', 'sqrt'} +end + +out.D = spm_eeg_tf_rescale(S); +out.Dfname = {fullfile(out.D.path,out.D.fname)}; + +%========================================================================== +function dep = vout_eeg_tf_rescale(job) +% return dependencies +dep(1) = cfg_dep; +dep(1).sname = 'Rescaled TF Data'; +dep(1).src_output = substruct('.','D'); +dep(1).tgt_spec = cfg_findspec({{'strtype','e'}}); +dep(2) = cfg_dep; +dep(2).sname = 'Rescaled TF Datafile'; +dep(2).src_output = substruct('.','Dfname'); +dep(2).tgt_spec = cfg_findspec({{'filter','mat'}}); diff --git a/config/spm_run_bms_dcm.m b/config/spm_run_bms_dcm.m index c10c9d0..71b137e 100644 --- a/config/spm_run_bms_dcm.m +++ b/config/spm_run_bms_dcm.m @@ -1,43 +1,43 @@ function out = spm_run_bms_dcm (varargin) -% API to compare DCMs on the basis of their log-evidences. Three methods +% API to compare DCMs on the basis of their log-evidences. Three methods % are available to identify the best among alternative models: % -% (1) single subject BMS using Bayes factors +% (1) single subject BMS using Bayes factors % (see Penny et al, NeuroImage, 2004) -% (2) fixed effects group BMS using group Bayes factors +% (2) fixed effects group BMS using group Bayes factors % (see Stephan et al,NeuroImage, 2007) % (3) random effects group BMS using exceedance probabilities % (see Stephan et al,NeuroImage, 2009) % -% Note: All functions use the negative free energy (F) as an approximation +% Note: All functions use the negative free energy (F) as an approximation % to the log model evidence. % % __________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Chun-Chuan Chen -% $Id: spm_run_bms_dcm.m 2793 2009-02-26 18:29:41Z cc $ +% $Id: spm_run_bms_dcm.m 3363 2009-09-04 15:11:19Z christophe $ job = varargin{1}; fname ='BMS.mat'; % Output filename -fname = [job.dir{1},fname]; % Output filename (including directory) +fname = fullfile(job.dir{1},fname);% Output filename (including directory) F = []; N = {}; % prepare the data -if isempty(job.load_f{1})==0 +if isempty(job.load_f{1})==0 data=job.load_f{1}; load(data); nm = size(F,2); % No of Models - N = 1:nm; + N = 1:nm; else ns = size(job.sess_dcm,2); % No of Subjects nsess = size(job.sess_dcm{1},2); % No of sessions nm = size(job.sess_dcm{1}(1).mod_dcm,1); % No of Models - + % Check if No of models > 2 if nm < 2 msgbox('Please select more than one file') @@ -52,9 +52,11 @@ if (nsess_now == nsess && nmodels== nm) % Check no of sess/mods + ID = zeros(nsess, nm); + for j=1:nm - F_sess = []; + F_sess = []; for h = 1:nsess_now @@ -62,6 +64,29 @@ DCM_tmp = load(tmp{1}); F_sess = [F_sess,DCM_tmp.DCM.F]; + % Data ID verification. At least for now we'll + % re-compute the IDs rather than use the ones stored + % with the DCM. + if job.verify_id + M = DCM_tmp.DCM.M; + + if isfield(DCM_tmp.DCM, 'xY') + Y = DCM_tmp.DCM.xY; %not fMRI + else + Y = DCM_tmp.DCM.Y; % fMRI + end + + if isfield(M,'FS') + try + ID(h, j) = spm_data_id(feval(M.FS,Y.y,M)); + catch + ID(h, j) = spm_data_id(feval(M.FS,Y.y)); + end + else + ID(h, j) = spm_data_id(Y.y); + end + + end end F_mod = sum(F_sess); @@ -69,8 +94,17 @@ N{j} = sprintf('model%d',j); end + + if job.verify_id + failind = find(max(abs(diff(ID))) > eps); + if ~isempty(failind) + out.files{1} = []; + msgbox(['Error: the models for subject ' num2str(k) ... + ' session(s) ' num2str(failind) ' were not fitted to the same data.']); + return + end + end else - out.files{1} = []; msgbox('Error: the number of sessions/models should be the same for all subjects!') return @@ -155,5 +189,15 @@ end + +spm_figure('GetWin','Graphics'); +axes('position', [0.01, 0.01, 0.01, 0.01]); +axis off +if job.verify_id + text(0, 0, 'Data identity has been verified'); +else + text(0, 0, 'Data identity has not been verified'); +end + end diff --git a/config/spm_run_bms_map.m b/config/spm_run_bms_map.m index bbdd969..87ee102 100644 --- a/config/spm_run_bms_map.m +++ b/config/spm_run_bms_map.m @@ -41,28 +41,45 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Maria Joao Rosa -% $Id: spm_run_bms_map.m 2915 2009-03-20 19:15:44Z maria $ +% $Id: spm_run_bms_map.m 3288 2009-07-27 09:23:54Z maria $ % Input % ------------------------------------------------------------------------- -method = job.method; % Inference method -direct = job.dir{1}; -fname = [direct,'BMS.mat']; % Output filename (including directory) -mask = length(job.mask{1}); % Mask image +method = job.method; % Inference method +direct = job.dir{1}; +fname = [direct,'BMS.mat']; % Output filename (including directory) +mask = length(job.mask{1}); % Mask image if mask mask_image = spm_vol(job.mask); % Mask image Vol end -nsamps = str2num(job.nsamp); % Number of samples (nmodels > 3) -do_ecp = job.out_file; % Compute Exceedance Probability +nsamps = str2num(job.nsamp); % Number of samples (nmodels > 3) +do_maps = job.out_file; +do_ecp = do_maps > 0; % Compute Exceedance Probability +do_alpha = do_maps > 1; % Compute Alpha Parameters % Nb. of subjects and models % ------------------------------------------------------------------------- -nsubjs = size(job.sess_map,2); -nmodels = size(job.sess_map{1}(1).mod_map,1); -nsess = size(job.sess_map{1},2); +nsubjs = size(job.sess_map,2); +nmodels = size(job.sess_map{1}(1).mod_map,1); +nsess = size(job.sess_map{1},2); +nnames = size(job.mod_name,2); +names = job.mod_name; + +% Name models +% ------------------------------------------------------------------------- +if nnames < nmodels + for nn=1:nmodels-nnames + names = [names, sprintf('m%d',nn)]; + end +end + +if size(unique(names(1:nmodels)),2) < nmodels, + id = 'Indentical names for different models!'; % Same name! + error(id); +end if nmodels < 2 - msgbox('Please select more than one file') % Models must be > 1! + msgbox('Please select more than one file!') % Models must be > 1! return end @@ -117,9 +134,9 @@ % Load Vols for all subjects/models for i = 1:nmodels, - model_ppm(i).fname = sprintf('%smodel%d_ppm.img',direct,i); - model_ppm(i).descrip = sprintf('PPM: model %d',i); - BMS.map.ffx.ppm{i} = model_ppm(i).fname; + model_ppm(i).fname = sprintf('%s%s_model_ppm.img',direct,names{i}); + model_ppm(i).descrip = sprintf('PPM: %s model',names{i}); + BMS.map.ffx.ppm{i} = model_ppm(i).fname; for s = 1:nsubjs, for se = 1:nsess, @@ -162,16 +179,6 @@ % BMS structure out.files{1} = fname; - - % Create alpha .img files for each model - model_alpha(1:nmodels) = struct(... - 'fname', '',... - 'dim', DIM',... - 'dt', [spm_type('float32') spm_platform('bigend')],... - 'mat', M,... - 'pinfo', [1 0 0]',... - 'n', [1 1], ... - 'descrip', ''); % Create PPM .img files for each model model_exp_r(1:nmodels) = struct(... @@ -184,29 +191,43 @@ 'descrip', ''); if do_ecp - % Create EPM .img files for each model - model_xp(1:nmodels) = struct(... - 'fname', '',... - 'dim', DIM',... - 'dt', [spm_type('float32') spm_platform('bigend')],... - 'mat', M,... - 'pinfo', [1 0 0]',... - 'n', [1 1], ... - 'descrip', ''); + % Create EPM .img files for each model + model_xp(1:nmodels) = struct(... + 'fname', '',... + 'dim', DIM',... + 'dt', [spm_type('float32') spm_platform('bigend')],... + 'mat', M,... + 'pinfo', [1 0 0]',... + 'n', [1 1], ... + 'descrip', ''); + end + + if do_alpha + % Create alpha .img files for each model + model_alpha(1:nmodels) = struct(... + 'fname', '',... + 'dim', DIM',... + 'dt', [spm_type('float32') spm_platform('bigend')],... + 'mat', M,... + 'pinfo', [1 0 0]',... + 'n', [1 1], ... + 'descrip', ''); end % Load Vols for all subjects/models for i = 1:nmodels, - model_alpha(i).fname = sprintf('%smodel%d_alpha.img',direct,i); - model_alpha(i).descrip = sprintf('Alpha: model %d',i); - BMS.map.rfx.alpha{i} = model_alpha(i).fname; - model_exp_r(i).fname = sprintf('%smodel%d_xppm.img',direct,i); - model_exp_r(i).descrip = sprintf('Exp_r: model %d',i); - BMS.map.rfx.ppm{i} = model_exp_r(i).fname; + model_exp_r(i).fname = sprintf('%s%s_model_xppm.img',direct,names{i}); + model_exp_r(i).descrip = sprintf('Exp_r: %s model',names{i}); + BMS.map.rfx.ppm{i} = model_exp_r(i).fname; if do_ecp - model_xp(i).fname = sprintf('%smodel%d_epm.img',direct,i); - model_xp(i).descrip = sprintf('XP: model %d',i); - BMS.map.rfx.epm{i} = model_xp(i).fname; + model_xp(i).fname = sprintf('%s%s_model_epm.img',direct,names{i}); + model_xp(i).descrip = sprintf('XP: %s model',names{i}); + BMS.map.rfx.epm{i} = model_xp(i).fname; + end + if do_alpha + model_alpha(i).fname = sprintf('%s%s_model_alpha.img',direct,names{i}); + model_alpha(i).descrip = sprintf('Alpha: %s model',names{i}); + BMS.map.rfx.alpha{i} = model_alpha(i).fname; end for s = 1:nsubjs, for se = 1:nsess, @@ -227,10 +248,10 @@ end end - % Create files - model_alpha = spm_create_vol(model_alpha); - model_exp_r = spm_create_vol(model_exp_r); - if do_ecp, model_xp = spm_create_vol(model_xp); end + % Create files + model_exp_r = spm_create_vol(model_exp_r); + if do_ecp, model_xp = spm_create_vol(model_xp); end + if do_alpha, model_alpha = spm_create_vol(model_alpha); end % Save data and BMS BMS.fname = fname; @@ -334,41 +355,42 @@ if Nvoxels > 0 % Initialise results - alpha_total = zeros(Nvoxels,nmodels); exp_r_total = zeros(Nvoxels,nmodels); if do_ecp, xp_total = zeros(Nvoxels,nmodels); end + if do_alpha, alpha_total = zeros(Nvoxels,nmodels); end % Do BMS in all voxels of slice z for n = 1:Nvoxels, lme = z_models(:,:,non_nan(n)); % Group BMS [alpha,exp_r,xp] = spm_BMS(lme,nsamps,0,0,do_ecp); - alpha_total(n,:) = alpha; % Dirichlet par. exp_r_total(n,:) = exp_r; % Cond. Expecta. if do_ecp, xp_total(n,:) = xp; end % Exceeda. Prob. + if do_alpha, alpha_total(n,:) = alpha; end % Dirichlet par. end % Write images for i = 1:nmodels, - j(non_nan) = alpha_total(:,i); - model_alpha(i) = spm_write_plane(model_alpha(i),j,z); j(non_nan) = exp_r_total(:,i); model_exp_r(i) = spm_write_plane(model_exp_r(i),j,z); if do_ecp j(non_nan) = xp_total(:,i); model_xp(i) = spm_write_plane(model_xp(i),j,z); end + if do_alpha + j(non_nan) = alpha_total(:,i); + model_alpha(i) = spm_write_plane(model_alpha(i),j,z); + end end else % Write images when Nvoxels = 0 for i = 1:nmodels, - model_alpha(i) = spm_write_plane(model_alpha(i),j,z); model_exp_r(i) = spm_write_plane(model_exp_r(i),j,z); if do_ecp, model_xp(i) = spm_write_plane(model_xp(i),j,z); end + if do_alpha, model_alpha(i) = spm_write_plane(model_alpha(i),j,z); end end end - - + end end % Loop over slices diff --git a/config/spm_run_bms_vis.m b/config/spm_run_bms_vis.m index b69a01c..8b6b051 100644 --- a/config/spm_run_bms_vis.m +++ b/config/spm_run_bms_vis.m @@ -13,12 +13,12 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Maria Joao Rosa -% $Id: spm_run_bms_vis.m 2788 2009-02-25 16:42:23Z maria $ +% $Id: spm_run_bms_vis.m 3288 2009-07-27 09:23:54Z maria $ % Input % ------------------------------------------------------------------------- if isempty(varargin) - job.file{1}=''; job.img{1}=''; job.thres = []; job.scale = []; + job.file{1}=''; job.img{1}=''; job.thres = []; job.scale = []; job.k=[]; else job = varargin{1}; end @@ -26,6 +26,7 @@ image = length(job.img{1}); thres_b = length(job.thres); odds_b = length(job.scale); +ext_thre = length(job.k); % Initialise SPM Interactive window % ------------------------------------------------------------------------- @@ -45,13 +46,14 @@ % Load BMS load(file_fname); +wd = fileparts(file_fname); % Select results (.img) from BMS Maps % ------------------------------------------------------------------------- if image post = job.img{1}; else - post = spm_select(1,'image','Select BMS Maps results (ex: ppm, alpha or epm image)'); + post = spm_select(1,'image','Select BMS Maps results (ex: ppm, alpha or epm image)',[],wd,'.img'); end % Select threshold to apply to image @@ -62,6 +64,14 @@ threshold = spm_input('Probability Threshold:', '+1', 'r', '0.5', [1, inf]); end +% Extent threshold +% ------------------------------------------------------------------------- +if ext_thre + k = job.k; +else + k = spm_input('& extent threshold {voxels}','+1','r',0,1,[0,Inf]); +end + % Select scale % ------------------------------------------------------------------------- if odds_b @@ -88,7 +98,7 @@ [pathstr,name_image] = fileparts(V.fname); undersc = find(name_image=='_'); res_name = name_image(undersc(end)+1); -subset_model = name_image(1:undersc(end)-1); +subset_model = name_image(1:undersc(end-1)-1); % Show results being displayed on graphics window switch res_name @@ -126,7 +136,7 @@ % ------------------------------------------------------------------------- if ~isempty(z_above) - z_ps = z_above; + z_orig = z_above; % Log odds transform if odds z_odds = log(z_above./(ones(1,length(z_above))-z_above)); @@ -136,14 +146,32 @@ odds = 0; % Don't plot odds end end + + % Calculate extent threshold filtering + % --------------------------------------------------------------------- + A = spm_clusters(xyz_above); + Q = []; + for i = 1:max(A) + j = find(A == i); + if length(j) >= k; Q = [Q j]; end + end + + % ...eliminate voxels + %---------------------------------------------------------------------- + Z = z_above(:,Q); + XYZ = xyz_above(:,Q); + z_ps = z_orig(:,Q); + if isempty(Q) + warning(sprintf('No voxels survive extent threshold k=%0.2g',k)) + end % Save data in xSPM structure - voxels_mm = M*[xyz_above;ones(1,size(xyz_above,2))]; + voxels_mm = M*[XYZ;ones(1,size(XYZ,2))]; voxels_mm = voxels_mm(1:3,:); xSPM.swd = file_fname; xSPM.STAT = ''; - xSPM.Z = z_above; - xSPM.XYZ = xyz_above; + xSPM.Z = Z; + xSPM.XYZ = XYZ; xSPM.XYZmm = voxels_mm; xSPM.xVol.M = M; xSPM.xVol.DIM = DIM; @@ -151,7 +179,7 @@ xSPM.M = M; xSPM.iM = iM; xSPM.n = 1; - xSPM.k = 0; + xSPM.k = k; xSPM.df = []; xSPM.u = threshold; xSPM.STAT = ''; diff --git a/config/spm_run_fmri_est.m b/config/spm_run_fmri_est.m index 6724a1a..20df6d3 100644 --- a/config/spm_run_fmri_est.m +++ b/config/spm_run_fmri_est.m @@ -10,7 +10,7 @@ %_______________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging -% $Id: spm_run_fmri_est.m 2928 2009-03-24 08:54:32Z lee $ +% $Id: spm_run_fmri_est.m 3327 2009-08-18 08:27:32Z volkmar $ global defaults @@ -34,28 +34,12 @@ %----------------------------------------------------------------------- cd(fileparts(job.spmmat{:})); -% COMMENTED OUT BY DRG. THIS SHOULD BE TAKEN CARE OF WITHIN SPM_SPM AND -% SPM_SPM_BAYES. REMOVE THIS SECTION ONCE THIS HAS BEEN VERIFIED. -%-If we've gotten to this point we're committed to overwriting files. -% Delete them so we don't get stuck in spm_spm -%----------------------------------------------------------------------- -% files = {'^mask\..{3}$','^ResMS\..{3}$','^RPV\..{3}$',... -% '^beta_.{4}\..{3}$','^con_.{4}\..{3}$','^ResI_.{4}\..{3}$',... -% '^ess_.{4}\..{3}$', '^spm\w{1}_.{4}\..{3}$'}; -% -% for i=1:length(files) -% j = spm_select('List',pwd,files{i}); -% for k=1:size(j,1) -% spm_unlink(deblank(j(k,:))); -% end -% end -% END COMMENTED OUT BY DRG - %======================================================================= % B A Y E S I A N 2nd L E V E L E S T I M A T I O N %======================================================================= if isfield(job.method,'Bayesian2') - %out.spmvar = spm_spm_Bayes(SPM); + SPM = spm_spm_Bayes(SPM); + %out.spmvar = SPM; cd(original_dir); % Change back fprintf('Done\n'); return diff --git a/exec/make_exec.m b/exec/make_exec.m index bbcbb9f..49410b1 100644 --- a/exec/make_exec.m +++ b/exec/make_exec.m @@ -18,8 +18,120 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: make_exec.m 1143 2008-02-07 19:33:33Z spm $ +% $Id: make_exec.m 3226 2009-06-25 18:13:11Z volkmar $ -mcc('-m','-v','-o',['spm_' computer],'exec_spm.m' ,'spm_load.m', '-I',spm('Dir'),'-R','-nojvm') -mcc('-m','-v','-o',['jobman_' computer],'exec_jobman.m','spm_load.m', '-I',spm('Dir'),'-R','-nojvm') +%======================================================================= +%-Files to include explicitly +%======================================================================= +% Matlab compiler will include all files that are referenced explicitly +% in the compiled code. If there is a file missing (e.g. data file, +% (f)eval'ed file), add it to the 'includefiles' list with its full +% path. If you want to add a directory and all of its contents, add it to +% the 'includedirs' list with its full path. +% By default, all files in spm('dir') are included and all subdirectories +% except matlabbatch, config and exec. Selected files from these +% directories will be included if they are referenced from any compiled +% function. Including spm('dir') instead would cause unwanted side +% effects during batch initialisation. +%-List of files +%----------------------------------------------------------------------- +[includefiles includedirs] = cfg_getfile('FPList',spm('dir'),'.*'); + +%-Clean up list of directories +%----------------------------------------------------------------------- +includedirs = includedirs(~(strcmp(includedirs,fullfile(spm('dir'),'config'))| ... + strcmp(includedirs,fullfile(spm('dir'),'exec'))| ... + strcmp(includedirs,fullfile(spm('dir'),'matlabbatch')))); + +includefiles = [includefiles; includedirs]; +%-Add '-a' switch for each file to include +%----------------------------------------------------------------------- +sw = cell(size(includefiles)); +[sw{:}] = deal('-a'); +tmp = [sw includefiles]'; +includefiles = tmp(:); + +%======================================================================= +%-Configuration management +%======================================================================= +% 1) locate all currently used batch configs +% 2) copy them to fullfile(spm('dir'),'exec') with unique names +% 3) create fullfile(spm('dir'),'exec','cfg_master.m') which calls the +% configs at runtime +% compiled spm_jobman will execute this file and add the applications + +%-Locate batch configs and copy them +%----------------------------------------------------------------------- +apps = which('cfg_mlbatch_appcfg','-all'); +cfgfiles = cell(1,2*numel(apps)+2); +for k = 1:numel(apps) + cfgfiles{2*k-1} = '-a'; + cfgfiles{2*k} = fullfile(spm('dir'),'exec',sprintf('cfg_mlbatch_appcfg_%d.m',k)); + copyfile(apps{k}, cfgfiles{2*k}); +end + +%-Create code for cfg_master.m +%----------------------------------------------------------------------- +cfgfiles{end-1} ='-a'; +cfgfiles{end} = fullfile(spm('dir'),'exec','cfg_master.m'); +fid = fopen(cfgfiles{end},'w'); +fprintf(fid,'function cfg_master\n'); +for k = 1:numel(apps) + fprintf(fid,'[cfg, def] = cfg_mlbatch_appcfg_%d;\n', k); + if strcmp(apps{k},... + fullfile(spm('dir'),'config','cfg_mlbatch_appcfg.m')) + % create code to insert toolbox config + %-Toolbox autodetection + %-Get the list of toolbox directories + tbxdir = fullfile(spm('Dir'),'toolbox'); + d = dir(tbxdir); d = {d([d.isdir]).name}; + dd = regexp(d,'^\.'); + %(Beware, regexp returns an array if input cell array is of dim 0 or 1) + if ~iscell(dd), dd = {dd}; end + d = {'' d{cellfun('isempty',dd)}}; + ft = {}; + ftc = {}; + %-Look for '*_cfg_*.m' or '*_config_*.m' files in these directories + for i=1:length(d) + d2 = fullfile(tbxdir,d{i}); + di = dir(d2); di = {di(~[di.isdir]).name}; + f2 = regexp(di,'.*_cfg_.*\.m$'); + if ~iscell(f2), f2 = {f2}; end + fi = {di{~cellfun('isempty',f2)}}; + if ~isempty(fi) + ft = {ft{:} fi{:}}; + else + % try *_config_*.m files, if toolbox does not have '*_cfg_*.m' files + f2 = regexp(di,'.*_config_.*\.m$'); + if ~iscell(f2), f2 = {f2}; end + fi = {di{~cellfun('isempty',f2)}}; + ftc = {ftc{:} fi{:}}; + end; + end + if ~isempty(ft)||~isempty(ftc) + if isempty(ft) + ftstr = ''; + else + ft = cellfun(@(cft)strtok(cft,'.'),ft,'UniformOutput',false); + ftstr = sprintf('%s ', ft{:}); + end + if isempty(ftc) + ftcstr = ''; + else + ftc = cellfun(@(cftc)strtok(cftc,'.'),ftc,'UniformOutput',false); + ftcstr = sprintf('cfg_struct2cfg(%s) ', ftc{:}); + end + % assume that tools are the last thing in SPM config + fprintf(fid,'cfg.values{end}.values = {%s %s};\n', ftstr, ftcstr); + end + end + fprintf(fid,'cfg_util(''addapp'', cfg, def);\n'); +end +fclose(fid); + +%======================================================================= +%-Compile +%======================================================================= +mcc('-m','-v','-o',['spm_' computer],'exec_spm.m' ,'spm_load.m', '-I',spm('Dir'),includefiles{:},cfgfiles{:}) +mcc('-m','-v','-o',['jobman_' computer],'exec_jobman.m','spm_load.m', '-I',spm('Dir'),includefiles{:},cfgfiles{:}) diff --git a/external/fieldtrip/private/apply_montage.m b/external/fieldtrip/private/apply_montage.m index e7a0f07..ccf0716 100644 --- a/external/fieldtrip/private/apply_montage.m +++ b/external/fieldtrip/private/apply_montage.m @@ -7,7 +7,11 @@ % forward computation and source reconstruction of the data. % % Use as -% [sens] = apply_montage(sens, montage, ...) +% [sens] = apply_montage(sens, montage, ...) +% [data] = apply_montage(data, montage, ...) +% [montage] = apply_montage(montage1, montage2, ...) +% where the input is a FieldTrip sensor definition as obtained from READ_SENS +% or a FieldTrip raw data structure as obtained from PREPROCESSING. % % A montage is specified as a structure with the fields % montage.tra = MxN matrix @@ -18,11 +22,35 @@ % 'keepunused' string, 'yes' or 'no' (default = 'no') % 'inverse' string, 'yes' or 'no' (default = 'no') % +% If the first input is a montage, then the second input montage will be +% applied to the first. In effect the resulting montage will first do +% montage1, then montage2. +% % See also READ_SENS, TRANSFORM_SENS % Copyright (C) 2008, Robert Oostenveld % % $Log: apply_montage.m,v $ +% Revision 1.19 2009/07/02 16:14:15 roboos +% allow to apply one montage on another one +% +% Revision 1.18 2009/07/02 09:18:41 vlalit +% Fixing a bug in building an inverse montage (confusion between 'montage' and 'tra') +% +% Revision 1.17 2009/07/01 09:21:37 roboos +% changed handling of rank-reduced montage for inversion, give warning +% removed the modification by vladimir for the sequential application of montages +% +% Revision 1.16 2009/06/27 13:24:54 vlalit +% Additional fixes to make custom balancing work. +% +% Revision 1.15 2009/06/26 17:39:17 vlalit +% Added the possiblity to handle custom montages applied to MEG sensors (for removing +% spatial confounds). Hopefully there won't be major side effects. +% +% Revision 1.14 2009/06/17 10:13:05 roboos +% improved documentation +% % Revision 1.13 2009/03/26 11:07:40 roboos % start with an explicit check on the channel number % @@ -70,6 +98,12 @@ keepunused = keyval('keepunused', varargin{:}); if isempty(keepunused), keepunused = 'no'; end inverse = keyval('inverse', varargin{:}); if isempty(inverse), inverse = 'no'; end +% check the consistency of the input sensor array or data +if isfield(sens, 'labelorg') && isfield(sens, 'labelnew') + % the input data structure is also a montage, i.e. apply the montages sequentially + sens.label = sens.labelnew; +end + % check the consistency of the montage if size(montage.tra,1)~=length(montage.labelnew) error('the number of channels in the montage is inconsistent'); @@ -79,9 +113,15 @@ if strcmp(inverse, 'yes') % apply the inverse montage, i.e. undo a previously applied montage - tmp.labelnew = montage.labelorg; - tmp.labelorg = montage.labelnew; - tmp.tra = inv(montage.tra); + tmp.labelnew = montage.labelorg; % swap around + tmp.labelorg = montage.labelnew; % swap around + tmp.tra = full(montage.tra); + if rank(tmp.tra) < length(tmp.tra) + warning('the linear projection for the montage is not full-rank, the resulting data will have reduced dimensionality'); + tmp.tra = pinv(tmp.tra); + else + tmp.tra = inv(tmp.tra); + end montage = tmp; end @@ -150,10 +190,17 @@ montage.tra = sparse(montage.tra(:,selmont)); montage.labelorg = montage.labelorg(selmont); -if isfield(sens, 'tra') +if isfield(sens, 'labelorg') && isfield(sens, 'labelnew') + % apply the montage on top of the other montage + sens = rmfield(sens, 'label'); + sens.tra = montage.tra * sens.tra; + sens.labelnew = montage.labelnew; + +elseif isfield(sens, 'tra') % apply the montage to the sensor array sens.tra = montage.tra * sens.tra; sens.label = montage.labelnew; + elseif isfield(sens, 'trial') % apply the montage to the data that was preprocessed using fieldtrip Ntrials = numel(sens.trial); @@ -177,5 +224,3 @@ function y = indx2logical(x, n) y = false(1,n); y(x) = true; - - diff --git a/external/fieldtrip/private/beamformer_dics.m b/external/fieldtrip/private/beamformer_dics.m index fe84b22..3cf9393 100644 --- a/external/fieldtrip/private/beamformer_dics.m +++ b/external/fieldtrip/private/beamformer_dics.m @@ -47,6 +47,9 @@ % Copyright (C) 2003-2008, Robert Oostenveld % % $Log: beamformer_dics.m,v $ +% Revision 1.15 2009/06/17 13:40:37 roboos +% small change in the order of the code for subspace projection +% % Revision 1.14 2009/05/14 19:25:12 roboos % added a FIXME comment % @@ -263,6 +266,13 @@ % compute the leadfield lf = compute_leadfield(dip.pos(i,:), grad, vol, 'reducerank', reducerank, 'normalize', normalize, 'normalizeparam', normalizeparam); end + if isfield(dip, 'subspace') + % do subspace projection of the forward model + lf = dip.subspace{i} * lf; + % the cross-spectral density becomes voxel dependent due to the projection + Cf = dip.subspace{i} * Cf_pre_subspace * dip.subspace{i}'; + invCf = pinv(dip.subspace{i} * (Cf_pre_subspace + lambda * eye(size(Cf))) * dip.subspace{i}'); + end if fixedori % compute the leadfield for the optimal dipole orientation % subsequently the leadfield for only that dipole orientation will be used for the final filter computation @@ -272,13 +282,6 @@ lf = lf * eta; dipout.ori{i} = eta; end - if isfield(dip, 'subspace') - % do subspace projection of the forward model - lf = dip.subspace{i} * lf; - % the cross-spectral density becomes voxel dependent due to the projection - Cf = dip.subspace{i} * Cf_pre_subspace * dip.subspace{i}'; - invCf = pinv(dip.subspace{i} * (Cf_pre_subspace + lambda * eye(size(Cf))) * dip.subspace{i}'); - end if isfield(dip, 'filter') % use the provided filter filt = dip.filter{i}; @@ -327,6 +330,13 @@ % compute the leadfield lf = compute_leadfield(dip.pos(i,:), grad, vol, 'reducerank', reducerank, 'normalize', normalize); end + if isfield(dip, 'subspace') + % do subspace projection of the forward model + lf = dip.subspace{i} * lf; + % the cross-spectral density becomes voxel dependent due to the projection + Cf = dip.subspace{i} * Cf_pre_subspace * dip.subspace{i}'; + invCf = pinv(dip.subspace{i} * (Cf_pre_subspace + lambda * eye(size(Cf))) * dip.subspace{i}'); + end if fixedori % compute the leadfield for the optimal dipole orientation % subsequently the leadfield for only that dipole orientation will be used for the final filter computation @@ -336,14 +346,6 @@ lf = lf * eta; dipout.ori{i} = eta; end - if isfield(dip, 'subspace') - % do subspace projection of the forward model - lf = dip.subspace{i} * lf; - % the cross-spectral density becomes voxel dependent due to the projection - Cr = dip.subspace{i} * Cr_pre_subspace; - Cf = dip.subspace{i} * Cf_pre_subspace * dip.subspace{i}'; - invCf = pinv(dip.subspace{i} * (Cf_pre_subspace + lambda * eye(size(Cf))) * dip.subspace{i}'); - end if isfield(dip, 'filter') % use the provided filter filt = dip.filter{i}; @@ -358,6 +360,7 @@ end csd = filt*Cr; % Gross eqn. 6 if powlambda1 + % FIXME this should use the dipole orientation with maximum power coh = lambda1(csd)^2 / (pow * Pr); % Gross eqn. 9 elseif powtrace coh = norm(csd)^2 / (pow * Pr); @@ -504,7 +507,7 @@ % standard Matlab function, except that the default tolerance is twice as % high. % Copyright 1984-2004 The MathWorks, Inc. -% $Revision: 1.14 $ $Date: 2009/05/14 19:25:12 $ +% $Revision: 1.15 $ $Date: 2009/06/17 13:40:37 $ % default tolerance increased by factor 2 (Robert Oostenveld, 7 Feb 2004) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function X = pinv(A,varargin) diff --git a/external/fieldtrip/private/channelposition.m b/external/fieldtrip/private/channelposition.m index 47fd1d6..b7742af 100644 --- a/external/fieldtrip/private/channelposition.m +++ b/external/fieldtrip/private/channelposition.m @@ -8,6 +8,27 @@ % Copyright (C) 2009, Robert Oostenveld & Vladimir Litvak % % $Log: channelposition.m,v $ +% Revision 1.9 2009/08/10 12:33:57 vlalit +% Adding thresholds to ignore small values in the tra also for Neuromag and planar +% +% Revision 1.8 2009/07/08 07:42:50 jansch +% undone previous adjustment. convention now is that sequential balancing steps +% should be recorded in the grad-structure itself; rather than having +% balance.XXX and balance.YYY, it should be balance.XXX_YYY and +% balance.current = 'XXX_YYY' +% +% Revision 1.7 2009/07/07 08:03:29 jansch +% allowing for sequential unbalancing, convention being that the individual +% balancing steps in balance.current are separated by '_', and the chronological +% ordering of the balancing steps are from right to left +% +% Revision 1.6 2009/06/26 17:39:04 vlalit +% Added the possiblity to handle custom montages applied to MEG sensors (for removing +% spatial confounds). Hopefully there won't be major side effects. +% +% Revision 1.5 2009/06/03 09:49:03 roboos +% change in whitespace +% % Revision 1.4 2009/04/03 08:14:27 vlalit % getting rid of the dependence on statistics toolbox I accidentally introduced by using % nanmin. @@ -23,24 +44,29 @@ % new function, will be used as helper function in prepare_layout % -switch senstype(sens) +if isfield(sens, 'balance') && ~strcmp(sens.balance.current, 'none') + fnames = setdiff(fieldnames(sens.balance), 'current'); + indx = find(ismember(fnames, sens.balance.current)); + + if length(indx)==1, + % undo the synthetic gradient balancing + fprintf('undoing the %s balancing\n', sens.balance.current); + sens = apply_montage(sens, getfield(sens.balance, sens.balance.current), 'inverse', 'yes'); + sens.balance.current = 'none'; + else + warning('cannot undo %s balancing\n', sens.balance.current); + end +end +switch senstype(sens) case {'ctf151', 'ctf275' 'bti148', 'bti248'} - if isfield(sens, 'balance') && ~strcmp(sens.balance.current, 'none') - - % undo the synthetic gradient balancing - fprintf('undoing the %s balancing\n', sens.balance.current); - sens = apply_montage(sens, getfield(sens.balance, sens.balance.current), 'inverse', 'yes'); - sens.balance.current = 'none'; - end - % remove the non-MEG channels altogether sel = chantype(sens, 'meg'); sens.label = sens.label(sel); sens.tra = sens.tra(sel,:); % subsequently remove the unused coils - used = any(abs(sens.tra)<10*eps, 1); % allow a little bit of rounding-off error + used = any(abs(sens.tra)<0.5, 1); % allow a little bit of rounding-off error sens.pnt = sens.pnt(used,:); sens.ori = sens.ori(used,:); sens.tra = sens.tra(:,used); @@ -49,7 +75,7 @@ dist = sqrt(sum((sens.pnt - repmat(mean(sens.pnt), size(sens.pnt, 1), 1)).^2, 2)); % put the corresponding distances instead of non-zero tra entries - dist = (abs(sens.tra)>10*eps).*repmat(dist', size(sens.tra, 1), 1); + dist = (abs(sens.tra)>0.5).*repmat(dist', size(sens.tra, 1), 1); % put nans instead of the zero entries dist(~dist) = inf; @@ -79,8 +105,8 @@ if length(sel)==2 ind = [ind; i]; lab(i,:) = {ch1, ch2}; - meanpnt1 = mean(sens.pnt(find(sens.tra(sel(1),:)),:), 1); - meanpnt2 = mean(sens.pnt(find(sens.tra(sel(2),:)),:), 1); + meanpnt1 = mean(sens.pnt(abs(sens.tra(sel(1),:))>0.5, :), 1); + meanpnt2 = mean(sens.pnt(abs(sens.tra(sel(2),:))>0.5, :), 1); pnt(i,:) = mean([meanpnt1; meanpnt2], 1); end end @@ -106,8 +132,8 @@ if (length(sel)==2) ind = [ind; i]; lab(i,:) = {ch1, ch2}; - meanpnt1 = mean(sens.pnt(find(sens.tra(sel(1),:)),:), 1); - meanpnt2 = mean(sens.pnt(find(sens.tra(sel(2),:)),:), 1); + meanpnt1 = mean(sens.pnt(abs(sens.tra(sel(1),:))>0.5,:), 1); + meanpnt2 = mean(sens.pnt(abs(sens.tra(sel(2),:))>0.5,:), 1); pnt(i,:) = mean([meanpnt1; meanpnt2], 1); end end @@ -135,9 +161,9 @@ if (length(sel)==3) ind = [ind; i]; lab(i,:) = {ch1, ch2, ch3}; - meanpnt1 = mean(sens.pnt(find(sens.tra(sel(1),:)),:), 1); - meanpnt2 = mean(sens.pnt(find(sens.tra(sel(2),:)),:), 1); - meanpnt3 = mean(sens.pnt(find(sens.tra(sel(3),:)),:), 1); + meanpnt1 = mean(sens.pnt(abs(sens.tra(sel(1),:))>0.5,:), 1); + meanpnt2 = mean(sens.pnt(abs(sens.tra(sel(2),:))>0.5,:), 1); + meanpnt3 = mean(sens.pnt(abs(sens.tra(sel(3),:))>0.5,:), 1); pnt(i,:) = mean([meanpnt1; meanpnt2; meanpnt3], 1); end end @@ -172,7 +198,7 @@ n = size(lab,2); % this is to fix the planar layouts, which cannot be plotted anyway if n>1 && size(lab, 1)>1 %this is to prevent confusion when lab happens to be a row array - pnt = repmat(pnt, n, 1); + pnt = repmat(pnt, n, 1); end -lab = lab(:); \ No newline at end of file +lab = lab(:); diff --git a/external/fieldtrip/private/channelselection.m b/external/fieldtrip/private/channelselection.m index 6407bde..390b5a2 100644 --- a/external/fieldtrip/private/channelselection.m +++ b/external/fieldtrip/private/channelselection.m @@ -14,8 +14,9 @@ % E.g. % 'all' is replaced by all channels in the datafile % 'gui' a graphical user interface will pop up to select the channels -% 'C*' is replaced by all channels that mactch the wildcard, e.g. C1, C2, C3, ... -% '*1' is replaced by all channels that mactch the wildcard, e.g. C1, P1, F1, ... +% 'C*' is replaced by all channels that match the wildcard, e.g. C1, C2, C3, ... +% '*1' is replaced by all channels that match the wildcard, e.g. C1, P1, F1, ... +% 'M*1' is replaced by all channels that match the wildcard, e.g. MEG0111, MEG0131, MEG0131, ... % 'MEG' is replaced by all MEG channels (works for CTF, 4D and Neuromag) % 'MEGREF' is replaced by all MEG reference channels (works for CTF and 4D) % 'EEG' is replaced by all recognized EEG channels (this is system dependent) @@ -48,6 +49,15 @@ % Copyright (C) 2003-2009, Robert Oostenveld % % $Log: channelselection.m,v $ +% Revision 1.39 2009/08/04 13:54:20 roboos +% allow datachannel as single string +% +% Revision 1.38 2009/07/08 07:27:37 roboos +% improved wildcard selection, now also in middle like "M*1" +% +% Revision 1.37 2009/06/19 16:51:30 vlalit +% Added biosemi64 system of Diane Whitmer, I don't know how generic it is. +% % Revision 1.36 2009/02/18 07:57:07 roboos % added support for selection based on wildcards (like 'C*' and '*1') % @@ -114,6 +124,11 @@ channel = {channel}; end +if ~iscell(datachannel) + % ensure that a single input argument like 'all' also works + datachannel = {datachannel}; +end + % ensure that both inputs are column vectors channel = channel(:); datachannel = datachannel(:); @@ -152,11 +167,20 @@ findreg = []; for i=1:length(channel) if length(channel{i})>1 && channel{i}(1)=='*' + % the wildcard is at the start labelreg = labelreg | ~cellfun(@isempty, regexp(datachannel, ['.*' channel{i}(2:end) '$'], 'once')); findreg = [findreg i]; elseif length(channel{i})>1 && channel{i}(end)=='*' + % the wildcard is at the end labelreg = labelreg | ~cellfun(@isempty, regexp(datachannel, ['^' channel{i}(1:end-1) '.*'], 'once')); findreg = [findreg i]; + elseif length(channel{i})>1 && any(channel{i}=='*') + % the wildcard is in the middle + sel = strfind(channel{i}, '*'); + str1 = channel{i}(1:(sel-1)); + str2 = channel{i}((sel+1):end); + labelreg = labelreg | ~cellfun(@isempty, regexp(datachannel, ['^' str1 '.*' str2 '$'], 'once')); + findreg = [findreg i]; end end labelreg = datachannel(labelreg); @@ -219,7 +243,7 @@ labelmeg = datachannel(strncmp('MEG', datachannel, length('MEG'))); labeleeg = datachannel(strncmp('EEG', datachannel, length('EEG'))); - case {'biosemi128', 'biosemi256', 'egi64', 'egi128', 'egi256', 'ext1020'} + case {'biosemi64', 'biosemi128', 'biosemi256', 'egi64', 'egi128', 'egi256', 'ext1020'} % use an external helper function to define the list with EEG channel names labeleeg = senslabel(senstype(datachannel)); diff --git a/external/fieldtrip/private/checkconfig.m b/external/fieldtrip/private/checkconfig.m index d7409cb..2b71823 100644 --- a/external/fieldtrip/private/checkconfig.m +++ b/external/fieldtrip/private/checkconfig.m @@ -44,6 +44,15 @@ % Copyright (C) 2007-2008, Robert Oostenveld, Saskia Haegens % % $Log: checkconfig.m,v $ +% Revision 1.18 2009/08/05 13:03:55 roboos +% changed selection of file based on 'gui' +% +% Revision 1.17 2009/07/15 12:10:07 jansch +% added subspace and keepsubspace for subcfg dics and lcmv +% +% Revision 1.16 2009/06/04 13:41:33 marvger +% renamed mvlap case +% % Revision 1.15 2009/05/22 13:20:18 marvger % added case for mvlap method % @@ -403,6 +412,8 @@ 'reducerank' 'keepcsd' 'realfilter' + 'subspace' + 'keepsubspace' }; case 'lcmv' @@ -420,6 +431,7 @@ 'reducerank' 'keepcov' 'subspace' + 'keepsubspace' }; case 'pcc' @@ -459,7 +471,7 @@ 'reducerank' }; - case 'mvlap' + case 'mvl' fieldname = {}; otherwise @@ -501,14 +513,11 @@ if ~isempty(cfg.dataset) if strcmp(cfg.dataset, 'gui'); - d = uigetdir; - if d==0 - [f, p] = uigetfile; - if f==0 - error('You should select a dataset file or directory'); - else - d = fullfile(p, f); - end + [f, p] = uigetfile('*.*', 'Select a file'); + if isequal(f, 0) + error('User pressed cancel'); + else + d = fullfile(p, f); end cfg.dataset = d; end diff --git a/external/fieldtrip/private/checkdata.m b/external/fieldtrip/private/checkdata.m index 5dbac44..ce5575c 100644 --- a/external/fieldtrip/private/checkdata.m +++ b/external/fieldtrip/private/checkdata.m @@ -30,9 +30,32 @@ % [data] = checkdata(data, 'senstype', {'ctf151', 'ctf275'}), e.g. in megrealign % [data] = checkdata(data, 'datatype', {'timelock', 'freq'}), e.g. in sourceanalysis -% Copyright (C) 2007-2008, Robert Oostenveld +% Copyright (C) 2007-2009, Robert Oostenveld % % $Log: checkdata.m,v $ +% Revision 1.18 2009/08/24 08:57:01 jansch +% some changes regarding dealing with dim in source data. made a temporary +% bypass for jan excluding sourcedata from fixdimord. this is done to be able +% to further develop this part of the code without creating an entirely parallel +% version of it. eventually the bypass will be removed +% +% Revision 1.17 2009/08/03 15:08:24 ingnie +% also send source and volume data to fixdimord +% +% Revision 1.16 2009/08/03 11:55:11 roboos +% added comp2raw conversion +% +% Revision 1.15 2009/07/15 12:11:19 jansch +% experimental bypass of sourcedescriptives to create source with single trial +% power per voxel: dimord = 'rpt_pos', undocumented +% +% Revision 1.14 2009/07/06 09:38:24 jansch +% added fixinside to source2volume +% +% Revision 1.13 2009/06/15 12:56:23 roboos +% added a fixcoh function (for sparse->full) +% added some explicit error handling to fixcsd function +% % Revision 1.12 2009/04/17 09:12:12 jansch % fixed bug in freq2raw and another typo in source2volume % @@ -243,6 +266,8 @@ isvolume = datatype(data, 'volume'); issource = datatype(data, 'source'); isdip = datatype(data, 'dip'); +ismvar = datatype(data, 'mvar'); +isfreqmvar = datatype(data, 'freqmvar'); % FIXME use the istrue function on ismeg and hasxxx options @@ -274,12 +299,24 @@ fprintf('the input is source data with %d positions\n', nsource); elseif isdip fprintf('the input is dipole data\n'); + elseif ismvar + fprintf('the input is mvar data\n'); + elseif isfreqmvar + fprintf('the input is freqmvar data\n'); end end % give feedback -if isfreq || istimelock || iscomp - % ensure consistency between the dimord string and the axes that describe the data dimensions - data = fixdimord(data); +%HACK for jan to bypass source and volume data to enter fixdimord +[st,result] = system('whoami'); +if isempty(strfind(result,'jan')) + if isfreq || istimelock || iscomp || issource || isvolume + % ensure consistency between the dimord string and the axes that describe the data dimensions + data = fixdimord(data); + end +else + if isfreq || istimelock || iscomp + data = fixdimord(data); + end end if istimelock @@ -325,6 +362,10 @@ okflag = okflag + issource; case 'dip' okflag = okflag + isdip; + case 'mvar' + okflag = okflag + ismvar; + case 'freqmvar' + okflag = okflag + isfreqmvar; end % switch dtype end % for dtype @@ -361,6 +402,11 @@ isfreq = 0; israw = 1; okflag = 1; + elseif isequal(dtype(iCell), {'raw'}) && iscomp + data = comp2raw(data); + iscomp = 0; + israw = 1; + okflag = 1; end end % for iCell end % if okflag @@ -472,7 +518,7 @@ if isfield(data, 'xgrid'), data = rmfield(data, 'xgrid'); end if isfield(data, 'ygrid'), data = rmfield(data, 'ygrid'); end if isfield(data, 'zgrid'), data = rmfield(data, 'zgrid'); end - + % the following section is to make a dimord-consistent representation of % volume and source data, taking trials, time and frequency into account if isequal(hasdimord, 'yes') && ~isfield(data, 'dimord') @@ -488,6 +534,19 @@ end if isfield(data, 'trial') && isstruct(data.trial) Nrpt = length(data.trial); + elseif isfield(data, 'avg') && isfield(data.avg, 'mom') && strcmp(sourcedimord, 'rpt_pos'), + Npos = size(data.pos,1); + Nrpt = length(data.cumtapcnt); + tmpmom = zeros(Npos, size(data.avg.mom{data.inside(1)},2)); + tmpmom(data.inside,:) = cat(1,data.avg.mom{data.inside}); + tmppow = zeros(Npos, Nrpt); + tapcnt = [0;cumsum(data.cumtapcnt)]; + for k = 1:Nrpt + Ntap = tapcnt(k+1)-tapcnt(k); + tmppow(data.inside,k) = sum(abs(tmpmom(data.inside,(tapcnt(k)+1):tapcnt(k+1))).^2,2)./Ntap; + end + data.pow = tmppow'; + data = rmfield(data, 'avg'); else Nrpt = 1; end @@ -538,12 +597,40 @@ data = rmfield(data, 'trial'); end end - + % ensure consistent dimensions of the source reconstructed data % reshape each of the source reconstructed parameters - dim = [data.dim 1]; - - param = parameterselection('all', data); + if isfield(data, 'dim'), + dim = [data.dim 1]; + else + %HACK + dimtok = tokenize(data.dimord, '_'); + for i=1:length(dimtok) + if strcmp(dimtok(i),'pos') + dim(1,i) = size(getsubfield(data,dimtok{i}),1); + elseif strcmp(dimtok(i),'rpt') + dim(1,i) = nan; + else + dim(1,i) = length(getsubfield(data,dimtok{i})); + end + end + i = find(isnan(dim)); + if ~isempty(i) + n = fieldnames(data); + for ii=1:length(n) + numels(1,ii) = numel(getfield(data,n{ii})); + end + nrpt = numels./prod(dim(setdiff(1:length(dim),i))); + nrpt = nrpt(nrpt==round(nrpt)); + dim(i) = max(nrpt); + end + if numel(dim)==1, dim(1,2) = 1; end; + end + + if issource, exclude = {'inside' 'fwhm' 'leadfield' 'q' 'rough'}; end + if isvolume, exclude = { 'fwhm' 'leadfield' 'q' 'rough'}; end + + param = setdiff(parameterselection('all', data), exclude); for i=1:length(param) if any(param{i}=='.') % the parameter is nested in a substructure, which can have multiple elements (e.g. source.trial(1).pow, source.trial(2).pow, ...) @@ -611,10 +698,14 @@ if ~isempty(cmbrepresentation) if istimelock data = fixcov(data, cmbrepresentation); - elseif isfreq + elseif isfreq && isfield(data, 'cohspctrm') + data = fixcoh(data, cmbrepresentation, channelcmb); + elseif isfreq && ~isfield(data, 'cohspctrm') + data = fixcsd(data, cmbrepresentation, channelcmb); + elseif isfreqmvar data = fixcsd(data, cmbrepresentation, channelcmb); else - error('This function requires data with a covariance or cross-spectrum'); + error('This function requires data with a covariance, coherence or cross-spectrum'); end end % cmbrepresentation @@ -626,9 +717,9 @@ % represent the covariance matrix in a particular manner %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function data = fixcov(data, desired) -if isfield(data, 'cov') && ~isfield(data, 'labelcmb') +if isfield(data, 'cov') && ~isfield(data, 'labelcmb') current = 'full'; -elseif isfield(data, 'cov') && isfield(data, 'labelcmb') +elseif isfield(data, 'cov') && isfield(data, 'labelcmb') current = 'sparse'; else error('Could not determine the current representation of the covariance matrix'); @@ -637,31 +728,87 @@ % nothing to do elseif strcmp(current, 'full') && strcmp(desired, 'sparse') % FIXME should be implemented + error('not yet implemented'); elseif strcmp(current, 'sparse') && strcmp(desired, 'full') % FIXME should be implemented + error('not yet implemented'); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% represent the covariance matrix in a particular manner +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function data = fixcoh(data, desired, channelcmb) +if isfield(data, 'cohspctrm') && ~isfield(data, 'labelcmb') + current = 'full'; +elseif isfield(data, 'cohspctrm') && isfield(data, 'labelcmb') + current = 'sparse'; +else + error('Could not determine the current representation of the coherence'); +end +if isequal(current, desired) + % nothing to do +elseif strcmp(current, 'full') && strcmp(desired, 'sparse') + % FIXME should be implemented + error('not yet implemented'); + +elseif strcmp(current, 'sparse') && strcmp(desired, 'full') + dimtok = tokenize(data.dimord, '_'); + if ~isempty(strmatch('freq', dimtok)), nfrq=length(data.freq); else nfrq = 1; end + if ~isempty(strmatch('time', dimtok)), ntim=length(data.time); else ntim = 1; end + nchan = length(data.label); + cmbindx = zeros(nchan); + for k = 1:size(data.labelcmb,1) + ch1 = find(strcmp(data.label, data.labelcmb(k,1))); + ch2 = find(strcmp(data.label, data.labelcmb(k,2))); + if ~isempty(ch1) && ~isempty(ch2), cmbindx(ch1,ch2) = k; end + end + cohspctrm = nan(nchan,nchan,nfrq,ntim); + for k = 1:ntim + for m = 1:nfrq + tmpdat = nan+zeros(nchan); + tmpdat(find(cmbindx)) = data.cohspctrm(cmbindx(find(cmbindx)),m,k); + tmpdat = ctranspose(tmpdat); + tmpdat(find(cmbindx)) = data.cohspctrm(cmbindx(find(cmbindx)),m,k); + cohspctrm(:,:,m,k) = tmpdat; + end + end + % remove obsolete fields + data = rmfield(data, 'powspctrm'); + data = rmfield(data, 'labelcmb'); + % replace updated fields + data.cohspctrm = cohspctrm; + try, data = rmfield(data, 'dof'); end + if ntim>1, + data.dimord = 'chan_chan_freq_time'; + else + data.dimord = 'chan_chan_freq'; + end + end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % represent the cross-spectral-density matrix in a particular manner %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function data = fixcsd(data, desired, channelcmb) -if isfield(data, 'powspctrm') +if isfield(data, 'crsspctrm') && isfield(data, 'powspctrm') current = 'sparsewithpow'; -elseif isfield(data, 'crsspctrm') && ~isfield(data, 'labelcmb') +elseif isfield(data, 'crsspctrm') && ~isfield(data, 'labelcmb') current = 'full'; -elseif isfield(data, 'crsspctrm') && isfield(data, 'labelcmb') +elseif isfield(data, 'crsspctrm') && isfield(data, 'labelcmb') current = 'sparse'; elseif isfield(data, 'fourierspctrm') && ~isfield(data, 'labelcmb') current = 'fourier'; else error('Could not determine the current representation of the cross-spectrum matrix'); end + if isequal(current, desired) % nothing to do + elseif (strcmp(current, 'full') && strcmp(desired, 'sparsewithpow')) + error('not yet implemented'); elseif (strcmp(current, 'fourier') && strcmp(desired, 'sparsewithpow')) - % this is what freqdescriptives currently does as an intermediate step dimtok = tokenize(data.dimord, '_'); if ~isempty(strmatch('rpttap', dimtok)), @@ -735,19 +882,21 @@ end if flag, siz = size(data.crsspctrm); data.crsspctrm = reshape(data.crsspctrm, siz(2:end)); end - elseif (strcmp(current, 'sparse') && strcmp(desired, 'sparsewithpow')) % convert back to crsspctrm/powspctrm representation: useful for plotting functions etc + error('not yet implemented'); - -elseif (strcmp(current, 'full') && strcmp(desired, 'fourier')) || ... +elseif (strcmp(current, 'full') && strcmp(desired, 'fourier')) || ... (strcmp(current, 'sparse') && strcmp(desired, 'fourier')) || ... (strcmp(current, 'sparsewithpow') && strcmp(desired, 'fourier')) % this is not possible + error('converting the cross-spectrum into a Fourier representation is not possible'); elseif strcmp(current, 'full') && strcmp(desired, 'sparse') % why would you want this? FIXME give explicit error -elseif strcmp(current, 'fourier') && strcmp(desired, 'sparse') + error('not yet implemented'); + +elseif strcmp(current, 'fourier') && strcmp(desired, 'sparse') if isempty(channelcmb), error('no channel combinations are specified'); end % this is what freqdescriptives currently does as an intermediate step @@ -776,7 +925,7 @@ crsspctrm = zeros(nrpt,ncmb,nfrq,ntim)+i.*zeros(nrpt,ncmb,nfrq,ntim); sumtapcnt = [0;cumsum(data.cumtapcnt(:))]; %for k = 1:ntim - % for m = 1:nfrq + %for m = 1:nfrq %for p = 1:nrpt % indx = (sumtapcnt(p)+1):sumtapcnt(p+1); % tmpdat1 = data.fourierspctrm(indx,cmbindx(:,1),m,k); @@ -789,7 +938,7 @@ tmpdat2 = data.fourierspctrm(indx,cmbindx(:,2),:,:); crsspctrm(p,:,:,:) = (sum(tmpdat1.*conj(tmpdat2),1))./data.cumtapcnt(p); end - % end + %end %end data.crsspctrm = crsspctrm; data.labelcmb = labelcmb; @@ -927,6 +1076,14 @@ end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% convert between datatypes +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function data = comp2raw(data) +% just remove the component topographies +data = rmfield(data, 'topo'); +data = rmfield(data, 'topolabel'); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % convert between datatypes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -949,12 +1106,16 @@ if isfield(data, 'dimord') % it is a modern source description - - %this part depends on the assumption that the list of positions is describing a full 3D volume in + + %this part depends on the assumption that the list of positions is describing a full 3D volume in %an ordered way which allows for the extraction of a transformation matrix %i.e. slice by slice - try, - data.dim = pos2dim3d(data.pos, data.dim); + try, + if isfield(data, 'dim'), + data.dim = pos2dim3d(data.pos, data.dim); + else + data.dim = pos2dim3d(data); + end catch end end @@ -967,7 +1128,7 @@ [x y z] = ndgrid(xgrid, ygrid, zgrid); ind = [x(:) y(:) z(:)]; % these are the positions expressed in voxel indices along each of the three axes pos = data.pos; % these are the positions expressed in head coordinates - % represent the positions in a manner that is compatible with the homogeneous matrix multiplication, + % represent the positions in a manner that is compatible with the homogeneous matrix multiplication, % i.e. pos = H * ind ind = ind'; ind(4,:) = 1; pos = pos'; pos(4,:) = 1; @@ -981,6 +1142,9 @@ if isfield(data, 'ygrid'), data = rmfield(data, 'ygrid'); end if isfield(data, 'zgrid'), data = rmfield(data, 'zgrid'); end +% make inside a volume +data = fixinside(data, 'logical'); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % convert between datatypes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/external/fieldtrip/private/combineplanar.m b/external/fieldtrip/private/combineplanar.m index 60b6102..5d18979 100644 --- a/external/fieldtrip/private/combineplanar.m +++ b/external/fieldtrip/private/combineplanar.m @@ -28,6 +28,13 @@ % Copyright (C) 2004, Ole Jensen, Robert Oostenveld % % $Log: combineplanar.m,v $ +% Revision 1.43 2009/07/23 08:11:29 crimic +% fixed tiny bug +% +% Revision 1.42 2009/07/17 08:17:24 jansch +% rewriting of big parts of the code; incorporating checkdata etc. implementation +% of 'svd' combinemethod also for time domain data +% % Revision 1.41 2009/01/20 13:01:31 sashae % changed configtracking such that it is only enabled when BOTH explicitly allowed at start % of the fieldtrip function AND requested by the user @@ -171,9 +178,13 @@ cfg = checkconfig(cfg, 'trackconfig', 'on'); % check if the input data is valid for this function -% TODO this is not yet consistent with the code that is approx. 15 lines below data = checkdata(data, 'datatype', {'raw', 'freq', 'timelock'}, 'feedback', 'yes', 'senstype', {'ctf151_planar', 'ctf275_planar', 'neuromag122', 'neuromag306', 'bti248_planar', 'bti148_planar'}); +israw = datatype(data, 'raw'); +isfreq = datatype(data, 'freq'); +istimelock = datatype(data, 'timelock'); +try, dimord = data.dimord; end + % set the defaults if ~isfield(cfg, 'blc'), cfg.blc = 'no'; end if ~isfield(cfg, 'blcwindow'), cfg.blcwindow = 'all'; end @@ -181,6 +192,7 @@ if ~isfield(cfg, 'combinemethod'), cfg.combinemethod = 'sum'; end if ~isfield(cfg, 'foilim'), cfg.foilim = []; end if ~isfield(cfg, 'trials'), cfg.trials = 'all'; end +if ~isfield(cfg, 'feedback'), cfg.feedback = 'none'; end if isfield(cfg, 'baseline') warning('only supporting cfg.baseline for backwards compatibility, please update your cfg'); cfg.blc = 'yes'; @@ -190,12 +202,6 @@ cfg.blcwindow = [min(data.time) max(data.time)]; end -% check the input data -% TODO make consistent with checkdata -if (~isfield(data, 'avg') && ~isfield(data, 'powspctrm')) && ~isfield(data, 'fourierspctrm') && ~isfield(data, 'trial'), - error('unsupported data format') -end - % select trials of interest if ~strcmp(cfg.trials, 'all') error('trial selection has not been implemented yet') % first fix checkdata (see above) @@ -223,7 +229,7 @@ % perform baseline correction if strcmp(cfg.blc, 'yes') - if ~isfield(data, 'avg') + if ~istimelock, error('baseline correction is only supported for ERFs') else if ischar(cfg.blcwindow) && strcmp(cfg.blcwindow, 'all') @@ -238,76 +244,112 @@ end end -if strcmp(cfg.combinemethod, 'sum'), - if isfield(data, 'powspctrm') - % compute the power of each planar channel, simply by adding the power - if strcmp(data.dimord, 'chan_freq') - tmp1 = data.powspctrm(sel_dH,:) + data.powspctrm(sel_dV,:); - tmp2 = data.powspctrm(sel_other,:); - data.powspctrm = [tmp1; tmp2]; - elseif strcmp(data.dimord, 'chan_freq_time') - tmp1 = data.powspctrm(sel_dH,:,:) + data.powspctrm(sel_dV,:,:); - tmp2 = data.powspctrm(sel_other,:,:); - data.powspctrm = cat(1, tmp1, tmp2); - elseif strcmp(data.dimord, 'rpt_chan_freq') - tmp1 = data.powspctrm(:,sel_dH,:) + data.powspctrm(:,sel_dV,:); - tmp2 = data.powspctrm(:,sel_other,:); - data.powspctrm = cat(2, tmp1, tmp2); - elseif strcmp(data.dimord, 'rpt_chan_freq_time') - tmp1 = data.powspctrm(:,sel_dH,:,:) + data.powspctrm(:,sel_dV,:,:); - tmp2 = data.powspctrm(:,sel_other,:,:); - data.powspctrm = cat(2, tmp1, tmp2); - else - error('unsupported dimension order of frequency data'); - end - else - % convert average to raw data using simple helper function - % this allows the same function to work on multiple data types - % TODO make consistent with checkdata - [data, inputdimord] = data2raw(data); - ntrial = length(data.trial); - for i=1:ntrial - % compute the magnitude of each planar channel, simply by pythagoras - tmp1 = sqrt(data.trial{i}(sel_dH,:).^2 + data.trial{i}(sel_dV,:).^2); - tmp2 = data.trial{i}(sel_other,:); - data.trial{i} = [tmp1; tmp2]; - end - % convert raw data back to average using simple helper function - % this allows the same function to work on multiple data types - % TODO make consistent with checkdata - [data] = raw2data(data, inputdimord); - end -elseif strcmp(cfg.combinemethod, 'svd'), - if isempty(cfg.foilim), cfg.foilim = [data.freq(1) data.freq(end)]; end; - fbin = nearest(data.freq, cfg.foilim(1)):nearest(data.freq, cfg.foilim(2)); - if isfield(data, 'fourierspctrm'), - Nrpt = size(data.fourierspctrm,1); - Nsgn = length(sel_dH); - Nfrq = length(fbin); - Ntim = size(data.fourierspctrm,4); - %fourier= complex(zeros(Nrpt,Nsgn,Nfrq,Ntim),zeros(Nrpt,Nsgn,Nfrq,Ntim)); - fourier= zeros(Nrpt,Nsgn,Nfrq,Ntim)+nan; - progress('init', 'textbar', 'computing the svd'); - for j = 1:Nsgn - progress(j/Nsgn, 'computing the svd of signal %d/%d\n', j, Nsgn); - for k = 1:Nfrq - for m = 1:Ntim - dum = data.fourierspctrm(:,[sel_dH(j) sel_dV(j)],fbin(k),m); - timbin = find(~isnan(dum(:,1))); - [fourier(timbin,j,k,m)] = svdfft(transpose(dum(timbin,:)),1); - end - end - end - progress('close'); - other = data.fourierspctrm(:,sel_other,fbin,:); - data = rmfield(data,'fourierspctrm'); - data.fourierspctrm = cat(2, fourier, other); - data.freq = data.freq(fbin); - else - error('this method is not yet implemented'); +if isfreq + switch cfg.combinemethod + case 'sum' + if isfield(data, 'powspctrm'), + % compute the power of each planar channel, by summing the horizontal and vertical gradients + dimtok = tokenize(dimord,'_'); + catdim = strmatch('chan',dimtok); + if catdim==1, + tmp1 = data.powspctrm(sel_dH,:,:,:) + data.powspctrm(sel_dV,:,:,:); + tmp2 = data.powspctrm(sel_other,:,:,:); + elseif catdim==2, + tmp1 = data.powspctrm(:,sel_dH,:,:,:) + data.powspctrm(:,sel_dV,:,:,:); + tmp2 = data.powspctrm(:,sel_other,:,:,:); + else + error('unsupported dimension order of frequency data'); + end + data.powspctrm = cat(catdim, tmp1, tmp2); + else + error('cfg.combinemethod = ''sum'' only works for frequency data with powspctrm'); + end + case 'svd' + if isfield(data, 'fourierspctrm'), + if isempty(cfg.foilim), cfg.foilim = [data.freq(1) data.freq(end)]; end; + fbin = nearest(data.freq, cfg.foilim(1)):nearest(data.freq, cfg.foilim(2)); + + Nrpt = size(data.fourierspctrm,1); + Nsgn = length(sel_dH); + Nfrq = length(fbin); + Ntim = size(data.fourierspctrm,4); + %fourier= complex(zeros(Nrpt,Nsgn,Nfrq,Ntim),zeros(Nrpt,Nsgn,Nfrq,Ntim)); + fourier= zeros(Nrpt,Nsgn,Nfrq,Ntim)+nan; + progress('init', cfg.feedback, 'computing the svd'); + for j = 1:Nsgn + progress(j/Nsgn, 'computing the svd of signal %d/%d\n', j, Nsgn); + for k = 1:Nfrq + dum = reshape(data.fourierspctrm(:,[sel_dH(j) sel_dV(j)],fbin(k),:), [Nrpt 2 Ntim]); + dum = permute(dum, [2 3 1]); + dum = reshape(dum, [2 Ntim*Nrpt]); + timbin = ~isnan(dum(1,:)); + dum2 = svdfft(dum(:,timbin),1); + dum(1,timbin) = dum2; + dum = reshape(dum(1,:),[Ntim Nrpt]); + fourier(:,j,k,:) = transpose(dum); + + %for m = 1:Ntim + % dum = data.fourierspctrm(:,[sel_dH(j) sel_dV(j)],fbin(k),m); + % timbin = find(~isnan(dum(:,1))); + % [fourier(timbin,j,k,m)] = svdfft(transpose(dum(timbin,:)),1); + %end + end + end + progress('close'); + other = data.fourierspctrm(:,sel_other,fbin,:); + data = rmfield(data,'fourierspctrm'); + data.fourierspctrm = cat(2, fourier, other); + data.freq = data.freq(fbin); + else + error('cfg.combinemethod = ''svd'' only works for frequency data with fourierspctrm'); + end + otherwise end else - error('unknown combinemethod'); + if istimelock, + data = checkdata(data, 'datatype', 'raw', 'feedback', 'yes'); + end + + switch cfg.combinemethod + case 'sum' + Nrpt = length(data.trial); + for k = 1:Nrpt + tmp1 = sqrt(data.trial{k}(sel_dH,:).^2 + data.trial{k}(sel_dV,:).^2); + tmp2 = data.trial{k}(sel_other,:); + data.trial{k} = [tmp1;tmp2]; + end + case 'svd' + Nrpt = length(data.trial); + Nsgn = length(sel_dH); + Nsmp = cellfun('size', data.trial, 2); + Csmp = cumsum([0 Nsmp]); + %do a 'fixed orientation' across all trials approach here + %this is different from the frequency case FIXME + tmpdat = zeros(2, sum(Nsmp)); + for k = 1:Nsgn + for m = 1:Nrpt + tmpdat(:, (Csmp(m)+1):Csmp(m+1)) = data.trial{m}([sel_dH(k) sel_dV(k)],:); + end + tmpdat2 = abs(svdfft(tmpdat,1)); + tmpdat2 = mat2cell(tmpdat2, 1, Nsmp); + for m = 1:Nrpt + if k==1, trial{m} = zeros(Nsgn, Nsmp(m)); end + trial{m}(k,:) = tmpdat2{m}; + end + end + + for m = 1:Nrpt + other = data.trial{m}(sel_other,:); + trial{m} = [trial{m}; other]; + end + data.trial = trial; + + otherwise + end + + if istimelock, + data = checkdata(data, 'datatype', 'timelock', 'feedback', 'yes'); + end end if strcmp(cfg.combinegrad, 'no') && ~isfield(data, 'grad') @@ -372,7 +414,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: combineplanar.m,v 1.41 2009/01/20 13:01:31 sashae Exp $'; +cfg.version.id = '$Id: combineplanar.m,v 1.43 2009/07/23 08:11:29 crimic Exp $'; % remember the configuration details of the input data try, cfg.previous = data.cfg; end % remember the exact configuration details in the output diff --git a/external/fieldtrip/private/componentbrowser.m b/external/fieldtrip/private/componentbrowser.m new file mode 100644 index 0000000..e2f4121 --- /dev/null +++ b/external/fieldtrip/private/componentbrowser.m @@ -0,0 +1,254 @@ +function [varargout] = componentbrowser(cfg, comp) + +% COMPONENTBROWSER plots topography and activations of ICA components +% +% Use as +% componentbrowser(cfg, comp) +% where comp is a FieldTrip structure obtained from COMPONENTANALYSIS. +% +% The configuration has the following parameters: +% cfg.comp = a vector with the components to plot (ex. 1:10) (optional) +% cfg.trial = choose which trial to plot first (optional, only one trial) +% cfg.layout = because the output of componentanalysis does not contain +% information on the layout, you need to specify in a variety of ways: +% - you can provide a pre-computed layout structure (see prepare_layout) +% - you can give the name of an ascii layout file with extension *.lay +% - you can give the name of an electrode file +% - you can give an electrode definition, i.e. "elec" structure +% - you can give a gradiometer definition, i.e. "grad" structure +% +% See also COMPONENTANALYSIS + +% Copyright (C) 2009, Giovanni Piantoni +% +% $Log: componentbrowser.m,v $ +% Revision 1.3 2009/08/05 08:58:54 roboos +% changed the order of the input arguments to plot_topo from (val, x, y) into (x, y, val) +% +% Revision 1.2 2009/07/29 15:05:41 giopia +% removed prepare_mask, added << >> buttons, updated help +% +% Revision 1.1 2009/07/17 14:25:39 giopia +% moved to main directory +% +% Revision 1.4 2009/07/15 08:38:28 giopia +% general cleanup, prepare_mask now standalone function +% +% Revision 1.3 2009/06/19 15:11:00 giopia +% allows scroll through components +% +% Revision 1.2 2009/06/03 14:00:26 roboos +% fixed cfg.lay, should be cfg.layout +% +% Revision 1.1 2009/06/02 15:48:58 giopia +% first implementation, plot topoplot, activations and simple interactive +% + +fieldtripdefs + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Prepare the data +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% check that the data comes from componentanalysis +comp = checkdata(comp, 'datatype', 'comp'); + +% set the defaults: +if ~isfield(cfg, 'comp'), cfg.comp = 1:10; end +if ~isfield(cfg, 'trial'), cfg.trial = 1; end + +if numel(cfg.trial) > 1, + warning('componentbrowser:cfg_onetrial', 'only one trial can be plotted at the time'); + cfg.trial = cfg.trial(1); +end + +% Read or create the layout that will be used for plotting: +[cfg.layout] = prepare_layout(cfg, comp); + +% Identify the channels to plot +[labels, cfg.chanidx.lay, cfg.chanidx.comp] = intersect(cfg.layout.label, comp.topolabel); % in case channels are missing +if isempty(cfg.chanidx.lay) + error('componentbrowser:labelmismatch', 'The channel labels in the data do not match the labels of the layout'); +end + +% fixed variables +cfg.shift = 1.2; % distance between topoplots + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Create figure and assign userdata +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% create figure and axes +cfg.h = figure('uni','pix', 'name', 'componentbrowser', 'vis', 'off', 'numbertitle', 'off'); +cfg.axis = axes; +hold on + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Buttons and Callbacks +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% scroll components +uicontrol(cfg.h,'uni','pix','pos',[105 5 25 18],'str','-',... + 'call',{@plottopography, comp}); + +cfg.ncomp = uicontrol(cfg.h,'sty','text','uni','pix','pos',[130 5 150 18],... + 'str',['comp n.' num2str(cfg.comp(1)) '-' num2str(cfg.comp(end))]); + +uicontrol(cfg.h,'uni','pix','pos',[280 5 25 18],'str','+',... + 'call',{@plottopography, comp}); + +% scroll trials +uicontrol(cfg.h,'uni','pix','pos',[330 5 25 18],'str','<<',... + 'call',{@plotactivation, comp}); + +uicontrol(cfg.h,'uni','pix','pos',[355 5 25 18],'str', '<',... + 'call',{@plotactivation, comp}); + +cfg.ntrl = uicontrol(cfg.h,'sty','text','uni','pix','pos',[380 5 70 18],... + 'str',['trial n.' num2str(cfg.trial)]); + +uicontrol(cfg.h,'uni','pix','pos',[450 5 25 18],'str', '>',... + 'call',{@plotactivation, comp}); + +uicontrol(cfg.h,'uni','pix','pos',[475 5 25 18],'str','>>',... + 'call',{@plotactivation, comp}); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% First callback and final adjustments +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% first call of the two plotting functions +plottopography([], cfg, comp) +plotactivation([], cfg, comp) + +% final adjustments +set(cfg.h, 'vis', 'on') +axis equal +axis off +hold off + +% the (optional) output is the handle +if nargout == 1; + varargout{1} = cfg.h; +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% PLOTTOPOGRAPHY +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function plottopography(h, cfg, comp) +% now plottopography is not associated with a callback, but it might in +% the future + +if isempty(h) % when called in isolation + set(cfg.h, 'user', cfg) +else + cfg = get(get(h, 'par'), 'user'); + + % which button has been pressed + if intersect(h, findobj(cfg.h, 'str', '+')) + + cfg.comp = cfg.comp + numel(cfg.comp); + if cfg.comp(end) > size(comp.label,1) + cfg.comp = cfg.comp - (cfg.comp(end) - size(comp.label,1)); + end + + elseif intersect(h, findobj(cfg.h, 'str', '-')) + + cfg.comp = cfg.comp - numel(cfg.comp); + if cfg.comp(1) < 1 + cfg.comp = cfg.comp - cfg.comp(1) + 1; + end + + end +end + +set(cfg.ncomp, 'str', ['comp n.' num2str(cfg.comp(1)) '-' num2str(cfg.comp(end))]) +drawnow +delete(findobj(cfg.h, 'tag', 'comptopo')) + +cnt = 0; +for k = cfg.comp + cnt = cnt + 1; + + % write number of the component on the left + h_text(cnt) = plot_text(-2.5, -cnt*cfg.shift, ['n. ' num2str(cfg.comp(cnt))]); + + % plot only topography (no layout) + h_topo(cnt) = plot_topo(cfg.layout.pos(cfg.chanidx.lay,1), cfg.layout.pos(cfg.chanidx.lay,2), comp.topo(cfg.chanidx.comp, k), ... + 'hpos', -1, 'vpos', -cnt*cfg.shift, 'mask', cfg.layout.mask); + % plot layout + plot_lay(cfg.layout, 'hpos', -1, 'vpos', -cnt*cfg.shift, 'point', false, 'box', false, 'label', false, 'mask', true, 'verbose', false); +end + +set(h_text, 'tag', 'comptopo') +set(h_topo, 'tag', 'comptopo') + +% in the colorbar, green should be zero +colorlimits = get(cfg.axis, 'clim'); +set(cfg.axis, 'clim', [-1 1] * max(abs(colorlimits))) + +plotactivation([], cfg, comp) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% PLOTACTIVATION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function plotactivation(h, cfg, comp) +% plotactivation can be called in isolation or by buttondownfcn +% cfg is stored in 'user' of the main figure + +if isempty(h) % when called in isolation + set(cfg.h, 'user', cfg) +else + cfg = get(get(h, 'par'), 'user'); + + % which button has been pressed + if intersect(h, findobj(cfg.h, 'str', '>>')) + + cfg.trial = cfg.trial + 10; + if cfg.trial > size(comp.trial,2) + cfg.trial = size(comp.trial,2); + end + + elseif intersect(h, findobj(cfg.h, 'str', '>')) + + cfg.trial = cfg.trial + 1; + if cfg.trial > size(comp.trial,2) + cfg.trial = size(comp.trial,2); + end + + elseif intersect(h, findobj(cfg.h, 'str', '<')) + + cfg.trial = cfg.trial - 1; + if cfg.trial < 1 + cfg.trial = 1; + end + + elseif intersect(h, findobj(cfg.h, 'str', '<<')) + + cfg.trial = cfg.trial - 10; + if cfg.trial < 1 + cfg.trial = 1; + end + + end +end + +set(cfg.ntrl,'str',['trial n. ' num2str(cfg.trial)]) +drawnow +delete(findobj(cfg.h,'tag', 'activations')); + +hold on +cnt = 0; +for k = cfg.comp + cnt = cnt + 1; + + % plot the activations + h_act(cnt) = plot_vector(comp.trial{cfg.trial}(k,:), 'hpos', 6 , 'vpos', -cnt*cfg.shift, 'width', 12, 'height', 1, 'box', true); +end + +h_inv = plot(6+12+1, -cnt*cfg.shift, '.'); % +set(h_inv, 'vis', 'off') + +set(h_act, 'tag', 'activations') +set(cfg.h, 'user', cfg) +hold off diff --git a/external/fieldtrip/private/ctf2grad.m b/external/fieldtrip/private/ctf2grad.m index 1910b30..7a8138d 100644 --- a/external/fieldtrip/private/ctf2grad.m +++ b/external/fieldtrip/private/ctf2grad.m @@ -14,6 +14,9 @@ % Copyright (C) 2004, Robert Oostenveld % % $Log: ctf2grad.m,v $ +% Revision 1.4 2009/08/05 08:36:57 roboos +% preallocate the memory that will hold the coil positions, orientations and weights -> significant speedup +% % Revision 1.3 2009/03/23 21:16:03 roboos % don't give error if balancing fails, but only warning and remove the balancing information % @@ -100,6 +103,16 @@ selREF = selREF(:)'; numMEG = length(selMEG); numREF = length(selREF); + + % determine the number of channels and coils + coilcount = 0; + coilcount = coilcount + sum([hdr.res4.senres(selREF).numCoils]); + coilcount = coilcount + sum([hdr.res4.senres(selMEG).numCoils]); + chancount = numMEG + numREF; + % preallocate the memory + grad.pnt = zeros(coilcount, 3); % this will hold the position of each coil + grad.ori = zeros(coilcount, 3); % this will hold the orientation of each coil + grad.tra = zeros(chancount, coilcount); % this describes how each coil contributes to each channel % combine the bottom and top coil of each MEG channel for i=1:numMEG @@ -123,6 +136,10 @@ grad.tra(i,i ) = 1; grad.tra(i,i+numMEG) = 1; end + + % the MEG channels always have 2 coils, the reference channels vary in the number of coils + chancount = 1*numMEG; + coilcount = 2*numMEG; % combine the coils of each reference channel for i=1:numREF @@ -136,12 +153,14 @@ ori = hdr.res4.senres(n).ori'; end % determine the number of coils for this channel - numcoils = sum(sum(pos.^2, 2)~=0); + numcoils = hdr.res4.senres(n).numCoils; % add the coils of this channel to the gradiometer array + chancount = chancount+1; for j=1:numcoils - grad.pnt(end+1, :) = pos(j,:); - grad.ori(end+1, :) = ori(j,:) .* -sign(hdr.res4.senres(n).properGain); - grad.tra(i+numMEG, end+1) = 1; + coilcount = coilcount+1; + grad.pnt(coilcount, :) = pos(j,:); + grad.ori(coilcount, :) = ori(j,:) .* -sign(hdr.res4.senres(n).properGain); + grad.tra(chancount, coilcount) = 1; end end @@ -260,9 +279,9 @@ numcoils = sum(sum(pos.^2, 2)~=0); % add the coils of this channel to the gradiometer array for j=1:numcoils - grad.pnt(end+1, :) = pos(j,:); - grad.ori(end+1, :) = ori(j,:) .* -sign(hdr.gainV(n)); - grad.tra(i+numMEG, end+1) = 1; + grad.pnt(numMEG+i, :) = pos(j,:); + grad.ori(numMEG+i, :) = ori(j,:) .* -sign(hdr.gainV(n)); + grad.tra(numMEG+i, 2*numMEG+i) = 1; end end diff --git a/external/fieldtrip/private/databrowser.m b/external/fieldtrip/private/databrowser.m new file mode 100644 index 0000000..c33166c --- /dev/null +++ b/external/fieldtrip/private/databrowser.m @@ -0,0 +1,890 @@ +function [cfg] = databrowser(cfg, data) + +% DATABROWSER can be used for visual inspection of data. Artifacts detected +% by FieldTrip artifact functions are marked. Data pieces can be marked as +% artifact by manual selection as well. +% +% Use as +% cfg = databrowser(cfg) +% with the cfg as obtained from DEFINETRIAL, or as +% cfg = databrowser(cfg, data) +% with the data as obtained from PREPROCESSING +% +% The following configuration options are supported: +% cfg.dataset = +% cfg.trl = +% cfg.continuous = 'yes' or 'no' +% cfg.channel = cell-array with channel labels, see CHANNELSELECTION +% cfg.zscale = [zmin zmax] or 'auto' (default = 'auto') +% cfg.blocksize = number (in seconds), only aplicable if data contains only 1 (long) trial +% cfg.artfctdef.xxx.artifact = Nx2 matrix with artifact segments +% cfg.viewmode = string, 'butterfly', 'vertical', 'component' (default = 'butterfly') +% cfg.selectfeature = string, name of feature to be selected/added (default = 'visual') +% cfg.selectmode = string, what to do with a selection, can be 'joint', 'individual', 'mutiplot', 'topoplot-avg', 'topoplot-pow' (default = 'joint' +% +% See also PREPROCESSING + +% Copyright (C) 2009, Robert Oostenveld, Ingrid Niewenhuis +% +% $Log: databrowser.m,v $ +% Revision 1.12 2009/08/05 14:16:53 roboos +% added various options for selectmode +% +% Revision 1.11 2009/08/05 09:14:58 roboos +% fixed problem with zscale=auto when all data was negative +% +% Revision 1.10 2009/08/05 08:58:54 roboos +% changed the order of the input arguments to plot_topo from (val, x, y) into (x, y, val) +% +% Revision 1.9 2009/08/05 08:26:41 roboos +% if not continuous, keep the original data segments and don't display the horizontal zoom +% if continuous, use the label 'segment', other 'trial' +% give more information in figure title +% implemented preprocessing of data, use cfg.preproc +% added cfg.viewmode for external specification of visualisation method +% various bug fixes ans speed improvements +% +% Revision 1.8 2009/08/04 16:15:35 roboos +% removed settings uicontrol elements and button +% converted trial/channel/horizontal/vertical from text label into pushbutton with input dialog +% +% Revision 1.7 2009/08/04 13:37:00 ingnie +% renamed cfg.selectbehaviour into selectmode (consistent with viewmode) +% fixed bug in displaying of multiple artifacts in a single trial for viewmode=vertical +% +% Revision 1.6 2009/08/04 11:59:06 roboos +% first attempt to add some buttons to the figure and to toggle between the setting and the normal display +% +% Revision 1.5 2009/08/03 20:49:28 roboos +% some improvements for channelselection when reading data fromk disk +% +% Revision 1.4 2009/08/03 18:28:44 roboos +% started with implementation viewmode=component/layout/vertical/butterfly +% many small changes +% +% Revision 1.3 2009/08/03 10:55:43 roboos +% restructured the main function (order in which figure and opt are constructed), added multiplotER feature +% +% Revision 1.2 2009/08/03 08:30:17 ingnie +% replaced the initial version of Giovanni with another databrowser that Ingrid and Robert implemented. The newer one already has more functionality, like adding/removing artifacts and horizontal and vertical zooming +% + +fieldtripdefs + +if nargin>1 + data = checkdata(data, 'datatype', {'raw', 'comp'}, 'feedback', 'yes'); +else + % check if the input cfg is valid for this function + cfg = checkconfig(cfg, 'dataset2files', {'yes'}); + cfg = checkconfig(cfg, 'required', {'headerfile', 'datafile'}); + cfg = checkconfig(cfg, 'renamed', {'datatype', 'continuous'}); + cfg = checkconfig(cfg, 'renamedval', {'continuous', 'continuous', 'yes'}); +end + +% set the defaults +if ~isfield(cfg, 'channel'), cfg.channel = 'all'; end +if ~isfield(cfg, 'continuous'), cfg.continuous = 'no'; end % only for reading from file +if ~isfield(cfg, 'zscale'), cfg.zscale = 'auto'; end +if ~isfield(cfg, 'artfctdef'), cfg.artfctdef = struct; end +if ~isfield(cfg, 'selectfeature'), cfg.selectfeature = 'visual'; end % string or cell-array +if ~isfield(cfg, 'selectmode'), cfg.selectmode = 'joint'; end % joint or individual +if ~isfield(cfg, 'viewmode'), cfg.viewmode = 'butterfly'; end % butterfly, vertical, component, settings +if ~isfield(cfg, 'blocksize'), cfg.blocksize = 1; end % only for segmenting continuous data, i.e. one long trial +if ~isfield(cfg, 'preproc'), cfg.preproc = []; end % see preproc for options + +if ischar(cfg.selectfeature) + % ensure that it is a cell array + cfg.selectfeature = {cfg.selectfeature}; +end + +if nargin>1 + % read or create the layout that will be used for plotting + cfg.layout = prepare_layout(cfg, data); +else + % read or create the layout that will be used for plotting + cfg.layout = prepare_layout(cfg); +end + +% get some initial parameters from the data +if nargin>1 + % fetch the header + hdr = fetch_header(data); + + cfg.channel = channelselection(cfg.channel, data.label); + chansel = match_str(data.label, cfg.channel); + fsample = 1/(data.time{1}(2)-data.time{1}(1)); + Nchans = length(chansel); + + % this is how the input data is segmented + trlorg = findcfg(data.cfg, 'trl'); + Ntrials = size(trlorg, 1); + +else + % read the header + hdr = read_header(cfg.headerfile, 'headerformat', cfg.headerformat); + + % this option relates to reading over trial boundaries in a pseudo-continuous dataset + if ~isfield(cfg, 'continuous') + if hdr.nTrials==1 + cfg.continuous = 'yes'; + else + cfg.continuous = 'no'; + end + end + + if ~isfield(cfg, 'trl') + % treat the data as continuous if possible, otherwise define all trials as indicated in the header + if strcmp(cfg.continuous, 'yes') + trl = zeros(1, 3); + trl(1,1) = 1; + trl(1,2) = hdr.nSamples*hdr.nTrials; + trl(1,3) = 0; + else + trl = zeros(hdr.nTrials, 3); + for i=1:hdr.nTrials + trl(i,1) = (i-1)*hdr.nSamples + 1; + trl(i,2) = (i )*hdr.nSamples ; + trl(i,3) = -hdr.nSamplesPre; + end + end + cfg.trl = trl; + end + + cfg.channel = channelselection(cfg.channel, hdr.label); + chansel = match_str(hdr.label, cfg.channel); + fsample = hdr.Fs; + Nchans = length(chansel); + + % this is how the data from file should be segmented + trlorg = cfg.trl; + Ntrials = size(trlorg, 1); +end + +if Nchans == 0 + error('no channels to display'); +end + +if Ntrials == 0 + error('no trials to display'); +end + +% collect the artifacts that have been detected from cfg.artfctdef.xxx.artifact +artlabel = fieldnames(cfg.artfctdef); +sel = zeros(size(artlabel)); +artifact = cell(size(artlabel)); +for i=1:length(artlabel) + sel(i) = issubfield(cfg.artfctdef, [artlabel{i} '.artifact']); + if sel(i) + artifact{i} = getsubfield(cfg.artfctdef, [artlabel{i} '.artifact']); + num = size(artifact{i}, 1); + if isempty(num) + num = 0; + end + fprintf('detected %3d %s artifacts\n', num, artlabel{i}); + end +end +artifact = artifact(sel==1); +artlabel = artlabel(sel==1); + +for i=1:length(cfg.selectfeature) + if ~any(strcmp(cfg.selectfeature{i}, artlabel)) + artifact = {[], artifact{:}}; + artlabel = {cfg.selectfeature{i}, artlabel{:}}; + end +end + +% FIXME ensure nice sorting of artifact labels + +% make a single vector representing all artifacts +datbegsample = min(trlorg(:,1)); +datendsample = max(trlorg(:,2)); +artdat = zeros(length(artifact), datendsample); +for i=1:length(artifact) + for j=1:size(artifact{i},1) + artbegsample = artifact{i}(j,1); + artendsample = artifact{i}(j,2); + artendsample = min(artendsample, datendsample); + artdat(i, artbegsample:artendsample) = 1; + end +end + +artdata = []; +artdata.trial{1} = artdat; +artdata.time{1} = offset2time(0, fsample, datendsample); +artdata.label = artlabel; +artdata.fsample = fsample; +artdata.cfg.trl = [1 datendsample 0]; + +if ischar(cfg.zscale) && strcmp(cfg.zscale, 'auto') + if nargin>1 + dat = data.trial{1}(chansel,:); + minval = min(dat(:)); + maxval = max(dat(:)); + cfg.zscale = max(abs(minval), abs(maxval)); + else + cfg.zscale = 1; % FIXME + end +end + +h = figure; +set(h, 'KeyPressFcn', @keyboard_cb); +set(h, 'WindowButtonDownFcn', {@select_range, 'multiple', false, 'xrange', true, 'yrange', false, 'clear', true, 'callback', {@select_range_cb, h}, 'event', 'WindowButtonDownFcn'}); +set(h, 'WindowButtonUpFcn', {@select_range, 'multiple', false, 'xrange', true, 'yrange', false, 'clear', true, 'callback', {@select_range_cb, h}, 'event', 'WindowButtonUpFcn'}); +set(h, 'WindowButtonMotionFcn', {@select_range, 'multiple', false, 'xrange', true, 'yrange', false, 'clear', true, 'callback', {@select_range_cb, h}, 'event', 'WindowButtonMotionFcn'}); + +% opt represents the global data/settings, it should contain +% - the original data, epoched or continuous +% - the artifacts represented as continuous data +% - the redraw_cb settings +% - the preproc settings +% - the select_range_cb settings (also used in keyboard_cb) + +% these elements are stored inside the figure so that the callback routines can modify them +opt = []; +if nargin<2 + opt.orgdata = []; % this means that it will look in opt.cfg.dataset +else + opt.orgdata = data; +end +opt.artdata = artdata; +opt.cfg = cfg; % the configuration of this function, not of the preprocessing +opt.hdr = hdr; +opt.trlop = 1; % active trial being displayed +opt.ftsel = 1; % current artifact/feature being selected +opt.trlorg = trlorg; +opt.fsample = fsample; +opt.cleanup = false; % this is needed for a corrent handling if the figure is closed (either in the corner or by "q") +if strcmp(cfg.continuous, 'yes') + opt.trialname = 'segment'; +else + opt.trialname = 'trial'; +end +guidata(h, opt); + +% make the user interface elements for the data view +uicontrol('tag', 'group1', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', opt.trialname, 'userdata', 't') +uicontrol('tag', 'group2', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '<', 'userdata', 'leftarrow') +uicontrol('tag', 'group2', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '>', 'userdata', 'rightarrow') + +uicontrol('tag', 'group1', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'channel','userdata', 'c') +uicontrol('tag', 'group2', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '<', 'userdata', 'uparrow') +uicontrol('tag', 'group2', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '>', 'userdata', 'downarrow') + +uicontrol('tag', 'group1a', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'horizontal', 'userdata', 'h') +uicontrol('tag', 'group2a', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '-', 'userdata', 'shift+leftarrow') +uicontrol('tag', 'group2a', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '+', 'userdata', 'shift+rightarrow') +if strcmp(cfg.continuous, 'no') + uilayout(h, 'tag', 'group1a', 'visible', 'off', 'retag', 'group1'); + uilayout(h, 'tag', 'group2a', 'visible', 'off', 'retag', 'group2'); +else + uilayout(h, 'tag', 'group1a', 'visible', 'on', 'retag', 'group1'); + uilayout(h, 'tag', 'group2a', 'visible', 'on', 'retag', 'group2'); +end + +uicontrol('tag', 'group1', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'vertical', 'userdata', 'v') +uicontrol('tag', 'group2', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '-', 'userdata', 'shift+downarrow') +uicontrol('tag', 'group2', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '+', 'userdata', 'shift+uparrow') + +uilayout(h, 'tag', 'group1', 'width', 0.10, 'height', 0.05); +uilayout(h, 'tag', 'group2', 'width', 0.05, 'height', 0.05); +uilayout(h, 'tag', 'group1', 'style', 'pushbutton', 'callback', @keyboard_cb); +uilayout(h, 'tag', 'group2', 'style', 'pushbutton', 'callback', @keyboard_cb); + +uilayout(h, 'tag', 'group1', 'retag', 'viewui'); +uilayout(h, 'tag', 'group2', 'retag', 'viewui'); +uilayout(h, 'tag', 'viewui', 'BackgroundColor', [0.8 0.8 0.8], 'hpos', 'auto', 'vpos', 0.01); + +definetrial_cb(h); +redraw_cb(h); + +if nargout + % wait until the user interface is closed, get the user data with the updated artifact details + set(h, 'CloseRequestFcn', @cleanup_cb); + + while ishandle(h) + uiwait(h); + opt = guidata(h); + if opt.cleanup + delete(h); + end + end + + % add the updated artifact definitions to the output cfg + for i=1:length(opt.artdata.label) + tmp = diff([0 opt.artdata.trial{1}(i,:) 0]); + artbeg = find(tmp==+1); + artend = find(tmp==-1) - 1; + cfg.artfctdef.(opt.artdata.label{i}).artifact = [artbeg' artend']; + end +end % if nargout + +% add version information to the configuration +try + % get the full name of the function + cfg.version.name = mfilename('fullpath'); +catch + % required for compatibility with Matlab versions prior to release 13 (6.5) + [st, i] = dbstack; + cfg.version.name = st(i); +end +cfg.version.id = '$Id: databrowser.m,v 1.12 2009/08/05 14:16:53 roboos Exp $'; +% remember the configuration details of the input data +try cfg.previous = data.cfg; end + +end % main function + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function cleanup_cb(h, eventdata) +opt = guidata(h); +opt.cleanup = true; +guidata(h, opt); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function definetrial_cb(h, eventdata) +opt = guidata(h); +if strcmp(opt.cfg.continuous, 'no') + % keep the original trial definition for visualisation + opt.trlvis = opt.trlorg; +else + % construct a trial definition for visualisation + if isfield(opt, 'trlvis') + thistrlbeg = opt.trlvis(opt.trlop,1); + thistrlend = opt.trlvis(opt.trlop,2); + % remember a representative sample of the current trial + % thissample = round((thistrlbeg+thistrlend)/2); + thissample = thistrlbeg; + end + % look at opt.cfg.blocksize and make opt.trl accordingly + % if original data contains more than one trial, it will fail in fetc_data + datbegsample = min(opt.trlorg(:,1)); + datendsample = max(opt.trlorg(:,2)); + smppertrl = round(opt.fsample * opt.cfg.blocksize); + begsamples = datbegsample:smppertrl:datendsample; + endsamples = datbegsample+smppertrl-1:smppertrl:datendsample; + if numel(endsamples)length(opt.hdr.label) + chansel = chansel - (max(chansel)-length(opt.hdr.label)); + end + % convert numeric array into cell-array with channel labels + opt.cfg.channel = opt.hdr.label(chansel); + disp(opt.cfg.channel); + guidata(h, opt); + redraw_cb(h, eventdata); + case 'shift+leftarrow' + opt.cfg.blocksize = opt.cfg.blocksize*sqrt(2); + disp(opt.cfg.blocksize); + guidata(h, opt); + definetrial_cb(h, eventdata); + redraw_cb(h, eventdata); + case 'shift+rightarrow' + opt.cfg.blocksize = opt.cfg.blocksize/sqrt(2); + disp(opt.cfg.blocksize); + guidata(h, opt); + definetrial_cb(h, eventdata); + redraw_cb(h, eventdata); + case 'shift+uparrow' + opt.cfg.zscale = opt.cfg.zscale/sqrt(2); + guidata(h, opt); + redraw_cb(h, eventdata); + case 'shift+downarrow' + opt.cfg.zscale = opt.cfg.zscale*sqrt(2); + guidata(h, opt); + redraw_cb(h, eventdata); + case 'q' + guidata(h, opt); + cleanup_cb(h); + case 't' + % select the trial to display + response = inputdlg(sprintf('%s to display', opt.trialname), 'specify', 1, {num2str(opt.trlop)}); + if ~isempty(response) + opt.trlop = str2double(response); + opt.trlop = min(opt.trlop, size(opt.trlvis,1)); % should not be larger than the number of trials + opt.trlop = max(opt.trlop, 1); % should not be smaller than 1 + end + guidata(h, opt); + redraw_cb(h, eventdata); + case 'h' + % select the horizontal scaling + response = inputdlg('horizontal scale', 'specify', 1, {num2str(opt.cfg.blocksize)}); + if ~isempty(response) + opt.cfg.blocksize = str2double(response); + end + guidata(h, opt); + definetrial_cb(h, eventdata); + redraw_cb(h, eventdata); + case 'v' + % select the vertical scaling + response = inputdlg('vertical scale', 'specify', 1, {num2str(opt.cfg.zscale)}); + if ~isempty(response) + opt.cfg.zscale = str2double(response); + end + guidata(h, opt); + redraw_cb(h, eventdata); + case 'c' + % select channels + select = match_str(opt.hdr.label, opt.cfg.channel); + select = select_channel_list(opt.hdr.label, select); + opt.cfg.channel = opt.hdr.label(select); + guidata(h, opt); + redraw_cb(h, eventdata); + case 'control+control' + % do nothing + case 'shift+shift' + % do nothing + otherwise + guidata(h, opt); + help_cb(h); +end +uiresume(h); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function toggle_viewmode_cb(h, eventdata, varargin) +opt = guidata(getparent(h)); +if ~isempty(varargin) && ischar(varargin{1}) + opt.cfg.viewmode = varargin{1}; +end +guidata(getparent(h), opt); +redraw_cb(h); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function h = getparent(h) +p = h; +while p~=0 + h = p; + p = get(h, 'parent'); +end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function redraw_cb(h, eventdata) +h = getparent(h); +opt = guidata(h); +figure(h); % ensure that the calling figure is in the front +cla; % clear the content in the current axis + +fprintf('redrawing with viewmode %s\n', opt.cfg.viewmode); + + +artcol = [0.9686 0.7608 0.7686; 0.7529 0.7098 0.9647; 0.7373 0.9725 0.6824;0.8118 0.8118 0.8118; 0.9725 0.6745 0.4784; 0.9765 0.9176 0.5686]; + +begsample = opt.trlvis(opt.trlop, 1); +endsample = opt.trlvis(opt.trlop, 2); +offset = opt.trlvis(opt.trlop, 3); +chanindx = match_str(opt.hdr.label, opt.cfg.channel); + +if isempty(opt.orgdata) + fprintf('reading data... '); + dat = read_data(opt.cfg.datafile, 'header', opt.hdr, 'begsample', begsample, 'endsample', endsample, 'chanindx', chanindx, 'checkboundary', strcmp(opt.cfg.continuous, 'no'), 'dataformat', opt.cfg.dataformat, 'headerformat', opt.cfg.headerformat); +else + fprintf('fetching data... '); + dat = fetch_data(opt.orgdata, 'header', opt.hdr, 'begsample', begsample, 'endsample', endsample, 'chanindx', chanindx); +end +fprintf('done\n'); + +fprintf('fetching artifacts... '); +art = fetch_data(opt.artdata, 'begsample', begsample, 'endsample', endsample); +fprintf('done\n'); + +% apply preprocessing and determine the time axis +fprintf('preprocessing data... '); +[dat, lab, tim] = preproc(dat, opt.hdr.label(chanindx), opt.fsample, opt.cfg.preproc, offset); +fprintf('done\n'); + +fprintf('plotting data... '); +switch opt.cfg.viewmode + case 'butterfly' + for i=1:length(opt.artdata.label) + tmp = diff([0 art(i,:) 0]); + artbeg = find(tmp==+1); + artend = find(tmp==-1) - 1; + for j=1:numel(artbeg) + plot_box([tim(artbeg(j)) tim(artend(j)) -opt.cfg.zscale opt.cfg.zscale], 'facecolor', artcol(i,:), 'edgecolor', 'none'); + end + end + + % plot the data on top of the box + plot_vector(tim, dat) + ax(1) = tim(1); + ax(2) = tim(end); + ax(3) = -opt.cfg.zscale; + ax(4) = opt.cfg.zscale; + axis(ax); + title(sprintf('%s %d, time from %g to %g s', opt.trialname, opt.trlop, tim(1), tim(end))); + xlabel('time'); + + case 'vertical' + tmpcfg = []; + tmpcfg.layout = 'vertical'; + tmpcfg.channel = opt.cfg.channel; + tmpcfg.skipcomnt = 'yes'; + tmpcfg.skipscale = 'yes'; + laytime = prepare_layout(tmpcfg, opt.orgdata); + + hlim = [tim(1) tim(end)]; + vlim = [-opt.cfg.zscale +opt.cfg.zscale]; + + % determine the position of each of the labels + labelx = laytime.pos(:,1) - laytime.width/2 - 0.01; + labely = laytime.pos(:,2); + + ax(1) = min(laytime.pos(:,1) - laytime.width/2); + ax(2) = max(laytime.pos(:,1) + laytime.width/2); + ax(3) = min(laytime.pos(:,2) - laytime.height/2); + ax(4) = max(laytime.pos(:,2) + laytime.height/2); + axis(ax) + + % remember the scaling of the horizontal axis, this is needed for mouse input + hpos(1) = laytime.pos(1,1) - laytime.width(1)/2; % the position of the left side of the timecourse box + hpos(2) = laytime.pos(1,1) + laytime.width(1)/2; % the position of the right side of the timecourse box + opt.hlim = hlim; + opt.hpos = hpos; + + for j=1:length(opt.artdata.label) + tmp = diff([0 art(j,:) 0]); + artbeg = find(tmp==+1); + artend = find(tmp==-1) - 1; + arttim = [tim(artbeg)' tim(artend)']; % convert the artifact sample number to time + arttim = (arttim - opt.hlim(1)) / (opt.hlim(2) - opt.hlim(1)); % convert to value relative to box, i.e. from 0 to 1 + arttim = arttim * (opt.hpos(2) - opt.hpos(1)) + opt.hpos(1); % convert from relative to actual value along the horizontal figure axis + + for k=1:numel(artbeg) + plot_box([arttim(k,1) arttim(k,2) ax(3) ax(4)], 'facecolor', artcol(j,:), 'edgecolor', 'none'); + end + end % for each of the artifact channels + + for i=1:length(chanindx) + datsel = i; + laysel = match_str(laytime.label, opt.hdr.label(chanindx(i))); + if ~isempty(datsel) + plot_text(labelx(laysel), labely(laysel), opt.hdr.label(chanindx(i)), 'HorizontalAlignment', 'right'); + plot_vector(tim, dat(datsel, :), 'hpos', laytime.pos(laysel,1), 'vpos', laytime.pos(laysel,2), 'width', laytime.width(laysel), 'height', laytime.height(laysel), 'hlim', hlim, 'vlim', vlim, 'box', false); + end + end + + set(gca, 'xTick', []) + set(gca, 'yTick', []) + title(sprintf('%s %d, time from %g to %g s', opt.trialname, opt.trlop, tim(1), tim(end))); + + case 'component' + compindx = chanindx; + clear chanindx + + tmpcfg = []; + tmpcfg.layout = 'vertical'; + tmpcfg.channel = opt.cfg.channel; + tmpcfg.skipcomnt = 'yes'; + tmpcfg.skipscale = 'yes'; + laytime = prepare_layout(tmpcfg, opt.orgdata); + + tmpcfg = []; + tmpcfg.layout = opt.cfg.layout; + laychan = prepare_layout(tmpcfg, opt.orgdata); + + % determine the position of each of the topographies + laytopo.pos(:,1) = laytime.pos(:,1) - laytime.width/2 - laytime.height*2; + laytopo.pos(:,2) = laytime.pos(:,2); + laytopo.width = laytime.height; + laytopo.height = laytime.height; + laytopo.label = laytime.label; + + % determine the position of each of the labels + labelx = laytopo.pos(:,1) + laytopo.width; + labely = laytopo.pos(:,2); + + hlim = [tim(1) tim(end)]; + vlim = [-opt.cfg.zscale +opt.cfg.zscale]; + + [sel1, sel2] = match_str(opt.orgdata.topolabel, laychan.label); + chanx = laychan.pos(sel2,1); + chany = laychan.pos(sel2,2); + + for i=1:length(compindx) + datsel = i; + laysel = match_str(laytime.label,opt.hdr.label(compindx(i))); + if ~isempty(datsel) + plot_text(labelx(laysel), labely(laysel), opt.hdr.label(compindx(i))); + % plot the timecourse of this component + plot_vector(tim, dat(datsel, :), 'hpos', laytime.pos(laysel,1), 'vpos', laytime.pos(laysel,2), 'width', laytime.width(laysel), 'height', laytime.height(laysel), 'hlim', hlim, 'vlim', vlim); + % plot the topography of this component + chanz = opt.orgdata.topo(sel1,compindx(i)); + plot_topo(chanx, chany, chanz, 'mask', laychan.mask, 'outline', laychan.outline, 'hpos', laytopo.pos(laysel,1), 'vpos', laytopo.pos(laysel,2), 'width', laytopo.width(laysel), 'height', laytopo.height(laysel)) + axis equal + drawnow + end + end + set(gca, 'xTick', []) + set(gca, 'yTick', []) + title(sprintf('%s %d, time from %g to %g s', opt.trialname, opt.trlop, tim(1), tim(end))); + + ax(1) = min(laytopo.pos(:,1) - laytopo.width/2); + ax(2) = max(laytime.pos(:,1) + laytime.width/2); + ax(3) = min(laytime.pos(:,2) - laytime.height/2); + ax(4) = max(laytime.pos(:,2) + laytime.height/2); + axis(ax) + + % remember the scaling of the horizontal axis, this is needed for mouse input + hpos(1) = laytime.pos(1,1) - laytime.width(1)/2; % the position of the left side of the timecourse box + hpos(2) = laytime.pos(1,1) + laytime.width(1)/2; % the position of the right side of the timecourse box + opt.hlim = hlim; + opt.hpos = hpos; + + case 'settings' + % FIXME implement further details + + otherwise + error('unknown viewmode "%s"', opt.cfg.viewmode); +end % switch viewmode +fprintf('done\n'); + +guidata(h, opt); +end diff --git a/external/fieldtrip/private/datatype.m b/external/fieldtrip/private/datatype.m index ccceb81..59e737a 100644 --- a/external/fieldtrip/private/datatype.m +++ b/external/fieldtrip/private/datatype.m @@ -13,6 +13,12 @@ % Copyright (C) 2008, Robert Oostenveld % % $Log: datatype.m,v $ +% Revision 1.8 2009/08/17 08:41:00 jansch +% added undocumented and experimental datatypes mvar and freqmvar +% +% Revision 1.7 2009/06/15 12:55:18 roboos +% also recognize cohspctrm as type=freq +% % Revision 1.6 2009/03/25 20:52:07 jansch % changed the conditional order to ensure correct behaviour for a comp-struct % @@ -35,18 +41,26 @@ % determine the type of input data, this can be raw, freq, timelock, comp, spike, source, volume, dip israw = isfield(data, 'label') && isfield(data, 'time') && isa(data.time, 'cell') && isfield(data, 'trial') && isa(data.trial, 'cell'); -isfreq = isfield(data, 'label') && isfield(data, 'freq') && (isfield(data, 'powspctrm') || isfield(data, 'crsspctrm') || isfield(data, 'fourierspctrm')); +isfreq = isfield(data, 'label') && isfield(data, 'freq') && (isfield(data, 'powspctrm') || isfield(data, 'crsspctrm') || isfield(data, 'cohspctrm') || isfield(data, 'fourierspctrm')); istimelock = isfield(data, 'label') && isfield(data, 'time') && ~isfield(data, 'freq') && ((isfield(data, 'avg') && isnumeric(data.avg)) || (isfield(data, 'trial') && isnumeric(data.trial) || (isfield(data, 'cov') && isnumeric(data.cov)))); iscomp = isfield(data, 'topo') || isfield(data, 'topolabel'); isspike = isfield(data, 'label') && isfield(data, 'waveform') && isa(data.waveform, 'cell') && isfield(data, 'timestamp') && isa(data.timestamp, 'cell'); isvolume = isfield(data, 'transform') && isfield(data, 'dim'); issource = isfield(data, 'pos'); isdip = isfield(data, 'dip'); +ismvar = isfield(data, 'dimord') && ~isempty(strfind(data.dimord, 'lag')); +isfreqmvar = isfield(data, 'freq') && isfield(data, 'transfer'); if iscomp type = 'comp'; %comp should conditionally go before raw, otherwise the returned datatype %will be raw +elseif isfreqmvar + type = 'freqmvar'; + %freqmvar should conditionally go before freq, otherwise the returned datatype + %will be freq in the case of frequency mvar data +elseif ismvar + type = 'mvar'; elseif israw type = 'raw'; elseif isfreq diff --git a/external/fieldtrip/private/denoise_synthetic.m b/external/fieldtrip/private/denoise_synthetic.m index 4e5242c..8b0d975 100644 --- a/external/fieldtrip/private/denoise_synthetic.m +++ b/external/fieldtrip/private/denoise_synthetic.m @@ -15,6 +15,9 @@ % Copyright (C) 2004-2008, Robert Oostenveld % % $Log: denoise_synthetic.m,v $ +% Revision 1.8 2009/07/08 07:20:48 roboos +% allow for arbitrary balancing and not only a small hardcoded list +% % Revision 1.7 2008/09/22 20:17:43 roboos % added call to fieldtripdefs to the begin of the function % @@ -80,41 +83,31 @@ current = data.grad.balance.current; desired = cfg.gradient; -% first undo/invert the previously applied balancing -switch current - case 'none' - % nothing to do - case 'G1BR' - data.grad = apply_montage(data.grad, data.grad.balance.G1BR, 'keepunused', 'yes', 'inverse', 'yes'); - data = apply_montage(data , data.grad.balance.G1BR, 'keepunused', 'yes', 'inverse', 'yes'); - case 'G2BR' - data.grad = apply_montage(data.grad, data.grad.balance.G2BR, 'keepunused', 'yes', 'inverse', 'yes'); - data = apply_montage(data , data.grad.balance.G2BR, 'keepunused', 'yes', 'inverse', 'yes'); - case 'G3BR' - data.grad = apply_montage(data.grad, data.grad.balance.G3BR, 'keepunused', 'yes', 'inverse', 'yes'); - data = apply_montage(data , data.grad.balance.G3BR, 'keepunused', 'yes', 'inverse', 'yes'); - otherwise +if ~strcmp(current, 'none') + % first undo/invert the previously applied balancing + try + current_montage = getfield(data.grad.balance, data.grad.balance.current); + catch error('unknown balancing for input data'); -end -data.grad.balance.current = 'none'; + end + fprintf('converting from "%s" to "none"\n', current); + data.grad = apply_montage(data.grad, current_montage, 'keepunused', 'yes', 'inverse', 'yes'); + data = apply_montage(data , current_montage, 'keepunused', 'yes', 'inverse', 'yes'); + data.grad.balance.current = 'none'; +end % if -% then apply the desired balancing -switch desired - case 'none' - % nothing to do - case 'G1BR' - data.grad = apply_montage(data.grad, data.grad.balance.G1BR, 'keepunused', 'yes', 'inverse', 'no'); - data = apply_montage(data , data.grad.balance.G1BR, 'keepunused', 'yes', 'inverse', 'no'); - case 'G2BR' - data.grad = apply_montage(data.grad, data.grad.balance.G2BR, 'keepunused', 'yes', 'inverse', 'no'); - data = apply_montage(data , data.grad.balance.G2BR, 'keepunused', 'yes', 'inverse', 'no'); - case 'G3BR' - data.grad = apply_montage(data.grad, data.grad.balance.G3BR, 'keepunused', 'yes', 'inverse', 'no'); - data = apply_montage(data , data.grad.balance.G3BR, 'keepunused', 'yes', 'inverse', 'no'); - otherwise +if ~strcmp(desired, 'none') + % then apply the desired balancing + try + desired_montage = getfield(data.grad.balance, cfg.gradient); + catch error('unknown balancing for input data'); -end -data.grad.balance.current = desired; + end + fprintf('converting from "none" to "%s"\n', desired); + data.grad = apply_montage(data.grad, desired_montage, 'keepunused', 'yes', 'inverse', 'yes'); + data = apply_montage(data , desired_montage, 'keepunused', 'yes', 'inverse', 'yes'); + data.grad.balance.current = desired; +end % if % reorder the channels to stay close to the original ordering [selorg, selnew] = match_str(labelorg, data.label); @@ -136,7 +129,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: denoise_synthetic.m,v 1.7 2008/09/22 20:17:43 roboos Exp $'; +cfg.version.id = '$Id: denoise_synthetic.m,v 1.8 2009/07/08 07:20:48 roboos Exp $'; % remember the configuration details of the input data try, cfg.previous = data.cfg; end % remember the exact configuration details in the output diff --git a/external/fieldtrip/private/dipole_fit.m b/external/fieldtrip/private/dipole_fit.m index e68a5a5..04137f1 100644 --- a/external/fieldtrip/private/dipole_fit.m +++ b/external/fieldtrip/private/dipole_fit.m @@ -28,6 +28,9 @@ % Copyright (C) 2003-2008, Robert Oostenveld % % $Log: dipole_fit.m,v $ +% Revision 1.15 2009/07/02 15:38:06 roboos +% use fixdipole for consistent dipole structure representation +% % Revision 1.14 2009/03/13 07:24:52 roboos % changed handling of optimization function (fminunc/fminsearch), now uses hastoolbox which is also able to check the availablity of a license for the optimimization toolbox % @@ -169,38 +172,15 @@ dat = avgref(dat); end -% check the input dipole position specification, should be Nx3 -[m, n] = size(dip.pos); -if n==3 - % this is ok -elseif m==3 - % it is possible to translate it into a Nx3 unambiguously - warning('input dipole positions should be specified as Nx3 matrix'); - dip.pos = dip.pos'; -else - error('input dipole positions should be specified as Nx3 matrix'); -end - -if isfield(dip, 'mom') - % check the input dipole moment specification, should be 3xN - [m, n] = size(dip.mom); - if m==3 - % this is ok - elseif n==3 - % it is possible to translate it into a 3xN unambiguously - warning('input dipole moments should be specified as 3xN matrix'); - dip.mom = dip.mom'; - else - error('input dipole moments should be specified as 3xN matrix'); - end -end +% ensure correct dipole position and moment specification +dip = fixdipole(dip); % reformat the position parameters in case of multiple dipoles, this % should result in the matrix changing from [x1 y1 z1; x2 y2 z2] to % [x1 y1 z1 x2 y2 z2] for the constraints to work -param = dip.pos'; -param = param(:)'; numdip = size(dip.pos, 1); +param = dip.pos'; +param = param(:)'; % add the orientation to the nonlinear parameters if isfield(constr, 'fixedori') && constr.fixedori @@ -271,9 +251,12 @@ dipout.mom = ori; % dipole orientation as vector dipout.ampl = mom; % dipole strength else - dipout.mom = mom; % dipole moment, which represents both the orientation and strength as vector + dipout.mom = mom; % dipole moment as vector or matrix, which represents both the orientation and strength as vector end +% ensure correct dipole position and moment specification +dipout = fixdipole(dipout); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % DIPFIT_ERROR computes the error between measured and model data % and can be used for non-linear fitting of dipole position diff --git a/external/fieldtrip/private/dipolefitting.m b/external/fieldtrip/private/dipolefitting.m index 102d6ca..88df1ae 100644 --- a/external/fieldtrip/private/dipolefitting.m +++ b/external/fieldtrip/private/dipolefitting.m @@ -26,7 +26,7 @@ % cfg.hdmfile = string, file containing the volume conduction model % or alternatively % cfg.vol = structure with volume conduction model -% If the sensor information is not contained in the data itself you should +% If the sensor information is not contained in the data itself you should % also specify the sensor information using % cfg.gradfile = string, file containing the gradiometer definition % cfg.elecfile = string, file containing the electrode definition @@ -104,7 +104,7 @@ % cfg.grid.inside, documented % cfg.grid.outside, documented % cfg.symmetry, documented -% cfg.mri +% cfg.mri % cfg.mriunits % cfg.smooth % cfg.sourceunits @@ -121,8 +121,22 @@ % cfg.vol, documented % Copyright (C) 2004-2006, Robert Oostenveld -% +% % $Log: dipolefitting.m,v $ +% Revision 1.57 2009/07/02 15:55:15 roboos +% fixed typo +% +% Revision 1.56 2009/07/02 15:54:15 roboos +% convert 1x6 dipole position (after scanning with symmetric pair) into 2x3 to prevent warning later +% +% Revision 1.55 2009/07/02 15:37:04 roboos +% use senstype instead of strcmp +% use fixdipole helper function for consistent dipole structure representation +% +% Revision 1.54 2009/06/03 09:51:16 roboos +% changed input specification of dip.mom into 3xNdipoles +% give explicit error if unsupported dipole model +% % Revision 1.53 2009/01/20 13:01:31 sashae % changed configtracking such that it is only enabled when BOTH explicitly allowed at start % of the fieldtrip function AND requested by the user @@ -443,7 +457,7 @@ end % check whether EEG is average referenced -if strcmp(sens.type, 'electrodes') +if senstype(sens, 'eeg') if any(rv(Vdata, avgref(Vdata))>0.001) warning('the EEG data is not average referenced, correcting this'); end @@ -453,7 +467,7 @@ % set to zeros if no initial dipole was specified if ~isfield(cfg, 'dip') cfg.dip.pos = zeros(cfg.numdipoles, 3); - cfg.dip.mom = zeros(cfg.numdipoles*3, 1); + cfg.dip.mom = zeros(3*cfg.numdipoles, 1); end % set to zeros if no initial dipole position was specified @@ -463,11 +477,11 @@ % set to zeros if no initial dipole moment was specified if ~isfield(cfg.dip, 'mom') - cfg.dip.mom = zeros(cfg.numdipoles*3, 1); + cfg.dip.mom = zeros(3*cfg.numdipoles, 1); end % check the specified dipole model -if prod(size(cfg.dip.pos))~=cfg.numdipoles*3 || prod(size(cfg.dip.mom))~=cfg.numdipoles*3 +if numel(cfg.dip.pos)~=cfg.numdipoles*3 || numel(cfg.dip.mom)~=cfg.numdipoles*3 error('inconsistent number of dipoles in configuration') end @@ -500,12 +514,14 @@ % dipole moment this makes the model potential U=lf*pinv(lf)*V and the % model error is norm(V-U) = norm(V-lf*pinv(lf)*V) = norm((eye-lf*pinv(lf))*V) switch cfg.model - case 'regional' - % sum the error over all latencies - grid.error(indx,1) = sum(sum(((eye(nchans)-lf*pinv(lf))*Vdata).^2)); - case 'moving' - % remember the error for each latency independently - grid.error(indx,:) = sum(((eye(nchans)-lf*pinv(lf))*Vdata).^2); + case 'regional' + % sum the error over all latencies + grid.error(indx,1) = sum(sum(((eye(nchans)-lf*pinv(lf))*Vdata).^2)); + case 'moving' + % remember the error for each latency independently + grid.error(indx,:) = sum(((eye(nchans)-lf*pinv(lf))*Vdata).^2); + otherwise + error('unsupported cfg.model'); end % switch model end % looping over the grid progress('close'); @@ -514,8 +530,9 @@ case 'regional' % find the grid point(s) with the minimum error [err, indx] = min(grid.error(grid.inside)); - dip.pos = grid.pos(grid.inside(indx),:); % note that for a symmetric dipole pair this results in a vector - dip.mom = zeros(cfg.numdipoles*3,1); % set the dipole moment to zero + dip.pos = grid.pos(grid.inside(indx),:); % note that for a symmetric dipole pair this results in a vector + dip.pos = reshape(dip.pos, cfg.numdipoles, 3); % convert to a Nx3 array + dip.mom = zeros(cfg.numdipoles*3,1); % set the dipole moment to zero if cfg.numdipoles==1 fprintf('found minimum after scanning on grid point [%g %g %g]\n', dip.pos(1), dip.pos(2), dip.pos(3)); elseif cfg.numdipoles==2 @@ -525,44 +542,45 @@ for t=1:ntime % find the grid point(s) with the minimum error [err, indx] = min(grid.error(grid.inside,t)); - dip(t).pos = grid.pos(grid.inside(indx),:); % note that for a symmetric dipole pair this results in a vector - dip(t).mom = zeros(cfg.numdipoles*3,1); % set the dipole moment to zero + dip(t).pos = grid.pos(grid.inside(indx),:); % note that for a symmetric dipole pair this results in a vector + dip(t).pos = reshape(dip(t).pos, cfg.numdipoles, 3); % convert to a Nx3 array + dip(t).mom = zeros(cfg.numdipoles*3,1); % set the dipole moment to zero if cfg.numdipoles==1 fprintf('found minimum after scanning for topography %d on grid point [%g %g %g]\n', t, dip(t).pos(1), dip(t).pos(2), dip(t).pos(3)); elseif cfg.numdipoles==2 fprintf('found minimum after scanning for topography %d on grid point [%g %g %g; %g %g %g]\n', t, dip(t).pos(1), dip(t).pos(2), dip(t).pos(3), dip(t).pos(4), dip(t).pos(5), dip(t).pos(6)); end end + otherwise + error('unsupported cfg.model'); end % switch model end % if gridsearch if strcmp(cfg.gridsearch, 'no') % use the initial guess supplied in the configuration for the remainder switch cfg.model - case 'regional' - dip = cfg.dip; - case 'moving' - for t=1:ntime - dip(t) = cfg.dip; - end + case 'regional' + dip = struct(cfg.dip); + case 'moving' + for t=1:ntime + dip(t) = struct(cfg.dip); + end + otherwise + error('unsupported cfg.model'); end % switch model -end +end % multiple dipoles can be represented either as a 1x(N*3) vector or as a Nx3 matrix, % i.e. [x1 y1 z1 x2 y2 z2] or [x1 y1 z1; x2 y2 z2] switch cfg.model -case 'regional' - if length(dip.pos)==cfg.numdipoles*3 - % reshape the vector representation into a N*3 matrix - dip.pos = transpose(reshape(dip.pos, 3, cfg.numdipoles)); - end -case 'moving' - for t=1:ntime - if length(dip(t).pos)==cfg.numdipoles*3 - % reshape the vector representation into a N*3 matrix - dip(t).pos = transpose(reshape(dip(t).pos, 3, cfg.numdipoles)); + case 'regional' + dip = fixdipole(dip); + case 'moving' + for t=1:ntime + dip(t) = fixdipole(dip(t)); end - end + otherwise + error('unsupported cfg.model'); end % switch model if isfield(cfg, 'dipfit') @@ -578,55 +596,60 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if strcmp(cfg.nonlinear, 'yes') switch cfg.model - case 'regional' - % perform the non-linear dipole fit for all latencies together - % catch errors due to non-convergence - try - dip = dipole_fit(dip, sens, vol, Vdata, optarg{:}); - success = 1; - if cfg.numdipoles==1 - fprintf('found minimum after non-linear optimization on [%g %g %g]\n', dip.pos(1), dip.pos(2), dip.pos(3)); - elseif cfg.numdipoles==2 - fprintf('found minimum after non-linear optimization on [%g %g %g; %g %g %g]\n', dip.pos(1,1), dip.pos(1,2), dip.pos(1,3), dip.pos(2,1), dip.pos(2,2), dip.pos(2,3)); - end - catch - success = 0; - disp(lasterr); - end - case 'moving' - % perform the non-linear dipole fit for each latency independently - % instead of using dip(t) = dipole_fit(dip(t),...), I am using temporary variables dipin and dipout - % this is to prevent errors of the type "Subscripted assignment between dissimilar structures" - dipin = dip; - for t=1:ntime + case 'regional' + % perform the non-linear dipole fit for all latencies together % catch errors due to non-convergence try - dipout(t) = dipole_fit(dipin(t), sens, vol, Vdata(:,t), optarg{:}); - success(t) = 1; + dip = dipole_fit(dip, sens, vol, Vdata, optarg{:}); + success = 1; if cfg.numdipoles==1 - fprintf('found minimum after non-linear optimization for topography %d on [%g %g %g]\n', t, dipout(t).pos(1), dipout(t).pos(2), dipout(t).pos(3)); + fprintf('found minimum after non-linear optimization on [%g %g %g]\n', dip.pos(1), dip.pos(2), dip.pos(3)); elseif cfg.numdipoles==2 - fprintf('found minimum after non-linear optimization for topography %d on [%g %g %g; %g %g %g]\n', t, dipout(t).pos(1,1), dipout(t).pos(1,2), dipout(t).pos(1,3), dipout(t).pos(2,1), dipout(t).pos(2,2), dipout(t).pos(2,3)); + fprintf('found minimum after non-linear optimization on [%g %g %g; %g %g %g]\n', dip.pos(1,1), dip.pos(1,2), dip.pos(1,3), dip.pos(2,1), dip.pos(2,2), dip.pos(2,3)); end catch - dipout(t).pos = dipin(t).pos; - dipout(t).mom = dipin(t).mom; - success(t) = 0; + success = 0; disp(lasterr); end - end - dip = dipout; - clear dipin dipout - end + case 'moving' + % perform the non-linear dipole fit for each latency independently + % instead of using dip(t) = dipole_fit(dip(t),...), I am using temporary variables dipin and dipout + % this is to prevent errors of the type "Subscripted assignment between dissimilar structures" + dipin = dip; + for t=1:ntime + % catch errors due to non-convergence + try + dipout(t) = dipole_fit(dipin(t), sens, vol, Vdata(:,t), optarg{:}); + success(t) = 1; + if cfg.numdipoles==1 + fprintf('found minimum after non-linear optimization for topography %d on [%g %g %g]\n', t, dipout(t).pos(1), dipout(t).pos(2), dipout(t).pos(3)); + elseif cfg.numdipoles==2 + fprintf('found minimum after non-linear optimization for topography %d on [%g %g %g; %g %g %g]\n', t, dipout(t).pos(1,1), dipout(t).pos(1,2), dipout(t).pos(1,3), dipout(t).pos(2,1), dipout(t).pos(2,2), dipout(t).pos(2,3)); + end + catch + dipout(t).pos = dipin(t).pos; + dipout(t).mom = dipin(t).mom; + success(t) = 0; + disp(lasterr); + end + end + dip = dipout; + clear dipin dipout + otherwise + error('unsupported cfg.model'); + end % switch model end % if nonlinear if strcmp(cfg.nonlinear, 'no') % the optimal dipole positions are either obrained from scanning or from the initial configured seed switch cfg.model - case 'regional' - success = 1; - case 'moving' - success = ones(1,ntime); + case 'regional' + success = 1; + case 'moving' + success = ones(1,ntime); + otherwise + error('unsupported cfg.model'); + end % switch model end @@ -634,42 +657,46 @@ % compute the model potential distribution and the residual variance %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% switch cfg.model -case 'regional' - if success - % re-compute the leadfield in order to compute the model potential and dipole moment - lf = compute_leadfield(dip.pos, sens, vol); - % compute all details of the final dipole model - dip.mom = pinv(lf)*Vdata; - dip.pot = lf*dip.mom; - dip.rv = rv(Vdata, dip.pot); - Vmodel = dip.pot; - end -case 'moving' - for t=1:ntime - if success(t) + case 'regional' + if success % re-compute the leadfield in order to compute the model potential and dipole moment - lf = compute_leadfield(dip(t).pos, sens, vol); + lf = compute_leadfield(dip.pos, sens, vol); % compute all details of the final dipole model - dip(t).mom = pinv(lf)*Vdata(:,t); - dip(t).pot = lf*dip(t).mom; - dip(t).rv = rv(Vdata(:,t), dip(t).pot); - Vmodel(:,t) = dip(t).pot; + dip.mom = pinv(lf)*Vdata; + dip.pot = lf*dip.mom; + dip.rv = rv(Vdata, dip.pot); + Vmodel = dip.pot; end - end + case 'moving' + for t=1:ntime + if success(t) + % re-compute the leadfield in order to compute the model potential and dipole moment + lf = compute_leadfield(dip(t).pos, sens, vol); + % compute all details of the final dipole model + dip(t).mom = pinv(lf)*Vdata(:,t); + dip(t).pot = lf*dip(t).mom; + dip(t).rv = rv(Vdata(:,t), dip(t).pot); + Vmodel(:,t) = dip(t).pot; + end + end + otherwise + error('unsupported cfg.model'); end % switch model switch cfg.model -case 'regional' - if isfreq - % the matrix with the dipole moment is encrypted and cannot be interpreted straight away - % reconstruct the frequency representation of the data at the source level - [dip.pow, dip.csd, dip.fourier] = timelock2freq(dip.mom); - end -case 'moving' - if isfreq - % although this is technically possible sofar, it does not make any sense - warning('a moving dipole model in the frequency domain is not supported'); - end + case 'regional' + if isfreq + % the matrix with the dipole moment is encrypted and cannot be interpreted straight away + % reconstruct the frequency representation of the data at the source level + [dip.pow, dip.csd, dip.fourier] = timelock2freq(dip.mom); + end + case 'moving' + if isfreq + % although this is technically possible sofar, it does not make any sense + warning('a moving dipole model in the frequency domain is not supported'); + end + otherwise + error('unsupported cfg.model'); end % switch model %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -705,7 +732,7 @@ end % get the output cfg -cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); +cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); % add the version details of this function call to the configuration try @@ -716,7 +743,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: dipolefitting.m,v 1.53 2009/01/20 13:01:31 sashae Exp $'; +cfg.version.id = '$Id: dipolefitting.m,v 1.57 2009/07/02 15:55:15 roboos Exp $'; % remember the configuration details of the input data try, cfg.previous = data.cfg; end % remember the exact configuration details in the output diff --git a/external/fieldtrip/private/dipolesimulation.m b/external/fieldtrip/private/dipolesimulation.m index bbd2c48..40a34fc 100644 --- a/external/fieldtrip/private/dipolesimulation.m +++ b/external/fieldtrip/private/dipolesimulation.m @@ -59,6 +59,9 @@ % Copyright (C) 2004, Robert Oostenveld % % $Log: dipolesimulation.m,v $ +% Revision 1.24 2009/07/02 15:37:32 roboos +% use fixdipole for consistent dipole structure representation +% % Revision 1.23 2009/03/23 21:19:51 roboos % allow different amplitudes for different dipoles % @@ -109,10 +112,8 @@ if ~isfield(cfg, 'feedback'), cfg.feedback = 'text'; end if ~isfield(cfg, 'channel'), cfg.channel = 'all'; end +cfg.dip = fixdipole(cfg.dip); Ndipoles = size(cfg.dip.pos,1); -if size(cfg.dip.pos,2)~=3 - error('dipole positions should be specified as [x, y, z]'); -end % prepare the volume conductor and the sensor array [vol, sens, cfg] = prepare_headmodel(cfg, []); @@ -126,10 +127,6 @@ end Ntrials = cfg.ntrials; -if all(size(cfg.dip.mom)==[1 3]) - cfg.dip.mom = cfg.dip.mom'; -end - if isfield(cfg.dip, 'frequency') % this should be a column vector cfg.dip.frequency = cfg.dip.frequency(:); @@ -252,7 +249,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: dipolesimulation.m,v 1.23 2009/03/23 21:19:51 roboos Exp $'; +cfg.version.id = '$Id: dipolesimulation.m,v 1.24 2009/07/02 15:37:32 roboos Exp $'; % remember the configuration details of the input data try, cfg.previous = data.cfg; end % remember the exact configuration details in the output diff --git a/external/fieldtrip/private/electroderealign.m b/external/fieldtrip/private/electroderealign.m index dea261c..0c39dfd 100644 --- a/external/fieldtrip/private/electroderealign.m +++ b/external/fieldtrip/private/electroderealign.m @@ -1,4 +1,4 @@ -function [norm] = electroderealign(cfg); +function [norm] = electroderealign(cfg) % ELECTRODEREALIGN rotates and translates electrode positions to % template electrode positions or towards the head surface. It can @@ -48,6 +48,7 @@ % cfg.casesensitive = 'yes' or 'no', determines whether string comparisons % between electrode labels are case sensitive (default = 'yes') % cfg.feedback = 'yes' or 'no' (default = 'no') +% cfg.outline = 'yes' or 'no' to add the outline characteristic landmarks such as sulci (default = 'no') % % The electrode set that will be realigned is specified as % cfg.elecfile = string with filename, or alternatively @@ -76,11 +77,17 @@ % single triangulated boundary, or a Nx3 matrix with surface % points % -% See also READ_FCDC_ELEC, VOLUMEREALIGN +% See also READ_ELEC, VOLUMEREALIGN -% Copyright (C) 2005-2008, Robert Oostenveld +% Copyright (C) 2005-2009, Robert Oostenveld % % $Log: electroderealign.m,v $ +% Revision 1.13 2009/06/30 07:10:11 roboos +% first proper implementation of manual clicking of electrode locations (using scalp mesh) +% +% Revision 1.12 2009/06/04 10:03:46 roboos +% fixed problem in the input og cfg.headshape when it was not required +% % Revision 1.11 2009/05/25 08:05:18 roboos % ensure that cfg.headshape is a sturct and not a config object (in case tracking is on) % @@ -165,11 +172,12 @@ % set the defaults if ~isfield(cfg, 'channel'), cfg.channel = 'all'; end if ~isfield(cfg, 'feedback'), cfg.feedback = 'no'; end +if ~isfield(cfg, 'outline'), cfg.outline = 'no'; end if ~isfield(cfg, 'casesensitive'), cfg.casesensitive = 'yes'; end if ~isfield(cfg, 'headshape'), cfg.headshape = []; end % for triangulated head surface, without labels if ~isfield(cfg, 'template'), cfg.template = []; end % for electrodes or fiducials, always with labels -if isa(cfg.headshape, 'config') +if isfield(cfg, 'headshape') && isa(cfg.headshape, 'config') % convert the nested config-object back into a normal structure cfg.headshape = struct(cfg.headshape); end @@ -224,7 +232,7 @@ end if ~isfield(headshape, 'tri') % generate a closed triangulation from the surface points - headshape.pnt = unique(headshape,pnt, 'rows'); + headshape.pnt = unique(headshape.pnt, 'rows'); headshape.tri = projecttri(headshape.pnt); end end @@ -494,16 +502,23 @@ norm = tmp; clear tmp - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% elseif strcmp(cfg.method, 'position') %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % open a figure + % open a figure fig = figure; rotate3d on - % plot_mesh - % select_point - - keyboard + plot_mesh(headshape, 'edgecolor', 'k') + xyz = select_point3d(headshape, 'multiple', true); + orig.pnt = xyz; + for i=1:size(orig.pnt,1) + orig.label{i,1} = 'unknown'; + end + + if strcmp(cfg.outline, 'yes') + % FIXME also go over the outlines with manual clicking + keyboard + end else error('unknown method'); @@ -511,10 +526,22 @@ % apply the spatial transformation to all electrodes, and replace the % electrode labels by their case-sensitive original values -if any(strcmp(cfg.method, {'rigidbody', 'globalrescale', 'traditional', 'nonlin1', 'nonlin2', 'nonlin3', 'nonlin4', 'nonlin5'})) - norm.pnt = warp_apply(norm.m, orig.pnt, cfg.method); -else - norm.pnt = warp_apply(norm.m, orig.pnt, 'homogenous'); +switch cfg.method + case {'rigidbody', 'globalrescale', 'traditional', 'nonlin1', 'nonlin2', 'nonlin3', 'nonlin4', 'nonlin5'} + norm.pnt = warp_apply(norm.m, orig.pnt, cfg.method); + if isfield(orig, 'outline') + % FIXME also apply the warp to the outlines + end + case {'interactive'} + norm.pnt = warp_apply(norm.m, orig.pnt, 'homogenous'); + if isfield(orig, 'outline') + % FIXME also apply the warp to the outlines + end + case {'position'} + % the positions are already assigned in correspondence with the mesh + norm = orig; + otherwise + error('unknown method'); end if isfield(orig, 'label') @@ -530,7 +557,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: electroderealign.m,v 1.11 2009/05/25 08:05:18 roboos Exp $'; +cfg.version.id = '$Id: electroderealign.m,v 1.13 2009/06/30 07:10:11 roboos Exp $'; % remember the configuration norm.cfg = cfg; diff --git a/external/fieldtrip/private/fetch_data.m b/external/fieldtrip/private/fetch_data.m index e19c77d..1c31527 100644 --- a/external/fieldtrip/private/fetch_data.m +++ b/external/fieldtrip/private/fetch_data.m @@ -11,6 +11,20 @@ % Copyright (C) 2008, Esther Meeuwissen % % $Log: fetch_data.m,v $ +% Revision 1.4 2009/08/05 08:23:25 roboos +% use preallocated integet vectors for all indexing +% skip the indexing of trials that are of no interest -> huge speedup +% +% Revision 1.3 2009/08/04 16:14:05 roboos +% give error for multiple occurence +% return nan for missing samples +% +% Revision 1.2 2009/07/06 09:41:18 jansch +% multiple changes. allowing for selection of rpt in frequency data when input +% data has rpttap. allowing for grandaveraging functionality in the case of +% multiple inputs with the same dimensionalities. this is equivalent to the +% XXXgrandaverage functions with keepindividual = 'yes'. +% % Revision 1.1 2008/11/13 09:55:36 roboos % moved from fieldtrip/private, fileio or from roboos/misc to new location at fieldtrip/public % @@ -62,22 +76,30 @@ % these are for bookkeeping maxsample = max(trl(:,2)); -count = zeros(1, maxsample); -trialnum = NaN(1, maxsample); -samplenum = NaN(1, maxsample); +count = zeros(1, maxsample, 'int32'); +trialnum = zeros(1, maxsample, 'int32'); +samplenum = zeros(1, maxsample, 'int32'); +% determine for each sample in the data where it originates from for trllop=1:trlnum + trlbeg = trl(trllop,1); + trlend = trl(trllop,2); + if trlbeg>endsample || trlend Nan - trialnum(trl(trllop,1):trl(trllop,2)) = trllop; + trialnum(trlbeg:trlend) = trllop; % make samplenum vector with samplenrs for each sample in the old trials - samplenum(trl(trllop,1):trl(trllop,2)) = 1:trllen(trllop); + samplenum(trlbeg:trlend) = 1:trllen(trllop); end % overlap --> NaN -trialnum(count>1) = NaN; -samplenum(count>1) = NaN; +%trialnum(count>1) = NaN; +%samplenum(count>1) = NaN; % make a subselection for the desired samples count = count(begsample:endsample); @@ -86,14 +108,18 @@ % check if all samples are present and are not present twice or more if any(count==0) - error('not all requested samples are present in the data'); + warning('not all requested samples are present in the data, filling with NaNs'); elseif any(count>1) error('some of the requested samples occur twice in the data'); end % construct the output data array -dat = zeros(length(chanindx), length(samplenum)); +dat = nan(length(chanindx), length(samplenum)); for smplop=1:length(samplenum) + if samplenum(smplop)==0 + dat(:, smplop) = nan; + else dat(:, smplop) = data.trial{trialnum(smplop)}(chanindx,samplenum(smplop)); + end end diff --git a/external/fieldtrip/private/filetype.m b/external/fieldtrip/private/filetype.m index b454bfd..25515ab 100644 --- a/external/fieldtrip/private/filetype.m +++ b/external/fieldtrip/private/filetype.m @@ -54,6 +54,9 @@ % Copyright (C) 2003-2007 Robert Oostenveld % % $Log: filetype.m,v $ +% Revision 1.98 2009/07/07 10:38:38 roboos +% added header check for dicom +% % Revision 1.97 2009/05/06 15:42:06 roboos % also remember/check previous directory and don't do filetype caching in case type=unknown % @@ -903,7 +906,7 @@ type = 'layout'; manufacturer = 'Ole Jensen'; content = 'layout of channels for plotting'; -elseif filetype_check_extension(filename, '.dcm') || filetype_check_extension(filename, '.ima') +elseif filetype_check_extension(filename, '.dcm') || filetype_check_extension(filename, '.ima') || filetype_check_header(filename, 'DICM', 128) type = 'dicom'; manufacturer = 'Dicom'; content = 'image data'; diff --git a/external/fieldtrip/private/fixdimord.m b/external/fieldtrip/private/fixdimord.m index 16ee59f..f694d76 100644 --- a/external/fieldtrip/private/fixdimord.m +++ b/external/fieldtrip/private/fixdimord.m @@ -10,8 +10,8 @@ % This will modify the data.dimord field to ensure consistency. % The name of the axis is the same as the name of the dimord, i.e. if % dimord='freq_time', then data.freq and data.time should be present. -% -% The default dimensions in the data are described by +% +% The default dimensions in the data are described by % 'time' % 'freq' % 'chan' @@ -21,9 +21,18 @@ % 'chancmb' % 'rpttap' -% Copyright (C) 2006, Robert Oostenveld, Jan-Mathijs Schoffelen +% Copyright (C) 2009, Robert Oostenveld, Jan-Mathijs Schoffelen % % $Log: fixdimord.m,v $ +% Revision 1.3 2009/08/03 15:53:32 ingnie +% fixed bug introduced in last revision, thanks to Esther +% +% Revision 1.2 2009/08/03 15:07:51 ingnie +% cut off dimord from source and volume data, dimord is not implemented in source and volume data yet, but some functions do add it which causes unexpected behavior +% +% Revision 1.1 2009/07/02 08:04:36 roboos +% moved fixdimord and fixinside from private to public +% % Revision 1.8 2006/04/12 09:11:23 roboos % added ; to the end of a line % @@ -49,10 +58,23 @@ % new implementation % +if strcmp('volume', datatype(data)) || strcmp('source', datatype(data)); + if isfield(data, 'dimord') + % data should not have a dimord (is not implemented yet, but some + % functions add a dimord to these data which leads to unexpected behavior) + warning(sprintf('unexpected dimord "%s", dimord is removed from data', data.dimord)); + data = rmfield(data, 'dimord'); + return + else + %is okay + return + end +end + if ~isfield(data, 'dimord') if ~isfield(data, 'trial') || ~iscell(data.trial) || ... - ~isfield(data, 'time') || ~iscell(data.time) || ... - ~isfield(data, 'label') || ~iscell(data.label) + ~isfield(data, 'time') || ~iscell(data.time) || ... + ~isfield(data, 'label') || ~iscell(data.label) error('The data does not contain a dimord, but it also does not resemble raw data'); elseif isfield(data, 'topo') % the data resembles a component decomposition @@ -68,43 +90,43 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for i=1:length(dimtok) -switch dimtok{i} -case {'tim' 'time' 'toi' 'latency'} - dimtok{i} = 'time'; + switch dimtok{i} + case {'tim' 'time' 'toi' 'latency'} + dimtok{i} = 'time'; -case {'frq' 'freq' 'foi' 'frequency'} - dimtok{i} = 'freq'; + case {'frq' 'freq' 'foi' 'frequency'} + dimtok{i} = 'freq'; -case {'sgn' 'label' 'chan'} - dimtok{i} = 'chan'; + case {'sgn' 'label' 'chan'} + dimtok{i} = 'chan'; -case {'rpt' 'trial'} - dimtok{i} = 'rpt'; + case {'rpt' 'trial'} + dimtok{i} = 'rpt'; -case {'subj' 'subject'} - dimtok{i} = 'subj'; + case {'subj' 'subject'} + dimtok{i} = 'subj'; -case {'comp'} - % don't change, it is ok + case {'comp'} + % don't change, it is ok -case {'sgncmb' 'labelcmb' 'chancmb'} - dimtok{i} = 'chan'; + case {'sgncmb' 'labelcmb' 'chancmb'} + dimtok{i} = 'chan'; -case {'rpttap'} - % this is a 2-D field, coding trials and tapers along the same dimension - % don't change, it is ok + case {'rpttap'} + % this is a 2-D field, coding trials and tapers along the same dimension + % don't change, it is ok -case {'refchan'} - % don't change, it is ok + case {'refchan'} + % don't change, it is ok -case {'vox' 'repl' 'wcond'} - % these are used in some fieldtrip functions, but are not considered standard - warning(sprintf('unexpected dimord "%s"', data.dimord)); + case {'vox' 'repl' 'wcond'} + % these are used in some fieldtrip functions, but are not considered standard + warning(sprintf('unexpected dimord "%s"', data.dimord)); -otherwise - error(sprintf('unexpected dimord "%s"', data.dimord)); + otherwise + error(sprintf('unexpected dimord "%s"', data.dimord)); -end % switch dimtok + end % switch dimtok end % for length dimtok %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -142,7 +164,7 @@ % elseif isfield(data, 'fourierspctrm') % mat = data.fourierspctrm; % end -% +% % add the descriptive axis for each dimension % for i=1:length(dimtok) % if isfield(data, dimtok{i}) diff --git a/external/fieldtrip/private/fixdipole.m b/external/fieldtrip/private/fixdipole.m new file mode 100644 index 0000000..8a9de8c --- /dev/null +++ b/external/fieldtrip/private/fixdipole.m @@ -0,0 +1,39 @@ +function dip = fixdipole(dip) + +% FIXDIPOLE ensures that the dipole position and moment are +% consistently represented throughout FieldTrip functions. + +% Copyright (C) 2009, Robert Oostenveld +% +% $Log: fixdipole.m,v $ +% Revision 1.1 2009/07/02 15:35:55 roboos +% helper function for consistent dipole representation +% + +[m, n] = size(dip.pos); + +if n==3 + % the input representation is Nx3, which is what we want +elseif m==3 + % it is possible to translate it into a Nx3 unambiguously + warning('input dipole positions should be specified as Nx3 matrix'); + dip.pos = dip.pos'; +elseif m==1 + % it is possible to translate it into a Nx3 unambiguously + warning('input dipole positions should be specified as Nx3 matrix'); + dip.pos = reshape(dip.pos, 3, n/3)'; +else + % it is not clear how to convert to a Nx3 matrix + error('input dipole positions should be specified as Nx3 matrix'); +end + +if isfield(dip, 'mom') + ndip = size(dip.pos,1); + if numel(dip.mom)==ndip*3 + ntime = 1; + else + ntime = numel(dip.mom)/(ndip*3); + end + dip.mom = reshape(dip.mom, ndip*3, ntime); +end + diff --git a/external/fieldtrip/private/fixinside.m b/external/fieldtrip/private/fixinside.m index b36f950..8ed9b9f 100644 --- a/external/fieldtrip/private/fixinside.m +++ b/external/fieldtrip/private/fixinside.m @@ -12,6 +12,9 @@ % Copyright (C) 2006, Robert Oostenveld % % $Log: fixinside.m,v $ +% Revision 1.1 2009/07/02 08:04:36 roboos +% moved fixdimord and fixinside from private to public +% % Revision 1.3 2007/07/17 10:34:34 roboos % prevent display of source structure on screen by adding ; % diff --git a/external/fieldtrip/private/freqanalysis.m b/external/fieldtrip/private/freqanalysis.m index 13409ed..40b04f5 100644 --- a/external/fieldtrip/private/freqanalysis.m +++ b/external/fieldtrip/private/freqanalysis.m @@ -40,6 +40,9 @@ % Copyright (C) 2004-2006, F.C. Donders Centre, Markus Siegel % % $Log: freqanalysis.m,v $ +% Revision 1.45 2009/07/30 19:44:43 ingnie +% also allow datatype comp (by chekdata) +% % Revision 1.44 2009/01/20 13:01:31 sashae % changed configtracking such that it is only enabled when BOTH explicitly allowed at start % of the fieldtrip function AND requested by the user @@ -162,7 +165,7 @@ fieldtripdefs % check if the input data is valid for this function -data = checkdata(data, 'datatype', 'raw', 'feedback', 'yes', 'hasoffset', 'yes'); +data = checkdata(data, 'datatype', {'raw', 'comp'}, 'feedback', 'yes', 'hasoffset', 'yes'); % check if the input cfg is valid for this function cfg = checkconfig(cfg, 'trackconfig', 'on'); diff --git a/external/fieldtrip/private/freqdescriptives.m b/external/fieldtrip/private/freqdescriptives.m index 1e4ee5e..78fda98 100644 --- a/external/fieldtrip/private/freqdescriptives.m +++ b/external/fieldtrip/private/freqdescriptives.m @@ -65,6 +65,9 @@ % Copyright (C) 2004-2006, Pascal Fries & Jan-Mathijs Schoffelen, F.C. Donders Centre % % $Log: freqdescriptives.m,v $ +% Revision 1.59 2009/06/12 11:48:22 jansch +% added default for cfg.keepfourier +% % Revision 1.58 2009/04/08 06:05:23 roboos % give warning in case plv and input only one trial (solves the problem of Wendy) % @@ -212,6 +215,7 @@ if ~isfield(cfg, 'foilim'), cfg.foilim = 'all'; end if ~isfield(cfg, 'toilim'), cfg.toilim = 'all'; end if ~isfield(cfg, 'keeptrials'), cfg.keeptrials = 'no'; end +if ~isfield(cfg, 'keepfourier'), cfg.keepfourier = 'no'; end if ~isfield(cfg, 'channelcmb'), if hascsd @@ -432,9 +436,9 @@ % new efficient version sumcrsspctrm(1,1:Ncmb,1:Nfrq,1:Ntim) = nansum(freq.crsspctrm,1); -% does the same as the old inefficient version +%% does the same as the old inefficient version % sumcrsspctrm = complex(zeros(1,Ncmb,Nfrq,Ntim), ... -% zeros(1,Ncmb,Nfrq,Ntim)); +% zeros(1,Ncmb,Nfrq,Ntim)); % for j = 1:Ncmb % sumcrsspctrm(1,j,:,:) = nansum(freq.crsspctrm(:,j,:,:),1); % end @@ -691,7 +695,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: freqdescriptives.m,v 1.58 2009/04/08 06:05:23 roboos Exp $'; +cfg.version.id = '$Id: freqdescriptives.m,v 1.59 2009/06/12 11:48:22 jansch Exp $'; try, cfg.previous = freq.cfg; end % remember the configuration details diff --git a/external/fieldtrip/private/hastoolbox.m b/external/fieldtrip/private/hastoolbox.m index 8289f0d..79640d3 100644 --- a/external/fieldtrip/private/hastoolbox.m +++ b/external/fieldtrip/private/hastoolbox.m @@ -10,6 +10,9 @@ % Copyright (C) 2005-2008, Robert Oostenveld % % $Log: hastoolbox.m,v $ +% Revision 1.36 2009/09/08 14:34:01 roboos +% also detect 64 bit windows version (thanks to arno) +% % Revision 1.35 2009/04/21 09:54:15 roboos % added prtools % @@ -318,7 +321,7 @@ % for windows computers in the F.C. Donders Centre prefix = 'h:\common\matlab'; - if ~status && strcmp(computer, 'PCWIN') + if ~status && (strcmp(computer, 'PCWIN') || strcmp(computer, 'PCWIN64')) status = myaddpath(fullfile(prefix, lower(toolbox)), silent); end diff --git a/external/fieldtrip/private/issubfield.m b/external/fieldtrip/private/issubfield.m index f494642..60d94f4 100644 --- a/external/fieldtrip/private/issubfield.m +++ b/external/fieldtrip/private/issubfield.m @@ -1,4 +1,4 @@ -function [r] = issubfield(s, f); +function [r] = issubfield(s, f) % ISSUBFIELD tests for the presence of a field in a structure just like the standard % Matlab ISFIELD function, except that you can also specify nested fields @@ -17,6 +17,9 @@ % Copyright (C) 2005, Robert Oostenveld % % $Log: issubfield.m,v $ +% Revision 1.2 2009/07/30 20:11:44 ingnie +% made output boolian +% % Revision 1.1 2008/11/13 09:55:36 roboos % moved from fieldtrip/private, fileio or from roboos/misc to new location at fieldtrip/public % @@ -26,7 +29,7 @@ try getsubfield(s, f); % if this works, then the subfield must be present - r = 1; + r = true; catch - r = 0; % apparently the subfield is not present + r = false; % apparently the subfield is not present end \ No newline at end of file diff --git a/external/fieldtrip/private/istrue.m b/external/fieldtrip/private/istrue.m index ce9a92c..2cfd5a9 100644 --- a/external/fieldtrip/private/istrue.m +++ b/external/fieldtrip/private/istrue.m @@ -6,6 +6,9 @@ % Copyright (C) 2009, Robert Oostenveld % % $Log: istrue.m,v $ +% Revision 1.4 2009/06/05 14:26:23 crimic +% added 'none' option +% % Revision 1.3 2009/05/14 11:57:37 crimic % introduced check for 'y' and 'n' % @@ -16,8 +19,8 @@ % extended and moved from plotting to public % -true_list = {'yes' 'true' 'on' 'y'}; -false_list = {'no' 'false' 'off' 'n'}; +true_list = {'yes' 'true' 'on' 'y' }; +false_list = {'no' 'false' 'off' 'n' 'none'}; if ischar(x) % convert string to boolean value diff --git a/external/fieldtrip/private/keyval.m b/external/fieldtrip/private/keyval.m index b9fefe7..106cf02 100644 --- a/external/fieldtrip/private/keyval.m +++ b/external/fieldtrip/private/keyval.m @@ -11,6 +11,12 @@ % Copyright (C) 2005-2007, Robert Oostenveld % % $Log: keyval.m,v $ +% Revision 1.5 2009/08/04 11:58:28 roboos +% perform a case-insensitive string comparison for keys +% +% Revision 1.4 2009/07/14 16:11:02 roboos +% speed up the input checks +% % Revision 1.3 2009/05/14 19:24:02 roboos % removed ; at end of function declaration % @@ -35,22 +41,20 @@ error('optional input arguments should come in key-value pairs, i.e. there should be an even number'); end -for i=1:2:length(varargin) - if ~ischar(varargin{i}) - % the 1st, 3rd, etc. contain the keys, the 2nd, 4th, etc. contain the values - error('optional input arguments should come in key-value pairs, the optional input argument %d is invalid (should be a string)', i); - end -end - +% the 1st, 3rd, etc. contain the keys, the 2nd, 4th, etc. contain the values keys = varargin(1:2:end); vals = varargin(2:2:end); -hit = find(strcmp(key, keys)); +if ~all(cellfun(@ischar, keys)) + error('optional input arguments should come in key-value pairs, the optional input argument %d is invalid (should be a string)', i); +end + +hit = find(strcmpi(key, keys)); if isempty(hit) % the requested key was not found val = []; elseif length(hit)==1 - % the requested key was found + % the requested key was found val = vals{hit}; else error('multiple input arguments with the same name'); diff --git a/external/fieldtrip/private/keyvalcheck.m b/external/fieldtrip/private/keyvalcheck.m index a85af05..b928f19 100644 --- a/external/fieldtrip/private/keyvalcheck.m +++ b/external/fieldtrip/private/keyvalcheck.m @@ -12,10 +12,28 @@ function keyvalcheck(arglist, varargin) % Copyright (C) 2009, Robert Oostenveld % % $Log: keyvalcheck.m,v $ +% Revision 1.4 2009/08/05 08:50:50 roboos +% allow different case in the option names, i.e. use case-insensitive comparisons +% +% Revision 1.3 2009/07/14 16:10:34 roboos +% added caching for previous input +% +% Revision 1.2 2009/06/15 14:23:26 roboos +% only check an option if specified (i.e. non-empty) +% % Revision 1.1 2009/04/14 19:37:44 roboos % new helper function, used in plot_xxx functions % +% this is to speed up subsequent calls with the same input arguments +persistent previous_argin + +current_argin = {arglist, varargin{:}}; +if ~isempty(previous_argin) && isequal(previous_argin, current_argin) + % the input is the same to the previous input, and that was OK + return +end + required = keyval('required', varargin); forbidden = keyval('forbidden', varargin); optional = keyval('optional', varargin); @@ -27,17 +45,34 @@ function keyvalcheck(arglist, varargin) error('optional input arguments should come in key-value pairs, i.e. there should be an even number'); end -set = intersect(keys, required); -if numel(set)~=numel(required) - error('the required input argument ''%s'' was not specified', set{:}); +keys = cellfun(@lower, keys, 'UniformOutput', false); + +if ~isempty(required) + % only check if specified + required = cellfun(@lower, required, 'UniformOutput', false); + set = intersect(keys, required); + if numel(set)~=numel(required) + error('the required input argument ''%s'' was not specified', set{:}); + end end -set = intersect(keys, forbidden); -if numel(set)~=0 - error('the input argument ''%s'' is forbidden', set{:}); +if ~isempty(forbidden) + % only check if specified + forbidden = cellfun(@lower, forbidden, 'UniformOutput', false); + set = intersect(keys, forbidden); + if numel(set)~=0 + error('the input argument ''%s'' is forbidden', set{:}); + end end -set = setdiff(keys, optional); -if numel(set)>0 - error('the input argument ''%s'' is forbidden', set{:}); +if ~isempty(optional) + % only check if specified + optional = cellfun(@lower, optional, 'UniformOutput', false); + set = setdiff(keys, optional); + if numel(set)>0 + error('the input argument ''%s'' is forbidden', set{:}); + end end + +% remember the current input arguments, which appear to be OK +previous_argin = current_argin; diff --git a/external/fieldtrip/private/layoutplot.m b/external/fieldtrip/private/layoutplot.m index da0c81a..4937eee 100644 --- a/external/fieldtrip/private/layoutplot.m +++ b/external/fieldtrip/private/layoutplot.m @@ -43,9 +43,15 @@ function layoutplot(cfg, data) % % See also prepare_layout, topoplotER, topoplotTFR, multiplotER, multiplotTFR +% Undocumented options +% cfg.montage + % Copyright (C) 2006-2008, Robert Oostenveld % % $Log: layoutplot.m,v $ +% Revision 1.11 2009/09/10 12:30:00 roboos +% added option for plotting an arrow for each of the bipolar electrode pairs, requires a montage and a layout +% % Revision 1.10 2009/05/12 12:35:15 roboos % moved plotting to seperate function (plot_lay) where it can be reused % @@ -137,3 +143,34 @@ function layoutplot(cfg, data) plot_lay(lay, 'point', true, 'box', true, 'label', true, 'mask', true, 'outline', true); +% the following code can be used to verify a bipolar montage, given the +% layout of the monopolar channels +if isfield(cfg, 'montage') && ~isempty(cfg.montage) + fprintf('plotting an arrow for each of the bipolar electrode pairs\n'); + % the arrow begins at the +1 electrode + % the arrow ends at the -1 electrode + for i=1:length(cfg.montage.labelnew) + begindx = find(cfg.montage.tra(i,:)==+1); + endindx = find(cfg.montage.tra(i,:)==-1); + if ~numel(begindx)==1 || ~numel(endindx)==1 + % the re-referenced channel does not seem to be a bipolar pair + continue + end + % find the position of the begin and end of the arrow + beglab = cfg.montage.labelorg{begindx}; + endlab = cfg.montage.labelorg{endindx}; + begindx = find(strcmp(lay.label, beglab)); % the index in the layout + endindx = find(strcmp(lay.label, endlab)); % the index in the layout + if ~numel(begindx)==1 || ~numel(endindx)==1 + % one of the channels in the bipolar pair does not seem to be in the layout + continue + end + + begpos = lay.pos(begindx,:); + endpos = lay.pos(endindx,:); + arrow(begpos, endpos, 'Length', 5) + + end % for all re-referenced channels +end % if montage + + diff --git a/external/fieldtrip/private/meg_leadfield1.m b/external/fieldtrip/private/meg_leadfield1.m index 447fcb0..c3ef0af 100644 --- a/external/fieldtrip/private/meg_leadfield1.m +++ b/external/fieldtrip/private/meg_leadfield1.m @@ -5,7 +5,7 @@ % [lf] = meg_leadfield1(R, pos, ori) % % with input arguments -% R position dipole +% R position dipole % pos position magnetometers % ori orientation magnetometers % @@ -21,6 +21,9 @@ % Copyright (C) 2002-2008, Robert Oostenveld % % $Log: meg_leadfield1.m,v $ +% Revision 1.3 2009/06/17 13:38:06 roboos +% cleaned up documentation +% % Revision 1.2 2009/03/12 11:05:04 roboos % implemented auto-compilation of the mex file in case it is missing % diff --git a/external/fieldtrip/private/megplanar.m b/external/fieldtrip/private/megplanar.m index 92e8948..e7cd281 100644 --- a/external/fieldtrip/private/megplanar.m +++ b/external/fieldtrip/private/megplanar.m @@ -2,7 +2,9 @@ % MEGPLANAR computes planar MEG gradients gradients for raw data % obtained from PREPROCESSING or an average ERF that was computed using -% TIMELOCKANALYSIS. +% TIMELOCKANALYSIS. It can also work on data in the frequency domain, +% obtained with FREQANALYSIS. Prerequisite for this is that the data contain +% complex-valued fourierspectra. % % Use as % [interp] = megplanar(cfg, data) @@ -21,7 +23,8 @@ % large number of dipoles that are placed in the upper layer of the brain % surface, followed by a forward computation towards a planar gradiometer % array. This requires the specification of a volume conduction model of -% the head and of a source model. +% the head and of a source model. The 'sourceproject' method is not supported for +% frequency domain data. % % A head model must be specified with % cfg.hdmfile = string, file containing the volume conduction model @@ -59,6 +62,25 @@ % Copyright (C) 2004, Robert Oostenveld % % $Log: megplanar.m,v $ +% Revision 1.45 2009/07/19 13:19:42 jansch +% fixed re-conversion into timelock datatype when input is timelock datatype +% +% Revision 1.44 2009/07/17 08:05:12 jansch +% fixed small bug +% +% Revision 1.43 2009/07/16 15:07:26 jansch +% updated documentation +% +% Revision 1.42 2009/07/16 15:05:15 jansch +% added possibility to perform planar transformation on frequency domain data. +% the frequency data of course should contain fourierspectra +% +% Revision 1.41 2009/07/14 12:35:36 roboos +% ensure that the input data is meg with a grad field +% +% Revision 1.40 2009/06/04 10:03:46 roboos +% fixed problem in the input og cfg.headshape when it was not required +% % Revision 1.39 2009/05/25 08:05:18 roboos % ensure that cfg.headshape is a sturct and not a config object (in case tracking is on) % @@ -198,8 +220,16 @@ cfg = checkconfig(cfg, 'trackconfig', 'on'); +isfreq = datatype(data, 'freq'); +israw = datatype(data, 'raw'); +istlck = datatype(data, 'timelock'); + % check if the input data is valid for this function -data = checkdata(data, 'datatype', 'raw', 'feedback', 'yes', 'senstype', {'ctf151', 'ctf275', 'bti148', 'bti248'}); +data = checkdata(data, 'datatype', {'raw' 'freq'}, 'feedback', 'yes', 'ismeg', 'yes', 'senstype', {'ctf151', 'ctf275', 'bti148', 'bti248'}); + +if isfreq, + if ~isfield(data, 'fourierspctrm'), error('freq data should containt fourier spectra'); end +end % set the default configuration if ~isfield(cfg, 'channel'), cfg.channel = 'MEG'; end @@ -213,7 +243,7 @@ if ~isfield(cfg, 'spheremesh'), cfg.spheremesh = 642; end end -if isa(cfg.headshape, 'config') +if isfield(cfg, 'headshape') && isa(cfg.headshape, 'config') % convert the nested config-object back into a normal structure cfg.headshape = struct(cfg.headshape); end @@ -223,7 +253,7 @@ cfg = checkconfig(cfg, 'renamedvalue', {'headshape', 'headmodel', []}); % select trials of interest -if ~strcmp(cfg.trials, 'all') +if ~strcmp(cfg.trials, 'all') && israw if islogical(cfg.trials), cfg.trials=find(cfg.trials); end fprintf('selecting %d trials\n', length(cfg.trials)); data.trial = data.trial(cfg.trials); @@ -241,9 +271,21 @@ cfg.trlold=trl; cfg.trl=trl(cfg.trials,:); end +elseif ~strcmp(cfg.trials, 'all') && isfreq + warning('subselection of trials is only supported for raw data as input'); end -Ntrials = length(data.trial); +if israw || istlck, + Ntrials = length(data.trial); +elseif isfreq, + Ntrials = length(data.cumtapcnt); + Nfreq = length(data.freq); + if isfield(data, 'time') + Ntime = length(data.time); + else + Ntime = 1; + end +end Nchan = length(data.label); % find the corresponding channels in the data and the gradiometer array @@ -441,7 +483,7 @@ gradC(chan,[chan neighbindx]) = A(3,:); end -elseif strcmp(cfg.planarmethod, 'sourceproject') +elseif strcmp(cfg.planarmethod, 'sourceproject') && israw %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Do an inverse computation with a simplified distributed source model % and compute forward again with the axial gradiometer array replaced by @@ -512,6 +554,8 @@ end end +elseif strcmp(cfg.planarmethod, 'sourceproject') && isfreq + error('the method ''sourceproject'' is not supported for frequency data as input'); else error('unknown method for computation of planar gradient'); end % cfg.planarmethod @@ -566,11 +610,6 @@ % combine the different sections in one linear transformation matrix transform = [transformH; transformV; transformC; transformO]; - % compute the planar gradient by multiplying the gradient-matrices with the data - for trial=1:Ntrials - interp.trial{trial} = transform * data.trial{trial}; - end - % combine the new labels into a single cell-array interp.label = [labelH(:); labelV(:); labelC(:); labelO(:)]; @@ -586,11 +625,41 @@ % remember the planar gradiometer definition interp.grad = planar.grad; -end % nearest neighbours methods + if israw || istlck, + % compute the planar gradient by multiplying the gradient-matrices with the data + for trial=1:Ntrials + interp.trial{trial} = transform * data.trial{trial}; + end + + % these should be remembered from the original data + interp.fsample = data.fsample; + interp.time = data.time; + + if istlck + % convert back into timelock structure + interp = checkdata(interp, 'datatype', 'timelock'); + end + + elseif isfreq + % compute the planar gradient by multiplying the gradient-matrices with the data + siz = size(data.fourierspctrm); + planardat = zeros(siz(1), size(transform,1), Nfreq, Ntime); + for foilop=1:Nfreq + for timlop = 1:Ntime + planardat(:,:,foilop,timlop) = data.fourierspctrm(:,:,foilop,timlop) * transform'; + end + end + interp.fourierspctrm = planardat; + + if isfield(data, 'time'), interp.time = data.time; end + if isfield(data, 'cumtapcnt'), interp.cumtapcnt = data.cumtapcnt; end + if isfield(data, 'cumsumcnt'), interp.cumsumcnt = data.cumsumcnt; end + interp.freq = data.freq; + interp.dimord = data.dimord; + interp.transform = transform; + end -% these should be remembered from the original data -interp.fsample = data.fsample; -interp.time = data.time; +end % nearest neighbours methods % get the output cfg cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); @@ -604,7 +673,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: megplanar.m,v 1.39 2009/05/25 08:05:18 roboos Exp $'; +cfg.version.id = '$Id: megplanar.m,v 1.45 2009/07/19 13:19:42 jansch Exp $'; % remember the configuration details of the input data try, cfg.previous = data.cfg; end % remember the exact configuration details in the output diff --git a/external/fieldtrip/private/multiplotCC.m b/external/fieldtrip/private/multiplotCC.m new file mode 100644 index 0000000..08299c0 --- /dev/null +++ b/external/fieldtrip/private/multiplotCC.m @@ -0,0 +1,133 @@ +function multiplotCC(cfg, data) + +% MULTIPLOTCC visualiuzes the coherence between channels by using multiple +% topoplots. The topoplot at a given channel location shows the coherence +% of that channel with all other channels. +% +% Use as +% multiplotCC(cfg, data) + +% Undocumented local options: +% cfg.layout = layout filename or a structure produced by prepare_layout +% cfg.xlim +% cfg.xparam +% cfg.zparam +% This function requires input from FREQSTATISTICS_SHIFTPREDICT +% This function should be rewritten, using the clean topoplot implementation + +% Copyright (C) 2005-2006, Jan-Mathijs Schoffelen, Robert Oostenveld +% +% $Log: multiplotCC.m,v $ +% Revision 1.12 2009/06/17 13:44:52 roboos +% cleaned up help +% +% Revision 1.11 2008/09/22 20:17:43 roboos +% added call to fieldtripdefs to the begin of the function +% +% Revision 1.10 2008/09/22 12:53:27 roboos +% ensure equal and tight axes +% +% Revision 1.9 2007/04/03 15:37:07 roboos +% renamed the checkinput function to checkdata +% +% Revision 1.8 2007/03/27 11:05:19 ingnie +% changed call to fixdimord in call to checkinput +% +% Revision 1.7 2007/03/21 16:24:13 chrhes +% updated documentation regarding the fact that cfg.layout can also contain a +% layout structure obtained using the function prepare_layout.m +% +% Revision 1.6 2006/04/20 09:58:34 roboos +% updated documentation +% +% Revision 1.5 2006/04/10 12:21:14 roboos +% improved documentation +% +% Revision 1.4 2006/03/14 08:09:22 roboos +% added copyrigth and cvs log statement +% + +fieldtripdefs + +if ~isfield(cfg, 'layout'), cfg.layout = 'CTF151s.lay'; end; +if ~isfield(cfg, 'xparam'), cfg.xparam = 'foi'; end; +if ~isfield(cfg, 'xlim'), cfg.xlim = 'all'; end; +if ~isfield(cfg, 'zparam'), cfg.zparam = 'avg.icohspctrm'; end; + +% for backward compatibility with old data structures +data = checkdata(data); + +if strcmp(cfg.zparam, 'avg.icohspctrm') && ~issubfield(data, 'avg.icohspctrm'), + data.avg.icohspctrm = abs(imag(data.avg.cohspctrm)); +end + +if strcmp(data.dimord, 'refchan_chan_freq'), + %reshape input-data, such that topoplotER will take it + cnt = 1; + siz = size(data.prob); + data.labelcmb = cell(siz(1)*siz(2),2); + data.prob = reshape(data.prob, [siz(1)*siz(2) siz(3)]); + data.stat = reshape(data.stat, [siz(1)*siz(2) siz(3)]); + for j = 1:length(data.label) + for k = 1:length(data.reflabel) + data.labelcmb(cnt,:) = [data.reflabel(k) data.label(j)]; + cnt = cnt + 1; + end + end + tmpdata = data; +else + dat = getsubfield(data, cfg.zparam); + scale = [0 max(dat(:))-0.2]; +end + +if isfield(cfg, 'xparam'), + xparam = getsubfield(data, cfg.xparam); + if ~strcmp(cfg.xlim, 'all'), + fbin = [nearest(xparam, cfg.xlim(1)) nearest(xparam, cfg.xlim(2))]; + else + fbin = [xparam(1) xparam(end)]; + end +end + +[chNum,X,Y,Width,Height,Lbl] = textread(cfg.layout,'%f %f %f %f %f %s'); + +xScaleFac = 1/(max(Width)+ max(X) - min(X)); +yScaleFac = 1/(max(Height)+ max(Y) - min(Y)); + + +Xpos = xScaleFac*(X-min(X)); +Ypos = 0.9*yScaleFac*(Y-min(Y)); + +for k=1:length(chNum) - 2 + subplotOL('position',[Xpos(k) Ypos(k)+(Height(k)*yScaleFac) Width(k)*xScaleFac*2 Height(k)*yScaleFac*2]) + config.layout = cfg.layout; + if exist('tmpdata'), + + config.style = 'straight'; + config.electrodes = 'off'; + config.hlinewidth = 0.5; + try, config.refmarker = strmatch(Lbl(k), data.reflabel); + catch, config.refmarker = strmatch(Lbl(k), data.label); end + config.maplimits = [0 0.5]; + config.ecolor = [1 1 1]; + config.interplimits = 'electrodes'; + if isfield(cfg, 'xparam'), + config.xparam = cfg.xparam; + config.xlim = xparam; + else + config.xparam = 'time'; + config.xlim = [k-0.5 k+0.5]; + end + config.zparam = cfg.zparam; + config.cohrefchannel = Lbl(k); + config.showxlim = 'no'; + config.showzlim = 'no'; + config.colorbar = 'no'; + config.zlim = scale; + config.grid_scale = 30; + topoplotER(config, data); + drawnow; + end +end + + diff --git a/external/fieldtrip/private/multiplotER.m b/external/fieldtrip/private/multiplotER.m index 5ccb9d9..44e6dd3 100644 --- a/external/fieldtrip/private/multiplotER.m +++ b/external/fieldtrip/private/multiplotER.m @@ -21,7 +21,7 @@ % cfg.maskparameter = field in the first dataset to be used for marking significant data % cfg.xlim = 'maxmin' or [xmin xmax] (default = 'maxmin') % cfg.ylim = 'maxmin' or [ymin ymax] (default = 'maxmin') -% cfg.cohrefchannel = Name of reference-channel, only for visualizing coherence +% cfg.cohrefchannel = name of reference channel for visualising coherence, can be 'gui' % cfg.baseline = 'yes','no' or [time1 time2] (default = 'no'), see TIMELOCKBASELINE or FREQBASELINE % cfg.baselinetype = 'absolute' or 'relative' (default = 'absolute') % cfg.trials = 'all' or a selection given as a 1xN vector (default = 'all') @@ -77,6 +77,15 @@ % Copyright (C) 2003-2006, Ole Jensen % % $Log: multiplotER.m,v $ +% Revision 1.51 2009/07/14 13:21:09 roboos +% changed the interactive plotting: instead of using plotSelection it now uses the selection function from the new plotting module (select_range and select_channel) and uses a local subfunction to update the cfg and call the next figure +% +% Revision 1.50 2009/06/17 13:44:52 roboos +% cleaned up help +% +% Revision 1.49 2009/06/17 13:35:53 roboos +% some minor changes to comments +% % Revision 1.48 2009/05/12 18:13:12 roboos % added handling of cfg.cohrefchannel='gui' for manual/interactive selection % @@ -325,16 +334,15 @@ cfg.yparam = cfg.zparam; end -% Old style coherence plotting with cohtargetchannel is no longer supported: +% Old style coherence plotting with cohtargetchannel is no longer supported cfg = checkconfig(cfg, 'unused', {'cohtargetchannel'}); -% Read or create the layout that will be used for plotting: +% Read or create the layout that will be used for plotting lay = prepare_layout(cfg, varargin{1}); -% Remember the layout to speed up subsequent plot calls cfg.layout = lay; for k=1:length(varargin) - % Check for unconverted coherence spectrum data: + % Check for unconverted coherence spectrum data if (strcmp(cfg.zparam,'cohspctrm')) && (isfield(varargin{k}, 'labelcmb')) % A reference channel is required: if ~isfield(cfg,'cohrefchannel'), @@ -346,15 +354,16 @@ h = clf; plot_lay(lay, 'box', false); title('Select the reference channel by clicking on it...'); - info = []; + % add the channel information to the figure + info = guidata(h); info.x = lay.pos(:,1); info.y = lay.pos(:,2); info.label = lay.label; guidata(h, info); - set(gcf, 'WindowButtonUpFcn', {@select_channel, 'callback', {@select_cohrefchannel, cfg, varargin{:}}}); + set(gcf, 'WindowButtonUpFcn', {@select_channel, 'callback', {@select_multiplotER, cfg, varargin{:}}}); return end - + % Convert 2-dimensional channel matrix to a single dimension: sel1 = strmatch(cfg.cohrefchannel, varargin{k}.labelcmb(:,2)); sel2 = strmatch(cfg.cohrefchannel, varargin{k}.labelcmb(:,1)); @@ -500,37 +509,17 @@ % Make the figure interactive: if strcmp(cfg.interactive, 'yes') - userData.hFigure = gcf; - userData.hAxes = gca; - for i=1:10 - userData.hSelection{i} = plot(mean(X), mean(Y)); - set(userData.hSelection{i}, 'XData', [mean(X)]); - set(userData.hSelection{i}, 'YData', [mean(Y)]); - set(userData.hSelection{i}, 'Color', [0 0 0]); - set(userData.hSelection{i}, 'EraseMode', 'xor'); - set(userData.hSelection{i}, 'LineStyle', '--'); - set(userData.hSelection{i}, 'LineWidth', 1.5); - set(userData.hSelection{i}, 'Visible', 'on'); - userData.range{i} = []; - end - userData.iSelection = 0; - userData.plotType = 'multiplot'; - userData.selecting = 0; - userData.selectionType = ''; - userData.selectAxes = 'z'; - userData.lastClick = []; - userData.cfg = cfg; - userData.data = varargin; - userData.chanX = chanX; - userData.chanY = chanY; - userData.chanLabels = chanLabels; - tag = sprintf('%.5f', 10000 * rand(1)); - set(gcf, 'Renderer', cfg.renderer); - set(gcf, 'Tag', tag); - set(gcf, 'UserData', userData); - set(gcf, 'WindowButtonMotionFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 0);']); - set(gcf, 'WindowButtonDownFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 1);']); - set(gcf, 'WindowButtonUpFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 2);']); + + % add the channel information to the figure + info = guidata(gcf); + info.x = lay.pos(:,1); + info.y = lay.pos(:,2); + info.label = lay.label; + guidata(gcf, info); + + set(gcf, 'WindowButtonUpFcn', {@select_channel, 'multiple', true, 'callback', {@select_singleplotER, cfg, varargin{:}}, 'event', 'WindowButtonUpFcn'}); + set(gcf, 'WindowButtonDownFcn', {@select_channel, 'multiple', true, 'callback', {@select_singleplotER, cfg, varargin{:}}, 'event', 'WindowButtonDownFcn'}); + set(gcf, 'WindowButtonMotionFcn', {@select_channel, 'multiple', true, 'callback', {@select_singleplotER, cfg, varargin{:}}, 'event', 'WindowButtonMotionFcn'}); end axis tight @@ -656,12 +645,31 @@ function plotWnd(x,y,xidc,xlim,ylim,xpos,ypos,width,height,label,cfg,style,mask) end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -% this function is called by select_channel +% SUBFUNCTION which is called after selecting channels in case of cfg.cohrefchannel='gui' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function select_cohrefchannel(label, cfg, varargin) -fprintf('selected "%s" as reference channel\n', label); +function select_multiplotER(label, cfg, varargin) cfg.cohrefchannel = label; -figure +fprintf('selected cfg.cohrefchannel = ''%s''\n', cfg.cohrefchannel); +p = get(gcf, 'Position'); +f = figure; +set(f, 'Position', p); multiplotER(cfg, varargin{:}); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION which is called after selecting channels in case of cfg.interactive='yes' +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function select_singleplotER(label, cfg, varargin) +if ~isempty(label) + cfg.xlim = 'maxmin'; + cfg.channel = label; + fprintf('selected cfg.channel = {'); + for i=1:(length(cfg.channel)-1) + fprintf('''%s'', ', cfg.channel{i}); + end + fprintf('''%s''}\n', cfg.channel{end}); + p = get(gcf, 'Position'); + f = figure; + set(f, 'Position', p); + singleplotER(cfg, varargin{:}); +end + diff --git a/external/fieldtrip/private/multiplotTFR.m b/external/fieldtrip/private/multiplotTFR.m index 54eb95e..431f2a2 100644 --- a/external/fieldtrip/private/multiplotTFR.m +++ b/external/fieldtrip/private/multiplotTFR.m @@ -21,7 +21,7 @@ % cfg.xlim = 'maxmin' or [xmin xmax] (default = 'maxmin') % cfg.ylim = 'maxmin' or [ymin ymax] (default = 'maxmin') % cfg.zlim = 'maxmin','absmax' or [zmin zmax] (default = 'maxmin') -% cfg.cohrefchannel = Name of reference-channel, only for visualizing coherence +% cfg.cohrefchannel = name of reference channel for visualising coherence, can be 'gui' % cfg.baseline = 'yes','no' or [time1 time2] (default = 'no'), see FREQBASELINE % cfg.baselinetype = 'absolute' or 'relative' (default = 'absolute') % cfg.trials = 'all' or a selection given as a 1xN vector (default = 'all') @@ -39,7 +39,7 @@ % interactive plot when a selected area is clicked. Multiple areas % can be selected by holding down the SHIFT key. % cfg.masknans = 'yes' or 'no' (default = 'yes') -% +% cfg.renderer = 'painters', 'zbuffer',' opengl' or 'none' (default = []) % cfg.layout = specify the channel layout for plotting using one of % the following ways: % @@ -73,6 +73,15 @@ % Copyright (C) 2003-2006, Ole Jensen % % $Log: multiplotTFR.m,v $ +% Revision 1.51 2009/07/14 13:52:16 roboos +% changed the interactive plotting: instead of using plotSelection it now uses the selection function from the new plotting module (select_range and select_channel) and uses a local subfunction to update the cfg and call the next figure +% +% Revision 1.50 2009/07/14 13:27:25 roboos +% consistent handling of cfg.renderer, default is to let matlab decide +% +% Revision 1.49 2009/06/17 13:44:52 roboos +% cleaned up help +% % Revision 1.48 2009/05/12 18:48:15 roboos % added handling of cfg.cohrefchannel='gui' for manual/interactive selection % changed default for cfg.renderer -> let matlab decide @@ -308,7 +317,7 @@ info.y = lay.pos(:,2); info.label = lay.label; guidata(h, info); - set(gcf, 'WindowButtonUpFcn', {@select_channel, 'callback', {@select_cohrefchannel, cfg, data}}); + set(gcf, 'WindowButtonUpFcn', {@select_channel, 'callback', {@select_multiplotTFR, cfg, data}}); return end @@ -494,45 +503,22 @@ end; % plot colorbar: -if isfield(cfg, 'colorbar') & (strcmp(cfg.colorbar, 'yes')) +if isfield(cfg, 'colorbar') && (strcmp(cfg.colorbar, 'yes')) colorbar; end % Make the figure interactive: if strcmp(cfg.interactive, 'yes') - userData.hFigure = gcf; - userData.hAxes = gca; - for i=1:10 - userData.hSelection{i} = plot(mean(chanX), mean(chanY)); - set(userData.hSelection{i}, 'XData', [mean(chanX)]); - set(userData.hSelection{i}, 'YData', [mean(chanY)]); - set(userData.hSelection{i}, 'Color', [0 0 0]); - set(userData.hSelection{i}, 'EraseMode', 'xor'); - set(userData.hSelection{i}, 'LineStyle', '--'); - set(userData.hSelection{i}, 'LineWidth', 1.5); - set(userData.hSelection{i}, 'Visible', 'on'); - userData.range{i} = []; - end - userData.iSelection = 0; - userData.plotType = 'multiplot'; - userData.selecting = 0; - userData.selectionType = ''; - userData.selectAxes = 'z'; - userData.lastClick = []; - userData.cfg = cfg; - userData.data = data; - userData.chanX = chanX; - userData.chanY = chanY; - userData.chanLabels = chanLabels; - tag = sprintf('%.5f', 10000 * rand(1)); - if ~isempty(cfg.renderer) - set(gcf, 'Renderer', cfg.renderer); - end - set(gcf, 'Tag', tag); - set(gcf, 'UserData', userData); - set(gcf, 'WindowButtonMotionFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 0);']); - set(gcf, 'WindowButtonDownFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 1);']); - set(gcf, 'WindowButtonUpFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 2);']); + % add the channel information to the figure + info = guidata(gcf); + info.x = lay.pos(:,1); + info.y = lay.pos(:,2); + info.label = lay.label; + guidata(gcf, info); + + set(gcf, 'WindowButtonUpFcn', {@select_channel, 'multiple', true, 'callback', {@select_singleplotTFR, cfg, data}, 'event', 'WindowButtonUpFcn'}); + set(gcf, 'WindowButtonDownFcn', {@select_channel, 'multiple', true, 'callback', {@select_singleplotTFR, cfg, data}, 'event', 'WindowButtonDownFcn'}); + set(gcf, 'WindowButtonMotionFcn', {@select_channel, 'multiple', true, 'callback', {@select_singleplotTFR, cfg, data}, 'event', 'WindowButtonMotionFcn'}); end axis tight @@ -559,12 +545,32 @@ end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -% this function is called by select_channel +% SUBFUNCTION which is called by select_channel in case cfg.cohrefchannel='gui' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function select_cohrefchannel(label, cfg, varargin) -fprintf('selected "%s" as reference channel\n', label); +function select_multiplotTFR(label, cfg, varargin) cfg.cohrefchannel = label; -figure +fprintf('selected cfg.cohrefchannel = ''%s''\n', cfg.cohrefchannel); +p = get(gcf, 'Position'); +f = figure; +set(f, 'Position', p); multiplotTFR(cfg, varargin{:}); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION which is called after selecting channels in case of cfg.interactive='yes' +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function select_singleplotTFR(label, cfg, varargin) +if ~isempty(label) + cfg.xlim = 'maxmin'; + cfg.ylim = 'maxmin'; + cfg.channel = label; + fprintf('selected cfg.channel = {'); + for i=1:(length(cfg.channel)-1) + fprintf('''%s'', ', cfg.channel{i}); + end + fprintf('''%s''}\n', cfg.channel{end}); + p = get(gcf, 'Position'); + f = figure; + set(f, 'Position', p); + singleplotTFR(cfg, varargin{:}); +end + diff --git a/external/fieldtrip/private/planarchannelset.m b/external/fieldtrip/private/planarchannelset.m index 2a8f489..0131f0a 100644 --- a/external/fieldtrip/private/planarchannelset.m +++ b/external/fieldtrip/private/planarchannelset.m @@ -12,6 +12,12 @@ % Copyright (C) 2005-2008, Robert Oostenveld % % $Log: planarchannelset.m,v $ +% Revision 1.10 2009/07/29 06:47:55 roboos +% resolved conflict related to simultaneous changes to the neuromag306alt handling +% +% Revision 1.9 2009/07/21 11:55:56 roboos +% added support for the alternative neuromag306 channel names, changed channel order for neuromag306 +% % Revision 1.8 2009/03/23 21:12:20 jansch % added support for bti148_planar % @@ -29,626 +35,742 @@ % switch lower(senstype(data)) -case 'ctf151_planar' -planar = { - 'MLC11_dH' 'MLC11_dV' 'MLC11' - 'MLC12_dH' 'MLC12_dV' 'MLC12' - 'MLC13_dH' 'MLC13_dV' 'MLC13' - 'MLC14_dH' 'MLC14_dV' 'MLC14' - 'MLC15_dH' 'MLC15_dV' 'MLC15' - 'MLC21_dH' 'MLC21_dV' 'MLC21' - 'MLC22_dH' 'MLC22_dV' 'MLC22' - 'MLC23_dH' 'MLC23_dV' 'MLC23' - 'MLC24_dH' 'MLC24_dV' 'MLC24' - 'MLC31_dH' 'MLC31_dV' 'MLC31' - 'MLC32_dH' 'MLC32_dV' 'MLC32' - 'MLC33_dH' 'MLC33_dV' 'MLC33' - 'MLC41_dH' 'MLC41_dV' 'MLC41' - 'MLC42_dH' 'MLC42_dV' 'MLC42' - 'MLC43_dH' 'MLC43_dV' 'MLC43' - 'MLF11_dH' 'MLF11_dV' 'MLF11' - 'MLF12_dH' 'MLF12_dV' 'MLF12' - 'MLF21_dH' 'MLF21_dV' 'MLF21' - 'MLF22_dH' 'MLF22_dV' 'MLF22' - 'MLF23_dH' 'MLF23_dV' 'MLF23' - 'MLF31_dH' 'MLF31_dV' 'MLF31' - 'MLF32_dH' 'MLF32_dV' 'MLF32' - 'MLF33_dH' 'MLF33_dV' 'MLF33' - 'MLF34_dH' 'MLF34_dV' 'MLF34' - 'MLF41_dH' 'MLF41_dV' 'MLF41' - 'MLF42_dH' 'MLF42_dV' 'MLF42' - 'MLF43_dH' 'MLF43_dV' 'MLF43' - 'MLF44_dH' 'MLF44_dV' 'MLF44' - 'MLF45_dH' 'MLF45_dV' 'MLF45' - 'MLF51_dH' 'MLF51_dV' 'MLF51' - 'MLF52_dH' 'MLF52_dV' 'MLF52' - 'MLO11_dH' 'MLO11_dV' 'MLO11' - 'MLO12_dH' 'MLO12_dV' 'MLO12' - 'MLO21_dH' 'MLO21_dV' 'MLO21' - 'MLO22_dH' 'MLO22_dV' 'MLO22' - 'MLO31_dH' 'MLO31_dV' 'MLO31' - 'MLO32_dH' 'MLO32_dV' 'MLO32' - 'MLO33_dH' 'MLO33_dV' 'MLO33' - 'MLO41_dH' 'MLO41_dV' 'MLO41' - 'MLO42_dH' 'MLO42_dV' 'MLO42' - 'MLO43_dH' 'MLO43_dV' 'MLO43' - 'MLP11_dH' 'MLP11_dV' 'MLP11' - 'MLP12_dH' 'MLP12_dV' 'MLP12' - 'MLP13_dH' 'MLP13_dV' 'MLP13' - 'MLP21_dH' 'MLP21_dV' 'MLP21' - 'MLP22_dH' 'MLP22_dV' 'MLP22' - 'MLP31_dH' 'MLP31_dV' 'MLP31' - 'MLP32_dH' 'MLP32_dV' 'MLP32' - 'MLP33_dH' 'MLP33_dV' 'MLP33' - 'MLP34_dH' 'MLP34_dV' 'MLP34' - 'MLT11_dH' 'MLT11_dV' 'MLT11' - 'MLT12_dH' 'MLT12_dV' 'MLT12' - 'MLT13_dH' 'MLT13_dV' 'MLT13' - 'MLT14_dH' 'MLT14_dV' 'MLT14' - 'MLT15_dH' 'MLT15_dV' 'MLT15' - 'MLT16_dH' 'MLT16_dV' 'MLT16' - 'MLT21_dH' 'MLT21_dV' 'MLT21' - 'MLT22_dH' 'MLT22_dV' 'MLT22' - 'MLT23_dH' 'MLT23_dV' 'MLT23' - 'MLT24_dH' 'MLT24_dV' 'MLT24' - 'MLT25_dH' 'MLT25_dV' 'MLT25' - 'MLT26_dH' 'MLT26_dV' 'MLT26' - 'MLT31_dH' 'MLT31_dV' 'MLT31' - 'MLT32_dH' 'MLT32_dV' 'MLT32' - 'MLT33_dH' 'MLT33_dV' 'MLT33' - 'MLT34_dH' 'MLT34_dV' 'MLT34' - 'MLT35_dH' 'MLT35_dV' 'MLT35' - 'MLT41_dH' 'MLT41_dV' 'MLT41' - 'MLT42_dH' 'MLT42_dV' 'MLT42' - 'MLT43_dH' 'MLT43_dV' 'MLT43' - 'MLT44_dH' 'MLT44_dV' 'MLT44' - 'MRC11_dH' 'MRC11_dV' 'MRC11' - 'MRC12_dH' 'MRC12_dV' 'MRC12' - 'MRC13_dH' 'MRC13_dV' 'MRC13' - 'MRC14_dH' 'MRC14_dV' 'MRC14' - 'MRC15_dH' 'MRC15_dV' 'MRC15' - 'MRC21_dH' 'MRC21_dV' 'MRC21' - 'MRC22_dH' 'MRC22_dV' 'MRC22' - 'MRC23_dH' 'MRC23_dV' 'MRC23' - 'MRC24_dH' 'MRC24_dV' 'MRC24' - 'MRC31_dH' 'MRC31_dV' 'MRC31' - 'MRC32_dH' 'MRC32_dV' 'MRC32' - 'MRC33_dH' 'MRC33_dV' 'MRC33' - 'MRC41_dH' 'MRC41_dV' 'MRC41' - 'MRC42_dH' 'MRC42_dV' 'MRC42' - 'MRC43_dH' 'MRC43_dV' 'MRC43' - 'MRF11_dH' 'MRF11_dV' 'MRF11' - 'MRF12_dH' 'MRF12_dV' 'MRF12' - 'MRF21_dH' 'MRF21_dV' 'MRF21' - 'MRF22_dH' 'MRF22_dV' 'MRF22' - 'MRF23_dH' 'MRF23_dV' 'MRF23' - 'MRF31_dH' 'MRF31_dV' 'MRF31' - 'MRF32_dH' 'MRF32_dV' 'MRF32' - 'MRF33_dH' 'MRF33_dV' 'MRF33' - 'MRF34_dH' 'MRF34_dV' 'MRF34' - 'MRF41_dH' 'MRF41_dV' 'MRF41' - 'MRF42_dH' 'MRF42_dV' 'MRF42' - 'MRF43_dH' 'MRF43_dV' 'MRF43' - 'MRF44_dH' 'MRF44_dV' 'MRF44' - 'MRF45_dH' 'MRF45_dV' 'MRF45' - 'MRF51_dH' 'MRF51_dV' 'MRF51' - 'MRF52_dH' 'MRF52_dV' 'MRF52' - 'MRO11_dH' 'MRO11_dV' 'MRO11' - 'MRO12_dH' 'MRO12_dV' 'MRO12' - 'MRO21_dH' 'MRO21_dV' 'MRO21' - 'MRO22_dH' 'MRO22_dV' 'MRO22' - 'MRO31_dH' 'MRO31_dV' 'MRO31' - 'MRO32_dH' 'MRO32_dV' 'MRO32' - 'MRO33_dH' 'MRO33_dV' 'MRO33' - 'MRO41_dH' 'MRO41_dV' 'MRO41' - 'MRO42_dH' 'MRO42_dV' 'MRO42' - 'MRO43_dH' 'MRO43_dV' 'MRO43' - 'MRP11_dH' 'MRP11_dV' 'MRP11' - 'MRP12_dH' 'MRP12_dV' 'MRP12' - 'MRP13_dH' 'MRP13_dV' 'MRP13' - 'MRP21_dH' 'MRP21_dV' 'MRP21' - 'MRP22_dH' 'MRP22_dV' 'MRP22' - 'MRP31_dH' 'MRP31_dV' 'MRP31' - 'MRP32_dH' 'MRP32_dV' 'MRP32' - 'MRP33_dH' 'MRP33_dV' 'MRP33' - 'MRP34_dH' 'MRP34_dV' 'MRP34' - 'MRT11_dH' 'MRT11_dV' 'MRT11' - 'MRT12_dH' 'MRT12_dV' 'MRT12' - 'MRT13_dH' 'MRT13_dV' 'MRT13' - 'MRT14_dH' 'MRT14_dV' 'MRT14' - 'MRT15_dH' 'MRT15_dV' 'MRT15' - 'MRT16_dH' 'MRT16_dV' 'MRT16' - 'MRT21_dH' 'MRT21_dV' 'MRT21' - 'MRT22_dH' 'MRT22_dV' 'MRT22' - 'MRT23_dH' 'MRT23_dV' 'MRT23' - 'MRT24_dH' 'MRT24_dV' 'MRT24' - 'MRT25_dH' 'MRT25_dV' 'MRT25' - 'MRT26_dH' 'MRT26_dV' 'MRT26' - 'MRT31_dH' 'MRT31_dV' 'MRT31' - 'MRT32_dH' 'MRT32_dV' 'MRT32' - 'MRT33_dH' 'MRT33_dV' 'MRT33' - 'MRT34_dH' 'MRT34_dV' 'MRT34' - 'MRT35_dH' 'MRT35_dV' 'MRT35' - 'MRT41_dH' 'MRT41_dV' 'MRT41' - 'MRT42_dH' 'MRT42_dV' 'MRT42' - 'MRT43_dH' 'MRT43_dV' 'MRT43' - 'MRT44_dH' 'MRT44_dV' 'MRT44' - 'MZC01_dH' 'MZC01_dV' 'MZC01' - 'MZC02_dH' 'MZC02_dV' 'MZC02' - 'MZF01_dH' 'MZF01_dV' 'MZF01' - 'MZF02_dH' 'MZF02_dV' 'MZF02' - 'MZF03_dH' 'MZF03_dV' 'MZF03' - 'MZO01_dH' 'MZO01_dV' 'MZO01' - 'MZO02_dH' 'MZO02_dV' 'MZO02' - 'MZP01_dH' 'MZP01_dV' 'MZP01' - 'MZP02_dH' 'MZP02_dV' 'MZP02' -}; + case 'ctf151_planar' + planar = { + 'MLC11_dH' 'MLC11_dV' 'MLC11' + 'MLC12_dH' 'MLC12_dV' 'MLC12' + 'MLC13_dH' 'MLC13_dV' 'MLC13' + 'MLC14_dH' 'MLC14_dV' 'MLC14' + 'MLC15_dH' 'MLC15_dV' 'MLC15' + 'MLC21_dH' 'MLC21_dV' 'MLC21' + 'MLC22_dH' 'MLC22_dV' 'MLC22' + 'MLC23_dH' 'MLC23_dV' 'MLC23' + 'MLC24_dH' 'MLC24_dV' 'MLC24' + 'MLC31_dH' 'MLC31_dV' 'MLC31' + 'MLC32_dH' 'MLC32_dV' 'MLC32' + 'MLC33_dH' 'MLC33_dV' 'MLC33' + 'MLC41_dH' 'MLC41_dV' 'MLC41' + 'MLC42_dH' 'MLC42_dV' 'MLC42' + 'MLC43_dH' 'MLC43_dV' 'MLC43' + 'MLF11_dH' 'MLF11_dV' 'MLF11' + 'MLF12_dH' 'MLF12_dV' 'MLF12' + 'MLF21_dH' 'MLF21_dV' 'MLF21' + 'MLF22_dH' 'MLF22_dV' 'MLF22' + 'MLF23_dH' 'MLF23_dV' 'MLF23' + 'MLF31_dH' 'MLF31_dV' 'MLF31' + 'MLF32_dH' 'MLF32_dV' 'MLF32' + 'MLF33_dH' 'MLF33_dV' 'MLF33' + 'MLF34_dH' 'MLF34_dV' 'MLF34' + 'MLF41_dH' 'MLF41_dV' 'MLF41' + 'MLF42_dH' 'MLF42_dV' 'MLF42' + 'MLF43_dH' 'MLF43_dV' 'MLF43' + 'MLF44_dH' 'MLF44_dV' 'MLF44' + 'MLF45_dH' 'MLF45_dV' 'MLF45' + 'MLF51_dH' 'MLF51_dV' 'MLF51' + 'MLF52_dH' 'MLF52_dV' 'MLF52' + 'MLO11_dH' 'MLO11_dV' 'MLO11' + 'MLO12_dH' 'MLO12_dV' 'MLO12' + 'MLO21_dH' 'MLO21_dV' 'MLO21' + 'MLO22_dH' 'MLO22_dV' 'MLO22' + 'MLO31_dH' 'MLO31_dV' 'MLO31' + 'MLO32_dH' 'MLO32_dV' 'MLO32' + 'MLO33_dH' 'MLO33_dV' 'MLO33' + 'MLO41_dH' 'MLO41_dV' 'MLO41' + 'MLO42_dH' 'MLO42_dV' 'MLO42' + 'MLO43_dH' 'MLO43_dV' 'MLO43' + 'MLP11_dH' 'MLP11_dV' 'MLP11' + 'MLP12_dH' 'MLP12_dV' 'MLP12' + 'MLP13_dH' 'MLP13_dV' 'MLP13' + 'MLP21_dH' 'MLP21_dV' 'MLP21' + 'MLP22_dH' 'MLP22_dV' 'MLP22' + 'MLP31_dH' 'MLP31_dV' 'MLP31' + 'MLP32_dH' 'MLP32_dV' 'MLP32' + 'MLP33_dH' 'MLP33_dV' 'MLP33' + 'MLP34_dH' 'MLP34_dV' 'MLP34' + 'MLT11_dH' 'MLT11_dV' 'MLT11' + 'MLT12_dH' 'MLT12_dV' 'MLT12' + 'MLT13_dH' 'MLT13_dV' 'MLT13' + 'MLT14_dH' 'MLT14_dV' 'MLT14' + 'MLT15_dH' 'MLT15_dV' 'MLT15' + 'MLT16_dH' 'MLT16_dV' 'MLT16' + 'MLT21_dH' 'MLT21_dV' 'MLT21' + 'MLT22_dH' 'MLT22_dV' 'MLT22' + 'MLT23_dH' 'MLT23_dV' 'MLT23' + 'MLT24_dH' 'MLT24_dV' 'MLT24' + 'MLT25_dH' 'MLT25_dV' 'MLT25' + 'MLT26_dH' 'MLT26_dV' 'MLT26' + 'MLT31_dH' 'MLT31_dV' 'MLT31' + 'MLT32_dH' 'MLT32_dV' 'MLT32' + 'MLT33_dH' 'MLT33_dV' 'MLT33' + 'MLT34_dH' 'MLT34_dV' 'MLT34' + 'MLT35_dH' 'MLT35_dV' 'MLT35' + 'MLT41_dH' 'MLT41_dV' 'MLT41' + 'MLT42_dH' 'MLT42_dV' 'MLT42' + 'MLT43_dH' 'MLT43_dV' 'MLT43' + 'MLT44_dH' 'MLT44_dV' 'MLT44' + 'MRC11_dH' 'MRC11_dV' 'MRC11' + 'MRC12_dH' 'MRC12_dV' 'MRC12' + 'MRC13_dH' 'MRC13_dV' 'MRC13' + 'MRC14_dH' 'MRC14_dV' 'MRC14' + 'MRC15_dH' 'MRC15_dV' 'MRC15' + 'MRC21_dH' 'MRC21_dV' 'MRC21' + 'MRC22_dH' 'MRC22_dV' 'MRC22' + 'MRC23_dH' 'MRC23_dV' 'MRC23' + 'MRC24_dH' 'MRC24_dV' 'MRC24' + 'MRC31_dH' 'MRC31_dV' 'MRC31' + 'MRC32_dH' 'MRC32_dV' 'MRC32' + 'MRC33_dH' 'MRC33_dV' 'MRC33' + 'MRC41_dH' 'MRC41_dV' 'MRC41' + 'MRC42_dH' 'MRC42_dV' 'MRC42' + 'MRC43_dH' 'MRC43_dV' 'MRC43' + 'MRF11_dH' 'MRF11_dV' 'MRF11' + 'MRF12_dH' 'MRF12_dV' 'MRF12' + 'MRF21_dH' 'MRF21_dV' 'MRF21' + 'MRF22_dH' 'MRF22_dV' 'MRF22' + 'MRF23_dH' 'MRF23_dV' 'MRF23' + 'MRF31_dH' 'MRF31_dV' 'MRF31' + 'MRF32_dH' 'MRF32_dV' 'MRF32' + 'MRF33_dH' 'MRF33_dV' 'MRF33' + 'MRF34_dH' 'MRF34_dV' 'MRF34' + 'MRF41_dH' 'MRF41_dV' 'MRF41' + 'MRF42_dH' 'MRF42_dV' 'MRF42' + 'MRF43_dH' 'MRF43_dV' 'MRF43' + 'MRF44_dH' 'MRF44_dV' 'MRF44' + 'MRF45_dH' 'MRF45_dV' 'MRF45' + 'MRF51_dH' 'MRF51_dV' 'MRF51' + 'MRF52_dH' 'MRF52_dV' 'MRF52' + 'MRO11_dH' 'MRO11_dV' 'MRO11' + 'MRO12_dH' 'MRO12_dV' 'MRO12' + 'MRO21_dH' 'MRO21_dV' 'MRO21' + 'MRO22_dH' 'MRO22_dV' 'MRO22' + 'MRO31_dH' 'MRO31_dV' 'MRO31' + 'MRO32_dH' 'MRO32_dV' 'MRO32' + 'MRO33_dH' 'MRO33_dV' 'MRO33' + 'MRO41_dH' 'MRO41_dV' 'MRO41' + 'MRO42_dH' 'MRO42_dV' 'MRO42' + 'MRO43_dH' 'MRO43_dV' 'MRO43' + 'MRP11_dH' 'MRP11_dV' 'MRP11' + 'MRP12_dH' 'MRP12_dV' 'MRP12' + 'MRP13_dH' 'MRP13_dV' 'MRP13' + 'MRP21_dH' 'MRP21_dV' 'MRP21' + 'MRP22_dH' 'MRP22_dV' 'MRP22' + 'MRP31_dH' 'MRP31_dV' 'MRP31' + 'MRP32_dH' 'MRP32_dV' 'MRP32' + 'MRP33_dH' 'MRP33_dV' 'MRP33' + 'MRP34_dH' 'MRP34_dV' 'MRP34' + 'MRT11_dH' 'MRT11_dV' 'MRT11' + 'MRT12_dH' 'MRT12_dV' 'MRT12' + 'MRT13_dH' 'MRT13_dV' 'MRT13' + 'MRT14_dH' 'MRT14_dV' 'MRT14' + 'MRT15_dH' 'MRT15_dV' 'MRT15' + 'MRT16_dH' 'MRT16_dV' 'MRT16' + 'MRT21_dH' 'MRT21_dV' 'MRT21' + 'MRT22_dH' 'MRT22_dV' 'MRT22' + 'MRT23_dH' 'MRT23_dV' 'MRT23' + 'MRT24_dH' 'MRT24_dV' 'MRT24' + 'MRT25_dH' 'MRT25_dV' 'MRT25' + 'MRT26_dH' 'MRT26_dV' 'MRT26' + 'MRT31_dH' 'MRT31_dV' 'MRT31' + 'MRT32_dH' 'MRT32_dV' 'MRT32' + 'MRT33_dH' 'MRT33_dV' 'MRT33' + 'MRT34_dH' 'MRT34_dV' 'MRT34' + 'MRT35_dH' 'MRT35_dV' 'MRT35' + 'MRT41_dH' 'MRT41_dV' 'MRT41' + 'MRT42_dH' 'MRT42_dV' 'MRT42' + 'MRT43_dH' 'MRT43_dV' 'MRT43' + 'MRT44_dH' 'MRT44_dV' 'MRT44' + 'MZC01_dH' 'MZC01_dV' 'MZC01' + 'MZC02_dH' 'MZC02_dV' 'MZC02' + 'MZF01_dH' 'MZF01_dV' 'MZF01' + 'MZF02_dH' 'MZF02_dV' 'MZF02' + 'MZF03_dH' 'MZF03_dV' 'MZF03' + 'MZO01_dH' 'MZO01_dV' 'MZO01' + 'MZO02_dH' 'MZO02_dV' 'MZO02' + 'MZP01_dH' 'MZP01_dV' 'MZP01' + 'MZP02_dH' 'MZP02_dV' 'MZP02' + }; + + case 'ctf275_planar' + planar = { + 'MLC11_dH' 'MLC11_dV' 'MLC11' + 'MLC12_dH' 'MLC12_dV' 'MLC12' + 'MLC13_dH' 'MLC13_dV' 'MLC13' + 'MLC14_dH' 'MLC14_dV' 'MLC14' + 'MLC15_dH' 'MLC15_dV' 'MLC15' + 'MLC16_dH' 'MLC16_dV' 'MLC16' + 'MLC17_dH' 'MLC17_dV' 'MLC17' + 'MLC21_dH' 'MLC21_dV' 'MLC21' + 'MLC22_dH' 'MLC22_dV' 'MLC22' + 'MLC23_dH' 'MLC23_dV' 'MLC23' + 'MLC24_dH' 'MLC24_dV' 'MLC24' + 'MLC25_dH' 'MLC25_dV' 'MLC25' + 'MLC31_dH' 'MLC31_dV' 'MLC31' + 'MLC32_dH' 'MLC32_dV' 'MLC32' + 'MLC41_dH' 'MLC41_dV' 'MLC41' + 'MLC42_dH' 'MLC42_dV' 'MLC42' + 'MLC51_dH' 'MLC51_dV' 'MLC51' + 'MLC52_dH' 'MLC52_dV' 'MLC52' + 'MLC53_dH' 'MLC53_dV' 'MLC53' + 'MLC54_dH' 'MLC54_dV' 'MLC54' + 'MLC55_dH' 'MLC55_dV' 'MLC55' + 'MLC61_dH' 'MLC61_dV' 'MLC61' + 'MLC62_dH' 'MLC62_dV' 'MLC62' + 'MLC63_dH' 'MLC63_dV' 'MLC63' + 'MLF11_dH' 'MLF11_dV' 'MLF11' + 'MLF12_dH' 'MLF12_dV' 'MLF12' + 'MLF13_dH' 'MLF13_dV' 'MLF13' + 'MLF14_dH' 'MLF14_dV' 'MLF14' + 'MLF21_dH' 'MLF21_dV' 'MLF21' + 'MLF22_dH' 'MLF22_dV' 'MLF22' + 'MLF23_dH' 'MLF23_dV' 'MLF23' + 'MLF24_dH' 'MLF24_dV' 'MLF24' + 'MLF25_dH' 'MLF25_dV' 'MLF25' + 'MLF31_dH' 'MLF31_dV' 'MLF31' + 'MLF32_dH' 'MLF32_dV' 'MLF32' + 'MLF33_dH' 'MLF33_dV' 'MLF33' + 'MLF34_dH' 'MLF34_dV' 'MLF34' + 'MLF35_dH' 'MLF35_dV' 'MLF35' + 'MLF41_dH' 'MLF41_dV' 'MLF41' + 'MLF42_dH' 'MLF42_dV' 'MLF42' + 'MLF43_dH' 'MLF43_dV' 'MLF43' + 'MLF44_dH' 'MLF44_dV' 'MLF44' + 'MLF45_dH' 'MLF45_dV' 'MLF45' + 'MLF46_dH' 'MLF46_dV' 'MLF46' + 'MLF51_dH' 'MLF51_dV' 'MLF51' + 'MLF52_dH' 'MLF52_dV' 'MLF52' + 'MLF53_dH' 'MLF53_dV' 'MLF53' + 'MLF54_dH' 'MLF54_dV' 'MLF54' + 'MLF55_dH' 'MLF55_dV' 'MLF55' + 'MLF56_dH' 'MLF56_dV' 'MLF56' + 'MLF61_dH' 'MLF61_dV' 'MLF61' + 'MLF62_dH' 'MLF62_dV' 'MLF62' + 'MLF63_dH' 'MLF63_dV' 'MLF63' + 'MLF64_dH' 'MLF64_dV' 'MLF64' + 'MLF65_dH' 'MLF65_dV' 'MLF65' + 'MLF66_dH' 'MLF66_dV' 'MLF66' + 'MLF67_dH' 'MLF67_dV' 'MLF67' + 'MLO11_dH' 'MLO11_dV' 'MLO11' + 'MLO12_dH' 'MLO12_dV' 'MLO12' + 'MLO13_dH' 'MLO13_dV' 'MLO13' + 'MLO14_dH' 'MLO14_dV' 'MLO14' + 'MLO21_dH' 'MLO21_dV' 'MLO21' + 'MLO22_dH' 'MLO22_dV' 'MLO22' + 'MLO23_dH' 'MLO23_dV' 'MLO23' + 'MLO24_dH' 'MLO24_dV' 'MLO24' + 'MLO31_dH' 'MLO31_dV' 'MLO31' + 'MLO32_dH' 'MLO32_dV' 'MLO32' + 'MLO33_dH' 'MLO33_dV' 'MLO33' + 'MLO34_dH' 'MLO34_dV' 'MLO34' + 'MLO41_dH' 'MLO41_dV' 'MLO41' + 'MLO42_dH' 'MLO42_dV' 'MLO42' + 'MLO43_dH' 'MLO43_dV' 'MLO43' + 'MLO44_dH' 'MLO44_dV' 'MLO44' + 'MLO51_dH' 'MLO51_dV' 'MLO51' + 'MLO52_dH' 'MLO52_dV' 'MLO52' + 'MLO53_dH' 'MLO53_dV' 'MLO53' + 'MLP11_dH' 'MLP11_dV' 'MLP11' + 'MLP12_dH' 'MLP12_dV' 'MLP12' + 'MLP21_dH' 'MLP21_dV' 'MLP21' + 'MLP22_dH' 'MLP22_dV' 'MLP22' + 'MLP23_dH' 'MLP23_dV' 'MLP23' + 'MLP31_dH' 'MLP31_dV' 'MLP31' + 'MLP32_dH' 'MLP32_dV' 'MLP32' + 'MLP33_dH' 'MLP33_dV' 'MLP33' + 'MLP34_dH' 'MLP34_dV' 'MLP34' + 'MLP35_dH' 'MLP35_dV' 'MLP35' + 'MLP41_dH' 'MLP41_dV' 'MLP41' + 'MLP42_dH' 'MLP42_dV' 'MLP42' + 'MLP43_dH' 'MLP43_dV' 'MLP43' + 'MLP44_dH' 'MLP44_dV' 'MLP44' + 'MLP45_dH' 'MLP45_dV' 'MLP45' + 'MLP51_dH' 'MLP51_dV' 'MLP51' + 'MLP52_dH' 'MLP52_dV' 'MLP52' + 'MLP53_dH' 'MLP53_dV' 'MLP53' + 'MLP54_dH' 'MLP54_dV' 'MLP54' + 'MLP55_dH' 'MLP55_dV' 'MLP55' + 'MLP56_dH' 'MLP56_dV' 'MLP56' + 'MLP57_dH' 'MLP57_dV' 'MLP57' + 'MLT11_dH' 'MLT11_dV' 'MLT11' + 'MLT12_dH' 'MLT12_dV' 'MLT12' + 'MLT13_dH' 'MLT13_dV' 'MLT13' + 'MLT14_dH' 'MLT14_dV' 'MLT14' + 'MLT15_dH' 'MLT15_dV' 'MLT15' + 'MLT16_dH' 'MLT16_dV' 'MLT16' + 'MLT21_dH' 'MLT21_dV' 'MLT21' + 'MLT22_dH' 'MLT22_dV' 'MLT22' + 'MLT23_dH' 'MLT23_dV' 'MLT23' + 'MLT24_dH' 'MLT24_dV' 'MLT24' + 'MLT25_dH' 'MLT25_dV' 'MLT25' + 'MLT26_dH' 'MLT26_dV' 'MLT26' + 'MLT27_dH' 'MLT27_dV' 'MLT27' + 'MLT31_dH' 'MLT31_dV' 'MLT31' + 'MLT32_dH' 'MLT32_dV' 'MLT32' + 'MLT33_dH' 'MLT33_dV' 'MLT33' + 'MLT34_dH' 'MLT34_dV' 'MLT34' + 'MLT35_dH' 'MLT35_dV' 'MLT35' + 'MLT36_dH' 'MLT36_dV' 'MLT36' + 'MLT37_dH' 'MLT37_dV' 'MLT37' + 'MLT41_dH' 'MLT41_dV' 'MLT41' + 'MLT42_dH' 'MLT42_dV' 'MLT42' + 'MLT43_dH' 'MLT43_dV' 'MLT43' + 'MLT44_dH' 'MLT44_dV' 'MLT44' + 'MLT45_dH' 'MLT45_dV' 'MLT45' + 'MLT46_dH' 'MLT46_dV' 'MLT46' + 'MLT47_dH' 'MLT47_dV' 'MLT47' + 'MLT51_dH' 'MLT51_dV' 'MLT51' + 'MLT52_dH' 'MLT52_dV' 'MLT52' + 'MLT53_dH' 'MLT53_dV' 'MLT53' + 'MLT54_dH' 'MLT54_dV' 'MLT54' + 'MLT55_dH' 'MLT55_dV' 'MLT55' + 'MLT56_dH' 'MLT56_dV' 'MLT56' + 'MLT57_dH' 'MLT57_dV' 'MLT57' + 'MRC11_dH' 'MRC11_dV' 'MRC11' + 'MRC12_dH' 'MRC12_dV' 'MRC12' + 'MRC13_dH' 'MRC13_dV' 'MRC13' + 'MRC14_dH' 'MRC14_dV' 'MRC14' + 'MRC15_dH' 'MRC15_dV' 'MRC15' + 'MRC16_dH' 'MRC16_dV' 'MRC16' + 'MRC17_dH' 'MRC17_dV' 'MRC17' + 'MRC21_dH' 'MRC21_dV' 'MRC21' + 'MRC22_dH' 'MRC22_dV' 'MRC22' + 'MRC23_dH' 'MRC23_dV' 'MRC23' + 'MRC24_dH' 'MRC24_dV' 'MRC24' + 'MRC25_dH' 'MRC25_dV' 'MRC25' + 'MRC31_dH' 'MRC31_dV' 'MRC31' + 'MRC32_dH' 'MRC32_dV' 'MRC32' + 'MRC41_dH' 'MRC41_dV' 'MRC41' + 'MRC42_dH' 'MRC42_dV' 'MRC42' + 'MRC51_dH' 'MRC51_dV' 'MRC51' + 'MRC52_dH' 'MRC52_dV' 'MRC52' + 'MRC53_dH' 'MRC53_dV' 'MRC53' + 'MRC54_dH' 'MRC54_dV' 'MRC54' + 'MRC55_dH' 'MRC55_dV' 'MRC55' + 'MRC61_dH' 'MRC61_dV' 'MRC61' + 'MRC62_dH' 'MRC62_dV' 'MRC62' + 'MRC63_dH' 'MRC63_dV' 'MRC63' + 'MRF11_dH' 'MRF11_dV' 'MRF11' + 'MRF12_dH' 'MRF12_dV' 'MRF12' + 'MRF13_dH' 'MRF13_dV' 'MRF13' + 'MRF14_dH' 'MRF14_dV' 'MRF14' + 'MRF21_dH' 'MRF21_dV' 'MRF21' + 'MRF22_dH' 'MRF22_dV' 'MRF22' + 'MRF23_dH' 'MRF23_dV' 'MRF23' + 'MRF24_dH' 'MRF24_dV' 'MRF24' + 'MRF25_dH' 'MRF25_dV' 'MRF25' + 'MRF31_dH' 'MRF31_dV' 'MRF31' + 'MRF32_dH' 'MRF32_dV' 'MRF32' + 'MRF33_dH' 'MRF33_dV' 'MRF33' + 'MRF34_dH' 'MRF34_dV' 'MRF34' + 'MRF35_dH' 'MRF35_dV' 'MRF35' + 'MRF41_dH' 'MRF41_dV' 'MRF41' + 'MRF42_dH' 'MRF42_dV' 'MRF42' + 'MRF43_dH' 'MRF43_dV' 'MRF43' + 'MRF44_dH' 'MRF44_dV' 'MRF44' + 'MRF45_dH' 'MRF45_dV' 'MRF45' + 'MRF46_dH' 'MRF46_dV' 'MRF46' + 'MRF51_dH' 'MRF51_dV' 'MRF51' + 'MRF52_dH' 'MRF52_dV' 'MRF52' + 'MRF53_dH' 'MRF53_dV' 'MRF53' + 'MRF54_dH' 'MRF54_dV' 'MRF54' + 'MRF55_dH' 'MRF55_dV' 'MRF55' + 'MRF56_dH' 'MRF56_dV' 'MRF56' + 'MRF61_dH' 'MRF61_dV' 'MRF61' + 'MRF62_dH' 'MRF62_dV' 'MRF62' + 'MRF63_dH' 'MRF63_dV' 'MRF63' + 'MRF64_dH' 'MRF64_dV' 'MRF64' + 'MRF65_dH' 'MRF65_dV' 'MRF65' + 'MRF66_dH' 'MRF66_dV' 'MRF66' + 'MRF67_dH' 'MRF67_dV' 'MRF67' + 'MRO11_dH' 'MRO11_dV' 'MRO11' + 'MRO12_dH' 'MRO12_dV' 'MRO12' + 'MRO13_dH' 'MRO13_dV' 'MRO13' + 'MRO14_dH' 'MRO14_dV' 'MRO14' + 'MRO21_dH' 'MRO21_dV' 'MRO21' + 'MRO22_dH' 'MRO22_dV' 'MRO22' + 'MRO23_dH' 'MRO23_dV' 'MRO23' + 'MRO24_dH' 'MRO24_dV' 'MRO24' + 'MRO31_dH' 'MRO31_dV' 'MRO31' + 'MRO32_dH' 'MRO32_dV' 'MRO32' + 'MRO33_dH' 'MRO33_dV' 'MRO33' + 'MRO34_dH' 'MRO34_dV' 'MRO34' + 'MRO41_dH' 'MRO41_dV' 'MRO41' + 'MRO42_dH' 'MRO42_dV' 'MRO42' + 'MRO43_dH' 'MRO43_dV' 'MRO43' + 'MRO44_dH' 'MRO44_dV' 'MRO44' + 'MRO51_dH' 'MRO51_dV' 'MRO51' + 'MRO52_dH' 'MRO52_dV' 'MRO52' + 'MRO53_dH' 'MRO53_dV' 'MRO53' + 'MRP11_dH' 'MRP11_dV' 'MRP11' + 'MRP12_dH' 'MRP12_dV' 'MRP12' + 'MRP21_dH' 'MRP21_dV' 'MRP21' + 'MRP22_dH' 'MRP22_dV' 'MRP22' + 'MRP23_dH' 'MRP23_dV' 'MRP23' + 'MRP31_dH' 'MRP31_dV' 'MRP31' + 'MRP32_dH' 'MRP32_dV' 'MRP32' + 'MRP33_dH' 'MRP33_dV' 'MRP33' + 'MRP34_dH' 'MRP34_dV' 'MRP34' + 'MRP35_dH' 'MRP35_dV' 'MRP35' + 'MRP41_dH' 'MRP41_dV' 'MRP41' + 'MRP42_dH' 'MRP42_dV' 'MRP42' + 'MRP43_dH' 'MRP43_dV' 'MRP43' + 'MRP44_dH' 'MRP44_dV' 'MRP44' + 'MRP45_dH' 'MRP45_dV' 'MRP45' + 'MRP51_dH' 'MRP51_dV' 'MRP51' + 'MRP52_dH' 'MRP52_dV' 'MRP52' + 'MRP53_dH' 'MRP53_dV' 'MRP53' + 'MRP54_dH' 'MRP54_dV' 'MRP54' + 'MRP55_dH' 'MRP55_dV' 'MRP55' + 'MRP56_dH' 'MRP56_dV' 'MRP56' + 'MRP57_dH' 'MRP57_dV' 'MRP57' + 'MRT11_dH' 'MRT11_dV' 'MRT11' + 'MRT12_dH' 'MRT12_dV' 'MRT12' + 'MRT13_dH' 'MRT13_dV' 'MRT13' + 'MRT14_dH' 'MRT14_dV' 'MRT14' + 'MRT15_dH' 'MRT15_dV' 'MRT15' + 'MRT16_dH' 'MRT16_dV' 'MRT16' + 'MRT21_dH' 'MRT21_dV' 'MRT21' + 'MRT22_dH' 'MRT22_dV' 'MRT22' + 'MRT23_dH' 'MRT23_dV' 'MRT23' + 'MRT24_dH' 'MRT24_dV' 'MRT24' + 'MRT25_dH' 'MRT25_dV' 'MRT25' + 'MRT26_dH' 'MRT26_dV' 'MRT26' + 'MRT27_dH' 'MRT27_dV' 'MRT27' + 'MRT31_dH' 'MRT31_dV' 'MRT31' + 'MRT32_dH' 'MRT32_dV' 'MRT32' + 'MRT33_dH' 'MRT33_dV' 'MRT33' + 'MRT34_dH' 'MRT34_dV' 'MRT34' + 'MRT35_dH' 'MRT35_dV' 'MRT35' + 'MRT36_dH' 'MRT36_dV' 'MRT36' + 'MRT37_dH' 'MRT37_dV' 'MRT37' + 'MRT41_dH' 'MRT41_dV' 'MRT41' + 'MRT42_dH' 'MRT42_dV' 'MRT42' + 'MRT43_dH' 'MRT43_dV' 'MRT43' + 'MRT44_dH' 'MRT44_dV' 'MRT44' + 'MRT45_dH' 'MRT45_dV' 'MRT45' + 'MRT46_dH' 'MRT46_dV' 'MRT46' + 'MRT47_dH' 'MRT47_dV' 'MRT47' + 'MRT51_dH' 'MRT51_dV' 'MRT51' + 'MRT52_dH' 'MRT52_dV' 'MRT52' + 'MRT53_dH' 'MRT53_dV' 'MRT53' + 'MRT54_dH' 'MRT54_dV' 'MRT54' + 'MRT55_dH' 'MRT55_dV' 'MRT55' + 'MRT56_dH' 'MRT56_dV' 'MRT56' + 'MRT57_dH' 'MRT57_dV' 'MRT57' + 'MZC01_dH' 'MZC01_dV' 'MZC01' + 'MZC02_dH' 'MZC02_dV' 'MZC02' + 'MZC03_dH' 'MZC03_dV' 'MZC03' + 'MZC04_dH' 'MZC04_dV' 'MZC04' + 'MZF01_dH' 'MZF01_dV' 'MZF01' + 'MZF02_dH' 'MZF02_dV' 'MZF02' + 'MZF03_dH' 'MZF03_dV' 'MZF03' + 'MZO01_dH' 'MZO01_dV' 'MZO01' + 'MZO02_dH' 'MZO02_dV' 'MZO02' + 'MZO03_dH' 'MZO03_dV' 'MZO03' + 'MZP01_dH' 'MZP01_dV' 'MZP01' + }; + + case 'neuromag122' + planar = { + 'MEG 001' 'MEG 002' 'MEG 001+002' + 'MEG 003' 'MEG 004' 'MEG 003+004' + 'MEG 005' 'MEG 006' 'MEG 005+006' + 'MEG 007' 'MEG 008' 'MEG 007+008' + 'MEG 009' 'MEG 010' 'MEG 009+010' + 'MEG 011' 'MEG 012' 'MEG 011+012' + 'MEG 013' 'MEG 014' 'MEG 013+014' + 'MEG 015' 'MEG 016' 'MEG 015+016' + 'MEG 017' 'MEG 018' 'MEG 017+018' + 'MEG 019' 'MEG 020' 'MEG 019+020' + 'MEG 021' 'MEG 022' 'MEG 021+022' + 'MEG 023' 'MEG 024' 'MEG 023+024' + 'MEG 025' 'MEG 026' 'MEG 025+026' + 'MEG 027' 'MEG 028' 'MEG 027+028' + 'MEG 029' 'MEG 030' 'MEG 029+030' + 'MEG 031' 'MEG 032' 'MEG 031+032' + 'MEG 033' 'MEG 034' 'MEG 033+034' + 'MEG 035' 'MEG 036' 'MEG 035+036' + 'MEG 037' 'MEG 038' 'MEG 037+038' + 'MEG 039' 'MEG 040' 'MEG 039+040' + 'MEG 041' 'MEG 042' 'MEG 041+042' + 'MEG 043' 'MEG 044' 'MEG 043+044' + 'MEG 045' 'MEG 046' 'MEG 045+046' + 'MEG 047' 'MEG 048' 'MEG 047+048' + 'MEG 049' 'MEG 050' 'MEG 049+050' + 'MEG 051' 'MEG 052' 'MEG 051+052' + 'MEG 053' 'MEG 054' 'MEG 053+054' + 'MEG 055' 'MEG 056' 'MEG 055+056' + 'MEG 057' 'MEG 058' 'MEG 057+058' + 'MEG 059' 'MEG 060' 'MEG 059+060' + 'MEG 061' 'MEG 062' 'MEG 061+062' + 'MEG 063' 'MEG 064' 'MEG 063+064' + 'MEG 065' 'MEG 066' 'MEG 065+066' + 'MEG 067' 'MEG 068' 'MEG 067+068' + 'MEG 069' 'MEG 070' 'MEG 069+070' + 'MEG 071' 'MEG 072' 'MEG 071+072' + 'MEG 073' 'MEG 074' 'MEG 073+074' + 'MEG 075' 'MEG 076' 'MEG 075+076' + 'MEG 077' 'MEG 078' 'MEG 077+078' + 'MEG 079' 'MEG 080' 'MEG 079+080' + 'MEG 081' 'MEG 082' 'MEG 081+082' + 'MEG 083' 'MEG 084' 'MEG 083+084' + 'MEG 085' 'MEG 086' 'MEG 085+086' + 'MEG 087' 'MEG 088' 'MEG 087+088' + 'MEG 089' 'MEG 090' 'MEG 089+090' + 'MEG 091' 'MEG 092' 'MEG 091+092' + 'MEG 093' 'MEG 094' 'MEG 093+094' + 'MEG 095' 'MEG 096' 'MEG 095+096' + 'MEG 097' 'MEG 098' 'MEG 097+098' + 'MEG 099' 'MEG 100' 'MEG 099+100' + 'MEG 101' 'MEG 102' 'MEG 101+102' + 'MEG 103' 'MEG 104' 'MEG 103+104' + 'MEG 105' 'MEG 106' 'MEG 105+106' + 'MEG 107' 'MEG 108' 'MEG 107+108' + 'MEG 109' 'MEG 110' 'MEG 109+110' + 'MEG 111' 'MEG 112' 'MEG 111+112' + 'MEG 113' 'MEG 114' 'MEG 113+114' + 'MEG 115' 'MEG 116' 'MEG 115+116' + 'MEG 117' 'MEG 118' 'MEG 117+118' + 'MEG 119' 'MEG 120' 'MEG 119+120' + 'MEG 121' 'MEG 122' 'MEG 121+122' + }; + + case 'neuromag306' + count1 = mean(ismember(data.label, senslabel('neuromag306'))); + count2 = mean(ismember(data.label, senslabel('neuromag306alt'))); + + if count1>count2 + % it looks like the channels have a space in them + % the last commented column contains the magnetometer labels + planar = { + 'MEG 0112' 'MEG 0113' 'MEG 0112+0113' % 'MEG 0111' + 'MEG 0122' 'MEG 0123' 'MEG 0122+0123' % 'MEG 0121' + 'MEG 0132' 'MEG 0133' 'MEG 0132+0133' % 'MEG 0131' + 'MEG 0142' 'MEG 0143' 'MEG 0142+0143' % 'MEG 0141' + 'MEG 0212' 'MEG 0213' 'MEG 0212+0213' % 'MEG 0211' + 'MEG 0222' 'MEG 0223' 'MEG 0222+0223' % 'MEG 0221' + 'MEG 0232' 'MEG 0233' 'MEG 0232+0233' % 'MEG 0231' + 'MEG 0242' 'MEG 0243' 'MEG 0242+0243' % 'MEG 0241' + 'MEG 0312' 'MEG 0313' 'MEG 0312+0313' % 'MEG 0311' + 'MEG 0322' 'MEG 0323' 'MEG 0322+0323' % 'MEG 0321' + 'MEG 0332' 'MEG 0333' 'MEG 0332+0333' % 'MEG 0331' + 'MEG 0342' 'MEG 0343' 'MEG 0342+0343' % 'MEG 0341' + 'MEG 0412' 'MEG 0413' 'MEG 0412+0413' % 'MEG 0411' + 'MEG 0422' 'MEG 0423' 'MEG 0422+0423' % 'MEG 0421' + 'MEG 0432' 'MEG 0433' 'MEG 0432+0433' % 'MEG 0431' + 'MEG 0442' 'MEG 0443' 'MEG 0442+0443' % 'MEG 0441' + 'MEG 0512' 'MEG 0513' 'MEG 0512+0513' % 'MEG 0511' + 'MEG 0522' 'MEG 0523' 'MEG 0522+0523' % 'MEG 0521' + 'MEG 0532' 'MEG 0533' 'MEG 0532+0533' % 'MEG 0531' + 'MEG 0542' 'MEG 0543' 'MEG 0542+0543' % 'MEG 0541' + 'MEG 0612' 'MEG 0613' 'MEG 0612+0613' % 'MEG 0611' + 'MEG 0622' 'MEG 0623' 'MEG 0622+0623' % 'MEG 0621' + 'MEG 0632' 'MEG 0633' 'MEG 0632+0633' % 'MEG 0631' + 'MEG 0642' 'MEG 0643' 'MEG 0642+0643' % 'MEG 0641' + 'MEG 0712' 'MEG 0713' 'MEG 0712+0713' % 'MEG 0711' + 'MEG 0722' 'MEG 0723' 'MEG 0722+0723' % 'MEG 0721' + 'MEG 0732' 'MEG 0733' 'MEG 0732+0733' % 'MEG 0731' + 'MEG 0742' 'MEG 0743' 'MEG 0742+0743' % 'MEG 0741' + 'MEG 0812' 'MEG 0813' 'MEG 0812+0813' % 'MEG 0811' + 'MEG 0822' 'MEG 0823' 'MEG 0822+0823' % 'MEG 0821' + 'MEG 0912' 'MEG 0913' 'MEG 0912+0913' % 'MEG 0911' + 'MEG 0922' 'MEG 0923' 'MEG 0922+0923' % 'MEG 0921' + 'MEG 0932' 'MEG 0933' 'MEG 0932+0933' % 'MEG 0931' + 'MEG 0942' 'MEG 0943' 'MEG 0942+0943' % 'MEG 0941' + 'MEG 1012' 'MEG 1013' 'MEG 1012+1013' % 'MEG 1011' + 'MEG 1022' 'MEG 1023' 'MEG 1022+1023' % 'MEG 1021' + 'MEG 1032' 'MEG 1033' 'MEG 1032+1033' % 'MEG 1031' + 'MEG 1042' 'MEG 1043' 'MEG 1042+1043' % 'MEG 1041' + 'MEG 1112' 'MEG 1113' 'MEG 1112+1113' % 'MEG 1111' + 'MEG 1122' 'MEG 1123' 'MEG 1122+1123' % 'MEG 1121' + 'MEG 1132' 'MEG 1133' 'MEG 1132+1133' % 'MEG 1131' + 'MEG 1142' 'MEG 1143' 'MEG 1142+1143' % 'MEG 1141' + 'MEG 1212' 'MEG 1213' 'MEG 1212+1213' % 'MEG 1211' + 'MEG 1222' 'MEG 1223' 'MEG 1222+1223' % 'MEG 1221' + 'MEG 1232' 'MEG 1233' 'MEG 1232+1233' % 'MEG 1231' + 'MEG 1242' 'MEG 1243' 'MEG 1242+1243' % 'MEG 1241' + 'MEG 1312' 'MEG 1313' 'MEG 1312+1313' % 'MEG 1311' + 'MEG 1322' 'MEG 1323' 'MEG 1322+1323' % 'MEG 1321' + 'MEG 1332' 'MEG 1333' 'MEG 1332+1333' % 'MEG 1331' + 'MEG 1342' 'MEG 1343' 'MEG 1342+1343' % 'MEG 1341' + 'MEG 1412' 'MEG 1413' 'MEG 1412+1413' % 'MEG 1411' + 'MEG 1422' 'MEG 1423' 'MEG 1422+1423' % 'MEG 1421' + 'MEG 1432' 'MEG 1433' 'MEG 1432+1433' % 'MEG 1431' + 'MEG 1442' 'MEG 1443' 'MEG 1442+1443' % 'MEG 1441' + 'MEG 1512' 'MEG 1513' 'MEG 1512+1513' % 'MEG 1511' + 'MEG 1522' 'MEG 1523' 'MEG 1522+1523' % 'MEG 1521' + 'MEG 1532' 'MEG 1533' 'MEG 1532+1533' % 'MEG 1531' + 'MEG 1542' 'MEG 1543' 'MEG 1542+1543' % 'MEG 1541' + 'MEG 1612' 'MEG 1613' 'MEG 1612+1613' % 'MEG 1611' + 'MEG 1622' 'MEG 1623' 'MEG 1622+1623' % 'MEG 1621' + 'MEG 1632' 'MEG 1633' 'MEG 1632+1633' % 'MEG 1631' + 'MEG 1642' 'MEG 1643' 'MEG 1642+1643' % 'MEG 1641' + 'MEG 1712' 'MEG 1713' 'MEG 1712+1713' % 'MEG 1711' + 'MEG 1722' 'MEG 1723' 'MEG 1722+1723' % 'MEG 1721' + 'MEG 1732' 'MEG 1733' 'MEG 1732+1733' % 'MEG 1731' + 'MEG 1742' 'MEG 1743' 'MEG 1742+1743' % 'MEG 1741' + 'MEG 1812' 'MEG 1813' 'MEG 1812+1813' % 'MEG 1811' + 'MEG 1822' 'MEG 1823' 'MEG 1822+1823' % 'MEG 1821' + 'MEG 1832' 'MEG 1833' 'MEG 1832+1833' % 'MEG 1831' + 'MEG 1842' 'MEG 1843' 'MEG 1842+1843' % 'MEG 1841' + 'MEG 1912' 'MEG 1913' 'MEG 1912+1913' % 'MEG 1911' + 'MEG 1922' 'MEG 1923' 'MEG 1922+1923' % 'MEG 1921' + 'MEG 1932' 'MEG 1933' 'MEG 1932+1933' % 'MEG 1931' + 'MEG 1942' 'MEG 1943' 'MEG 1942+1943' % 'MEG 1941' + 'MEG 2012' 'MEG 2013' 'MEG 2012+2013' % 'MEG 2011' + 'MEG 2022' 'MEG 2023' 'MEG 2022+2023' % 'MEG 2021' + 'MEG 2032' 'MEG 2033' 'MEG 2032+2033' % 'MEG 2031' + 'MEG 2042' 'MEG 2043' 'MEG 2042+2043' % 'MEG 2041' + 'MEG 2112' 'MEG 2113' 'MEG 2112+2113' % 'MEG 2111' + 'MEG 2122' 'MEG 2123' 'MEG 2122+2123' % 'MEG 2121' + 'MEG 2132' 'MEG 2133' 'MEG 2132+2133' % 'MEG 2131' + 'MEG 2142' 'MEG 2143' 'MEG 2142+2143' % 'MEG 2141' + 'MEG 2212' 'MEG 2213' 'MEG 2212+2213' % 'MEG 2211' + 'MEG 2222' 'MEG 2223' 'MEG 2222+2223' % 'MEG 2221' + 'MEG 2232' 'MEG 2233' 'MEG 2232+2233' % 'MEG 2231' + 'MEG 2242' 'MEG 2243' 'MEG 2242+2243' % 'MEG 2241' + 'MEG 2312' 'MEG 2313' 'MEG 2312+2313' % 'MEG 2311' + 'MEG 2322' 'MEG 2323' 'MEG 2322+2323' % 'MEG 2321' + 'MEG 2332' 'MEG 2333' 'MEG 2332+2333' % 'MEG 2331' + 'MEG 2342' 'MEG 2343' 'MEG 2342+2343' % 'MEG 2341' + 'MEG 2412' 'MEG 2413' 'MEG 2412+2413' % 'MEG 2411' + 'MEG 2422' 'MEG 2423' 'MEG 2422+2423' % 'MEG 2421' + 'MEG 2432' 'MEG 2433' 'MEG 2432+2433' % 'MEG 2431' + 'MEG 2442' 'MEG 2443' 'MEG 2442+2443' % 'MEG 2441' + 'MEG 2512' 'MEG 2513' 'MEG 2512+2513' % 'MEG 2511' + 'MEG 2522' 'MEG 2523' 'MEG 2522+2523' % 'MEG 2521' + 'MEG 2532' 'MEG 2533' 'MEG 2532+2533' % 'MEG 2531' + 'MEG 2542' 'MEG 2543' 'MEG 2542+2543' % 'MEG 2541' + 'MEG 2612' 'MEG 2613' 'MEG 2612+2613' % 'MEG 2611' + 'MEG 2622' 'MEG 2623' 'MEG 2622+2623' % 'MEG 2621' + 'MEG 2632' 'MEG 2633' 'MEG 2632+2633' % 'MEG 2631' + 'MEG 2642' 'MEG 2643' 'MEG 2642+2643' % 'MEG 2641' + }; + elseif count1 mskana - % % TODO: HERE THE FUNCTION THAT MAKES TO SLICE DIMENSION ALWAYS THE THIRD - % % DIMENSION, AND ALSO KEEP TRANSFORMATION MATRIX UP TO DATE - % zoiets - %if hasana; ana = shiftdim(ana,slicedim-1); end; - %if hasfun; fun = shiftdim(fun,slicedim-1); end; - %if hasmsk; msk = shiftdim(msk,slicedim-1); end; +slicerange = keyval('slicerange', varargin); if isempty(slicerange),slicerange='auto'; end +nslices = keyval('nslices', varargin); if isempty(nslices),nslices=4; end +slicedim = keyval('slicedim', varargin); if isempty(slicedim),slicedim=3; end +title_ = keyval('title', varargin); if isempty(title_),title_=''; end +colorbar1 = keyval('colorbar', varargin); if isempty(colorbar1),colorbar1='yes'; end +funparameter = keyval('funparameter', varargin); if isempty(funparameter),funparameter=[]; end +anaparameter = keyval('anaparameter', varargin); if isempty(anaparameter),anaparameter='anatomy'; end +maskparameter = keyval('maskparameter', varargin); if isempty(maskparameter),maskparameter=[]; end +flat2D = keyval('flat2D', varargin); if isempty(flat2D),flat2D=false; end +map = keyval('map', varargin); if isempty(map),map='gray'; end +transform = keyval('transform', varargin); if isempty(transform),transform=eye(4); end +tag = keyval('tag', varargin); if isempty(tag),tag=[]; end + +%%% funparameter +% has fun? +if ~isempty(funparameter) + if issubfield(data, funparameter) + hasfun = 1; + fun = getsubfield(data, funparameter); + else + error('funparameter not found in data'); + end +else + hasfun = 0; + fprintf('no functional parameter\n'); +end +if hasfun + handle_fun(fun) +end +%%% anaparameter +if isequal(anaparameter,'anatomy') + if isfield(data, 'anatomy') + hasana = 1; + mri8 = isa(data.anatomy, 'uint8'); + mri16 = isa(data.anatomy, 'uint16'); + % convert integers to single precision float if neccessary + if mri8 || mri16 + fprintf('converting anatomy to double\n'); + ana = double(data.anatomy); + else + ana = data.anatomy; + end + else + warning('no anatomical volume present, not plotting anatomy\n') + hasana = 0; + end +elseif isempty(anaparameter); + hasana = 0; + fprintf('not plotting anatomy\n'); +else + warning('do not understand anaparameter, not plotting anatomy\n') + hasana = 0; +end +%%% maskparameter +% has mask? +if ~isempty(maskparameter) + if issubfield(data, maskparameter) + if ~hasfun + error('you can not have a mask without functional data') + else + hasmsk = 1; + msk = getsubfield(data, maskparameter); + if islogical(msk) %otherwise sign() not posible + msk = double(msk); + end + end + else + error('maskparameter not found in data'); + end +else + hasmsk = 0; + fprintf('no masking parameter\n'); +end +if hasmsk + handle_msk(msk); +end + + %%%%% select slices + ss = setdiff([1 2 3], slicedim); + if ~isstr(slicerange) ind_fslice = slicerange(1); ind_lslice = slicerange(2); elseif isequal(slicerange, 'auto') if hasfun %default if isfield(data,'inside') - ind_fslice = min(find(max(max(data.inside,[],1),[],2))); - ind_lslice = max(find(max(max(data.inside,[],1),[],2))); + ind_fslice = min(find(max(max(data.inside,[],ss(1)),[],ss(2)))); + ind_lslice = max(find(max(max(data.inside,[],ss(1)),[],ss(2)))); else - ind_fslice = min(find(~isnan(max(max(fun,[],1),[],2)))); - ind_lslice = max(find(~isnan(max(max(fun,[],1),[],2)))); + ind_fslice = min(find(~isnan(max(max(fun,[],ss(1)),[],ss(2))))); + ind_lslice = max(find(~isnan(max(max(fun,[],ss(1)),[],ss(2))))); end elseif hasana %if only ana, no fun - ind_fslice = min(find(max(max(ana,[],1),[],2))); - ind_lslice = max(find(max(max(ana,[],1),[],2))); + ind_fslice = min(find(max(max(ana,[],ss(1)),[],ss(2)))); + ind_lslice = max(find(max(max(ana,[],ss(1)),[],ss(2)))); else error('no functional parameter and no anatomical parameter, can not plot'); end else error('do not understand slicerange'); end - ind_allslice = linspace(ind_fslice,ind_lslice,nslices); - ind_allslice = round(ind_allslice); - % make new ana, fun, msk, mskana with only the slices that will be plotted (slice dim is always third dimension) - if hasana; new_ana = ana(:,:,ind_allslice); clear ana; ana=new_ana; clear new_ana; end; - if hasfun; new_fun = fun(:,:,ind_allslice); clear fun; fun=new_fun; clear new_fun; end; - if hasmsk; new_msk = msk(:,:,ind_allslice); clear msk; msk=new_msk; clear new_msk; end; - %if hasmskana; new_mskana = mskana(:,:,ind_allslice); clear mskana; mskana=new_mskana; clear new_mskana; end; - - % update the dimensions of the volume - if hasana; dim=size(ana); else dim=size(fun); end; - - %%%%% make "quilts", that contain all slices on 2D patched sheet - % Number of patches along sides of Quilt (M and N) - % Size (in voxels) of side of patches of Quilt (m and n) - m = dim(1); - n = dim(2); - M = ceil(sqrt(dim(3))); - N = ceil(sqrt(dim(3))); - num_patch = N*M; - if slicedim~=3 - error('only supported for slicedim=3'); + + if nslices==1 + ind_allslice = (ind_fslice+ind_lslice)/2; + elseif nslices==2 + ind_allslice = [ind_fslice+(ind_lslice-ind_fslice)/5 ind_fslice+4*(ind_lslice-ind_fslice)/5]; + else + ind_allslice = linspace(ind_fslice,ind_lslice,nslices); end - num_slice = (dim(slicedim)); - num_empt = num_patch-num_slice; - % put empty slides on ana, fun, msk, mskana to fill Quilt up - if hasana; ana(:,:,end+1:num_patch)=0; end; - if hasfun; fun(:,:,end+1:num_patch)=0; end; - if hasmsk; msk(:,:,end+1:num_patch)=0; end; - %if hasmskana; mskana(:,:,end:num_patch)=0; end; - % put the slices in the quilt - for iSlice = 1:num_slice - xbeg = floor((iSlice-1)./M); - ybeg = mod(iSlice-1, M); - if hasana - quilt_ana(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(ana(:,:,iSlice)); + ind_allslice = round(ind_allslice); + + + % if i want to plot a 2D representation of several slices + if flat2D + + % make new ana, fun, msk, mskana with only the slices that will be plotted (slice dim is always third dimension) + if slicedim == 3 + if hasana; new_ana = ana(:,:,ind_allslice); clear ana; ana=new_ana; clear new_ana; end; + if hasfun; new_fun = fun(:,:,ind_allslice); clear fun; fun=new_fun; clear new_fun; end; + if hasmsk; new_msk = msk(:,:,ind_allslice); clear msk; msk=new_msk; clear new_msk; end; + elseif slicedim == 2 + if hasana; new_ana = ana(:,ind_allslice,:); clear ana; ana=new_ana; clear new_ana; end; + if hasfun; new_fun = fun(:,ind_allslice,:); clear fun; fun=new_fun; clear new_fun; end; + if hasmsk; new_msk = msk(:,ind_allslice,:); clear msk; msk=new_msk; clear new_msk; end; + elseif slicedim == 1 + if hasana; new_ana = ana(ind_allslice,:,:); clear ana; ana=new_ana; clear new_ana; end; + if hasfun; new_fun = fun(ind_allslice,:,:); clear fun; fun=new_fun; clear new_fun; end; + if hasmsk; new_msk = msk(ind_allslice,:,:); clear msk; msk=new_msk; clear new_msk; end; + else + error('Error: incorrect slice dimension specification') end - if hasfun - quilt_fun(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(fun(:,:,iSlice)); + %if hasmskana; new_mskana = mskana(:,:,ind_allslice); clear mskana; mskana=new_mskana; clear new_mskana; end; + + % update the dimensions of the volume + if hasana; dim=size(ana); else dim=size(fun); end; + + %%%%% make "quilts", that contain all slices on 2D patched sheet + % Number of patches along sides of Quilt (M and N) + % Size (in voxels) of side of patches of Quilt (m and n) + + if slicedim == 3 + m = dim(1); + n = dim(2); + if length(dim)==2 + dim(3) = 1; + end + M = ceil(sqrt(dim(3))); + N = ceil(sqrt(dim(3))); + elseif slicedim == 2 + m = dim(1); + n = dim(3); + M = ceil(sqrt(dim(2))); + N = ceil(sqrt(dim(2))); + elseif slicedim == 1 + m = dim(2); + n = dim(3); + M = ceil(sqrt(dim(1))); + N = ceil(sqrt(dim(1))); end - if hasmsk - quilt_msk(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(msk(:,:,iSlice)); + + num_patch = N*M; + % if slicedim~=3 + % error('only supported for slicedim=3'); + % end + num_slice = (dim(slicedim)); + num_empt = num_patch-num_slice; + + % put empty slides on ana, fun, msk, mskana to fill Quilt up + if slicedim == 3 + if hasana; ana(:,:,end+1:num_patch)=0; end; + if hasfun; fun(:,:,end+1:num_patch)=0; end; + if hasmsk; msk(:,:,end+1:num_patch)=0; end; + elseif slicedim == 2 + if hasana; ana(:,end+1:num_patch,:)=0; end; + if hasfun; fun(:,end+1:num_patch,:)=0; end; + if hasmsk; msk(:,end+1:num_patch,:)=0; end; + elseif slicedim == 1 + if hasana; ana(end+1:num_patch,:,:)=0; end; + if hasfun; fun(end+1:num_patch,:,:)=0; end; + if hasmsk; msk(end+1:num_patch,:,:)=0; end; end - % if hasmskana - % quilt_mskana(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(mskana(:,:,iSlice)); - % end - end - % make vols and scales, containes volumes to be plotted (fun, ana, msk) %added ingnie - if hasana; vols2D{1} = quilt_ana; scales{1} = []; end; % needed when only plotting ana - if hasfun; vols2D{2} = quilt_fun; scales{2} = [fcolmin fcolmax]; end; - if hasmsk; vols2D{3} = quilt_msk; scales{3} = [opacmin opacmax]; end; - plot2D(vols2D, scales); + %if hasmskana; mskana(:,:,end:num_patch)=0; end; + % put the slices in the quilt + for iSlice = 1:num_slice + xbeg = floor((iSlice-1)./M); + ybeg = mod(iSlice-1, M); + if slicedim == 3 + if hasana + quilt_ana(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(ana(:,:,iSlice)); + end + if hasfun + quilt_fun(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(fun(:,:,iSlice)); + end + if hasmsk + quilt_msk(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(msk(:,:,iSlice)); + end + elseif slicedim == 2 + if hasana + quilt_ana(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(ana(:,iSlice,:)); + end + if hasfun + quilt_fun(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(fun(:,iSlice,:)); + end + if hasmsk + quilt_msk(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(msk(:,iSlice,:)); + end + elseif slicedim == 1 + if hasana + quilt_ana(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(ana(iSlice,:,:)); + end + if hasfun + quilt_fun(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(fun(iSlice,:,:)); + end + if hasmsk + quilt_msk(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(msk(iSlice,:,:)); + end + end - axis off + % if hasmskana + % quilt_mskana(ybeg.*m+1:(ybeg+1).*m, xbeg.*n+1:(xbeg+1).*n)=squeeze(mskana(:,:,iSlice)); + % end + end + % make vols and scales, containes volumes to be plotted (fun, ana, msk) %added ingnie + if hasana; vols2D{1} = quilt_ana; scales{1} = []; end; % needed when only plotting ana + if hasfun; vols2D{2} = quilt_fun; scales{2} = [fcolmin fcolmax]; end; + if hasmsk; vols2D{3} = quilt_msk; scales{3} = [opacmin opacmax]; end; - if strcmp(colorbar1, 'yes'), - if hasfun - % use a normal Matlab coorbar - hc = colorbar; - set(hc, 'YLim', [fcolmin fcolmax]); - else - warning('no colorbar possible without functional data') + plot2D(vols2D, scales); + axis off + + if strcmp(colorbar1, 'yes'), + if hasfun + % use a normal Matlab colorbar + hc = colorbar; + set(hc, 'YLim', [fcolmin fcolmax]); + else + warning('no colorbar possible without functional data') + end end + + else + plot_slice_sub(ana,slicedim,ind_allslice,map,transform); end + + if ~isempty(title_), title(title_); end - if ~isempty(title), title(title); end +function plot_slice_sub(data,slicedim,ind_allslice,map,transform) +if ~ishold, hold on, end +ds = size(data); - % get the output cfg - cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); +% determine location of each anatomical voxel in its own voxel coordinates +i = 1:ds(1); +j = 1:ds(2); +k = 1:ds(3); +[I, J, K] = ndgrid(i, j, k); +ijk = [I(:) J(:) K(:) ones(prod(ds),1)]'; -end +% determine location of each anatomical voxel in head coordinates +xyz = transform * ijk; +xyz = permute(xyz,[2 1 3]); +xdata = reshape(xyz(:,1), [ds(2) ds(1) ds(3)]); +ydata = reshape(xyz(:,2), [ds(2) ds(1) ds(3)]); +zdata = reshape(xyz(:,3), [ds(2) ds(1) ds(3)]); +if slicedim == 3 + for i=1:length(ind_allslice) + cdata = squeeze(data(:,:,ind_allslice(i))); + xdata_ = squeeze(xdata(:,:,ind_allslice(i))); + ydata_ = squeeze(ydata(:,:,ind_allslice(i))); + zdata_ = squeeze(zdata(:,:,ind_allslice(i))); + news = surface('cdata',cdata,'alphadata',cdata, 'xdata',xdata_, 'ydata',ydata_, 'zdata',zdata_); + set(news,'facec','interp','edgec','n','facea',0.5); + end +elseif slicedim == 2 + for i=1:length(ind_allslice) + cdata = squeeze(data(:,ind_allslice(i),:)); + xdata_ = squeeze(xdata(:,ind_allslice(i),:)); + ydata_ = squeeze(ydata(:,ind_allslice(i),:)); + zdata_ = squeeze(zdata(:,ind_allslice(i),:)); + news = surface('cdata',cdata,'alphadata',cdata, 'xdata',xdata_, 'ydata',ydata_, 'zdata',zdata_); + set(news,'facec','interp','edgec','n','facea',0.5); + end +elseif slicedim == 1 + for i=1:length(ind_allslice) + cdata = squeeze(data(ind_allslice(i),:,:)); + xdata_ = squeeze(xdata(ind_allslice(i),:,:)); + ydata_ = squeeze(ydata(ind_allslice(i),:,:)); + zdata_ = squeeze(zdata(ind_allslice(i),:,:)); + news = surface('cdata',cdata,'alphadata',cdata, 'xdata',xdata_, 'ydata',ydata_, 'zdata',zdata_); + set(news,'facec','interp','edgec','n','facea',0.5); + end +end +view(45,45) +colormap(map) +axis off +axis vis3d +axis equal -function plot2D(vols2D, scales); +function plot2D(vols2D, scales) cla; % put 2D volumes in fun, ana and msk hasana = length(vols2D)>0 && ~isempty(vols2D{1}); @@ -166,12 +377,14 @@ function plot_slice(data,varargin) clear amin amax; % convert anatomy into RGB values ana = cat(3, ana, ana, ana); - ha = imagesc(ana); +% ha = imagesc(ana); + plot_matrix(ana) end hold on if hasfun - hf = imagesc(fun); +% hf = imagesc(fun); + plot_matrix(fun) caxis(scales{2}); % apply the opacity mask to the functional data if hasmsk @@ -188,40 +401,110 @@ function plot_slice(data,varargin) axis tight axis xy -function [vols2D] = handle_ortho(vols, indx, slicedir, dim); - -% put 2Dvolumes in fun, ana and msk -if length(vols)>=1 && isempty(vols{1}); hasana=0; else ana=vols{1}; hasana=1; end; -if length(vols)>=2 - if isempty(vols{2}); hasfun=0; else fun=vols{2}; hasfun=1; end; -else hasfun=0; end -if length(vols)>=3 - if isempty(vols{3}); hasmsk=0; else msk=vols{3}; hasmsk=1; end; -else hasmsk=0; end - -% select the indices of the intersection -xi = indx(1); -yi = indx(2); -zi = indx(3); - -% select the slice to plot -if slicedir==1 - yi = 1:dim(2); - zi = 1:dim(3); -elseif slicedir==2 - xi = 1:dim(1); - zi = 1:dim(3); -elseif slicedir==3 - xi = 1:dim(1); - yi = 1:dim(2); -end - -% cut out the slice of interest -if hasana; ana = squeeze(ana(xi,yi,zi)); end; -if hasfun; fun = squeeze(fun(xi,yi,zi)); end; -if hasmsk; msk = squeeze(msk(xi,yi,zi)); end; +function handle_fun(fun) + % determine scaling min and max (fcolmin fcolmax) and funcolormap + funmin = min(fun(:)); + funmax = max(fun(:)); + % smart lims: make from auto other string + if isequal(cfg.funcolorlim,'auto') + if sign(funmin)>-1 && sign(funmax)>-1 + cfg.funcolorlim = 'zeromax'; + elseif sign(funmin)<1 && sign(funmax)<1 + cfg.funcolorlim = 'minzero'; + else + cfg.funcolorlim = 'maxabs'; + end + end + if ischar(cfg.funcolorlim) + % limits are given as string + if isequal(cfg.funcolorlim,'maxabs') + fcolmin = -max(abs([funmin,funmax])); + fcolmax = max(abs([funmin,funmax])); + if isequal(cfg.funcolormap,'auto'); cfg.funcolormap = 'jet'; end; + elseif isequal(cfg.funcolorlim,'zeromax') + fcolmin = 0; + fcolmax = funmax; + if isequal(cfg.funcolormap,'auto'); cfg.funcolormap = 'hot'; end; + elseif isequal(cfg.funcolorlim,'minzero') + fcolmin = funmin; + fcolmax = 0; + if isequal(cfg.funcolormap,'auto'); cfg.funcolormap = 'cool'; end; + else + error('do not understand cfg.funcolorlim'); + end + else + % limits are numeric + fcolmin = cfg.funcolorlim(1); + fcolmax = cfg.funcolorlim(2); + % smart colormap + if isequal(cfg.funcolormap,'auto') + if sign(fcolmin) == -1 && sign(fcolmax) == 1 + cfg.funcolormap = 'jet'; + else + if fcolmin < 0 + cfg.funcolormap = 'cool'; + else + cfg.funcolormap = 'hot'; + end + end + end + end %if ischar + clear funmin funmax; + % ensure that the functional data is real + if ~isreal(fun) + fprintf('taking absolute value of complex data\n'); + fun = abs(fun); + end -%put fun, ana and msk in vols2D -if hasana; vols2D{1} = ana; end; -if hasfun; vols2D{2} = fun; end; -if hasmsk; vols2D{3} = msk; end; +function handle_msk(msk) + mskmin = min(msk(:)); + mskmax = max(msk(:)); + % determine the opacity limits and the opacity map + % smart lims: make from auto other string, or equal to funcolorlim if funparameter == maskparameter + if isequal(cfg.opacitylim,'auto') + if isequal(cfg.funparameter,cfg.maskparameter) + cfg.opacitylim = cfg.funcolorlim; + else + if sign(mskmin)>-1 && sign(mskmax)>-1 + cfg.opacitylim = 'zeromax'; + elseif sign(mskmin)<1 && sign(mskmax)<1 + cfg.opacitylim = 'minzero'; + else + cfg.opacitylim = 'maxabs'; + end + end + end + if ischar(cfg.opacitylim) + % limits are given as string + switch cfg.opacitylim + case 'zeromax' + opacmin = 0; + opacmax = mskmax; + if isequal(cfg.opacitymap,'auto'), cfg.opacitymap = 'rampup'; end; + case 'minzero' + opacmin = mskmin; + opacmax = 0; + if isequal(cfg.opacitymap,'auto'), cfg.opacitymap = 'rampdown'; end; + case 'maxabs' + opacmin = -max(abs([mskmin, mskmax])); + opacmax = max(abs([mskmin, mskmax])); + if isequal(cfg.opacitymap,'auto'), cfg.opacitymap = 'vdown'; end; + otherwise + error('incorrect specification of cfg.opacitylim'); + end + else + % limits are numeric + opacmin = cfg.opacitylim(1); + opacmax = cfg.opacitylim(2); + if isequal(cfg.opacitymap,'auto') + if sign(opacmin)>-1 && sign(opacmax)>-1 + cfg.opacitymap = 'rampup'; + elseif sign(opacmin)<1 && sign(opacmax)<1 + cfg.opacitymap = 'rampdown'; + else + cfg.opacitymap = 'vdown'; + end + end + end % handling opacitylim and opacitymap + clear mskmin mskmax; + \ No newline at end of file diff --git a/external/fieldtrip/private/plot_text.m b/external/fieldtrip/private/plot_text.m index a657901..73b3a94 100644 --- a/external/fieldtrip/private/plot_text.m +++ b/external/fieldtrip/private/plot_text.m @@ -1,10 +1,17 @@ -function plot_text(X, Y, str, varargin) +function [varargout] = plot_text(X, Y, str, varargin) % PLOT_TEXT % Copyrights (C) 2009, Robert Oostenveld % % $Log: plot_text.m,v $ +% Revision 1.5 2009/08/05 08:52:09 roboos +% added HorizontalAlignment option +% use CamelCase for options that are passed on to the default set() function +% +% Revision 1.4 2009/06/02 15:40:36 giopia +% added varargout to pass handle +% % Revision 1.3 2009/04/14 19:48:28 roboos % added keyvalcheck % @@ -13,16 +20,17 @@ function plot_text(X, Y, str, varargin) % % get the optional input arguments -keyvalcheck(varargin, 'optional', {'hpos', 'vpos', 'width', 'height', 'hlim', 'vlim', 'color', 'fontsize', 'fontname'}); +keyvalcheck(varargin, 'optional', {'hpos', 'vpos', 'width', 'height', 'hlim', 'vlim', 'Color', 'FontSize', 'FontName', 'HorizontalAlignment'}); hpos = keyval('hpos', varargin); vpos = keyval('vpos', varargin); width = keyval('width', varargin); height = keyval('height', varargin); hlim = keyval('hlim', varargin); vlim = keyval('vlim', varargin); -color = keyval('color', varargin); if isempty(color), color = 'k'; end -fontsize = keyval('fontsize', varargin); -fontname = keyval('fontname', varargin); +Color = keyval('Color', varargin); if isempty(Color), Color = 'k'; end +FontSize = keyval('FontSize', varargin); +FontName = keyval('FontName', varargin); +HorizontalAlignment = keyval('HorizontalAlignment', varargin); if isempty(HorizontalAlignment), HorizontalAlignment = 'center'; end abc = axis; if isempty(hlim) @@ -68,10 +76,12 @@ function plot_text(X, Y, str, varargin) Y = Y + vpos; h = text(X, Y, str); -set(h, 'HorizontalAlignment', 'center'); -% set(h, 'VerticalAlignment', 'middle'); % this is already the default -set(h, 'Color', color); -if ~isempty(fontsize), set(h, 'FontSize', fontsize); end -if ~isempty(fontname), set(h, 'FontName', fontname); end - +set(h, 'HorizontalAlignment', HorizontalAlignment); +set(h, 'Color', Color); +if ~isempty(FontSize), set(h, 'FontSize', FontSize); end +if ~isempty(FontName), set(h, 'FontName', FontName); end +% the (optional) output is the handle +if nargout == 1; + varargout{1} = h; +end diff --git a/external/fieldtrip/private/plot_topo.m b/external/fieldtrip/private/plot_topo.m index e69de29..61db650 100644 --- a/external/fieldtrip/private/plot_topo.m +++ b/external/fieldtrip/private/plot_topo.m @@ -0,0 +1,122 @@ +function [varargout] = plot_topo(chanX, chanY, dat, varargin) + +% PLOT_TOPO interpolates and plots the 2-D spatial topography of the +% potential or field distribution over the head +% +% Use as +% plot_topo(x, y, val, ...) +% +% Additional options should be specified in key-value pairs and can be +% 'hpos' +% 'vpos' +% 'width' +% 'height' +% 'shading' +% 'gridscale' +% 'mask' +% 'outline' + +% Copyrights (C) 2009, Giovanni Piantoni +% +% $Log: plot_topo.m,v $ +% Revision 1.7 2009/08/12 15:15:18 jansch +% also changed the order of inputs on the first line of the function +% +% Revision 1.6 2009/08/05 08:58:54 roboos +% changed the order of the input arguments to plot_topo from (val, x, y) into (x, y, val) +% +% Revision 1.5 2009/08/05 08:53:20 roboos +% plot the outline of the head if specified +% keep hold on/off the same +% +% Revision 1.4 2009/07/29 15:04:16 giopia +% resolved ambiguity of var mask +% +% Revision 1.3 2009/07/29 10:24:24 roboos +% construct the binary image for masking inside this function and reuse it as long as the relevant input does not change +% this is achieved with a persistent variable and by checking the input arguments +% +% Revision 1.2 2009/06/02 15:36:25 giopia +% first implementation based on topoplot.m +% + +% these are for speeding up the plotting on subsequent calls +persistent previous_argin previous_maskimage + +holdflag = ishold; +hold on + +% get the optional input arguments +keyvalcheck(varargin, 'optional', {'hpos', 'vpos', 'width', 'height', 'gridscale', 'shading', 'mask', 'outline'}); +hpos = keyval('hpos', varargin); if isempty(hpos); hpos = 0; end +vpos = keyval('vpos', varargin); if isempty(vpos); vpos = 0; end +width = keyval('width', varargin); if isempty(width); width = 1; end +height = keyval('height', varargin); if isempty(height); height = 1; end +gridscale = keyval('gridscale', varargin); if isempty(gridscale); gridscale = 67; end; % 67 in original +shading = keyval('shading', varargin); if isempty(shading); shading = 'flat'; end; +mask = keyval('mask', varargin); +outline = keyval('outline', varargin); + +% try to speed up the preparation of the mask on subsequent calls +current_argin = {chanX, chanY, gridscale, mask}; +if isequal(current_argin, previous_argin) + % don't construct the binary image, but reuse it from the previous call + maskimage = previous_maskimage; +elseif ~isempty(mask) + % convert the mask into a binary image + maskimage = false(gridscale); + hlim = [min(chanX) max(chanX)]; + vlim = [min(chanY) max(chanY)]; + xi = linspace(hlim(1), hlim(2), gridscale); % x-axis for interpolation (row vector) + yi = linspace(vlim(1), vlim(2), gridscale); % y-axis for interpolation (row vector) + [Xi,Yi] = meshgrid(xi', yi); + for i=1:length(mask) + mask{i}(end+1,:) = mask{i}(1,:); % force them to be closed + maskimage(inside_contour([Xi(:) Yi(:)], mask{i})) = true; + end +else + maskimage = []; +end + +chanX = chanX * width + hpos; +chanY = chanY * height + vpos; + +hlim = [min(chanX) max(chanX)]; +vlim = [min(chanY) max(chanY)]; + +xi = linspace(hlim(1), hlim(2), gridscale); % x-axis for interpolation (row vector) +yi = linspace(vlim(1), vlim(2), gridscale); % y-axis for interpolation (row vector) +[Xi,Yi,Zi] = griddata(chanX', chanY, dat, xi', yi, 'v4'); % interpolate the topographic data + +if ~isempty(maskimage) + % apply anatomical mask to the data, i.e. that determines that the interpolated data outside the circle is not displayed + Zi(~maskimage) = NaN; +end + +deltax = xi(2)-xi(1); % length of grid entry +deltay = yi(2)-yi(1); % length of grid entry +h = surface(Xi-deltax/2,Yi-deltay/2,zeros(size(Zi)), Zi, 'EdgeColor', 'none', 'FaceColor', shading); + +% plot the outline of the head, ears and nose +for i=1:length(outline) + xval = outline{i}(:,1) * width + hpos; + yval = outline{i}(:,2) * height + vpos; + plot(xval, yval, 'Color', 'k', 'LineWidth', 1) +end + +% the (optional) output is the handle +if nargout == 1 + varargout{1} = h; +end + +% remember the current input and output arguments, so that they can be +% reused on a subsequent call in case the same input argument is given +if isempty(previous_argin) + previous_argin = current_argin; + previous_maskimage = maskimage; +end + +if ~holdflag + hold off +end + diff --git a/external/fieldtrip/private/plot_topo3d.m b/external/fieldtrip/private/plot_topo3d.m index e69de29..3a7b5d1 100644 --- a/external/fieldtrip/private/plot_topo3d.m +++ b/external/fieldtrip/private/plot_topo3d.m @@ -0,0 +1,168 @@ +function plot_topo3d(pnt, val, varargin) + +% PLOT_TOPO3D makes a 3-D topographic representation of the electric +% potential or field at the sensor locations +% +% Use as +% plot_topo3d(pos, val, ...); +% where the channel positions are given as a Nx3 matrix and the values are +% given as Nx1 vector. + +% Optional input arguments should be specified in key-value pairs and can include +% ... +% +% See also PLOT_TOPO2D, PLOTTING + +% Copyright (C) 2009, Robert Oostenveld +% +% $Log: plot_topo3d.m,v $ +% Revision 1.3 2009/06/03 09:55:10 roboos +% prevent camera lighting of the interpolated helmet/scalp surface +% +% Revision 1.2 2009/06/03 08:55:55 roboos +% first functional version +% + +% get the optional input arguments +topostyle = keyval('topostyle', varargin); if isempty(topostyle), topostyle = 'color'; end +contourstyle = keyval('contourstyle', varargin); if isempty(contourstyle), contourstyle = false; end +isocontour = keyval('isocontour', varargin); if isempty(isocontour), isocontour = 'auto'; end + +% the interpolation requires a triangulation +tri = projecttri(pnt, 'delaunay'); + +% everything is added to the current figure +holdflag = ishold; +hold on + +if ~isequal(topostyle, false) + switch topostyle + case 'color' + % plot a 2D or 3D triangulated surface with linear interpolation + if length(val)==size(pnt,1) + hs = patch('Vertices', pnt, 'Faces', tri, 'FaceVertexCData', val, 'FaceColor', 'interp'); + else + hs = patch('Vertices', pnt, 'Faces', tri, 'CData', val, 'FaceColor', 'flat'); + end + set(hs, 'EdgeColor', 'none'); + set(hs, 'FaceLighting', 'none'); + otherwise + error('unsupported topostyle'); + end % switch contourstyle +end % plot the interpolated topography + + +if ~isequal(contourstyle, false) + + if isequal(isocontour, 'auto') + minval = min(val); + maxval = max(val); + scale = max(abs(minval), abs(maxval)); + scale = 10^(floor(log10(scale))-1); + minval = floor(minval/scale)*scale; + maxval = ceil(maxval/scale)*scale; + isocontour = minval:scale:maxval; + end + + triangle_val = val(tri); + triangle_min = min(triangle_val, [], 2); + triangle_max = max(triangle_val, [], 2); + + for cnt_indx=1:length(isocontour) + cnt = isocontour(cnt_indx); + use = cnt>=triangle_min & cnt<=triangle_max; + counter = 0; + intersect1 = []; + intersect2 = []; + + for tri_indx=find(use)' + pos = pnt(tri(tri_indx,:), :); + v(1) = triangle_val(tri_indx,1); + v(2) = triangle_val(tri_indx,2); + v(3) = triangle_val(tri_indx,3); + la(1) = (cnt-v(1)) / (v(2)-v(1)); % abcissa between vertex 1 and 2 + la(2) = (cnt-v(2)) / (v(3)-v(2)); % abcissa between vertex 2 and 3 + la(3) = (cnt-v(3)) / (v(1)-v(3)); % abcissa between vertex 1 and 2 + abc(1,:) = pos(1,:) + la(1) * (pos(2,:) - pos(1,:)); + abc(2,:) = pos(2,:) + la(2) * (pos(3,:) - pos(2,:)); + abc(3,:) = pos(3,:) + la(3) * (pos(1,:) - pos(3,:)); + counter = counter + 1; + sel = find(la>=0 & la<=1); + intersect1(counter, :) = abc(sel(1),:); + intersect2(counter, :) = abc(sel(2),:); + end + + % remember the details for external reference + contour(cnt_indx).level = cnt; + contour(cnt_indx).n = counter; + contour(cnt_indx).intersect1 = intersect1; + contour(cnt_indx).intersect2 = intersect2; + end + + % collect all different contour isocontour for plotting + intersect1 = []; + intersect2 = []; + cntlevel = []; + for cnt_indx=1:length(isocontour) + intersect1 = [intersect1; contour(cnt_indx).intersect1]; + intersect2 = [intersect2; contour(cnt_indx).intersect2]; + cntlevel = [cntlevel; ones(contour(cnt_indx).n,1) * isocontour(cnt_indx)]; + end + + X = [intersect1(:,1) intersect2(:,1)]'; + Y = [intersect1(:,2) intersect2(:,2)]'; + C = [cntlevel(:) cntlevel(:)]'; + + if size(pnt,2)>2 + Z = [intersect1(:,3) intersect2(:,3)]'; + else + Z = zeros(2, length(cntlevel)); + end + + switch contourstyle + case 'black' + % make black-white contours + hc = []; + for i=1:length(cntlevel) + if cntlevel(i)>0 + linestyle = '-'; + linewidth = 1; + elseif cntlevel(i)<0 + linestyle = '--'; + linewidth = 1; + else + linestyle = '-'; + linewidth = 2; + end + h1 = patch('XData', X(:,i), 'Ydata', Y(:,i), ... + 'ZData', Z(:,i), 'CData', C(:,i), ... + 'facecolor','none','edgecolor','black', ... + 'linestyle', linestyle, 'linewidth', linewidth, ... + 'userdata',cntlevel(i)); + hc = [hc; h1]; + end + + case 'color' + % make full-color contours + hc = []; + for i=1:length(cntlevel) + h1 = patch('XData', X(:,i), 'Ydata', Y(:,i), ... + 'ZData', Z(:,i), 'CData', C(:,i), ... + 'facecolor','none','edgecolor','flat',... + 'userdata',cntlevel(i)); + hc = [hc; h1]; + end + + otherwise + error('unsupported contourstyle'); + end % switch contourstyle + +end % plot the contours + +axis off +axis vis3d +axis equal + +if ~holdflag + hold off +end diff --git a/external/fieldtrip/private/plot_vector.m b/external/fieldtrip/private/plot_vector.m index ddeec7e..16114ab 100644 --- a/external/fieldtrip/private/plot_vector.m +++ b/external/fieldtrip/private/plot_vector.m @@ -1,4 +1,4 @@ -function plot_vector(varargin) +function [varargout] = plot_vector(varargin) % PLOT_VECTOR % @@ -15,8 +15,10 @@ function plot_vector(varargin) % 'hlim' % 'vlim' % 'style' -% 'axis' can be 'yes' or 'no' -% 'box' can be 'yes' or 'no' +% 'axis' can be 'yes' or 'no' +% 'box' can be 'yes' or 'no' +% 'highlight' +% 'highlightstyle' % % Example use % plot_vector(randn(1,100), 'width', 1, 'height', 1, 'hpos', 0, 'vpos', 0) @@ -24,6 +26,19 @@ function plot_vector(varargin) % Copyrights (C) 2009, Robert Oostenveld % % $Log: plot_vector.m,v $ +% Revision 1.10 2009/07/30 09:13:58 ingnie +% fixed bug in determining if function was called as plot(x,y,...) or plot(y,...) +% +% Revision 1.9 2009/07/14 16:14:45 roboos +% fixed the plotting of the axes, which were not at [0, 0] +% some general cleanup +% +% Revision 1.8 2009/06/04 13:11:54 crimic +% added highlight option +% +% Revision 1.7 2009/06/02 15:42:52 giopia +% correct error in first if-statement and added varargout for handle +% % Revision 1.6 2009/04/15 20:00:45 roboos % small change in input parsing % @@ -37,8 +52,10 @@ function plot_vector(varargin) % many small changes to make it fully functional % -if nargin>1 && isnumeric(varargin{1}) && isnumeric(varargin{2}) -if nargin>2 && all(cellfun(@isnumeric, varargin(1:2))) +holdflag = ishold; +hold on + +if nargin>1 && all(cellfun(@isnumeric, varargin(1:2))) % the function was called like plot(x, y, ...) hdat = varargin{1}; vdat = varargin{2}; @@ -55,25 +72,26 @@ function plot_vector(varargin) end % get the optional input arguments -keyvalcheck(varargin, 'optional', {'hpos', 'vpos', 'width', 'height', 'hlim', 'vlim', 'style', 'axis', 'box'}); -hpos = keyval('hpos', varargin); -vpos = keyval('vpos', varargin); -width = keyval('width', varargin); -height = keyval('height', varargin); -hlim = keyval('hlim', varargin); -vlim = keyval('vlim', varargin); -style = keyval('style', varargin); if isempty(style), style = '-'; end -axis = keyval('axis', varargin); if isempty(axis), axis = false; end -box = keyval('box', varargin); if isempty(box), box = false; end -% label = keyval('label', varargin); % FIXME - -if isempty(hlim) - hlim = 'maxmin'; -end +keyvalcheck(varargin, 'optional', {'hpos', 'vpos', 'width', 'height', 'hlim', 'vlim', 'style', 'label', 'fontsize', 'axis', 'box','highlight','highlightstyle'}); +hpos = keyval('hpos', varargin); +vpos = keyval('vpos', varargin); +width = keyval('width', varargin); +height = keyval('height', varargin); +hlim = keyval('hlim', varargin); if isempty(hlim), hlim = 'maxmin'; end +vlim = keyval('vlim', varargin); if isempty(vlim), vlim = 'maxmin'; end +style = keyval('style', varargin); if isempty(style), style = '-'; end +label = keyval('label', varargin); +fontsize = keyval('fontsize', varargin); +axis = keyval('axis', varargin); if isempty(axis), axis = false; end +box = keyval('box', varargin); if isempty(box), box = false; end +highlight = keyval('highlight', varargin); +highlightstyle = keyval('highlightstyle', varargin); if isempty(highlightstyle), highlightstyle = 'box'; end + +% convert the yes/no strings into boolean values +axis = istrue(axis); +box = istrue(box); -if isempty(vlim) - vlim = 'maxmin'; -end +% label = keyval('label', varargin); % FIXME if ischar(hlim) switch hlim @@ -99,19 +117,19 @@ function plot_vector(varargin) end % switch end % if ischar -if isempty(hpos); + +if isempty(hpos) && ~isempty(hlim) hpos = (hlim(1)+hlim(2))/2; end - -if isempty(vpos); +if isempty(vpos) && ~isempty(vlim) vpos = (vlim(1)+vlim(2))/2; end -if isempty(width), +if isempty(width) && ~isempty(hlim) width = hlim(2)-hlim(1); end -if isempty(height), +if isempty(height) && ~isempty(vlim) height = vlim(2)-vlim(1); end @@ -123,7 +141,6 @@ function plot_vector(varargin) hdat = hdat .* width; % then shift to the new horizontal position hdat = hdat + hpos; - % first shift the vertical axis to zero vdat = vdat - (vlim(1)+vlim(2))/2; % then scale to length 1 @@ -133,17 +150,62 @@ function plot_vector(varargin) % then shift to the new vertical position vdat = vdat + vpos; -plot(hdat, vdat, style); +if ~isempty(highlight) + switch highlightstyle + case 'box' + % find the sample number where the highligh begins and ends + if ~islogical(highlight) + highlight=logical(highlight); + warning('converting mask to logical values') + end + begsample = find(diff([0 highlight 0])== 1); + endsample = find(diff([0 highlight 0])==-1)-1; + for i=1:length(begsample) + begx = hdat(begsample(i)); + endx = hdat(endsample(i)); + plot_box([begx endx vpos-height/2 vpos+height/2], 'facecolor', [.6 .6 .6], 'edgecolor', 'none'); + end + case 'thickness' + error('unsupported highlightstyle') + case 'opacity' + error('unsupported highlightstyle') + otherwise + error('unsupported highlightstyle') + end % switch highlightstyle +end -if istrue(box) - boxposition = zeros(1,4); - % this plots a box around the original hpos/vpos with appropriate width/height +h = plot(hdat, vdat, style); + +if ~isempty(label) boxposition(1) = hpos - width/2; boxposition(2) = hpos + width/2; boxposition(3) = vpos - height/2; boxposition(4) = vpos + height/2; - plot_box(boxposition); + h = text(boxposition(1), boxposition(4), label); + if ~isempty(fontsize) + set(h, 'Fontsize', fontsize); + end +end +if box + boxposition = zeros(1,4); + % this plots a box around the original hpos/vpos with appropriate width/height + x1 = hpos - width/2; + x2 = hpos + width/2; + y1 = vpos - height/2; + y2 = vpos + height/2; + + X = [x1 x2 x2 x1 x1]; + Y = [y1 y1 y2 y2 y1]; + line(X, Y); + +% % this plots a box around the original hpos/vpos with appropriate width/height +% boxposition(1) = hpos - width/2; +% boxposition(2) = hpos + width/2; +% boxposition(3) = vpos - height/2; +% boxposition(4) = vpos + height/2; +% plot_box(boxposition, 'facecolor', 'none', 'edgecolor', 'k'); + % this plots a box around the complete data % boxposition(1) = hlim(1); % boxposition(2) = hlim(2); @@ -152,23 +214,29 @@ function plot_vector(varargin) % plot_box(boxposition, 'hpos', hpos, 'vpos', vpos, 'width', width, 'height', height, 'hlim', hlim, 'vlim', vlim); end -if istrue(axis) - % X = hlim; - % Y = [0 0]; - % plot_line(X, Y, 'hpos', hpos, 'vpos', vpos, 'width', width, 'height', height, 'hlim', hlim, 'vlim', vlim); - % str = sprintf('%g', hlim(1)); plot_text(X(1), Y(1), str, 'hpos', hpos, 'vpos', vpos, 'width', width, 'height', height, 'hlim', hlim, 'vlim', vlim); - % str = sprintf('%g', hlim(2)); plot_text(X(2), Y(2), str, 'hpos', hpos, 'vpos', vpos, 'width', width, 'height', height, 'hlim', hlim, 'vlim', vlim); - +if axis + % determine where the original [0, 0] in the data is located in the scaled and shifted axes + x0 = interp1(hlim, hpos + [-width/2 width/2 ], 0, 'linear', 'extrap'); + y0 = interp1(vlim, vpos + [-height/2 height/2], 0, 'linear', 'extrap'); + X = [hpos-width/2 hpos+width/2]; - Y = [vpos vpos]; + Y = [y0 y0]; plot_line(X, Y); - str = sprintf('%g', hlim(1)); plot_text(X(1), Y(1), str); - str = sprintf('%g', hlim(2)); plot_text(X(2), Y(2), str); - - X = [hpos hpos]; + % str = sprintf('%g', hlim(1)); plot_text(X(1), Y(1), str); + % str = sprintf('%g', hlim(2)); plot_text(X(2), Y(2), str); + + X = [x0 x0]; Y = [vpos-height/2 vpos+height/2]; plot_line(X, Y); - str = sprintf('%g', vlim(1)); plot_text(X(1), Y(1), str); - str = sprintf('%g', vlim(2)); plot_text(X(2), Y(2), str); + % str = sprintf('%g', vlim(1)); plot_text(X(1), Y(1), str); + % str = sprintf('%g', vlim(2)); plot_text(X(2), Y(2), str); end +% the (optional) output is the handle +if nargout == 1; + varargout{1} = h; +end + +if ~holdflag + hold off +end diff --git a/external/fieldtrip/private/plot_vol.m b/external/fieldtrip/private/plot_vol.m index fa6736d..adf337b 100644 --- a/external/fieldtrip/private/plot_vol.m +++ b/external/fieldtrip/private/plot_vol.m @@ -9,24 +9,29 @@ function plot_vol(vol, varargin) % Graphic facilities are available for vertices, edges and faces. A list of % the arguments is given below with the correspondent admitted choices. % -% 'faces' ['yes', 'no', 1, 0, 'true', 'false'] -% 'facecolor' ['brain', 'cortex', 'skin', 'black', 'red', 'r', ..., [0.5 1 0], ...] -% 'faceindex' ['yes', 'no', 1, 0, 'true', 'false'] -% 'vertices' ['yes', 'no', 1, 0, 'true', 'false'] -% 'vertexcolor' ['brain', 'cortex', 'skin', 'black', 'red', 'r', ..., [0.5 1 0], ...] -% 'vertexindex' ['yes', 'no', 1, 0, 'true', 'false'] -% 'edges' ['yes', 'no', 1, 0, 'true', 'false'] -% 'edgecolor' ['brain', 'cortex', 'skin', 'black', 'red', 'r', ..., [0.5 1 0], ...] -% 'colormap' ['gray', 'hot', 'cool', 'copper', 'spring', 'summer', ...] +% 'facecolor' [r g b] values or string, for example 'brain', 'cortex', 'skin', 'black', 'red', 'r' +% 'vertexcolor' [r g b] values or string, for example 'brain', 'cortex', 'skin', 'black', 'red', 'r' +% 'edgecolor' [r g b] values or string, for example 'brain', 'cortex', 'skin', 'black', 'red', 'r' +% 'faceindex' true or false +% 'vertexindex' true or false % % Example -% vol.r = [1 5 10]; -% vol.o = [0 0 4]; -% figure, plot_vol(vol,'colormap','cool') -% -% Copyright (C) 2009, Cristiano Micheli +% vol.r = [86 88 92 100]; +% vol.o = [0 0 40]; +% figure, plot_vol(vol) + +% Copyright (C) 2009, Cristiano Micheli % % $Log: plot_vol.m,v $ +% Revision 1.10 2009/09/04 09:26:28 crimic +% made sphere mesh lighter, added plot_mesh options +% +% Revision 1.9 2009/06/25 16:03:06 crimic +% function now compatible with toolbox guidelines +% +% Revision 1.8 2009/06/03 10:05:40 roboos +% changed handling of mesh generation and teh actual plotting +% % Revision 1.7 2009/04/22 11:45:02 crimic % updated help % @@ -46,82 +51,59 @@ function plot_vol(vol, varargin) % created function to plot forward model geometry % +keyvalcheck(varargin, 'forbidden', {'faces', 'edges', 'vertices'}); % get the optional input arguments -faces = keyval('faces', varargin); -facecolor = keyval('facecolor', varargin); -faceindex = keyval('faceindex', varargin); -vertices = keyval('vertices', varargin); if isempty(vertices),vertices=1;end -vertexcolor = keyval('vertexcolor', varargin); -vertexindex = keyval('vertexindex', varargin); -vertexsize = keyval('vertexsize', varargin); if isempty(vertexsize),vertexsize=10;end -edges = keyval('edges', varargin); if isempty(edges),edges=1;end -edgecolor = keyval('edgecolor', varargin); -map = keyval('colormap', varargin); -% we will probably need a sphere, so let's prepare one -[pnt, tri] = icosahedron642; +faceindex = keyval('faceindex', varargin); if isempty(faceindex),faceindex = 'none';end +vertexindex = keyval('vertexindex', varargin); if isempty(faceindex),vertexindex ='none';end +vertexsize = keyval('vertexsize', varargin); if isempty(vertexsize), vertexsize = 10; end +facecolor = keyval('facecolor', varargin); if isempty(facecolor),facecolor = 'white'; end +vertexcolor = keyval('vertexcolor', varargin); if isempty(vertexcolor),vertexcolor ='none';end +edgecolor = keyval('edgecolor', varargin); if isempty(edgecolor),edgecolor = 'k';end +facealpha = keyval('facealpha', varargin); if isempty(facealpha),facealpha = 1;end +map = keyval('colormap', varargin); -if ~isempty(map) - try - cmap=colormap(map);close(gcf); - catch - error('Colormap does not exist') - end - % set the color of the sphere: - color = [(linspace(cmap(1,1),cmap(64,1),length(vol.bnd)))' (linspace(cmap(1,2),cmap(64,2),length(vol.bnd)))' ... - (linspace(cmap(1,3),cmap(64,3),length(vol.bnd)))']; -end +faceindex = istrue(faceindex); +vertexindex = istrue(vertexindex); + -switch voltype(vol) - case {'singlesphere' 'concentric'} +% we will probably need a sphere, so let's prepare one +[pnt, tri] = icosahedron162; +% prepare a single or multiple triangulated boundaries +switch voltype(vol) + case {'singlesphere' 'concentric'} vol.r = sort(vol.r); + bnd = []; for i=1:length(vol.r) - bnd = []; - bnd.pnt = pnt*vol.r(i); - bnd.pnt(:,1) = bnd.pnt(:,1) + vol.o(1); - bnd.pnt(:,2) = bnd.pnt(:,2) + vol.o(2); - bnd.pnt(:,3) = bnd.pnt(:,3) + vol.o(3); - - bnd.tri = tri; - - if ~isempty(map) - plot_mesh(bnd,'edgecolor', [color(i,1) color(i,2) color(i,3)], ... - 'vertexcolor', [color(i,1) color(i,2) color(i,3)]); - else - plot_mesh(bnd); - end + bnd(i).pnt(:,1) = pnt(:,1)*vol.r(i) + vol.o(1); + bnd(i).pnt(:,2) = pnt(:,2)*vol.r(i) + vol.o(2); + bnd(i).pnt(:,3) = pnt(:,3)*vol.r(i) + vol.o(3); + bnd(i).tri = tri; end - + case 'multisphere' + bnd = []; for i=1:length(vol.label) - bnd = []; - bnd.pnt = pnt*vol.r(i); - bnd.pnt(:,1) = bnd.pnt(:,1) + vol.o(i,1); - bnd.pnt(:,2) = bnd.pnt(:,2) + vol.o(i,2); - bnd.pnt(:,3) = bnd.pnt(:,3) + vol.o(i,3); - - bnd.tri = tri; - - if ~isempty(map) - plot_mesh(bnd,'edgecolor', [color(i,1) color(i,2) color(i,3)], ... - 'vertexcolor', [color(i,1) color(i,2) color(i,3)]); - else - plot_mesh(bnd); - end - end + bnd(i).pnt(:,1) = pnt(:,1)*vol.r(i) + vol.o(i,1); + bnd(i).pnt(:,2) = pnt(:,2)*vol.r(i) + vol.o(i,2); + bnd(i).pnt(:,3) = pnt(:,3)*vol.r(i) + vol.o(i,3); + bnd(i).tri = tri; + end case {'bem', 'dipoli', 'asa', 'avo', 'bemcp', 'nolte'} - for i=1:length(vol.bnd) - if ~isempty(map) - plot_mesh(vol.bnd(i),'edgecolor', [color(i,1) color(i,2) color(i,3)], ... - 'vertexcolor', [color(i,1) color(i,2) color(i,3)]); - else - plot_mesh(vol.bnd(i)); - end - end - + % these already contain one or multiple triangulated surfaces for the boundaries + bnd = vol.bnd; + otherwise error('unsupported voltype') end + +% plot the triangulated surfaces of the volume conduction model +for i=1:length(bnd) + plot_mesh(bnd(i),'faceindex',faceindex,'vertexindex',vertexindex, ... + 'vertexsize',vertexsize,'facecolor',facecolor,'edgecolor',edgecolor, ... + 'vertexcolor',vertexcolor,'facealpha',facealpha); +end + diff --git a/external/fieldtrip/private/prepare_atlas.m b/external/fieldtrip/private/prepare_atlas.m index ba66e5d..819d6d5 100644 --- a/external/fieldtrip/private/prepare_atlas.m +++ b/external/fieldtrip/private/prepare_atlas.m @@ -14,6 +14,9 @@ % Copyright (C) 2005-2008, Robert Oostenveld, Ingrid Nieuwenhuis % % $Log: prepare_atlas.m,v $ +% Revision 1.2 2009/07/14 07:27:30 roboos +% replaced read_fcdc_mri with read_mri to avoid warning +% % Revision 1.1 2008/12/05 13:46:24 ingnie % this function replaces atlas_init % @@ -35,7 +38,7 @@ % check whether the required AFNI toolbox is available hastoolbox('afni', 1); - atlas = read_fcdc_mri(filename); + atlas = read_mri(filename); % the AFNI atlas contains two volumes at 1mm resolution atlas.brick0 = atlas.anatomy(:,:,:,1); diff --git a/external/fieldtrip/private/prepare_bemmodel.m b/external/fieldtrip/private/prepare_bemmodel.m index e22909c..4153e04 100644 --- a/external/fieldtrip/private/prepare_bemmodel.m +++ b/external/fieldtrip/private/prepare_bemmodel.m @@ -1,12 +1,14 @@ -function [vol] = prepare_bemmodel(cfg, mri) +function [vol, cfg] = prepare_bemmodel(cfg, mri) % PREPARE_BEMMODEL constructs triangulations of the boundaries between % multiple segmented tissue types in an anatomical MRI and subsequently % computes the BEM system matrix. % % Use as -% [vol] = prepare_bemmodel(cfg, mri), or -% [vol] = prepare_bemmodel(cfg, vol) +% [vol, cfg] = prepare_bemmodel(cfg, mri), or +% [vol, cfg] = prepare_bemmodel(cfg, seg), or +% [vol, cfg] = prepare_bemmodel(cfg, vol), or +% [vol, cfg] = prepare_bemmodel(cfg) % % The configuration can contain % cfg.tissue = [1 2 3], segmentation value of each tissue type @@ -27,6 +29,9 @@ % Copyright (C) 2005-2009, Robert Oostenveld % % $Log: prepare_bemmodel.m,v $ +% Revision 1.17 2009/07/16 09:11:19 crimic +% added link to prepare_mesh.m and modified help +% % Revision 1.16 2009/03/30 15:06:14 roboos % added the patch from Alexandre to support openmeeg % @@ -49,26 +54,18 @@ if ~isfield(cfg, 'tissue'), cfg.tissue = [8 12 14]; end if ~isfield(cfg, 'numvertices'), cfg.numvertices = [1 2 3] * 500; end -if ~isfield(cfg, 'conductivity'), cfg.conductivity = [1 1/80 1] * 0.33; end if ~isfield(cfg, 'hdmfile'), cfg.hdmfile = []; end if ~isfield(cfg, 'isolatedsource'), cfg.isolatedsource = []; end -if ~isfield(cfg, 'method'), cfg.method = 'dipoli'; end - -% there are two types of input possible -hasmri = isfield(mri, 'transform'); -hasvol = isfield(mri, 'bnd'); - -if hasvol && ~hasmri - % rename the second input argument - vol = mri; - clear mri; -elseif hasmri && ~hasvol - % start with an empty volume conductor - vol = []; +if ~isfield(cfg, 'method'), cfg.method = 'dipoli'; end % dipoli, openmeeg, bemcp, brainstorm +if ~isfield(cfg, 'conductivity') && isfield(mri, 'cond') + cfg.conductivity = mri.cond; else - error('invalid input arguments'); + cfg.conductivity = [1 1/80 1] * 0.33; end +% start with an empty volume conductor +vol = []; + if ~isfield(vol, 'cond') % assign the conductivity of each compartment vol.cond = cfg.conductivity; @@ -77,32 +74,11 @@ % determine the number of compartments Ncompartment = length(vol.cond); -if hasmri - fprintf('using the segmented MRI\n'); - [mrix, mriy, mriz] = ndgrid(1:size(mri.seg,1), 1:size(mri.seg,2), 1:size(mri.seg,3)); - % construct the triangulations of the boundaries from the segmented MRI - for i=1:Ncompartment - fprintf('triangulating the boundary of compartment %d\n', i); - seg = imfill((mri.seg==cfg.tissue(i)), 'holes'); - ori(1) = mean(mrix(seg(:))); - ori(2) = mean(mriy(seg(:))); - ori(3) = mean(mriz(seg(:))); - [pnt, tri] = triangulate_seg(seg, cfg.numvertices(i), ori); - % apply the coordinate transformation from voxel to head coordinates - pnt(:,4) = 1; - pnt = (mri.transform * (pnt'))'; - pnt = pnt(:,1:3); - vol.bnd(i).pnt = pnt; - vol.bnd(i).tri = tri; - end +% construct the geometry of the BEM boundaries +if nargin==1 + vol.bnd = prepare_mesh(cfg); else - fprintf('using the pre-specified triangulated boundaries\n'); -end - -% ensure that the vertices and triangles are double precision, otherwise the bemcp mex files will crash -for i=1:length(vol.bnd) - vol.bnd(i).pnt = double(vol.bnd(i).pnt); - vol.bnd(i).tri = double(vol.bnd(i).tri); + vol.bnd = prepare_mesh(cfg, mri); end vol.source = find_innermost_boundary(vol.bnd); @@ -130,28 +106,18 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % this uses an implementation that was contributed by Thom Oostendorp %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - % determine whether the command-line DIPOLI executable is available hastoolbox('dipoli', 1); + % use the dipoli wrapper function vol = dipoli(vol, cfg.isolatedsource); - -elseif strcmp(cfg.method, 'brainstorm') - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % this uses an implementation from the BrainStorm toolbox - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - hastoolbox('brainstorm', 1); - - error('not yet implemented'); - + vol.type = 'dipoli'; + elseif strcmp(cfg.method, 'bemcp') %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % this uses an implementation that was contributed by Christophe Philips %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% hastoolbox('bemcp', 1); - - vol.type = 'bemcp'; - + % do some sanity checks if length(vol.bnd)~=3 error('this only works for three surfaces'); @@ -162,15 +128,15 @@ if vol.source~=1 error('the source compartment should correspond to the first surface'); end - + % Build Triangle 4th point vol = triangle4pt(vol); - + % 2. BEM model estimation, only for the scalp surface - + defl =[ 0 0 1/size(vol.bnd(vol.skin).pnt,1)]; % ensure deflation for skin surface, i.e. average reference over skin - + % NOTE: % Calculation proceeds by estimating each submatrix C_ij and combine them. % There are 2 options: @@ -182,52 +148,52 @@ % The latter option requires less memory, but would take much more time to % estimate. % This faster but memory hungry solution is implemented here. - + % Deal first with surface 1 and 2 (inner and outer skull %-------------------------------- - + % NOTE: % C11st/C22st/C33st are simply the matrix C11/C22/C33 minus the identity % matrix, i.e. C11st = C11-eye(N) - + weight = (vol.cond(1)-vol.cond(2))/((vol.cond(1)+vol.cond(2))*2*pi); C11st = bem_Cii_lin(vol.bnd(1).tri,vol.bnd(1).pnt, weight,defl(1),vol.bnd(1).pnt4); weight = (vol.cond(1)-vol.cond(2))/((vol.cond(2)+vol.cond(3))*2*pi); C21 = bem_Cij_lin(vol.bnd(2).pnt,vol.bnd(1).pnt,vol.bnd(1).tri, weight,defl(1)); tmp1 = C21/C11st; - + weight = (vol.cond(2)-vol.cond(3))/((vol.cond(1)+vol.cond(2))*2*pi); C12 = bem_Cij_lin(vol.bnd(1).pnt,vol.bnd(2).pnt,vol.bnd(2).tri, weight,defl(2)); weight = (vol.cond(2)-vol.cond(3))/((vol.cond(2)+vol.cond(3))*2*pi); C22st = bem_Cii_lin(vol.bnd(2).tri,vol.bnd(2).pnt, weight,defl(2),vol.bnd(2).pnt4); tmp2 = C12/C22st; - + % Try to spare some memory: tmp10 = - tmp2 * C21 + C11st; clear C21 C11st tmp11 = - tmp1 * C12 + C22st; clear C12 C22st - + % Combine with the effect of surface 3 (scalp) on the first 2 %------------------------------------------------------------ weight = (vol.cond(1)-vol.cond(2))/(vol.cond(3)*2*pi); C31 = bem_Cij_lin(vol.bnd(3).pnt,vol.bnd(1).pnt,vol.bnd(1).tri, weight,defl(1)); -% tmp4 = C31/(- tmp2 * C21 + C11st ); -% clear C31 C21 C11st + % tmp4 = C31/(- tmp2 * C21 + C11st ); + % clear C31 C21 C11st tmp4 = C31/tmp10; clear C31 tmp10 - + weight = (vol.cond(2)-vol.cond(3))/(vol.cond(3)*2*pi); C32 = bem_Cij_lin(vol.bnd(3).pnt,vol.bnd(2).pnt,vol.bnd(2).tri, weight,defl(2)); -% tmp3 = C32/(- tmp1 * C12 + C22st ); -% clear C12 C22st C32 + % tmp3 = C32/(- tmp1 * C12 + C22st ); + % clear C12 C22st C32 tmp3 = C32/tmp11; clear C32 tmp11 - + tmp5 = tmp3*tmp1-tmp4; tmp6 = tmp4*tmp2-tmp3; clear tmp1 tmp2 tmp3 tmp4 - + % Finally include effect of surface 3 on the others %-------------------------------------------------- % As the gama1 intermediate matrix is built as the sum of 3 matrices, I can @@ -235,32 +201,39 @@ weight = vol.cond(3)/((vol.cond(1)+vol.cond(2))*2*pi); Ci3 = bem_Cij_lin(vol.bnd(1).pnt,vol.bnd(3).pnt,vol.bnd(3).tri, weight,defl(3)); gama1 = - tmp5*Ci3; % gama1 = - tmp5*C13; - + weight = vol.cond(3)/((vol.cond(2)+vol.cond(3))*2*pi); Ci3 = bem_Cij_lin(vol.bnd(2).pnt,vol.bnd(3).pnt,vol.bnd(3).tri, weight,defl(3)); gama1 = gama1 - tmp6*Ci3; % gama1 = - tmp5*C13 - tmp6*C23; - + weight = 1/(2*pi); Ci3 = bem_Cii_lin(vol.bnd(3).tri,vol.bnd(3).pnt, weight,defl(3),vol.bnd(3).pnt4); gama1 = gama1 - Ci3; % gama1 = - tmp5*C13 - tmp6*C23 - C33st; clear Ci3 - + % Build system matrix %-------------------- i_gama1 = inv(gama1); vol.mat = [i_gama1*tmp5 i_gama1*tmp6 i_gama1]; - + vol.type = 'bemcp'; + elseif strcmp(cfg.method, 'openmeeg') %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % this uses an implementation that was contributed by INRIA Odyssee Team %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - % determine whether the OpenMEEG command-line executables is available hastoolbox('openmeeg', 1); + % use the openmeeg wrapper function vol = openmeeg(vol); vol.type = 'openmeeg'; - + +elseif strcmp(cfg.method, 'brainstorm') + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % this uses an implementation from the BrainStorm toolbox + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + hastoolbox('brainstorm', 1); + error('not yet implemented'); + else error('unsupported method'); end % which method diff --git a/external/fieldtrip/private/prepare_concentricspheres.m b/external/fieldtrip/private/prepare_concentricspheres.m index d4ea5f8..b6d4ded 100644 --- a/external/fieldtrip/private/prepare_concentricspheres.m +++ b/external/fieldtrip/private/prepare_concentricspheres.m @@ -1,8 +1,7 @@ function [vol, cfg] = prepare_concentricspheres(cfg) -% PREPARE_CONCENTRICSPHERES creates a MEG volume conductor model with a sphere -% for every sensor. You can also use it to create a single sphere -% model that is fitted to the MRI or to the head shape points. +% PREPARE_CONCENTRICSPHERES creates a EEG volume conductor model with +% multiple concentric spheres. % % Use as % [vol, cfg] = prepare_concentricspheres(cfg) @@ -15,7 +14,6 @@ % cfg.nonlinear = 'yes' or 'no' (default = 'yes') % cfg.feedback = 'yes' or 'no' (default = 'yes') % -% % Example: % % % first create 4 surfaces that represent the brain, csf, skull and skin @@ -38,6 +36,12 @@ % Copyright (C) 2009, Vladimir Litvak & Robert Oostenveld % % $Log: prepare_concentricspheres.m,v $ +% Revision 1.8 2009/07/16 09:14:52 crimic +% part of code reimplemented in function prepare_mesh_headshape.m +% +% Revision 1.7 2009/06/23 14:59:28 crimic +% use of plotting toolbox funtion: plot_mesh +% % Revision 1.6 2009/05/29 11:40:07 roboos % only convert cfg.headshape from config to struct in case it is present % @@ -65,6 +69,7 @@ if ~isfield(cfg, 'fitind'), cfg.fitind = 'all'; end if ~isfield(cfg, 'feedback'), cfg.feedback = 'yes'; end if ~isfield(cfg, 'conductivity'), cfg.conductivity = [0.3300 1 0.0042 0.3300]; end +if ~isfield(cfg, 'numvertices'), cfg.numvertices = 'same'; end if isfield(cfg, 'headshape') && isa(cfg.headshape, 'config') % convert the nested config-object back into a normal structure @@ -74,25 +79,7 @@ cfg = checkconfig(cfg, 'forbidden', 'nonlinear'); % get the surface describing the head shape -if isstruct(cfg.headshape) && isfield(cfg.headshape, 'pnt') - % use the headshape surface specified in the configuration - headshape = cfg.headshape; -elseif isnumeric(cfg.headshape) && size(cfg.headshape,2)==3 - % use the headshape points specified in the configuration - headshape.pnt = cfg.headshape; -elseif ischar(cfg.headshape) - % read the headshape from file - headshape = read_headshape(cfg.headshape); -else - error('cfg.headshape is not specified correctly') -end -if ~isfield(headshape, 'tri') - for i=1:length(headshape) - % generate a closed triangulation from the surface points - headshape(i).pnt = unique(headshape(i).pnt, 'rows'); - headshape(i).tri = projecttri(headshape(i).pnt); - end -end +headshape = prepare_mesh_headshape(cfg); if strcmp(cfg.fitind, 'all') fitind = 1:numel(headshape); @@ -140,15 +127,17 @@ headshape(end-i+1).tri = []; end - % FIXME switch to plot_mesh % plot the original surface - triplot(headshape(end-i+1).pnt, headshape(end-i+1).tri, [], 'edges'); + bndtmp = []; + bndtmp.pnt = headshape(end-i+1).pnt; + bndtmp.tri = headshape(end-i+1).tri; + plot_mesh(bndtmp,'facecolor','none') - % FIXME switch to plot_mesh % plot the sphere surface - spnt = sphere_pnt*vol.r(i) + repmat(single_o, size(sphere_pnt, 1), 1); - hs = triplot(spnt, sphere_tri, [], 'edges'); - set(hs, 'EdgeColor', colors{mod(i, numel(colors)) + 1}); + bndtmp = []; + bndtmp.pnt = sphere_pnt*vol.r(i) + repmat(single_o, size(sphere_pnt, 1), 1); + bndtmp.tri = sphere_tri; + plot_mesh(bndtmp,'edgecolor',colors{mod(i, numel(colors)) + 1},'facecolor','none'); end end diff --git a/external/fieldtrip/private/prepare_freq_matrices.m b/external/fieldtrip/private/prepare_freq_matrices.m index da88108..fde81e9 100644 --- a/external/fieldtrip/private/prepare_freq_matrices.m +++ b/external/fieldtrip/private/prepare_freq_matrices.m @@ -11,6 +11,9 @@ % Copyright (C) 2004-2006, Robert Oostenveld % % $Log: prepare_freq_matrices.m,v $ +% Revision 1.25 2009/08/16 12:43:22 jansch +% added default empty cfg.refchan +% % Revision 1.24 2009/02/05 10:22:07 roboos % better support for single-trial data, thanks to Vladimir % @@ -98,6 +101,7 @@ % set the defaults if ~isfield(cfg, 'dicsfix'), cfg.dicsfix = 'yes'; end if ~isfield(cfg, 'quickflag'), cfg.quickflag = 0; end +if ~isfield(cfg, 'refchan'), cfg.refchan = []; end quickflag = cfg.quickflag==1; diff --git a/external/fieldtrip/private/prepare_layout.m b/external/fieldtrip/private/prepare_layout.m index a469fa3..bac6e7f 100644 --- a/external/fieldtrip/private/prepare_layout.m +++ b/external/fieldtrip/private/prepare_layout.m @@ -24,6 +24,7 @@ % cfg.output filename to which the layout will be written (default = []) % cfg.montage 'no' or a montage structure (default = 'no') % cfg.image filename, use an image to construct a layout (e.g. usefull for ECoG grids) +% cfg.bw if an image is used and bw = 1 transforms the image in black and white (default = 0, do not transform) % % Alternatively the layout can be constructed from either % data.elec structure with electrode positions @@ -46,6 +47,31 @@ % Copyright (C) 2007-2009, Robert Oostenveld % % $Log: prepare_layout.m,v $ +% Revision 1.40 2009/08/05 08:22:09 roboos +% better detection of empty/absent input data +% +% Revision 1.39 2009/08/05 06:32:41 roboos +% fixed layout generation for ordered and vertical when no data was given as input, labels fully depend on cfg.channel, not on data.label +% +% Revision 1.38 2009/08/04 13:57:00 roboos +% make tight vertical and orderer layout in case cfg.channel is specified +% allow skipping the COMNT and SCALE positions through the cfg +% +% Revision 1.37 2009/06/30 07:08:55 roboos +% removed debug keyboard statement +% +% Revision 1.36 2009/06/17 14:03:41 roboos +% consistent handling of ginput in case figure is closed +% +% Revision 1.35 2009/06/05 15:30:05 crimic +% updated help +% +% Revision 1.34 2009/06/05 15:28:03 crimic +% updated cfg +% +% Revision 1.33 2009/06/05 15:26:32 crimic +% minor change +% % Revision 1.32 2009/05/18 15:59:44 roboos % optinal plotting of RGB as greyscale image % @@ -186,14 +212,18 @@ if ~isfield(cfg, 'feedback'), cfg.feedback = 'no'; end if ~isfield(cfg, 'montage'), cfg.montage = 'no'; end if ~isfield(cfg, 'image'), cfg.image = []; end +if ~isfield(cfg, 'bw'), cfg.bw = 0; end +if ~isfield(cfg, 'channel'), cfg.channel = 'all'; end +if ~isfield(cfg, 'skipscale'), cfg.skipscale = 'no'; end +if ~isfield(cfg, 'skipcomnt'), cfg.skipcomnt = 'no'; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % try to generate the layout structure %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -skipscale = false; % in general a scale is desired -skipcomnt = false; % in general a comment desired +skipscale = strcmp(cfg.skipscale, 'yes'); % in general a scale is desired +skipcomnt = strcmp(cfg.skipcomnt, 'yes'); % in general a comment desired if isa(cfg.layout, 'config') % convert the nested config-object back into a normal structure @@ -205,7 +235,7 @@ % a lay structure) if isstruct(cfg.layout) && isfield(cfg.layout, 'pos') && isfield(cfg.layout, 'label') && isfield(cfg.layout, 'width') && isfield(cfg.layout, 'height') lay = cfg.layout; - + elseif isstruct(cfg.layout) && isfield(cfg.layout, 'pos') && isfield(cfg.layout, 'label') && (~isfield(cfg.layout, 'width') || ~isfield(cfg.layout, 'height')) lay = cfg.layout; % add width and height for multiplotting @@ -217,10 +247,18 @@ mindist = min(d(:)); lay.width = ones(nchans,1) * mindist * 0.8; lay.height = ones(nchans,1) * mindist * 0.6; - + elseif isequal(cfg.layout, 'butterfly') - nchan = length(data.label); - lay.label = data.label; + if nargin>1 && ~isempty(data) + % look at the data to determine the overlapping channels + cfg.channel = channelselection(cfg.channel, data.label); + chanindx = match_str(data.label, cfg.channel); + nchan = length(data.label(chanindx)); + lay.label = data.label(chanindx); + else + nchan = length(cfg.channel); + lay.label = cfg.channel; + end lay.pos = zeros(nchan,2); % centered at (0,0) lay.width = ones(nchan,1) * 1.0; lay.height = ones(nchan,1) * 1.0; @@ -228,10 +266,18 @@ lay.outline = {}; skipscale = true; % a scale is not desired skipcomnt = true; % a comment is initially not desired, or at least requires more thought - + elseif isequal(cfg.layout, 'vertical') - nchan = length(data.label); - lay.label = data.label; + if nargin>1 && ~isempty(data) + % look at the data to determine the overlapping channels + cfg.channel = channelselection(cfg.channel, data.label); + chanindx = match_str(data.label, cfg.channel); + nchan = length(data.label(chanindx)); + lay.label = data.label(chanindx); + else + nchan = length(cfg.channel); + lay.label = cfg.channel; + end for i=1:(nchan+2) x = 0.5; y = 1-i/(nchan+1+2); @@ -246,9 +292,18 @@ end lay.mask = {}; lay.outline = {}; - + elseif isequal(cfg.layout, 'ordered') - nchan = length(data.label); + if nargin>1 && ~isempty(data) + % look at the data to determine the overlapping channels + cfg.channel = channelselection(cfg.channel, data.label); + chanindx = match_str(data.label, cfg.channel); + nchan = length(data.label(chanindx)); + lay.label = data.label(chanindx); + else + nchan = length(cfg.channel); + lay.label = cfg.channel; + end ncol = ceil(sqrt(nchan))+1; nrow = ceil(sqrt(nchan))+1; k = 0; @@ -264,68 +319,66 @@ end end end - lay.label = data.label; - + lay.label{end+1} = 'SCALE'; lay.width(end+1) = 0.8 * 1/ncol; lay.height(end+1) = 0.8 * 1/nrow; x = (ncol-2)/ncol; y = 0/nrow; lay.pos(end+1,:) = [x y]; - + lay.label{end+1} = 'COMNT'; lay.width(end+1) = 0.8 * 1/ncol; lay.height(end+1) = 0.8 * 1/nrow; x = (ncol-1)/ncol; y = 0/nrow; - lay.pos(end+1,:) = [x y]; - - + lay.pos(end+1,:) = [x y]; + % try to generate layout from other configuration options elseif ischar(cfg.layout) && filetype(cfg.layout, 'matlab') fprintf('reading layout from file %s\n', cfg.layout); load(cfg.layout, 'lay'); - + elseif ischar(cfg.layout) && filetype(cfg.layout, 'layout') fprintf('reading layout from file %s\n', cfg.layout); lay = readlay(cfg.layout); - + elseif ischar(cfg.layout) && ~filetype(cfg.layout, 'layout') % assume that cfg.layout is an electrode file fprintf('creating layout from electrode file %s\n', cfg.layout); lay = sens2lay(read_sens(cfg.layout), cfg.rotate, cfg.projection, cfg.style); - + elseif ischar(cfg.elecfile) fprintf('creating layout from electrode file %s\n', cfg.elecfile); lay = sens2lay(read_sens(cfg.elecfile), cfg.rotate, cfg.projection, cfg.style); - + elseif ~isempty(cfg.elec) && isstruct(cfg.elec) fprintf('creating layout from cfg.elec\n'); lay = sens2lay(cfg.elec, cfg.rotate, cfg.projection, cfg.style); - + elseif isfield(data, 'elec') && isstruct(data.elec) fprintf('creating layout from data.elec\n'); lay = sens2lay(data.elec, cfg.rotate, cfg.projection, cfg.style); - + elseif ischar(cfg.gradfile) fprintf('creating layout from gradiometer file %s\n', cfg.gradfile); lay = sens2lay(read_sens(cfg.gradfile), cfg.rotate, cfg.projection, cfg.style); - + elseif ~isempty(cfg.grad) && isstruct(cfg.grad) fprintf('creating layout from cfg.grad\n'); lay = sens2lay(cfg.grad, cfg.rotate, cfg.projection, cfg.style); - + elseif isfield(data, 'grad') && isstruct(data.grad) fprintf('creating layout from data.grad\n'); lay = sens2lay(data.grad, cfg.rotate, cfg.projection, cfg.style); - + elseif ~isempty(cfg.image) && isempty(cfg.layout) fprintf('reading background image from %s\n', cfg.image); img = imread(cfg.image); img = flipdim(img, 1); % in combination with "axis xy" - + figure - bw = 1; + bw = cfg.bw; if bw % convert to greyscale image @@ -341,7 +394,7 @@ axis equal axis off axis xy - + % get the electrode positions pos = zeros(0,2); electrodehelp = [ ... @@ -355,13 +408,20 @@ while again fprintf(electrodehelp) disp(round(pos)); % values are integers/pixels - [x, y, k] = ginput(1) + try + [x, y, k] = ginput(1); + catch + % this happens if the figure is closed + return; + end + switch k case 1 pos = cat(1, pos, [x y]); % add it to the figure plot(x, y, 'b.'); plot(x, y, 'yo'); + case 8 if size(pos,1)>0 % remove the last point @@ -375,13 +435,15 @@ plot(pos(:,1), pos(:,2), 'b.'); plot(pos(:,1), pos(:,2), 'yo'); end + case 'q' again = 0; + otherwise warning('invalid button (%d)', k); end end - + % get the mask outline polygon = {}; thispolygon = 1; @@ -401,10 +463,15 @@ for i=1:length(polygon) fprintf('polygon %d has %d points\n', i, size(polygon{i},1)); end - - [x, y, k] = ginput(1); + + try + [x, y, k] = ginput(1); + catch + % this happens if the figure is closed + return; + end + switch k - case 1 polygon{thispolygon} = cat(1, polygon{thispolygon}, [x y]); % add the last line segment to the figure @@ -413,7 +480,7 @@ y = polygon{i}([end-1 end],2); end plot(x, y, 'g.-'); - + case 8 % backspace if size(polygon{thispolygon},1)>0 % remove the last point @@ -438,7 +505,7 @@ plot(x, y, 'g.-'); end end - + case 'c' if size(polygon{thispolygon},1)>0 % close the polygon @@ -451,7 +518,7 @@ thispolygon = thispolygon + 1; polygon{thispolygon} = zeros(0,2); end - + case 'q' if size(polygon{thispolygon},1)>0 % close the polygon @@ -462,15 +529,15 @@ plot(x, y, 'g.-'); end again = 0; - + otherwise warning('invalid button (%d)', k); end end % remember this set of polygons as the mask mask = polygon; - - + + % get the outline, e.g. head shape and sulci polygon = {}; thispolygon = 1; @@ -491,10 +558,15 @@ for i=1:length(polygon) fprintf('polygon %d has %d points\n', i, size(polygon{i},1)); end - - [x, y, k] = ginput(1); + + try + [x, y, k] = ginput(1); + catch + % this happens if the figure is closed + return; + end + switch k - case 1 polygon{thispolygon} = cat(1, polygon{thispolygon}, [x y]); % add the last line segment to the figure @@ -503,7 +575,7 @@ y = polygon{i}([end-1 end],2); end plot(x, y, 'm.-'); - + case 8 % backspace if size(polygon{thispolygon},1)>0 % remove the last point @@ -528,13 +600,12 @@ plot(x, y, 'm.-'); end end - + case 'c' if size(polygon{thispolygon},1)>0 x = polygon{thispolygon}(1,1); y = polygon{thispolygon}(1,2); polygon{thispolygon} = cat(1, polygon{thispolygon}, [x y]); - keyboard % add the last line segment to the figure x = polygon{i}([end-1 end],1); y = polygon{i}([end-1 end],2); @@ -543,24 +614,24 @@ thispolygon = thispolygon + 1; polygon{thispolygon} = zeros(0,2); end - + case 'n' if size(polygon{thispolygon},1)>0 % switch to the next polygon thispolygon = thispolygon + 1; polygon{thispolygon} = zeros(0,2); end - + case 'q' again = 0; - + otherwise warning('invalid button (%d)', k); end end % remember this set of polygons as the outline outline = polygon; - + % convert electrode positions into a layout structure lay.pos = pos; nchans = size(pos,1); @@ -578,7 +649,7 @@ % add mask and outline polygons lay.mask = mask; lay.outline = outline; - + else fprintf('reverting to 151 channel CTF default\n'); lay = readlay('CTF151.lay'); @@ -631,7 +702,7 @@ if ~strcmp(cfg.montage, 'no') Norg = length(cfg.montage.labelorg); Nnew = length(cfg.montage.labelnew); - + for i=1:Nnew cfg.montage.tra(i,:) = abs(cfg.montage.tra(i,:)); cfg.montage.tra(i,:) = cfg.montage.tra(i,:) ./ sum(cfg.montage.tra(i,:)); @@ -663,6 +734,13 @@ Y = max(lay.pos(:,2)); Y = min(lay.pos(:,2)); lay.pos(end+1,:) = [X Y]; +elseif any(strcmp('COMNT', lay.label)) && skipcomnt + % remove the scale entry + sel = find(strcmp('COMNT', lay.label)); + lay.label(sel) = []; + lay.pos(sel,:) = []; + lay.width(sel) = []; + lay.height(sel) = []; end if ~any(strcmp('SCALE', lay.label)) && strcmpi(cfg.style, '2d') && ~skipscale @@ -674,6 +752,13 @@ Y = max(lay.pos(:,2)); Y = min(lay.pos(:,2)); lay.pos(end+1,:) = [X Y]; +elseif any(strcmp('SCALE', lay.label)) && skipscale + % remove the scale entry + sel = find(strcmp('SCALE', lay.label)); + lay.label(sel) = []; + lay.pos(sel,:) = []; + lay.width(sel) = []; + lay.height(sel) = []; end % to plot the layout for debugging, you can use this code snippet @@ -737,7 +822,7 @@ rz = 90; case {'neuromag122', 'neuromag306'} rz = 0; - case 'electrode' + case 'electrode' rz = 90; otherwise rz = 0; @@ -749,19 +834,19 @@ [pnt, label] = channelposition(sens); if strcmpi(style, '3d') - lay.pos = pnt; - lay.label = label; + lay.pos = pnt; + lay.label = label; else - prj = elproj(pnt, method); - d = dist(prj'); - d(find(eye(size(d)))) = inf; - mindist = min(d(:)); - X = prj(:,1); - Y = prj(:,2); - Width = ones(size(X)) * mindist * 0.8; - Height = ones(size(X)) * mindist * 0.6; - lay.pos = [X Y]; - lay.width = Width; - lay.height = Height; - lay.label = label; -end + prj = elproj(pnt, method); + d = dist(prj'); + d(find(eye(size(d)))) = inf; + mindist = min(d(:)); + X = prj(:,1); + Y = prj(:,2); + Width = ones(size(X)) * mindist * 0.8; + Height = ones(size(X)) * mindist * 0.6; + lay.pos = [X Y]; + lay.width = Width; + lay.height = Height; + lay.label = label; +end diff --git a/external/fieldtrip/private/prepare_localspheres.m b/external/fieldtrip/private/prepare_localspheres.m index ba20922..c64c9f9 100644 --- a/external/fieldtrip/private/prepare_localspheres.m +++ b/external/fieldtrip/private/prepare_localspheres.m @@ -1,12 +1,13 @@ -function [vol, cfg] = prepare_localspheres(cfg, mri); +function [vol, cfg] = prepare_localspheres(cfg, mri) % PREPARE_LOCALSPHERES creates a MEG volume conductor model with a sphere % for every sensor. You can also use it to create a single sphere % model that is fitted to the MRI or to the head shape points. % % Use as +% [vol, cfg] = prepare_localspheres(cfg, seg), or +% [vol, cfg] = prepare_localspheres(cfg, mri), or % [vol, cfg] = prepare_localspheres(cfg) -% [vol, cfg] = prepare_localspheres(cfg, seg) % % The input configuration should contain % cfg.grad = structure with gradiometer definition, or @@ -40,6 +41,12 @@ % Copyright (C) 2005-2006, Jan-Mathijs Schoffelen & Robert Oostenveld % % $Log: prepare_localspheres.m,v $ +% Revision 1.29 2009/07/29 06:40:41 roboos +% updated plotting functions +% +% Revision 1.28 2009/07/16 09:17:17 crimic +% link to prepare_mesh.m function +% % Revision 1.27 2009/05/29 10:47:19 roboos % only convert to struct in case headshape is specified % @@ -145,111 +152,13 @@ if ~isfield(cfg, 'singlesphere'), cfg.singlesphere = 'no'; end if ~isfield(cfg, 'headshape'), cfg.headshape = []; end -if isfield(cfg, 'headshape') && isa(cfg.headshape, 'config') - % convert the nested config-object back into a normal structure - cfg.headshape = struct(cfg.headshape); -end - -if nargin>1 && isempty(cfg.headshape) - basedonmri = 1; - basedonheadshape = 0; -elseif nargin==1 && ~isempty(cfg.headshape) - basedonmri = 0; - basedonheadshape = 1; +% construct the geometry of the headshape using a single boundary +if nargin==1 + headshape = prepare_mesh(cfg); else - error('inconsistent configuration, cfg.headshape should not be used in combination with an mri input') + headshape = prepare_mesh(cfg, mri); end -if basedonmri - % obtain the head shape from the segmented MRI - seg = zeros(mri.dim); - if isfield(mri, 'gray') - fprintf('including gray matter in segmentation for brain compartment\n') - seg = seg | (mri.gray>(cfg.threshold*max(mri.gray(:)))); - end - if isfield(mri, 'white') - fprintf('including white matter in segmentation for brain compartment\n') - seg = seg | (mri.white>(cfg.threshold*max(mri.white(:)))); - end - if isfield(mri, 'csf') - fprintf('including CSF in segmentation for brain compartment\n') - seg = seg | (mri.csf>(cfg.threshold*max(mri.csf(:)))); - end - if ~strcmp(cfg.smooth, 'no'), - % check whether the required SPM2 toolbox is available - hastoolbox('spm2', 1); - fprintf('smoothing the segmentation with a %d-pixel FWHM kernel\n',cfg.smooth); - seg = double(seg); - spm_smooth(seg, seg, cfg.smooth); - end - % threshold for the last time - seg = (seg>(cfg.threshold*max(seg(:)))); - % determine the center of gravity of the segmented brain - xgrid = 1:mri.dim(1); - ygrid = 1:mri.dim(2); - zgrid = 1:mri.dim(3); - [X, Y, Z] = ndgrid(xgrid, ygrid, zgrid); - ori(1) = mean(X(seg)); - ori(2) = mean(Y(seg)); - ori(3) = mean(Z(seg)); - pnt = triangulate_seg(seg, cfg.spheremesh, ori); - pnt(:,4) = 1; - pnt = (mri.transform * pnt')'; - % convert the MRI surface points into the same units as the source/gradiometer - scale = 1; - switch cfg.sourceunits - case 'mm' - scale = scale * 1000; - case 'cm' - scale = scale * 100; - case 'dm' - scale = scale * 10; - case 'm' - scale = scale * 1; - otherwise - error('unknown physical dimension in cfg.sourceunits'); - end - switch cfg.mriunits - case 'mm' - scale = scale / 1000; - case 'cm' - scale = scale / 100; - case 'dm' - scale = scale / 10; - case 'm' - scale = scale / 1; - otherwise - error('unknown physical dimension in cfg.mriunits'); - end - if scale~=1 - fprintf('converting MRI surface points from %s into %s\n', cfg.sourceunits, cfg.mriunits); - headshape.pnt = pnt(:,1:3) * scale; - else - headshape.pnt = pnt(:,1:3); - end - fprintf('placed %d points on the brain surface\n', length(headshape.pnt)); - -elseif basedonheadshape - % get the surface describing the head shape - if isstruct(cfg.headshape) && isfield(cfg.headshape, 'pnt') - % use the headshape surface specified in the configuration - headshape = cfg.headshape; - elseif isnumeric(cfg.headshape) && size(cfg.headshape,2)==3 - % use the headshape points specified in the configuration - headshape.pnt = cfg.headshape; - elseif ischar(cfg.headshape) - % read the headshape from file - headshape = read_headshape(cfg.headshape); - else - error('cfg.headshape is not specified correctly') - end - headshape.pnt = unique(headshape.pnt, 'rows'); - % this function does not use the triangulation - if isfield(headshape, 'tri') - headshape = rmfield(headshape, 'tri'); - end -end % basedonmri or basedonheadshape - % read the gradiometer definition from file or copy it from the configuration if isfield(cfg, 'gradfile') grad = read_sens(cfg.gradfile); @@ -273,8 +182,8 @@ % plot all channels and headshape points if strcmp(cfg.feedback, 'yes') cla - plot3(grad.pnt(:,1), grad.pnt(:,2), grad.pnt(:,3), 'b.'); % all coils - plot3(headshape.pnt(:,1), headshape.pnt(:,2), headshape.pnt(:,3), 'g.'); + plot_sens(grad); + plot_mesh(headshape, 'vertexcolor', 'g', 'facecolor', 'none', 'edgecolor', 'none'); drawnow end @@ -282,6 +191,8 @@ [single_o, single_r] = fitsphere(headshape.pnt); fprintf('single sphere, %5d surface points, center = [%4.1f %4.1f %4.1f], radius = %4.1f\n', Nshape, single_o(1), single_o(2), single_o(3), single_r); +vol = []; + if strcmp(cfg.singlesphere, 'yes') % only return a single sphere vol.r = single_r; @@ -298,13 +209,13 @@ coilsel = find(grad.tra(chan,:)~=0); allpnt = grad.pnt(coilsel, :); % position of all coils belonging to this channel allori = grad.ori(coilsel, :); % orientation of all coils belonging to this channel - + if strcmp(cfg.feedback, 'yes') cla plot3(grad.pnt(:,1), grad.pnt(:,2), grad.pnt(:,3), 'b.'); % all coils plot3(allpnt(:,1), allpnt(:,2), allpnt(:,3), 'r*'); % this channel in red end - + % determine the average position and orientation of this channel thispnt = mean(allpnt,1); [u, s, v] = svd(allori); @@ -313,13 +224,13 @@ % the orientation should be outwards pointing thisori = -thisori; end - + % compute the distance from every coil along this channels orientation - dist = []; + dist = zeros(size(coilsel)); for i=1:length(coilsel) dist(i) = dot((allpnt(i,:)-thispnt), thisori); end - + [m, i] = min(dist); % check whether the minimum difference is larger than a typical distance if abs(m)>(cfg.baseline/4) @@ -327,15 +238,15 @@ % except when the center of the channel is approximately just as good (planar gradiometer) thispnt = allpnt(i,:); end - + % find the headshape points that are close to this channel dist = sqrt(sum((headshape.pnt-repmat(thispnt,Nshape,1)).^2, 2)); shapesel = find(dist10 [o, r] = fitsphere(headshape.pnt(shapesel,:)); @@ -345,21 +256,21 @@ o = single_o; r = single_r; end - + if r > cfg.maxradius fprintf('channel = %s, not enough surface points, using all points\n', grad.label{chan}); o = single_o; r = single_r; end - + % add this sphere to the volume conductor vol.o(chan,:) = o; vol.r(chan) = r; vol.label{chan} = grad.label{chan}; -end +end % for all channels vol.type = 'multisphere'; % get the output cfg -cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); +cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); diff --git a/external/fieldtrip/private/prepare_mask.m b/external/fieldtrip/private/prepare_mask.m new file mode 100755 index 0000000..b414cb9 --- /dev/null +++ b/external/fieldtrip/private/prepare_mask.m @@ -0,0 +1,50 @@ +function [mask] = prepare_mask(layout, varargin) +% PREPARE_MASK calculate anatomical mask only once, based only on layout +% Use as: +% [mask] = prepare_mask(layout, 'key', 'val'); +% with optional parameter: +% 'gridscale' scale of the interpolated grid (default: 67) +% +% See also COMPONENTBROWSER, DATABROWSER + +% Copyright (C) 2009 +% +% $Log: prepare_mask.m,v $ +% Revision 1.1 2009/07/15 08:40:54 giopia +% from topoplot, it relies on inside_contour +% + +% check the input +if ~isfield(layout, 'mask') + warning('prepare_mask:nomask', 'Mask is not present in layout. Mask not created'); + mask = []; + return +end + +[gridscale] = keyval('gridscale', varargin); +if isempty(gridscale); gridscale = 67; end + +% find limits for interpolation: +xmin = +inf; +xmax = -inf; +ymin = +inf; +ymax = -inf; + +for i=1:length(layout.mask) + xmin = min([xmin; layout.mask{i}(:,1)]); + xmax = max([xmax; layout.mask{i}(:,1)]); + ymin = min([ymin; layout.mask{i}(:,2)]); + ymax = max([ymax; layout.mask{i}(:,2)]); +end + +xi = linspace(xmin, xmax, gridscale); % x-axis for interpolation (row vector) +yi = linspace(ymin, ymax, gridscale); % y-axis for interpolation (row vector) +Xi = ones(gridscale,1)*xi; +Yi = (ones(gridscale,1)*yi)'; + +% apply anatomical mask to the data, i.e. that determines that the interpolated data outside the circle is not displayed +mask = false(gridscale); +for i=1:length(layout.mask) + layout.mask{i}(end+1,:) = layout.mask{i}(1,:); % force them to be closed + mask(inside_contour([Xi(:) Yi(:)], layout.mask{i})) = true; +end diff --git a/external/fieldtrip/private/prepare_mesh.m b/external/fieldtrip/private/prepare_mesh.m new file mode 100644 index 0000000..02c96cb --- /dev/null +++ b/external/fieldtrip/private/prepare_mesh.m @@ -0,0 +1,121 @@ +function bnd = prepare_mesh(cfg, mri) + +% PREPARE_MESH creates a triangulated surface mesh for the volume +% conduction model. The mesh can either be selected manually from raw +% mri data or can be generated starting from a segmented volume +% information stored in the mri structure. The result is a bnd +% structure which contains the information about all segmented surfaces +% related to mri and are expressed in world coordinates. +% +% Use as +% bnd = prepare_mesh(cfg, mri) +% +% Configuration options: +% cfg.method = 'segmentation' or 'manual' +% cfg.tissue = list with segmentation values corresponding with each compartment +% cfg.downsample = integer (1,2, ...) defines the level of refinement of the mri data +% cfg.headshape = a filename containing headshape, a Nx3 matrix with surface +% points, or a structure with a single or multiple boundaries +% +% Example use: +% mri = read_mri('Subject01.mri'); +% cfg = []; +% cfg.method = 'manual'; +% cfg.downsample = 2; +% bnd = prepare_mesh(cfg, mri); + +% Copyrights (C) 2009, Cristiano Micheli & Robert Oostenveld +% +% $Log: prepare_mesh.m,v $ +% Revision 1.11 2009/07/29 06:43:16 roboos +% fixed basedonseg for headshape input (thanks to Vladimir) +% +% Revision 1.10 2009/07/16 09:00:51 crimic +% added choice of multiple mesh methods and fixed a small typo +% +% Revision 1.9 2009/06/17 13:38:41 roboos +% cleaned up handling of method=manual +% +% Revision 1.8 2009/06/15 14:01:08 roboos +% minor updates in documentation and default cfg +% +% Revision 1.7 2009/06/03 12:07:11 crimic +% changed help +% +% Revision 1.6 2009/06/03 12:05:06 crimic +% added cfg.numcompartments option as input for automatic segmentation +% +% Revision 1.5 2009/06/02 10:18:39 crimic +% minor changes +% +% Revision 1.4 2009/05/14 19:23:33 roboos +% small cleanup, nothing functionally changed +% +% Revision 1.3 2009/05/06 16:09:17 roboos +% renamed gui_mesh into prepare_mesh_manual and moved to private +% some cleanup of the code +% +% Revision 1.2 2009/05/06 08:46:29 crimic +% First implementation +% + +cfg = checkconfig(cfg, 'forbidden', 'numcompartments'); + +% set the defaults +if ~isfield(cfg, 'downsample'), cfg.downsample = 1; end +if ~isfield(cfg, 'tissue'), cfg.tissue = []; end +if ~isfield(cfg, 'numvertices'), cfg.numvertices = []; end + +if isfield(cfg, 'headshape') && isa(cfg.headshape, 'config') + % convert the nested cmethodonfig-object back into a normal structure + cfg.headshape = struct(cfg.headshape); +end + +% there are three types of input possible +if nargin>1 && (~isfield(cfg,'headshape') || isempty(cfg.headshape)) + basedonseg = isfield(mri, 'transform') && any(isfield(mri, {'seg', 'csf', 'white', 'gray'})); + basedonmri = isfield(mri, 'transform') && ~basedonseg; + basedonvol = isfield(mri, 'bnd'); + basedonheadshape = 0; +elseif nargin==1 && isfield(cfg,'headshape') && ~isempty(cfg.headshape) + basedonseg = 0; + basedonmri = 0; + basedonvol = 0; + basedonheadshape = 1; +else + error('inconsistent configuration, cfg.headshape should not be used in combination with an mri input') +end + +if basedonseg || basedonmri + % optionally downsample the anatomical MRI and/or the tissue segmentation + tmpcfg = []; + tmpcfg.downsample = cfg.downsample; + mri = volumedownsample(tmpcfg, mri); +end + +if basedonseg + fprintf('using the segmentation approach\n'); + bnd = prepare_mesh_segmentation(cfg, mri); + +elseif basedonmri + fprintf('using the manual approach\n'); + bnd = prepare_mesh_manual(cfg, mri); + +elseif basedonheadshape + fprintf('using the head shape to construct a triangulated mesh\n'); + bnd = prepare_mesh_headshape(cfg); + +elseif basedonvol + fprintf('using the mesh specified in the input volume conductor\n'); + bnd = mri.bnd; + +else + error('unsupported cfg.method and/or input') +end + +% ensure that the vertices and triangles are double precision, otherwise the bemcp mex files will crash +for i=1:length(bnd) + bnd(i).pnt = double(bnd(i).pnt); + bnd(i).tri = double(bnd(i).tri); +end + diff --git a/external/fieldtrip/private/prepare_mesh_headshape.m b/external/fieldtrip/private/prepare_mesh_headshape.m new file mode 100644 index 0000000..157f361 --- /dev/null +++ b/external/fieldtrip/private/prepare_mesh_headshape.m @@ -0,0 +1,59 @@ +function bnd = prepare_mesh_headshape(cfg) + +% PREPARE_MESH_HEADSHAPE +% +% See also PREPARE_MESH_MANUAL, PREPARE_MESH_SEGMENTATION + +% Copyrights (C) 2009, Robert Oostenveld +% +% $Log: prepare_mesh_headshape.m,v $ +% Revision 1.2 2009/09/09 14:34:55 roboos +% fixed bug in case the input consisted of multiple boundaries +% +% Revision 1.1 2009/07/13 14:45:06 crimic +% copy code of existin funtions into stand-alone functions for inclusion in the prepare_mesh helper function +% + +% get the surface describing the head shape +if isstruct(cfg.headshape) && isfield(cfg.headshape, 'pnt') + % use the headshape surface specified in the configuration + headshape = cfg.headshape; +elseif isnumeric(cfg.headshape) && size(cfg.headshape,2)==3 + % use the headshape points specified in the configuration + headshape.pnt = cfg.headshape; +elseif ischar(cfg.headshape) + % read the headshape from file + headshape = read_headshape(cfg.headshape); +else + error('cfg.headshape is not specified correctly') +end + +% usually a headshape only describes a single surface boundaries, but there are cases +% that multiple surfaces are included, e.g. skin, skull, brain +nbnd = numel(headshape); + +if ~isfield(headshape, 'tri') + % generate a closed triangulation from the surface points + for i=1:nbnd + headshape(i).pnt = unique(headshape(i).pnt, 'rows'); + headshape(i).tri = projecttri(headshape(i).pnt); + end +end + +if ~isempty(cfg.numvertices) && ~strcmp(cfg.numvertices, 'same') + for i=1:nbnd + [tri1, pnt1] = reducepatch(headshape(i).tri, headshape(i).pnt, 3*cfg.numvertices); + % remove double vertices + pnt1 = unique(pnt1, 'rows'); + % reconstruct the triangulation + tri1 = projecttri(pnt1); + % replace the probably unevenly distributed triangulation with a regular one + % and retriangulate it to the desired accuracy + [pnt2, tri2] = msphere(cfg.numvertices); % this is a regular triangulation + [headshape(i).pnt, headshape(i).tri] = retriangulate(pnt1, tri1, pnt2, tri2, 2); + end +end + +% the output should only describe one or multiple boundaries and should not +% include any other fields +bnd = rmfield(headshape, setdiff(fieldnames(headshape), {'pnt', 'tri'})); diff --git a/external/fieldtrip/private/prepare_mesh_manual.m b/external/fieldtrip/private/prepare_mesh_manual.m new file mode 100644 index 0000000..8a0544d --- /dev/null +++ b/external/fieldtrip/private/prepare_mesh_manual.m @@ -0,0 +1,1073 @@ +function bnd = prepare_mesh_manual(cfg, mri) + +% PREPARE_MESH_MANUAL is called by PREPARE_MESH and opens a GUI to manually +% select points/polygons in an mri dataset. +% +% It allows: +% Visualization of 3d data in 3 different projections +% Adjustment of brightness for every slice +% Storage of the data points in an external .mat file +% Retrieval of previously saved data points +% Slice fast scrolling with keyboard arrows +% Polygons or points selection/deselection +% +% See also PREPARE_MESH_SEGMENTATION, PREPARE_MESH_HEADSHAPE + +% Copyrights (C) 2009, Cristiano Micheli & Robert Oostenveld +% +% $Log: prepare_mesh_manual.m,v $ +% Revision 1.10 2009/08/31 11:13:45 crimic +% minibug fix +% +% Revision 1.9 2009/08/27 16:12:13 crimic +% minibug fix +% +% Revision 1.8 2009/07/13 14:43:05 crimic +% inserted spherical harmonics sub-functions +% +% Revision 1.7 2009/06/30 11:45:38 roboos +% renamed select_point2d function call +% +% Revision 1.6 2009/06/26 14:33:28 crimic +% added mesh smoothing option and subfunctions +% +% Revision 1.5 2009/06/26 13:06:21 crimic +% point delete function implemented +% +% Revision 1.4 2009/06/17 13:37:11 roboos +% used autoindentation to clean up whitespace +% +% Revision 1.3 2009/06/03 11:29:59 crimic +% small fixes +% +% Revision 1.2 2009/06/03 10:36:29 crimic +% changes of layout and small fixes +% +% Revision 1.1 2009/05/06 16:09:17 roboos +% renamed gui_mesh into prepare_mesh_manual and moved to private +% some cleanup of the code +% +% Revision 1.1 2009/05/06 13:55:37 crimic +% First implementation +% + +% FIXME: control slice's cmap referred to abs values +% FIXME: clean structure slicedata +% FIXME: check function assign3dpoints + +global obj + +bnd.pnt = []; +bnd.tri = []; + +hasheadshape = isfield(cfg, 'headshape'); +hasbnd = isfield(cfg, 'bnd'); % FIXME why is this in cfg? +hasmri = nargin>1; + +% check the consistency of the input arguments +if hasheadshape && hasbnd + error('you should not specify cfg.headshape and cfg.bnd simultaneously'); +end + +% check the consistency of the input arguments +if ~hasmri + % FIXME give a warning or so? + mri.anatomy = []; + mri.transform = eye(4); +else + % ensure that it is double precision + mri.anatomy = double(mri.anatomy); +end + +if hasheadshape + if ~isempty(cfg.headshape) + % get the surface describing the head shape + if isstruct(cfg.headshape) && isfield(cfg.headshape, 'pnt') + % use the headshape surface specified in the configuration + headshape = cfg.headshape; + elseif isnumeric(cfg.headshape) && size(cfg.headshape,2)==3 + % use the headshape points specified in the configuration + headshape.pnt = cfg.headshape; + elseif ischar(cfg.headshape) + % read the headshape from file + headshape = read_headshape(cfg.headshape); + else + headshape = []; + end + if ~isfield(headshape, 'tri') + for i=1:length(headshape) + % generate a closed triangulation from the surface points + headshape(i).pnt = unique(headshape(i).pnt, 'rows'); + headshape(i).tri = projecttri(headshape(i).pnt); + end + end + % start with the headshape + bnd = headshape; + end +elseif hasbnd + if ~isempty(cfg.bnd) + % start with the prespecified boundaries + bnd = cfg.bnd; + end +else + % start with an empty boundary if not specified + bnd.pnt = []; + bnd.tri = []; +end + +% creating the GUI +fig = figure; + +% initialize some values +slicedata = []; points = bnd.pnt; +axs = floor(size(mri.anatomy,1)/2); + +% initialize properties +set(fig, 'KeyPressFcn',@keypress); +set(fig, 'CloseRequestFcn', @cb_close); +setappdata(fig,'data',mri.anatomy); +setappdata(fig,'prop',{1,axs,0,[]}); +setappdata(fig,'slicedata',slicedata); +setappdata(fig,'points',points); +setappdata(fig,'box',[]); + +% add GUI elements +cb_creategui(gca); +cb_redraw(gca); +waitfor(fig); + +% close sequence +global pnt +try + tmp = pnt; + clear global pnt + pnt = tmp; + clear tmp + [tri] = projecttri(pnt); + bnd.pnt = pnt; + bnd.tri = tri; +catch + bnd.pnt = []; + bnd.tri = []; +end + + +function cb_redraw(hObject, eventdata, handles); +fig = get(hObject, 'parent'); +prop = getappdata(fig,'prop'); +data = getappdata(fig,'data'); +slicedata = getappdata(fig,'slicedata'); +points = getappdata(fig,'points'); + +% draw image +try + cla + % i,j,k axes + if (prop{1}==1) %jk + imh=imagesc(squeeze(data(prop{2},:,:))); + xlabel('k'); + ylabel('j'); + colormap bone + elseif (prop{1}==2) %ik + imh=imagesc(squeeze(data(:,prop{2},:))); + xlabel('k'); + ylabel('i'); + colormap bone + elseif (prop{1}==3) %ij + imh=imagesc(squeeze(data(:,:,prop{2}))); + xlabel('j'); + ylabel('i'); + colormap bone + end + axis equal; axis tight + brighten(prop{3}); + title([ 'slice ' num2str(prop{2}) ]) +catch +end +if ~ishold,hold on,end +% draw points and lines +for zz = 1:length(slicedata) + if(slicedata(zz).proj==prop{1}) + if(slicedata(zz).slice==prop{2}) + polygon = slicedata(zz).polygon; + for kk=1:length(polygon) + if ~isempty(polygon{kk}) + [xs,ys]=deal(polygon{kk}(:,1),polygon{kk}(:,2)); + plot(xs, ys, 'g.-'); + end + end + end + end +end % end for + +% draw other slices points +points = getappdata(fig,'points'); +pnts = round(points); +if ~isempty(pnts) + % exclude polygons inside the same slice + indx = find(points(:,prop{1})==prop{2}); + tmp = ones(1,size(points,1)); + tmp(indx) = 0; + pnts = points(find(tmp),:); + % calculate indexes of other proj falling in current slice + indx2 = find(round(pnts(:,prop{1}))==prop{2}); + rest = setdiff([1 2 3],prop{1}); + % plot the points + for jj=1:length(indx2) + [x,y] = deal(pnts(indx2(jj),rest(1)),pnts(indx2(jj),rest(2))); + plot(y,x,'g*') + end +end + +% add blue cross markers in case of box selection +box = getappdata(fig,'box'); +point2mark = getappdata(fig,'point2mark'); + +if ~isempty(point2mark) + plot(point2mark.x,point2mark.y,'marker','+') +end + +if ~isempty(box) + for kk=1:size(box,1) + proj = box(kk,1); + slice = box(kk,2); + rowmin = box(kk,3); + rowmax = box(kk,4); + colmin = box(kk,5); + colmax = box(kk,6); + aaa = get(findobj(fig, 'color', 'g')); + for ii=1:length(aaa) + if ispolygon(aaa(ii)) + L = length(aaa(ii).YData); + for jj=1:L + cond = lt(aaa(ii).YData(jj),rowmax).*gt(aaa(ii).YData(jj),rowmin).* ... + lt(aaa(ii).XData(jj),colmax).*gt(aaa(ii).XData(jj),colmin); + if cond + plot(aaa(ii).XData(jj),aaa(ii).YData(jj),'marker','+') + end + end + elseif ispoint(aaa(ii)) + cond = lt(aaa(ii).YData,rowmax).*gt(aaa(ii).YData,rowmin).* ... + lt(aaa(ii).XData,colmax).*gt(aaa(ii).XData,colmin); + % the test after cond takes care the box doesnt propagate in 3D + if (cond && (proj == prop{1}) && (slice == prop{2})) + plot(aaa(ii).XData,aaa(ii).YData,'marker','+') + end + end + end + end +end + +if get(findobj(fig, 'tag', 'toggle axes'), 'value') + axis on +else + axis off +end + +function cb_creategui(hObject, eventdata, handles) + +fig = get(hObject, 'parent'); +% define the position of each GUI element +position = { + [1] % view radio + [1 1] % del ins + [1] % label slice + [1 1] % slice + [1] % brightness + [1 1] % brightness + [1] % axis visible + [1 1] % view ij + [1 1] % jk ik + [1] % label mesh + [1 1] % view open mesh + [1 1] % save exit buttons + }; + +% define the style of each GUI element +style = { + {'radiobutton'} + {'radiobutton' 'radiobutton'} + {'text' } + {'pushbutton' 'pushbutton'} + {'text'} + {'pushbutton' 'pushbutton'} + {'checkbox'} + {'text' 'radiobutton'} + {'radiobutton' 'radiobutton'} + {'text' } + {'pushbutton' 'pushbutton'} + {'pushbutton' 'pushbutton'} + }; + +% define the descriptive string of each GUI element +string = { + {'View'} + {'Ins' 'Del'} + {'slice'} + {'<' '>'} + {'brightness'} + {'+' '-'} + {'axes'} + {'plane' 'jk'} + {'ik' 'ij'} + {'mesh'} + {'smooth' 'view'} + {'Cancel' 'OK'} + }; + +% define the value of each GUI element + +prop = getappdata(fig,'prop'); +value = { + {1} + {0 0} + {[]} + {[] []} + {[]} + {[] []} + {0} + {[] 1} + {0 0} + {[]} + {1 1} + {1 1} + }; + +% define a tag for each GUI element +tag = { + {'view'} + {'ins' 'del' } + {'slice'} + {'min' 'max'} + {'brightness'} + {'plus' 'minus'} + {'toggle axes'} + {'plane' 'one'} + {'two' 'three'} + {'mesh'} + {'smoothm' 'viewm'} + {'cancel' 'OK'} + }; + +% define the callback function of each GUI element +callback = { + {@which_task1} + {@which_task2 @which_task3} + {[]} + {@cb_btn @cb_btn} + {[]} + {@cb_btn @cb_btn} + {@cb_redraw} + {[] @cb_btnp1} + {@cb_btnp2 @cb_btnp3} + {[]} + {@smooth_mesh @view_mesh} + {@cancel_mesh @cb_btn} + }; + +visible = { + {'on'} + {'on' 'on'} + {'on'} + {'on' 'on'} + {'on'} + {'on' 'on'} + {'on'} + {'on' 'on'} + {'on' 'on'} + {'on'} + {'on' 'on'} + {'on' 'on'} + }; + +layoutgui(fig, [0.77 0.10 0.20 0.85], position, style, string, value, tag, callback,visible); + +function h = layoutgui(fig, geometry, position, style, string, value, tag, callback,visible); +horipos = geometry(1); % lower left corner of the GUI part in the figure +vertpos = geometry(2); % lower left corner of the GUI part in the figure +width = geometry(3); % width of the GUI part in the figure +height = geometry(4); % height of the GUI part in the figure +horidist = 0.05; +vertdist = 0.05; +options = {'units', 'normalized', 'HorizontalAlignment', 'center'}; % 'VerticalAlignment', 'middle' +Nrow = size(position,1); +h = cell(Nrow,1); +for i=1:Nrow + if isempty(position{i}) + continue; + end + position{i} = position{i} ./ sum(position{i}); + Ncol = size(position{i},2); + ybeg = (Nrow-i )/Nrow + vertdist/2; + yend = (Nrow-i+1)/Nrow - vertdist/2; + for j=1:Ncol + xbeg = sum(position{i}(1:(j-1))) + horidist/2; + xend = sum(position{i}(1:(j ))) - horidist/2; + pos(1) = xbeg*width + horipos; + pos(2) = ybeg*height + vertpos; + pos(3) = (xend-xbeg)*width; + pos(4) = (yend-ybeg)*height; + h{i}{j} = uicontrol(fig, ... + options{:}, ... + 'position', pos, ... + 'style', style{i}{j}, ... + 'string', string{i}{j}, ... + 'tag', tag{i}{j}, ... + 'value', value{i}{j}, ... + 'callback', callback{i}{j}, ... + 'visible', visible{i}{j} ... + ); + end +end + +function cb_btn(hObject, eventdata, handles) +fig = get(gca, 'parent'); +prop = getappdata(fig,'prop'); +slice = prop{2}; +br = prop{3}; +datdim = size(getappdata(fig,'data')); +% p1 = [];p2 = [];p3 = []; +p1 = get(findobj('string','jk'),'value'); +p2 = get(findobj('string','ik'),'value'); +p3 = get(findobj('string','ij'),'value'); +set(findobj('string','-'),'Value',prop{3}); +set(findobj('string','+'),'Value',prop{3}); +beta = get(findobj('string','-'),'Value'); + +if (p1) %jk + setappdata(fig,'prop',{1,round(datdim(1)/2),br,prop{4}}); + cb_redraw(gca); +elseif (p2) %ik + setappdata(fig,'prop',{2,round(datdim(2)/2),br,prop{4}}); + cb_redraw(gca); +elseif (p3) %ij + setappdata(fig,'prop',{3,round(datdim(3)/2),br,prop{4}}); + cb_redraw(gca); +end +if strcmp(get(hObject,'string'),'-') + beta=beta-0.05; + set(findobj('string','-'),'Value',beta); + set(findobj('string','+'),'Value',beta); + setappdata(fig,'prop',{prop{1},prop{2},beta,prop{4}}); + cb_redraw(gca); +elseif strcmp(get(hObject,'string'),'+') + beta=beta+0.05; + set(findobj('string','+'),'Value',beta); + setappdata(fig,'prop',{prop{1},prop{2},beta,prop{4}}); + cb_redraw(gca); +end + +if strcmp(get(hObject,'string'),'<') + setappdata(fig,'prop',{prop{1},slice-1,0,prop{4}}); + cb_redraw(gca); +end +if strcmp(get(hObject,'string'),'>') + setappdata(fig,'prop',{prop{1},slice+1,0,prop{4}}); + cb_redraw(gca); +end +if strcmp(get(hObject,'string'),'OK') + close(fig) +end + +function keypress(h, eventdata, handles, varargin) +fig = get(gca, 'parent'); +prop = getappdata(fig,'prop'); +dat = guidata(gcbf); +key = get(gcbf, 'CurrentCharacter'); +slice = prop{2}; +if key + switch key + case 28 + setappdata(fig,'prop',{prop{1},slice-1,0,prop{4}}); + cb_redraw(gca); + case 29 + setappdata(fig,'prop',{prop{1},slice+1,0,prop{4}}); + cb_redraw(gca); + otherwise + end +end +uiresume(h) + +function build_polygon +fig = get(gca, 'parent'); +prop = getappdata(fig,'prop'); +data = getappdata(fig,'data'); +ss = size(data); +proj = prop{1}; +slice = prop{2}; +thispolygon =1; +polygon{thispolygon} = zeros(0,2); +maskhelp = [ ... + '------------------------------------------------------------------------\n' ... + 'specify polygons for masking the topographic interpolation\n' ... + 'press the right mouse button to add another point to the current polygon\n' ... + 'press backspace on the keyboard to remove the last point\n' ... + 'press "c" on the keyboard to close this polygon and start with another\n' ... + 'press "q" or ESC on the keyboard to continue\n' ... + ]; +again = 1; +if ~ishold,hold,end +fprintf(maskhelp); +fprintf('\n'); + +while again + for i=1:length(polygon) + fprintf('polygon %d has %d points\n', i, size(polygon{i},1)); + end + + [x, y, k] = ginput(1); + okflag = 0; + + % check points do not fall out of image boundaries + if get(findobj('string','Ins'),'value') + if (proj == 1 && y1 && y>1) + okflag = 1; + elseif (proj == 2 && y1 && y>1) + okflag = 1; + elseif (proj == 3 && y1 && y>1) + okflag = 1; + else + okflag = 0; + end + end + + if okflag + switch lower(k) + case 1 + polygon{thispolygon} = cat(1, polygon{thispolygon}, [x y]); + % add the last line segment to the figure + if size(polygon{thispolygon},1)>1 + x = polygon{i}([end-1 end],1); + y = polygon{i}([end-1 end],2); + end + plot(x, y, 'g.-'); + + case 8 % backspace + if size(polygon{thispolygon},1)>0 + % redraw existing polygons + cb_redraw(gca); + % remove the last point of current polygon + polygon{thispolygon} = polygon{thispolygon}(1:end-1,:); + for i=1:length(polygon) + x = polygon{i}(:,1); + y = polygon{i}(:,2); + if i~=thispolygon + % close the polygon in the figure + x(end) = x(1); + y(end) = y(1); + end + set(gca,'nextplot','new') + plot(x, y, 'g.-'); + end + end + + case 'c' + if size(polygon{thispolygon},1)>0 + % close the polygon + polygon{thispolygon}(end+1,:) = polygon{thispolygon}(1,:); + % close the polygon in the figure + x = polygon{i}([end-1 end],1); + y = polygon{i}([end-1 end],2); + plot(x, y, 'g.-'); + % switch to the next polygon + thispolygon = thispolygon + 1; + polygon{thispolygon} = zeros(0,2); + slicedata = getappdata(fig,'slicedata'); + m = length(slicedata); + slicedata(m+1).proj = prop{1}; + slicedata(m+1).slice = prop{2}; + slicedata(m+1).polygon = polygon; + setappdata(fig,'slicedata',slicedata); + end + + case {'q', 27} + if size(polygon{thispolygon},1)>0 + % close the polygon + polygon{thispolygon}(end+1,:) = polygon{thispolygon}(1,:); + % close the polygon in the figure + x = polygon{i}([end-1 end],1); + y = polygon{i}([end-1 end],2); + plot(x, y, 'g.-'); + end + again = 0; + %set(gcf,'WindowButtonDownFcn',''); + slicedata = getappdata(fig,'slicedata'); + m = length(slicedata); + if ~isempty(polygon{thispolygon}) + slicedata(m+1).proj = prop{1}; + slicedata(m+1).slice = prop{2}; + slicedata(m+1).polygon = polygon; + setappdata(fig,'slicedata',slicedata); + end + otherwise + warning('invalid button (%d)', k); + end + + end + assign_points3d +end + +function cb_btn_dwn(hObject, eventdata, handles) +build_polygon +uiresume + +function cb_btn_dwn2(hObject, eventdata, handles) +global obj +erase_points2d(obj) +uiresume + + +function erase_points2d (obj) +fig = get(gca, 'parent'); +prop = getappdata(fig,'prop'); +slicedata = getappdata(fig,'slicedata'); +pntsslice = []; + +% 2d slice selected box boundaries +if strcmp(get(obj,'string'),'box') + [x, y] = select_box; + rowmin = min(y); rowmax = max(y); + colmin = min(x); colmax = max(x); + box = getappdata(fig,'box'); + box = [box; prop{1} prop{2} rowmin rowmax colmin colmax]; + setappdata(fig,'box',box); +else + % converts lines to points + pos = slicedata2pnts(prop{1},prop{2}); + tmp = select_point(pos); + point2mark.x = tmp(1); point2mark.y = tmp(2); + setappdata(fig,'point2mark',point2mark); +end + +maskhelp = [ ... + '------------------------------------------------------------------------\n' ... + 'Delete points:\n' ... + 'press "c" on the keyboard to undo all selections\n' ... + 'press "d" or DEL on the keyboard to delete all selections\n' ... + ]; +fprintf(maskhelp); +fprintf('\n'); +again = 1; +if ~ishold,hold,end +cb_redraw(gca); + +% waits for a key press +while again + [junk1, junk2, k] = ginput(1); + + switch lower(k) + case 'c' + if strcmp(get(obj,'string'),'box') + % undoes all the selections, but only in the current slice + ind = []; + for ii=1:size(box,1) + if box(ii,1)==prop{1} && box(ii,2)==prop{2} + ind = [ind,ii]; + end + end + box(ind,:) = []; + setappdata(fig,'box',box); + cb_redraw(gca); + again = 0; + set(gcf,'WindowButtonDownFcn',''); + else + setappdata(fig,'point2mark',[]); + cb_redraw(gca); + again = 0; + set(gcf,'WindowButtonDownFcn',''); + end + case {'d', 127} + if strcmp(get(obj,'string'),'box') + update_slicedata + % deletes only the points selected in the current slice + ind = []; + for ii=1:size(box,1) + if box(ii,1)==prop{1} && box(ii,2)==prop{2} + ind = [ind,ii]; + end + end + box(ind,:) = []; + setappdata(fig,'box',box); + cb_redraw(gca); + again = 0; + set(gcf,'WindowButtonDownFcn',''); + else + update_slicedata + setappdata(fig,'point2mark',[]); + cb_redraw(gca); + again = 0; + set(gcf,'WindowButtonDownFcn',''); + end + otherwise + end +end +assign_points3d + +function assign_points3d +fig = get(gca, 'parent'); +slicedata = getappdata(fig,'slicedata'); + +points = []; +for zz = 1:length(slicedata) + slice = slicedata(zz).slice; + proj = slicedata(zz).proj; + polygon = slicedata(zz).polygon; + for kk=1:length(polygon) + if ~isempty(polygon{kk}) + [xs,ys]=deal(polygon{kk}(:,1),polygon{kk}(:,2)); + if (proj==1) %jk + tmp = [ slice*ones(length(xs),1),ys(:),xs(:) ]; + points = [points; unique(tmp,'rows')]; + elseif (proj==2) %ik + tmp = [ ys(:),slice*ones(length(xs),1),xs(:) ]; + points = [points; unique(tmp,'rows')]; + elseif (proj==3) %ij + tmp = [ ys(:),xs(:),slice*ones(length(xs),1) ]; + points = [points; unique(tmp,'rows')]; + end + end + end +end +setappdata(fig,'points',points); + +function update_slicedata +fig = get(gca, 'parent'); +prop = getappdata(fig,'prop'); +slicedata = getappdata(fig,'slicedata'); +box = getappdata(fig,'box'); +point2mark = getappdata(fig,'point2mark'); +% case of box selection +if ~isempty(box) && ~isempty(slicedata) + for zz = 1:length(slicedata) + slice = slicedata(zz).slice; + proj = slicedata(zz).proj; + polygon = slicedata(zz).polygon; + for uu=1:size(box,1) + if (box(uu,1)==proj) && (box(uu,2)==slice) + for kk=1:length(polygon) + if ~isempty(polygon{kk}) + [xs,ys] = deal(polygon{kk}(:,1),polygon{kk}(:,2)); + tmpind = lt(ys,ones(length(xs),1)*box(uu,4)).*gt(ys,ones(length(xs),1)*box(uu,3)).* ... + lt(xs,ones(length(xs),1)*box(uu,6)).*gt(xs,ones(length(xs),1)*box(uu,5)); + slicedata(zz).polygon{kk} = [ xs(find(~tmpind)),ys(find(~tmpind)) ]; + end + end + end + end + end + % case of point selection +else + if ~isempty(slicedata) + for zz = 1:length(slicedata) + slice = slicedata(zz).slice; + proj = slicedata(zz).proj; + polygon = slicedata(zz).polygon; + for kk=1:length(polygon) + if ~isempty(polygon{kk}) + [xs,ys] = deal(polygon{kk}(:,1),polygon{kk}(:,2)); + tmpind = eq(xs,point2mark.x).*eq(ys,point2mark.y); + slicedata(zz).polygon{kk} = [ xs(find(~tmpind)),ys(find(~tmpind)) ]; + end + end + end + end +end +setappdata(fig,'slicedata',slicedata) + +% FIXME: obsolete: replaced by headshape at the beginning +function smooth_mesh(hObject, eventdata, handles) +fig = get(gca,'parent'); +disp('not yet implemented') + +% FIXME: make it work for pre-loaded meshes +function view_mesh(hObject, eventdata, handles) +fig = get(gca, 'parent'); +assign_points3d +pnt = getappdata(fig,'points'); +pnt_ = pnt; +if ~isempty(pnt) + tri = projecttri(pnt); + bnd.pnt = pnt_; + bnd.tri = tri; + slicedata = getappdata(fig,'slicedata'); + figure,plot_mesh(bnd,'vertexcolor','k') +end + +function cancel_mesh(hObject, eventdata, handles) +fig = get(gca, 'parent'); +close(fig) + +function cb_close(hObject, eventdata, handles) +% get the points from the figure +fig = hObject; +global pnt +pnt = getappdata(fig, 'points'); +set(fig, 'CloseRequestFcn', @delete); +delete(fig); + +function cb_btnp1(hObject, eventdata, handles) +set(findobj('string','jk'),'value',1); +set(findobj('string','ik'),'value',0); +set(findobj('string','ij'),'value',0); +cb_btn(hObject); + +function cb_btnp2(hObject, eventdata, handles) +set(findobj('string','jk'),'value',0); +set(findobj('string','ik'),'value',1); +set(findobj('string','ij'),'value',0); +cb_btn(hObject); + +function cb_btnp3(hObject, eventdata, handles) +set(findobj('string','jk'),'value',0); +set(findobj('string','ik'),'value',0); +set(findobj('string','ij'),'value',1); +cb_btn(hObject); + +function which_task1(hObject, eventdata, handles) +set(gcf,'WindowButtonDownFcn',''); +set(findobj('string','View'),'value',1); +set(findobj('string','Ins'), 'value',0); +set(findobj('string','Del'), 'value',0); +set(gcf,'WindowButtonDownFcn',''); +change_panel + +function which_task2(hObject, eventdata, handles) +set(findobj('string','View'),'value',0); +set(findobj('string','Ins'), 'value',1); +set(findobj('string','Del'), 'value',0); +set(gcf,'WindowButtonDownFcn',@cb_btn_dwn); +change_panel + +function which_task3(hObject, eventdata, handles) +set(gcf,'WindowButtonDownFcn',''); +set(findobj('string','View'),'value',0); +set(findobj('string','Ins'), 'value',0); +set(findobj('string','Del'), 'value',1); +change_panel + +function change_panel +% affect panel visualization +w1 = get(findobj('string','View'),'value'); +w2 = get(findobj('string','Ins'), 'value'); +w3 = get(findobj('string','Del'), 'value'); + +if w1 + set(findobj('tag','slice'),'string','slice'); + set(findobj('tag','min'),'string','<'); + set(findobj('tag','max'),'string','>'); + set(findobj('tag','min'),'style','pushbutton'); + set(findobj('tag','max'),'style','pushbutton'); + set(findobj('tag','min'),'callback',@cb_btn); + set(findobj('tag','max'),'callback',@cb_btn); + set(findobj('tag','mesh'),'visible','on'); + set(findobj('tag','openm'),'visible','on'); + set(findobj('tag','viewm'),'visible','on'); + set(findobj('tag','brightness'),'visible','on'); + set(findobj('tag','plus'),'visible','on'); + set(findobj('tag','minus'),'visible','on'); + set(findobj('tag','toggle axes'),'visible','on'); + set(findobj('tag','plane'),'visible','on'); + set(findobj('tag','one'),'visible','on'); + set(findobj('tag','two'),'visible','on'); + set(findobj('tag','three'),'visible','on'); +elseif w2 + set(findobj('tag','slice'),'string','select points'); + set(findobj('tag','min'),'string','close'); + set(findobj('tag','max'),'string','next'); + set(findobj('tag','min'),'style','pushbutton'); + set(findobj('tag','max'),'style','pushbutton'); + set(findobj('tag','min'),'callback',@tins_close); + set(findobj('tag','max'),'callback',@tins_next); + set(findobj('tag','mesh'),'visible','off'); + set(findobj('tag','openm'),'visible','off'); + set(findobj('tag','viewm'),'visible','off'); + set(findobj('tag','brightness'),'visible','off'); + set(findobj('tag','plus'),'visible','off'); + set(findobj('tag','minus'),'visible','off'); + set(findobj('tag','toggle axes'),'visible','off'); + set(findobj('tag','plane'),'visible','off'); + set(findobj('tag','one'),'visible','off'); + set(findobj('tag','two'),'visible','off'); + set(findobj('tag','three'),'visible','off'); +elseif w3 + set(findobj('tag','slice'),'string','select points'); + set(findobj('tag','min'),'string','box'); + set(findobj('tag','max'),'string','point'); + set(findobj('tag','min'),'style','pushbutton'); + set(findobj('tag','max'),'style','pushbutton'); + set(findobj('tag','min'),'callback',@tdel_box); + set(findobj('tag','max'),'callback',@tdel_single); + set(findobj('tag','mesh'),'visible','off'); + set(findobj('tag','openm'),'visible','off'); + set(findobj('tag','viewm'),'visible','off'); + set(findobj('tag','brightness'),'visible','off'); + set(findobj('tag','plus'),'visible','off'); + set(findobj('tag','minus'),'visible','off'); + set(findobj('tag','toggle axes'),'visible','off'); + set(findobj('tag','plane'),'visible','off'); + set(findobj('tag','one'),'visible','off'); + set(findobj('tag','two'),'visible','off'); + set(findobj('tag','three'),'visible','off'); +end + +function tins_close(hObject, eventdata, handles) +set(gcf,'WindowButtonDownFcn',''); + +function tins_next(hObject, eventdata, handles) +set(gcf,'WindowButtonDownFcn',''); + + +function tdel_box(hObject, eventdata, handles) +global obj +obj = gco; +set(gcf,'WindowButtonDownFcn',@cb_btn_dwn2); + +function tdel_single(hObject, eventdata, handles) +global obj +obj = gco; +set(gcf,'WindowButtonDownFcn',@cb_btn_dwn2); + +% accessory functions: +function [pnts] = slicedata2pnts(proj,slice) +fig = get(gca, 'parent'); +slicedata = getappdata(fig,'slicedata'); +pnts = []; +for i=1:length(slicedata) + if slicedata(i).slice==slice && slicedata(i).proj == proj + for j=1:length(slicedata(i).polygon) + if ~isempty(slicedata(i).polygon{j}) + tmp = slicedata(i).polygon{j}; + xs = tmp(:,1); ys = tmp(:,2); + pnts = [pnts;[unique([xs,ys],'rows')]]; + end + end + end +end + +function h = ispolygon(x) +h = length(x.XData)>1; + +function h = ispoint(x) +h = length(x.XData)==1; + +function [T,P] = points2param(pnt) +x = pnt(:,1); y = pnt(:,2); z = pnt(:,3); +[phi,theta,R] = cart2sph(x,y,z); +theta = theta + pi/2; +phi = phi + pi; +T = theta(:); +P = phi(:); + +function [bnd2,a,Or] = spherical_harmonic_mesh(bnd, nl) + +% SPHERICAL_HARMONIC_MESH realizes the smoothed version of a mesh contained in the first +% argument bnd. The boundary argument (bnd) contains typically 2 fields +% called .pnt and .tri referring to vertices and triangulation of a mesh. +% The degree of smoothing is given by the order in the second input: nl +% +% Use as +% bnd2 = spherical_harmonic_mesh(bnd, nl); + +% nl = 12; % from van 't Ent paper + +bnd2 = []; + +% calculate midpoint +Or = mean(bnd.pnt); +% rescale all points +x = bnd.pnt(:,1) - Or(1); +y = bnd.pnt(:,2) - Or(2); +z = bnd.pnt(:,3) - Or(3); +X = [x(:), y(:), z(:)]; + +% convert points to parameters +[T,P] = points2param(X); + +% basis function +B = shlib_B(nl, T, P); +Y = B'*X; + +% solve the linear system +a = pinv(B'*B)*Y; + +% build the surface +Xs = zeros(size(X)); +for l = 0:nl-1 + for m = -l:l + if m<0 + Yml = shlib_Yml(l, abs(m), T, P); + Yml = (-1)^m*conj(Yml); + else + Yml = shlib_Yml(l, m, T, P); + end + indx = l^2 + l + m+1; + Xs = Xs + Yml*a(indx,:); + end + fprintf('%d of %d\n', l, nl); +end +% Just take the real part +Xs = real(Xs); +% reconstruct the matrices for plotting. +xs = reshape(Xs(:,1), size(x,1), size(x,2)); +ys = reshape(Xs(:,2), size(x,1), size(x,2)); +zs = reshape(Xs(:,3), size(x,1), size(x,2)); + +bnd2.pnt = [xs(:)+Or(1) ys(:)+Or(2) zs(:)+Or(3)]; +[bnd2.tri] = projecttri(bnd.pnt); + +function B = shlib_B(nl, theta, phi) +% function B = shlib_B(nl, theta, phi) +% +% Constructs the matrix of basis functions +% where b(i,l^2 + l + m) = Yml(theta, phi) +% +% See also: shlib_Yml.m, shlib_decomp.m, shlib_gen_shfnc.m +% +% Dr. A. I. Hanna (2006). +B = zeros(length(theta), nl^2+2*nl+1); +for l = 0:nl-1 + for m = -l:l + if m<0 + Yml = shlib_Yml(l, abs(m), theta, phi); + Yml = (-1)^m*conj(Yml); + else + Yml = shlib_Yml(l, m, theta, phi); + end + indx = l^2 + l + m+1; + B(:, indx) = Yml; + end +end +return; + +function Yml = shlib_Yml(l, m, theta, phi) +% function Yml = shlib_Yml(l, m, theta, phi) +% +% A matlab function that takes a given order and degree, and the matrix of +% theta and phi and constructs a spherical harmonic from these. The +% analogue in the 1D case would be to give a particular frequency. +% +% Inputs: +% m - order of the spherical harmonic +% l - degree of the spherical harmonic +% theta - matrix of polar coordinates \theta \in [0, \pi] +% phi - matrix of azimuthal cooridinates \phi \in [0, 2\pi) +% +% Example: +% +% [x, y] = meshgrid(-1:.1:1); +% z = x.^2 + y.^2; +% [phi,theta,R] = cart2sph(x,y,z); +% Yml = spharm_aih(2,2, theta(:), phi(:)); +% +% See also: shlib_B.m, shlib_decomp.m, shlib_gen_shfnc.m +% +% Dr. A. I. Hanna (2006) +Pml=legendre(l,cos(theta)); +if l~=0 + Pml=squeeze(Pml(m+1,:,:)); +end +Pml = Pml(:); +% Yml = sqrt(((2*l+1)/4).*(factorial(l-m)/factorial(l+m))).*Pml.*exp(sqrt(-1).*m.*phi); +% new: +Yml = sqrt(((2*l+1)/(4*pi)).*(factorial(l-m)/factorial(l+m))).*Pml.*exp(sqrt(-1).*m.*phi); +return; diff --git a/external/fieldtrip/private/prepare_mesh_segmentation.m b/external/fieldtrip/private/prepare_mesh_segmentation.m new file mode 100644 index 0000000..349d54a --- /dev/null +++ b/external/fieldtrip/private/prepare_mesh_segmentation.m @@ -0,0 +1,107 @@ +function bnd = prepare_mesh_segmentation(cfg, mri) + +% PREPARE_MESH_SEGMENTATION +% +% See also PREPARE_MESH_MANUAL,PREPARE_MESH_HEADSHAPE + +% Copyrights (C) 2009, Robert Oostenveld +% +% $Log: prepare_mesh_segmentation.m,v $ +% Revision 1.4 2009/08/11 12:47:20 jansch +% fixed bug in assignment of default cfg.threshold +% +% Revision 1.3 2009/07/17 10:10:56 crimic +% added initial checks and variables consinstency +% +% Revision 1.2 2009/07/16 15:30:55 crimic +% fixed tiny error +% +% Revision 1.1 2009/07/13 14:45:06 crimic +% copy code of existing functions into stand-alone functions for inclusion in the prepare_mesh helper function +% + +% some initial checks +cfg = checkconfig(cfg, 'forbidden', 'numcompartments'); +if ~isfield(mri, 'tissue') && isfield(mri, 'gray'), cfg.tissue = 1; end +if ~isfield(cfg, 'threshold'), cfg.threshold = 0; end + +fprintf('using the segmented MRI\n'); + +if ~isfield(mri, 'seg') && isequal(cfg.tissue, 1) + mri.seg = zeros(size(mri.gray )); + % construct the single image segmentation from the three probabilistic + % tissue segmentations for csf, white and gray matter + if isfield(mri, 'gray') + fprintf('including gray matter in segmentation for brain compartment\n') + mri.seg = mri.seg | (mri.gray>(cfg.threshold*max(mri.gray(:)))); + end + if isfield(mri, 'white') + fprintf('including white matter in segmentation for brain compartment\n') + mri.seg = mri.seg | (mri.white>(cfg.threshold*max(mri.white(:)))); + end + if isfield(mri, 'csf') + fprintf('including CSF in segmentation for brain compartment\n') + mri.seg = mri.seg | (mri.csf>(cfg.threshold*max(mri.csf(:)))); + end + if ~strcmp(cfg.smooth, 'no'), + % check whether the required SPM2 toolbox is available + hastoolbox('spm2', 1); + fprintf('smoothing the segmentation with a %d-pixel FWHM kernel\n',cfg.smooth); + mri.seg = double(mri.seg); + spm_smooth(mri.seg, mri.seg, cfg.smooth); + end + % threshold for the last time + mri.seg = (mri.seg>(cfg.threshold*max(mri.seg(:)))); +end + +[mrix, mriy, mriz] = ndgrid(1:size(mri.seg,1), 1:size(mri.seg,2), 1:size(mri.seg,3)); + +% construct the triangulations of the boundaries from the segmented MRI +for i=1:length(cfg.tissue) + fprintf('triangulating the boundary of compartment %d\n', i); + seg = imfill((mri.seg==cfg.tissue(i)), 'holes'); + ori(1) = mean(mrix(seg(:))); + ori(2) = mean(mriy(seg(:))); + ori(3) = mean(mriz(seg(:))); + [pnt, tri] = triangulate_seg(seg, cfg.numvertices(i), ori); + % apply the coordinate transformation from voxel to head coordinates + pnt(:,4) = 1; + pnt = (mri.transform * (pnt'))'; + pnt = pnt(:,1:3); + + % convert the MRI surface points into the same units as the source/gradiometer + scale = 1; + switch cfg.sourceunits + case 'mm' + scale = scale * 1000; + case 'cm' + scale = scale * 100; + case 'dm' + scale = scale * 10; + case 'm' + scale = scale * 1; + otherwise + error('unknown physical dimension in cfg.sourceunits'); + end + switch cfg.mriunits + case 'mm' + scale = scale / 1000; + case 'cm' + scale = scale / 100; + case 'dm' + scale = scale / 10; + case 'm' + scale = scale / 1; + otherwise + error('unknown physical dimension in cfg.mriunits'); + end + if scale~=1 + fprintf('converting MRI surface points from %s into %s\n', cfg.sourceunits, cfg.mriunits); + pnt = pnt* scale; + end + + bnd(i).pnt = pnt; + bnd(i).tri = tri; + fprintf(['segmentation compartment %d of ' num2str(length(cfg.tissue)) ' completed\n'],i); +end + diff --git a/external/fieldtrip/private/prepare_singleshell.m b/external/fieldtrip/private/prepare_singleshell.m index 0b7a90f..914d7f8 100644 --- a/external/fieldtrip/private/prepare_singleshell.m +++ b/external/fieldtrip/private/prepare_singleshell.m @@ -1,4 +1,4 @@ -function [vol, cfg] = prepare_singleshell(cfg, mri); +function [vol, cfg] = prepare_singleshell(cfg, mri) % PREPARE_SINGLESHELL creates a simple and fast method for the MEG forward % calculation for one shell of arbitrary shape. This is based on a @@ -7,14 +7,15 @@ % constructed from spherical harmonics. % % Use as -% [vol] = prepare_singleshell(cfg) -% [vol] = prepare_singleshell(cfg, seg) +% [vol, cfg] = prepare_singleshell(cfg, seg), or +% [vol, cfg] = prepare_singleshell(cfg, mri), or +% [vol, cfg] = prepare_singleshell(cfg) % % If you do not use a segmented MRI, the configuration should contain % cfg.headshape = a filename containing headshape, a structure containing a % single triangulated boundary, or a Nx3 matrix with surface % points -% cfg.spheremesh = number, to retriangulate the mesh with a sphere (default = 3000) +% cfg.numvertices = number, to retriangulate the mesh with a sphere (default = 3000) % instead of specifying a number, you can specify 'same' to keep the % vertices of the mesh identical to the original headshape points % @@ -35,6 +36,12 @@ % Copyright (C) 2006-2007, Robert Oostenveld % % $Log: prepare_singleshell.m,v $ +% Revision 1.22 2009/07/16 09:08:32 crimic +% added link with prepare_mesh function +% +% Revision 1.21 2009/05/29 12:31:04 roboos +% only convert cfg.headshape from config to struct in case it is present +% % Revision 1.20 2009/05/25 08:05:18 roboos % ensure that cfg.headshape is a sturct and not a config object (in case tracking is on) % @@ -101,158 +108,24 @@ fieldtripdefs cfg = checkconfig(cfg, 'trackconfig', 'on'); +cfg = checkconfig(cfg, 'renamed', {'spheremesh', 'numvertices'}); % set the defaults if ~isfield(cfg, 'smooth'); cfg.smooth = 5; end % in voxels if ~isfield(cfg, 'mriunits'); cfg.mriunits = 'mm'; end if ~isfield(cfg, 'sourceunits'), cfg.sourceunits = 'cm'; end if ~isfield(cfg, 'threshold'), cfg.threshold = 0.5; end % relative -if ~isfield(cfg, 'spheremesh'), cfg.spheremesh = 4000; end % approximate number of vertices in spere - -if isa(cfg.headshape, 'config') - % convert the nested config-object back into a normal structure - cfg.headshape = struct(cfg.headshape); -end - -if nargin>1 && (~isfield(cfg,'headshape') || isempty(cfg.headshape)) - basedonmri = 1; - basedonheadshape = 0; -elseif nargin==1 && isfield(cfg,'headshape') && ~isempty(cfg.headshape) - basedonmri = 0; - basedonheadshape = 1; -else - error('inconsistent configuration, cfg.headshape should not be used in combination with an mri input') -end - -if basedonmri - % obtain the head shape from the segmented MRI - seg = zeros(mri.dim); - if isfield(mri, 'gray') - fprintf('including gray matter in segmentation for brain compartment\n') - seg = seg | (mri.gray>(cfg.threshold*max(mri.gray(:)))); - end - if isfield(mri, 'white') - fprintf('including white matter in segmentation for brain compartment\n') - seg = seg | (mri.white>(cfg.threshold*max(mri.white(:)))); - end - if isfield(mri, 'csf') - fprintf('including CSF in segmentation for brain compartment\n') - seg = seg | (mri.csf>(cfg.threshold*max(mri.csf(:)))); - end - if ~strcmp(cfg.smooth, 'no'), - % check whether the required SPM2 toolbox is available - hastoolbox('spm2', 1); - fprintf('smoothing the segmentation with a %d-pixel FWHM kernel\n',cfg.smooth); - seg = double(seg); - spm_smooth(seg, seg, cfg.smooth); - end - % threshold for the last time - seg = (seg>(cfg.threshold*max(seg(:)))); - % determine the center of gravity of the segmented brain - xgrid = 1:mri.dim(1); - ygrid = 1:mri.dim(2); - zgrid = 1:mri.dim(3); - [X, Y, Z] = ndgrid(xgrid, ygrid, zgrid); - ori(1) = mean(X(seg)); - ori(2) = mean(Y(seg)); - ori(3) = mean(Z(seg)); - pnt = triangulate_seg(seg, cfg.spheremesh, ori); - pnt(:,4) = 1; - pnt = (mri.transform * pnt')'; - % convert the MRI surface points into the same units as the source/gradiometer - scale = 1; - switch cfg.sourceunits - case 'mm' - scale = scale * 1000; - case 'cm' - scale = scale * 100; - case 'dm' - scale = scale * 10; - case 'm' - scale = scale * 1; - otherwise - error('unknown physical dimension in cfg.sourceunits'); - end - switch cfg.mriunits - case 'mm' - scale = scale / 1000; - case 'cm' - scale = scale / 100; - case 'dm' - scale = scale / 10; - case 'm' - scale = scale / 1; - otherwise - error('unknown physical dimension in cfg.mriunits'); - end - if scale~=1 - fprintf('converting MRI surface points from %s into %s\n', cfg.sourceunits, cfg.mriunits); - shape = pnt(:,1:3) * scale; - else - shape = pnt(:,1:3); - end - - % compute triangulation - shape = unique(shape, 'rows'); - pnt = shape; - Npnt = size(pnt,1); - avg = mean(pnt, 1); - pnt = pnt - repmat(avg, Npnt, 1); % shift center of points towards the origin - dist = sqrt(sum(pnt.^2,2)); - pnt = pnt ./ repmat(dist, 1, 3); % normalize to a unit sphere - tri = convhulln(pnt); % construct the triangulation using a convex hull - pnt = shape; - - fprintf('placed %d points on the brain surface\n', length(shape)); -elseif basedonheadshape - % get the surface describing the head shape - if isstruct(cfg.headshape) && isfield(cfg.headshape, 'pnt') - % use the headshape surface specified in the configuration - headshape = cfg.headshape; - elseif isnumeric(cfg.headshape) && size(cfg.headshape,2)==3 - % use the headshape points specified in the configuration - headshape.pnt = cfg.headshape; - elseif ischar(cfg.headshape) - % read the headshape from file - headshape = read_headshape(cfg.headshape); - else - error('cfg.headshape is not specified correctly') - end - if ~isfield(headshape, 'tri') - % generate a closed triangulation from the surface points - headshape.pnt = unique(headshape.pnt, 'rows'); - headshape.tri = projecttri(headshape.pnt); - end - - pnt = headshape.pnt; - tri = headshape.tri; - -end % basedonmri or basedonheadshape - -if nargin<2 - % the triangulation is based on the shape, - if isequal(cfg.spheremesh, 'same') - % keep the same triangulation - tri = projecttri(pnt); % the triangulation is not guaranteed closed, reconstruct it - else - [tri1, pnt1] = reducepatch(tri, pnt, 3*cfg.spheremesh); - % remove double vertices - pnt1 = unique(pnt1, 'rows'); - % reconstruct the triangulation - tri1 = projecttri(pnt1); - % replace the probably unevenly distributed triangulation with a regular one - % and retriangulate it to the desired accuracy - [pnt2, tri2] = msphere(cfg.spheremesh); % this is a regular triangulation - [pnt, tri] = retriangulate(pnt1, tri1, pnt2, tri2, 2); - end -end +if ~isfield(cfg, 'spheremesh'), cfg.numvertices = 4000; end % approximate number of vertices in sphere % construct the geometry of the volume conductor model, containing a single boundary % the initialization of the forward computation code is done later in prepare_headmodel vol = []; -vol.bnd.pnt = pnt; -vol.bnd.tri = tri; -vol.type = 'nolte'; +if nargin==1 + vol.bnd = prepare_mesh(cfg); +else + vol.bnd = prepare_mesh(cfg, mri); +end +vol.type = 'nolte'; % get the output cfg cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); diff --git a/external/fieldtrip/private/prepare_vol_sens.m b/external/fieldtrip/private/prepare_vol_sens.m index fef176e..6a47ff5 100644 --- a/external/fieldtrip/private/prepare_vol_sens.m +++ b/external/fieldtrip/private/prepare_vol_sens.m @@ -40,6 +40,9 @@ % Copyright (C) 2004-2009, Robert Oostenveld % % $Log: prepare_vol_sens.m,v $ +% Revision 1.19 2009/05/29 11:50:34 vlalit +% Fixed a bug with wrong kind of brackets +% % Revision 1.18 2009/05/25 11:50:40 roboos % consistent handling of multiple spheres in case of ctf localspheres.hdm and fieldtrip prepare_localspheres, also in case of synthetic gradients. % @@ -228,7 +231,7 @@ warning('using the global fitted single sphere for %d channels that do not have a local sphere', length(missing)); end for i=1:length(missing) - vol.label(end+1) = missing{i}; + vol.label(end+1) = missing(i); vol.r(end+1,:) = singlesphere.r; vol.o(end+1,:) = singlesphere.o; end diff --git a/external/fieldtrip/private/preproc.m b/external/fieldtrip/private/preproc.m index 958fb20..79eb30b 100644 --- a/external/fieldtrip/private/preproc.m +++ b/external/fieldtrip/private/preproc.m @@ -96,6 +96,13 @@ % Copyright (C) 2004-2009, Robert Oostenveld % % $Log: preproc.m,v $ +% Revision 1.37 2009/08/05 08:36:09 roboos +% don't fill up the complete data with nans if a nan is detected +% the behaviour of not filtering and giving a warning remains the same +% +% Revision 1.36 2009/06/17 10:12:26 roboos +% cfg.montage=[] should also work (just like 'no') +% % Revision 1.35 2009/03/13 13:24:00 jansch % added support for preproc_denoise % @@ -340,7 +347,7 @@ dat = preproc_rereference(dat, refindx); end -if ~strcmp(cfg.montage, 'no') +if ~strcmp(cfg.montage, 'no') && ~isempty(cfg.montage) % this is an alternative approach for rereferencing, with arbitrary complex linear combinations of channels tmp.trial = {dat}; tmp.label = label; @@ -351,8 +358,7 @@ end if any(any(isnan(dat))) - % filtering is not possible for at least a selection of the data, replace all data with nans - dat(:) = nan; + % filtering is not possible for at least a selection of the data if nargout>2 nsamples = size(dat,2); time = (offset - begpadding + (0:(nsamples-1)))/fsample; diff --git a/external/fieldtrip/private/preprocessing.m b/external/fieldtrip/private/preprocessing.m index e8624d5..86d0b5f 100644 --- a/external/fieldtrip/private/preprocessing.m +++ b/external/fieldtrip/private/preprocessing.m @@ -86,6 +86,8 @@ % Undocumented local options: % cfg.artfctdef % cfg.removemcg +% cfg.output.dataset +% cfg.output.dataformat % % This function depends on PREPROC which has the following options: % cfg.absdiff @@ -125,6 +127,15 @@ % Copyright (C) 2003-2007, Robert Oostenveld, SMI, FCDC % % $Log: preprocessing.m,v $ +% Revision 1.109 2009/09/08 14:23:21 roboos +% changed the syntaxt for concatenation of implicitref to cfg.channel +% +% Revision 1.108 2009/06/17 13:44:14 roboos +% fixed output label in case of rereferencing with a montage +% +% Revision 1.107 2009/06/17 10:14:26 roboos +% added support for preprocessing data from disk and immediate writing to disk (trial by trial), which allows for preprocessing very large datasets without having them completely in memory (e.g. rereferencing sdma datasets) +% % Revision 1.106 2009/01/28 10:24:04 jansch % changed dataformat in call to checkdata into datatype % @@ -384,20 +395,20 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % do preprocessing of data that has already been read %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - + % the input data must be raw data = checkdata(data, 'datatype', 'raw', 'hasoffset', 'yes'); - + % check if the input cfg is valid for this function cfg = checkconfig(cfg, 'forbidden', {'trl', 'dataset', 'datafile', 'headerfile'}); - + if cfg.padding>0 error('cfg.padding should be zero, since filter padding is only possible while reading the data from file'); end - + % set the defaults if ~isfield(cfg, 'trials'), cfg.trials = 'all'; end - + % select trials of interest if ~strcmp(cfg.trials, 'all') if islogical(cfg.trials), cfg.trials=find(cfg.trials); end @@ -406,23 +417,40 @@ data.time = data.time(cfg.trials); data.offset = data.offset(cfg.trials); end - + % translate the channel groups (like 'all' and 'MEG') into real labels cfg.channel = channelselection(cfg.channel, data.label); rawindx = match_str(data.label, cfg.channel); - + % this will contain the newly processed data dataout = []; - + progress('init', cfg.feedback, 'preprocessing'); ntrl = length(data.trial); for i=1:ntrl progress(i/ntrl, 'preprocessing trial %d from %d\n', i, ntrl); % do the preprocessing on the selected channels [dataout.trial{i}, dataout.label, dataout.time{i}, cfg] = preproc(data.trial{i}(rawindx,:), data.label(rawindx), data.fsample, cfg, data.offset(i)); + + if isfield(cfg, 'output') && ~isempty(cfg.output) + % write the processed data to file + newhdr = []; + newhdr.Fs = hdr.Fs; + newhdr.label = label; + newhdr.nChans = length(newhdr.label); + + % only append for the second and consecutive trials + write_data(cfg.output.dataset, dataout.trial{i}, 'dataformat', cfg.output.dataformat, 'header', newhdr, 'append', i~=1); + + if nargout==0 + % don't keep the data in memory + dataout.trial{i} = []; + end + end + end % for all trials progress('close'); - + % update the trial definition (trl) in case of trial selection if ~strcmp(cfg.trials, 'all') % try to locate the trl in the nested configuration @@ -442,37 +470,37 @@ % get the output cfg cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); - + % remember the configuration details of the input data if isfield(data, 'cfg'); cfg.previous = data.cfg; end - + % take along relevant fields of input data to output data if isfield(data, 'hdr'); dataout.hdr = data.hdr; end if isfield(data, 'fsample'); dataout.fsample = data.fsample; end if isfield(data, 'grad'); dataout.grad = data.grad; end if isfield(data, 'elec'); dataout.elec = data.elec; end - + % replace the input data with the output data data = dataout; - + else %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % read the data from file and do the preprocessing %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - + if isfield(cfg, 'trialdef') && ~isfield(cfg, 'trl') error('you must call DEFINETRIAL prior to PREPROCESSING'); end - + % check if the input cfg is valid for this function cfg = checkconfig(cfg, 'dataset2files', {'yes'}); cfg = checkconfig(cfg, 'required', {'headerfile', 'datafile'}); cfg = checkconfig(cfg, 'renamed', {'datatype', 'continuous'}); cfg = checkconfig(cfg, 'renamedval', {'continuous', 'continuous', 'yes'}); - + % read the header hdr = read_header(cfg.headerfile, 'headerformat', cfg.headerformat); - + % this option relates to reading over trial boundaries in a pseudo-continuous dataset if ~isfield(cfg, 'continuous') if hdr.nTrials==1 @@ -481,32 +509,32 @@ cfg.continuous = 'no'; end end - + % this should be a cell array if ~iscell(cfg.channel) && ischar(cfg.channel) cfg.channel = {cfg.channel}; end - + % this should be a cell array if ~iscell(cfg.refchannel) && ischar(cfg.refchannel) cfg.refchannel = {cfg.refchannel}; end - + % do a sanity check for the re-referencing if strcmp(cfg.reref, 'no') && ~isempty(cfg.refchannel) warning('no re-referencing is performed'); cfg.refchannel = {}; end - + % translate the channel groups (like 'all' and 'MEG') into real labels cfg.channel = channelselection(cfg.channel, hdr.label); - + if ~isempty(cfg.implicitref) % add the label of the implicit reference channel to these cell-arrays - cfg.channel = {cfg.channel{:} cfg.implicitref}; + cfg.channel = cat(1, cfg.channel(:), cfg.implicitref); end cfg.refchannel = channelselection(cfg.refchannel, cfg.channel); - + % determine the length in samples to which the data should be padded before filtering is applied % the filter padding is done by reading a longer segment of data from the original data file if cfg.padding>0 @@ -527,7 +555,7 @@ % no padding was requested padding = 0; end - + if ~isfield(cfg, 'trl') % treat the data as continuous if possible, otherwise define all trials as indicated in the header if strcmp(cfg.continuous, 'yes') @@ -545,7 +573,7 @@ end cfg.trl = trl; end - + if any(strmatch('reject', fieldnames(cfg))) || ... any(strmatch('rejecteog', fieldnames(cfg))) || ... any(strmatch('rejectmuscle', fieldnames(cfg))) || ... @@ -553,12 +581,12 @@ % this is only for backward compatibility error('you should call REJECTARTIFACT prior to PREPROCESSING, please update your scripts'); end - + ntrl = size(cfg.trl,1); if ntrl<1 error('no trials were selected for preprocessing, see DEFINETRIAL for help'); end - + % compute the template for MCG and the QRS latency indices, and add it to the configuration if strcmp(cfg.removemcg, 'yes') cfg = template_mcg(cfg); @@ -568,10 +596,10 @@ fprintf('removing mcg on channel %s\n', mcgchannel{i}); end end - + % determine the channel numbers of interest for preprocessing [chnindx, rawindx] = match_str(cfg.channel, hdr.label); - + progress('init', cfg.feedback, 'reading and preprocessing'); for i=1:ntrl progress(i/ntrl, 'reading and preprocessing trial %d from %d\n', i, ntrl); @@ -600,29 +628,45 @@ endsample = hdr.nSamples*hdr.nTrials; end end - + % ONLY RELEVANT FOR NEUROSCAN CNT if ~isfield(cfg, 'nsdf') hdr.nsdf=16; else hdr.nsdf=cfg.nsdf; end - + % read the raw data with padding on both sides of the trial dat = read_data(cfg.datafile, 'header', hdr, 'begsample', begsample, 'endsample', endsample, 'chanindx', rawindx, 'checkboundary', strcmp(cfg.continuous, 'no'), 'dataformat', cfg.dataformat); - + % do the preprocessing on the padded trial data and remove the padding after filtering [cutdat{i}, label, time{i}, cfg] = preproc(dat, hdr.label(rawindx), hdr.Fs, cfg, cfg.trl(i,3), begpadding, endpadding); - + + if isfield(cfg, 'output') && ~isempty(cfg.output) + % write the processed data to file + newhdr = []; + newhdr.Fs = hdr.Fs; + newhdr.label = label; + newhdr.nChans = length(newhdr.label); + + % only append for the second and consecutive trials + write_data(cfg.output.dataset, cutdat{i}, 'dataformat', cfg.output.dataformat, 'header', newhdr, 'append', i~=1); + + if nargout==0 + % don't keep the data in memory + cutdat{i} = []; + end + end + % ONLY RELEVANT FOR NEUROSCAN CNT hdr=rmfield(hdr,'nsdf'); - + end % for all trials progress('close'); - + % collect the results data.hdr = hdr; % header details of the datafile - data.label = cfg.channel; % labels of channels that have been read + data.label = label; % labels of channels that have been read, can be different from labels in file due to montage data.trial = cutdat; % cell-array with TIMExCHAN data.time = time; % vector with the timeaxis for each individual trial data.fsample = hdr.Fs; @@ -632,7 +676,7 @@ % get the output cfg cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); - + end % if nargin>1 % add the version details of this function call to the configuration @@ -644,7 +688,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: preprocessing.m,v 1.106 2009/01/28 10:24:04 jansch Exp $'; +cfg.version.id = '$Id: preprocessing.m,v 1.109 2009/09/08 14:23:21 roboos Exp $'; % remember the exact configuration details in the output data.cfg = cfg; diff --git a/external/fieldtrip/private/read_24bit.c b/external/fieldtrip/private/read_24bit.c index 95c8998..fdb3b16 100644 --- a/external/fieldtrip/private/read_24bit.c +++ b/external/fieldtrip/private/read_24bit.c @@ -19,6 +19,9 @@ * * * $Log: read_24bit.c,v $ + * Revision 1.4 2009/06/08 16:33:08 roboos + * include sys/types.h for all platforms, not only WIN32/64, since also required for OSX 10.4 + * * Revision 1.3 2009/03/30 07:23:03 roboos * changed ifdef syntax * @@ -59,11 +62,11 @@ #define _LARGEFILE_SOURCE #include +#include #include "mex.h" #include "matrix.h" #if defined(_WIN32) || defined(_WIN64) -#include #define int32_t INT32_T #define int64_t INT64_T #define fseeko fseek diff --git a/external/fieldtrip/private/read_eeglabdata.m b/external/fieldtrip/private/read_eeglabdata.m index 730adbf..4c2f13e 100644 --- a/external/fieldtrip/private/read_eeglabdata.m +++ b/external/fieldtrip/private/read_eeglabdata.m @@ -36,6 +36,10 @@ % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA % $Log: read_eeglabdata.m,v $ +% Revision 1.3 2009/07/01 15:35:19 vlalit +% Try several different ways of looking for the data file +% before giving up (fix suggested by Jakob Scherer) +% % Revision 1.2 2009/02/27 12:03:09 vlalit % Arno's fix for a bug reported by Antanas Spokas % @@ -72,7 +76,20 @@ if strcmpi(header.orig.data(end-2:end), 'set'), header.ori = load('-mat', filename); else - fid = fopen(header.orig.data); + + % assuming that the data file is in the current directory + fid = fopen(header.orig.data); + + % assuming the .dat and .set files are located in the same directory + if fid == -1 + pathstr = fileparts(filename); + fid = fopen(fullfile(pathstr, header.orig.data)); + end + + if fid == -1 + fid = fopen(fullfile(header.orig.filepath, header.orig.data)); % + end + if fid == -1, error('Cannot not find data file'); end; % only read the desired trials diff --git a/external/fieldtrip/private/read_eeglabevent.m b/external/fieldtrip/private/read_eeglabevent.m index 1c438b7..28c0f6a 100644 --- a/external/fieldtrip/private/read_eeglabevent.m +++ b/external/fieldtrip/private/read_eeglabevent.m @@ -33,6 +33,12 @@ % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA % $Log: read_eeglabevent.m,v $ +% Revision 1.4 2009/08/29 05:11:31 josdie +% After consultation with Arno, changed so that value (when present) and type fields in EEGlab .set files are treated as being reversed from value and type fields in FieldTrip files. +% +% Revision 1.3 2009/08/09 01:45:24 josdie +% Changed event value to equal EEGlab's event value rather than type. +% % Revision 1.2 2009/02/02 20:45:34 josdie % FieldTrip's .value field now set to EEGlab's .type field. FieldTrip's .type field set to 'trigger'. FieldTrip's .duration field set to 0 rather than empty. % @@ -64,8 +70,14 @@ event = []; oldevent = header.orig.event; for index = 1:length(oldevent) - event(index).value = num2str( oldevent(index).type ); - event(index).type = 'trigger'; + event(index).value = num2str( oldevent(index).type ); + if isfield(oldevent,'code') + event(index).type = oldevent(index).code; + elseif isfield(oldevent,'value') + event(index).type = oldevent(index).value; + else + event(index).type = 'trigger'; + end; if header.nTrials > 1 event(index).sample = oldevent(index).latency-header.nSamplesPre; event(index).offset = header.nSamplesPre; diff --git a/external/fieldtrip/private/read_eeglabheader.m b/external/fieldtrip/private/read_eeglabheader.m index 65845cd..5ab7e60 100644 --- a/external/fieldtrip/private/read_eeglabheader.m +++ b/external/fieldtrip/private/read_eeglabheader.m @@ -30,6 +30,18 @@ % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA % $Log: read_eeglabheader.m,v $ +% Revision 1.6 2009/08/08 04:07:02 josdie +% Bug in Joe's brain fixed. Change to header.nSamplesPre calculation changed back. +% +% Revision 1.5 2009/08/08 03:17:26 josdie +% Fixed bug that was causing hdr.label to have as many labels as there are time points rather than matching the number of channels. +% +% Revision 1.4 2009/08/08 03:05:29 josdie +% Fixed bug in calculation of header.nSamplesPre. +% +% Revision 1.3 2009/07/01 16:08:21 vlalit +% Fixing a bug in converting channel locations to elec struct (reproted by Jakib Scherer) +% % Revision 1.2 2009/01/23 15:35:46 roboos % create default channel names if EEG.chanlocs.labels is missing % @@ -70,18 +82,20 @@ header.label = { EEG.chanlocs.labels }'; catch warning('creating default channel names'); - for i=1:header.nSamples + for i=1:header.nChans header.label{i} = sprintf('chan%03d', i); end end -for ind = 1:length( EEG.chanlocs ) - header.elec.label{ind} = EEG.chanlocs(ind).labels; - if ~isempty(EEG.chanlocs(ind).X) - % this channel has a position - header.elec.pnt(ind,1) = EEG.chanlocs(ind).X; - header.elec.pnt(ind,2) = EEG.chanlocs(ind).Y; - header.elec.pnt(ind,3) = EEG.chanlocs(ind).Z; - end; +ind = 1; +for i = 1:length( EEG.chanlocs ) + if ~isempty(EEG.chanlocs(i).X) + header.elec.label{ind, 1} = EEG.chanlocs(i).labels; + % this channel has a position + header.elec.pnt(ind,1) = EEG.chanlocs(i).X; + header.elec.pnt(ind,2) = EEG.chanlocs(i).Y; + header.elec.pnt(ind,3) = EEG.chanlocs(i).Z; + ind = ind+1; + end; end; % remove data diff --git a/external/fieldtrip/private/read_event.m b/external/fieldtrip/private/read_event.m index 4e1748b..bb88b15 100644 --- a/external/fieldtrip/private/read_event.m +++ b/external/fieldtrip/private/read_event.m @@ -59,6 +59,19 @@ % Copyright (C) 2004-2008, Robert Oostenveld % % $Log: read_event.m,v $ +% Revision 1.103 2009/08/21 12:03:42 vlalit +% Fixed a bug with handling of 16 and 32-bit Neuroscan cnt variants. +% +% Revision 1.102 2009/08/09 03:34:55 josdie +% Modified egi_egia so that combined subject average files have the cell names start with a four character subject code (e.g., S001) so that other software can decode the subject number more reliably. +% +% Revision 1.101 2009/07/28 11:22:54 roboos +% improved detection of binary trigger channels for neuromag +% +% Revision 1.100 2009/06/09 13:54:30 marvger +% removed warning for empty events; interferes with continuous pooling in +% realtime applications +% % Revision 1.99 2009/05/22 09:02:29 marvger % changed tcp handling % @@ -897,7 +910,7 @@ event(eventCount).sample = (eventCount-1)*hdr.nSamples + 1; event(eventCount).offset = -hdr.nSamplesPre; event(eventCount).duration = hdr.nSamples; - event(eventCount).value = ['S' num2str(subject) cnames{cell}]; + event(eventCount).value = ['S' sprintf('%03d',subject) cnames{cell}]; end end @@ -1221,9 +1234,13 @@ end if iscontinuous - binary = {'STI 014', 'STI 015', 'STI 016'}; - binaryindx = match_str(hdr.label, binary); analogindx = strmatch('STI', hdr.label); + binaryindx = find(strcmp(chantype(hdr), 'trigger')); + if isempty(binaryindx) + % use a predefined set of channel names + binary = {'STI 014', 'STI 015', 'STI 016'}; + binaryindx = match_str(hdr.label, binary); + end if ~isempty(binaryindx) % add triggers based on the binary trigger channel, this is based on @@ -1518,10 +1535,10 @@ event(end ).offset = -hdr.nSamplesPre; event(end ).value = []; - case 'ns_cnt' + case {'ns_cnt', 'ns_cnt16', 'ns_cnt32'} % read the header, the original header includes the event table if isempty(hdr) - hdr = read_header(filename); + hdr = read_header(filename, 'headerformat', eventformat); end % translate the event table into known FieldTrip event types for i=1:hdr.orig.nevent @@ -1618,8 +1635,8 @@ % this has the side effect that events without a sample number are discarded [dum, indx] = sort([event.sample]); event = event(indx); -else - warning(sprintf('no events found in %s', filename)); +% else +% warning(sprintf('no events found in %s', filename)); end % apply the optional filters diff --git a/external/fieldtrip/private/read_header.m b/external/fieldtrip/private/read_header.m index 7b72f3e..37afb3b 100644 --- a/external/fieldtrip/private/read_header.m +++ b/external/fieldtrip/private/read_header.m @@ -55,6 +55,18 @@ % Copyright (C) 2003-2008, Robert Oostenveld, F.C. Donders Centre % % $Log: read_header.m,v $ +% Revision 1.98 2009/08/05 00:26:38 josdie +% Workaround for defect in number of subjects field in EGIS average files generated by NetStation. +% +% Revision 1.97 2009/07/19 19:49:21 josdie +% Deleted egi_egis changing a zero hdr.nSamplesPre to a one. +% +% Revision 1.96 2009/07/09 10:00:45 roboos +% no leading spaces for fake channel names in fcdc_buffer +% +% Revision 1.95 2009/06/17 13:43:20 roboos +% don't include LastTimeStamp in output +% % Revision 1.94 2009/04/20 17:19:01 vlalit % Changed the MNE reader not to set hdr.elec when there are no EEG channels. % @@ -841,7 +853,8 @@ for i = 1:hdr.nChans hdr.label{i} = ['e' num2str(i)]; end; - hdr.nTrials = fhdr(11)*fhdr(18); %number of trials is numSubjects * numCells + %since NetStation does not properly set the fhdr(11) field, use the number of subjects from the chdr instead + hdr.nTrials = chdr(1,2)*fhdr(18); %number of trials is numSubjects * numCells hdr.nSamplesPre = ceil(fhdr(14)/(1000/hdr.Fs)); if any(chdr(:,3)-chdr(1,3)) @@ -878,10 +891,6 @@ % actually allocated to the age of the subject, although NetStation % does not use it when generating an EGIS session file. - if hdr.nSamplesPre == 0 - hdr.nSamplesPre = 1; %If baseline was left as zero, then change to "1" to avoid possible issues with software expecting a non-zero baseline. - end; - if any(chdr(:,3)-chdr(1,3)) error('Number of samples not the same for all cells.'); end; @@ -945,7 +954,7 @@ hdr.nTrials = 1; % since continuous warning('creating fake channel names'); for i=1:hdr.nChans - hdr.label{i} = sprintf('%3d', i); + hdr.label{i} = sprintf('%d', i); end % this should be a column vector hdr.label = hdr.label(:); @@ -1006,7 +1015,6 @@ LastTimeStamp = ncs.hdr.LastTimeStamp; % this is the first timestamp of the last block, i.e. not the timestamp of the last sample hdr.TimeStampPerSample = double(LastTimeStamp - FirstTimeStamp) ./ ((ncs.NRecords-1)*512); hdr.FirstTimeStamp = FirstTimeStamp; - hdr.LastTimeStamp = LastTimeStamp + uint64((512-1)*hdr.TimeStampPerSample); case 'neuralynx_nse' nse = read_neuralynx_nse(filename, 1, 0); @@ -1018,7 +1026,7 @@ hdr.nSamples = 32; % there are 32 samples in each waveform hdr.nSamplesPre = 0; hdr.orig = nse.hdr; - % FIXME add hdr.FirstTimeStamp and hdr.LastTimeStamp + % FIXME add hdr.FirstTimeStamp and hdr.TimeStampPerSample case {'neuralynx_ttl', 'neuralynx_tsl', 'neuralynx_tsh'} % these are hardcoded, they contain an 8-byte header and int32 values for a single channel @@ -1361,7 +1369,6 @@ % the timestamps indicate the beginning of each block, hence the timestamp of the last block corresponds with the end of the previous block hdr.TimeStampPerSample = double(ts(end)-ts(1))/sum(num(1:(end-1))); hdr.FirstTimeStamp = ts(1); % the timestamp of the first continuous sample - hdr.LastTimeStamp = ts(end) + uint64(hdr.TimeStampPerSample * num(end)); % the timestamp of the last sample (not the beginning of the last block) % also make the spike channels visible for i=1:length(orig.ChannelHeader) diff --git a/external/fieldtrip/private/read_mri.m b/external/fieldtrip/private/read_mri.m index eabfeaa..ecf08de 100644 --- a/external/fieldtrip/private/read_mri.m +++ b/external/fieldtrip/private/read_mri.m @@ -16,6 +16,22 @@ % Copyright (C) 2004-2009, Robert Oostenveld % % $Log: read_mri.m,v $ +% Revision 1.11 2009/07/16 12:57:12 roboos +% fixed fprintf feedback for dicom reader +% +% Revision 1.10 2009/07/09 15:07:59 roboos +% use another coordinate transformation matrix for fif MRI +% +% Revision 1.9 2009/07/08 08:08:45 roboos +% also support mne toolbox fiff_read_mri function +% +% Revision 1.8 2009/07/07 10:40:37 roboos +% Improved handling of sequence of files, also when the files don't have nice names. +% Use SeriesNumber from the DICOM header to figure out which slices go together. +% +% Revision 1.7 2009/07/02 15:04:09 roboos +% construct transformation matrix for dicom files +% % Revision 1.6 2009/03/11 15:02:18 vlalit % Use either SPM5 or SPM8 to read *.nii files. % @@ -185,43 +201,102 @@ transform(2,4) = -dim(2) - transform(2,4); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -elseif filetype(filename, 'neuromag_fif') - % use the neuromag functions to read the Neuromag MRI - % FIXME this needs to be checked to ensure consistency with the FieldTrip definition of volume data +elseif filetype(filename, 'neuromag_fif') && hastoolbox('mne') + % use the mne functions to read the Neuromag MRI + hdr = fiff_read_mri(filename); + img = cat(3, hdr.slices.data); + hdr.slices = rmfield(hdr.slices, 'data'); % remove the image data to save memory + % hmm, which transformation matrix should I use? + if issubfield(hdr.voxel_trans, 'trans') + transform = hdr.voxel_trans.trans; + elseif issubfield(hdr.trans, 'trans') + transform = hdr.trans.trans; + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +elseif filetype(filename, 'neuromag_fif') && hastoolbox('meg_pd') + % use the meg_pd functions to read the Neuromag MRI [img,coords] = loadmri(filename); dev = loadtrans(filename,'MRI','HEAD'); - transform = dev*coords; + transform = dev*coords; hdr.coords = coords; - hdr.dev = dev; + hdr.dev = dev; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +elseif filetype(filename, 'neuromag_fif') + error('reading MRI data from a fif file requires either the MNE toolbox or the meg_pd toolbox to be installed'); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% elseif filetype(filename, 'dicom') % this uses the Image processing toolbox - % the DICOM file probably represents a stack of slices + % the DICOM file probably represents a stack of slices, possibly even multiple volumes + orig = dicominfo(filename); + dim(1) = orig.Rows; + dim(2) = orig.Columns; + [p, f] = fileparts(filename); + + % this works for the Siemens scanners at the FCDC tok = tokenize(f, '.'); for i=5:length(tok) - tok{i} = '*'; % this works for the Siemens scanners at the FCDC + tok{i} = '*'; end filename = sprintf('%s.', tok{:}); % reconstruct the filename with wildcards and '.' between the segments filename = filename(1:end-1); % remove the last '.' dirlist = dir(fullfile(p, filename)); dirlist = {dirlist.name}; + + if length(dirlist)==1 + % try something else to get a list of all the slices + dirlist = dir(fullfile(p, '*')); + dirlist = {dirlist(~[dirlist.isdir]).name}; + end + + keep = false(1, length(dirlist)); for i=1:length(dirlist) filename = char(fullfile(p, dirlist{i})); - fprintf('reading ''%s''\n', filename); - info = dicominfo(filename); - img(:,:,i) = dicomread(info); - hdr(i) = info; - if i==1 - % this pre-allocates enough space for the other slices - img(1,1,length(dirlist)) = 0; + if ~filetype(filename, 'dicom') + keep(i) = false; + fprintf('skipping ''%s'' because of incorrect filetype\n', filename); + end + % read the header information + info = dicominfo(filename); + if info.SeriesNumber~=orig.SeriesNumber + keep(i) = false; + fprintf('skipping ''%s'' because of different SeriesNumber\n', filename); + else + keep(i) = true; + hdr(i) = info; end end + % remove the files that were skipped + hdr = hdr(keep); + dirlist = dirlist(keep); + + % pre-allocate enough space for the subsequent slices + dim(3) = length(dirlist); + img = zeros(dim(1), dim(2), dim(3)); + for i=1:length(dirlist) + filename = char(fullfile(p, dirlist{i})); + fprintf('reading image data from ''%s''\n', filename); + img(:,:,i) = dicomread(hdr(i)); + end + % reorder the slices [z, indx] = sort(cell2mat({hdr.SliceLocation})); hdr = hdr(indx); img = img(:,:,indx); + + try + % construct a homgenous transformation matrix that performs the scaling from voxels to mm + dx = hdr(1).PixelSpacing(1); + dy = hdr(1).PixelSpacing(2); + dz = hdr(2).SliceLocation - hdr(1).SliceLocation; + transform = eye(4); + transform(1,1) = dx; + transform(2,2) = dy; + transform(3,3) = dz; + end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% else diff --git a/external/fieldtrip/private/read_sens.m b/external/fieldtrip/private/read_sens.m index c21a80e..e30027b 100644 --- a/external/fieldtrip/private/read_sens.m +++ b/external/fieldtrip/private/read_sens.m @@ -32,6 +32,13 @@ % Copyright (C) 2005-2008, Robert Oostenveld % % $Log: read_sens.m,v $ +% Revision 1.14 2009/07/02 10:34:21 vlalit +% Added eeglab_set to the list of formats where electrode locations can be found in +% the header. +% +% Revision 1.13 2009/06/03 09:52:15 roboos +% added zebris_sfp +% % Revision 1.12 2009/02/02 16:10:15 vlalit % Provide the 'headertype' argument to the internal read_header call. % @@ -171,7 +178,7 @@ % This is for EEG formats where electrode positions can be stored with the data %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - case {'spmeeg_mat'} + case {'spmeeg_mat', 'eeglab_set'} % check the availability of the required low-level toolbox % this is required because the read_sens function is also on itself included in the forwinv toolbox hastoolbox('fileio'); @@ -218,6 +225,13 @@ else error('no electrodes or gradiometers found in Matlab file'); end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % these are created by a Zebris tracker, at CRC in Liege at least. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + case 'zebris_sfp' + [sens.fid, sens.pnt, sens.fid_label, sens.label] = read_zebris(filename, 0); otherwise error('unknown fileformat for electrodes or gradiometers'); diff --git a/external/fieldtrip/private/redefinetrial.m b/external/fieldtrip/private/redefinetrial.m index aba8d2a..9e48b48 100644 --- a/external/fieldtrip/private/redefinetrial.m +++ b/external/fieldtrip/private/redefinetrial.m @@ -41,6 +41,10 @@ % Copyright (C) 2006-2008, Robert Oostenveld % % $Log: redefinetrial.m,v $ +% Revision 1.25 2009/08/13 20:47:02 jansch +% changed typo in key-value pair 'hdr',hdr. this should read 'header',hdr and +% speeds up the function considerably +% % Revision 1.24 2009/01/14 15:11:22 roboos % corrected documentation for cfg.begsample/endsample % fixed bug in output data.cfg.trl (one sample too long at the end) for input cfg.toilim and input cfg.begsample/endsample @@ -266,7 +270,7 @@ endsample = trl(iTrl,2); offset = trl(iTrl,3); trllength = endsample - begsample + 1; - data.trial{iTrl} = fetch_data(dataold, 'hdr', hdr, 'begsample', begsample, 'endsample', endsample, 'chanindx', 1:hdr.nChans); + data.trial{iTrl} = fetch_data(dataold, 'header', hdr, 'begsample', begsample, 'endsample', endsample, 'chanindx', 1:hdr.nChans, 'docheck', 0); data.time{iTrl} = offset2time(offset, dataold.fsample, trllength); end data.hdr = hdr; @@ -313,7 +317,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: redefinetrial.m,v 1.24 2009/01/14 15:11:22 roboos Exp $'; +cfg.version.id = '$Id: redefinetrial.m,v 1.25 2009/08/13 20:47:02 jansch Exp $'; % remember the configuration details of the input data try, cfg.previous = data.cfg; end try, cfg.previous = dataold.cfg; end % in case of ~isempty(cfg.trl) diff --git a/external/fieldtrip/private/rejectartifact.m b/external/fieldtrip/private/rejectartifact.m index f3a61f9..abb2e36 100644 --- a/external/fieldtrip/private/rejectartifact.m +++ b/external/fieldtrip/private/rejectartifact.m @@ -52,6 +52,9 @@ % Copyright (C) 2003-2007, Robert Oostenveld % % $Log: rejectartifact.m,v $ +% Revision 1.48 2009/06/23 18:33:17 roboos +% use the trl from the input data and not from the config in case of nargin>1 +% % Revision 1.47 2009/03/23 21:20:10 roboos % removed extra ; % @@ -226,10 +229,15 @@ end end +if nargin>1 + trl = findcfg(data.cfg, 'trl'); +elseif isfield(cfg, 'trl') + trl = cfg.trl; +end + % ensure that there are trials that can be scanned for artifacts and/or rejected -if ~isfield(cfg, 'trl') || isempty(cfg.trl) - warning('no trials were selected, cannot perform artifact detection/rejection'); - return; +if isempty(trl) + error('no trials were selected, cannot perform artifact detection/rejection'); end % prevent double occurences of artifact types, ensure that the order remains the same @@ -267,13 +275,13 @@ cfg.artfctdef.type = dum(find(sel)); % combine all trials into a single boolean vector -trialall = zeros(1,max(cfg.trl(:,2))); -for j=1:size(cfg.trl,1) - trialall(cfg.trl(j,1):cfg.trl(j,2)) = 1; +trialall = zeros(1,max(trl(:,2))); +for j=1:size(trl,1) + trialall(trl(j,1):trl(j,2)) = 1; end % combine all artifacts into a single boolean vector -rejectall = zeros(1,max(cfg.trl(:,2))); +rejectall = zeros(1,max(trl(:,2))); for i=1:length(cfg.artfctdef.type) dum = artifact{i}; for j=1:size(dum,1) @@ -301,7 +309,7 @@ if strcmp(cfg.artfctdef.feedback, 'yes') COLOR = 'grcmykgrcmykgrcmykgrcmyk'; % use the trial definition present in the local configuration - trl = cfg.trl; + trl = trl; % compute the time axis that corresponds with each trial time = {}; for i=1:size(trl,1) @@ -341,7 +349,7 @@ plot(x, y, 'b') for j=1:length(cfg.artfctdef.type) x = time{i}; - y = rejectall(cfg.trl(i,1):cfg.trl(i,2)); + y = rejectall(trl(i,1):trl(i,2)); y(y~=j) = nan; y(y==j) = i + j*0.03; plot(x, y, COLOR(j)); @@ -382,7 +390,7 @@ % remove the trials that (partially) coincide with a rejection mark %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if strcmp(cfg.artfctdef.reject, 'partial') || strcmp(cfg.artfctdef.reject, 'complete') - trl = cfg.trl; + trl = trl; trialok = []; count_complete_reject = 0; count_partial_reject = 0; @@ -441,7 +449,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: rejectartifact.m,v 1.47 2009/03/23 21:20:10 roboos Exp $'; +cfg.version.id = '$Id: rejectartifact.m,v 1.48 2009/06/23 18:33:17 roboos Exp $'; % % remember the exact configuration details in the output % cfgtmp = cfg; diff --git a/external/fieldtrip/private/rejectcomponent.m b/external/fieldtrip/private/rejectcomponent.m index e36d40d..9c090fa 100644 --- a/external/fieldtrip/private/rejectcomponent.m +++ b/external/fieldtrip/private/rejectcomponent.m @@ -25,6 +25,10 @@ % Copyright (C) 2005-2009, Robert Oostenveld % % $Log: rejectcomponent.m,v $ +% Revision 1.12 2009/08/14 09:30:20 jansch +% also pass configuration of input data-structure to the output data.cfg in +% case of nargin==3 +% % Revision 1.11 2009/03/26 13:21:50 jansch % ensure correct montage in the case of hasdata % @@ -111,7 +115,7 @@ invtopo = pinv(topo); tra = eye(length(selcomp)) - topo(:, cfg.component)*invtopo(cfg.component, :); %I am not sure about this, but it gives comparable results to the ~hasdata case - %when comp contains non-orthogonal (=ica) topographies + %when comp contains non-orthogonal (=ica) topographies, and contains a complete decomposition %the following is incorrect %topo = comp.topo(selcomp, cfg.component); @@ -175,9 +179,18 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: rejectcomponent.m,v 1.11 2009/03/26 13:21:50 jansch Exp $'; -% remember the configuration details of the input data -try, cfg.previous = comp.cfg; end +cfg.version.id = '$Id: rejectcomponent.m,v 1.12 2009/08/14 09:30:20 jansch Exp $'; +if nargin==2, + % remember the configuration details of the input data + try, cfg.previous = comp.cfg; end +elseif nargin==3, + try, cfg.previous{2} = comp.cfg; end + try, cfg.previous{1} = data.cfg; end + %the configuration of the data is relatively more important + %potential use of findcfg in subsequent analysis steps looks into + %the previous{1} first +end + % keep the configuration in the output data.cfg = cfg; diff --git a/external/fieldtrip/private/rejectvisual.m b/external/fieldtrip/private/rejectvisual.m index 322542a..1508e08 100644 --- a/external/fieldtrip/private/rejectvisual.m +++ b/external/fieldtrip/private/rejectvisual.m @@ -107,6 +107,9 @@ % Copyright (C) 2005-2006, Markus Bauer, Robert Oostenveld % % $Log: rejectvisual.m,v $ +% Revision 1.29 2009/07/21 08:33:15 crimic +% corrected typo +% % Revision 1.28 2009/03/31 18:39:43 roboos % don't print removed if empty (thanks to Irina) % @@ -394,7 +397,7 @@ for i=1:(length(removed)-1) fprintf('%s, ', data.label{removed(i)}); end - fprintf('%s\n', data.label{removed(i)}); + fprintf('%s\n', data.label{removed(end)}); % remove channels that are not selected for i=1:length(data.trial) @@ -408,7 +411,7 @@ for i=1:(length(removed)-1) fprintf('%s, ', data.label{removed(i)}); end - fprintf('%s\n', data.label{removed(i)}); + fprintf('%s\n', data.label{removed(end)}); % fill the data from the bad channels with nans for i=1:length(data.trial) @@ -431,7 +434,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: rejectvisual.m,v 1.28 2009/03/31 18:39:43 roboos Exp $'; +cfg.version.id = '$Id: rejectvisual.m,v 1.29 2009/07/21 08:33:15 crimic Exp $'; % remember the configuration details of the input data try, cfg.previous = data.cfg; end % remember the exact configuration details in the output diff --git a/external/fieldtrip/private/resampledata.m b/external/fieldtrip/private/resampledata.m index 4b3b0b5..c12d630 100644 --- a/external/fieldtrip/private/resampledata.m +++ b/external/fieldtrip/private/resampledata.m @@ -44,6 +44,11 @@ % Copyright (C) 2004-2009, FC Donders Centre, Robert Oostenveld % % $Log: resampledata.m,v $ +% Revision 1.22 2009/08/14 09:35:45 jansch +% pass an updated trl matrix to the output as cfg.resampletrl, containing a +% 'consistent' trial description matrix relative to the new sampling rate. +% this is needed if in a later step fetch_data is invoked +% % Revision 1.21 2009/04/03 08:06:35 jansch % included possibility to resample data structures with an original non-integer % sampling rate @@ -141,6 +146,15 @@ cfg.resamplefs = 256; end +% this is needed if only a subset of trials is requested, +% and for an attempt to pass in the output a trl matrix +% which contains the indexing in the updated sampling rate +if isfield(data, 'cfg') % try to locate the trl in the nested configuration + trl = findcfg(data.cfg, 'trl'); +else + trl = []; +end + % select trials of interest if ~strcmp(cfg.trials, 'all') if islogical(cfg.trials), cfg.trials=find(cfg.trials); end @@ -148,11 +162,6 @@ data.trial = data.trial(cfg.trials); data.time = data.time(cfg.trials); % update the trial definition (trl) - if isfield(data, 'cfg') % try to locate the trl in the nested configuration - trl = findcfg(data.cfg, 'trl'); - else - trl = []; - end if isempty(trl) % a trial definition is expected in each continuous data set warning('could not locate the trial definition ''trl'' in the data structure'); @@ -238,6 +247,20 @@ end % if usefsample or usetime +%try to give an updated trl matrix, which is necessary to fool +%fetch_data and fetch_header at a potential later stage of the +%analysis pipeline +%FIXME this is only done in case of usefsample, think of whether +%it is possible as well in the other case +if ~isempty(trl) && usefsample, + trlorig = trl; + offsindx = round((trl(:,1)-trl(:,3)).*(fsres./fsorig)); + offs = round(trl(:,3).*(fsres./fsorig)); + nsmp = cellfun('size',data.trial,2)'; + trl = [offsindx+offs offsindx+offs+nsmp-1 offs]; + cfg.resampletrl = trl; +end + fprintf('original sampling rate = %d Hz\nnew sampling rate = %d Hz\n', cfg.origfs, data.fsample); % get the output cfg @@ -252,7 +275,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: resampledata.m,v 1.21 2009/04/03 08:06:35 jansch Exp $'; +cfg.version.id = '$Id: resampledata.m,v 1.22 2009/08/14 09:35:45 jansch Exp $'; % remember the configuration details of the input data try, cfg.previous = data.cfg; end % remember the exact configuration details in the output diff --git a/external/fieldtrip/private/select_box.m b/external/fieldtrip/private/select_box.m index 19811f6..460ceed 100644 --- a/external/fieldtrip/private/select_box.m +++ b/external/fieldtrip/private/select_box.m @@ -1,29 +1,53 @@ -function [x, y] = select_box; +function [x, y] = select_box(handle, eventdata, varargin) % SELECT_BOX helper function for selecting a rectangular region % in the current figure using the mouse. % % Use as -% [x, y] = select_box; +% [x, y] = select_box(...) % -% It returns a 2-element vector x and a 2-element vector y +% It returns a 2-element vector x and a 2-element vector y % with the corners of the selected region. +% +% Optional input arguments should come in key-value pairs and can include +% 'multiple' true/false, make multiple selections by dragging, clicking +% in one will finalize the selection (default = false) % Copyright (C) 2006, Robert Oostenveld % % $Log: select_box.m,v $ +% Revision 1.6 2009/07/14 13:18:33 roboos +% updated channel selection, use select_range and two local helper functions, also support multiple selections +% +% Revision 1.5 2009/06/04 10:50:50 roboos +% changed handling of inputs +% +% Revision 1.4 2009/06/03 11:21:49 crimic +% bug fixes +% +% Revision 1.3 2009/05/29 15:56:08 roboos +% added input argument handling for 'multiple', the actual implementation does not support it yet +% % Revision 1.2 2009/04/15 12:34:29 crimic % added code of original select2d.m function % % Revision 1.1 2006/05/17 14:38:09 roboos % new implementation -% -k = waitforbuttonpress; -point1 = get(gca,'CurrentPoint'); % button down detected -finalRect = rbbox; % return figure units -point2 = get(gca,'CurrentPoint'); % button up detected -point1 = point1(1,1:2); % extract x and y -point2 = point2(1,1:2); -x = sort([point1(1) point2(1)]); -y = sort([point1(2) point2(2)]); +% get the optional arguments +multiple = keyval('multiple', varargin); if isempty(multiple), multiple = false; end + +if multiple + error('not yet implemented'); +else + k = waitforbuttonpress; + point1 = get(gca,'CurrentPoint'); % button down detected + finalRect = rbbox; % return figure units + point2 = get(gca,'CurrentPoint'); % button up detected + point1 = point1(1,1:2); % extract x and y + point2 = point2(1,1:2); + x = sort([point1(1) point2(1)]); + y = sort([point1(2) point2(2)]); +end + + diff --git a/external/fieldtrip/private/select_channel.m b/external/fieldtrip/private/select_channel.m index b56ce30..f00db25 100644 --- a/external/fieldtrip/private/select_channel.m +++ b/external/fieldtrip/private/select_channel.m @@ -1,4 +1,4 @@ -function label = select_channel(h, eventdata, varargin) +function select_channel(handle, eventdata, varargin) % SELECT_CHANNEL is a helper function that can be used as callback function % in a figure. It allows the user to select a channel. The channel labels @@ -7,10 +7,11 @@ % Use as % label = select_channel(h, eventdata, ...) % The first two arguments are automatically passed by Matlab to any -% callback function. Additional options should be specified in key-value -% pairs and can be -% 'callback' = function handle to be executed after channels have been -% selected +% callback function. +% +% Additional options should be specified in key-value pairs and can be +% 'callback' = function handle to be executed after channels have been selected +% % You can pass additional arguments to the callback function in a cell-array % like {@function_handle,arg1,arg2} % @@ -26,8 +27,13 @@ % info.label = lay.label % guidata(gcf, info) % -% % add this function ass callback -% set(gcf, 'WindowButtonUpFcn', {@select_channel, 'callback', @disp}) +% % add this function as the callback to make a single selection +% set(gcf, 'WindowButtonDownFcn', {@select_channel, 'callback', @disp}) +% +% % or to make multiple selections +% set(gcf, 'WindowButtonDownFcn', {@select_channel, 'multiple', true, 'callback', @disp, 'event', 'WindowButtonDownFcn'}) +% set(gcf, 'WindowButtonUpFcn', {@select_channel, 'multiple', true, 'callback', @disp, 'event', 'WindowButtonDownFcn'}) +% set(gcf, 'WindowButtonMotionFcn', {@select_channel, 'multiple', true, 'callback', @disp, 'event', 'WindowButtonDownFcn'}) % % Subsequently you can click in the figure and you'll see that the disp % function is executed as callback and that it displays the selected @@ -36,6 +42,9 @@ % Copyright (C) 2009, Robert Oostenveld % % $Log: select_channel.m,v $ +% Revision 1.3 2009/07/14 13:18:33 roboos +% updated channel selection, use select_range and two local helper functions, also support multiple selections +% % Revision 1.2 2009/05/12 18:10:43 roboos % added handling of follow-up callback function % @@ -44,39 +53,57 @@ % % get optional input arguments -callback = keyval('callback', varargin{:}); +callback = keyval('callback', varargin); +multiple = keyval('multiple', varargin); if isempty(multiple), multiple = false; end -pos = get(gca, 'CurrentPoint'); -x = pos(1,1); -y = pos(1,2); +% convert 'yes/no' string to boolean value +multiple = istrue(multiple); -info = guidata(h); -chan_x = info.x; -chan_y = info.y; -chan_lab = info.label; +if multiple + % the selection is done using select_range, which will subsequently call select_channel_multiple + set(gcf, 'WindowButtonDownFcn', {@select_range, 'multiple', true, 'callback', {@select_channel_multiple, callback}, 'event', 'WindowButtonDownFcn'}); + set(gcf, 'WindowButtonUpFcn', {@select_range, 'multiple', true, 'callback', {@select_channel_multiple, callback}, 'event', 'WindowButtonUpFcn'}); + set(gcf, 'WindowButtonMotionFcn', {@select_range, 'multiple', true, 'callback', {@select_channel_multiple, callback}, 'event', 'WindowButtonMotionFcn'}); +else + % the selection is done using select_channel_single + pos = get(gca, 'CurrentPoint'); + pos = pos(1,1:2); + select_channel_single(pos, callback) +end % if multiple -% compute the distance between the clicked point and all channels -chan_pos = [chan_x chan_y]; -chan_dist = dist(chan_pos'); -chan_dist = triu(chan_dist, 1); -chan_dist = chan_dist(:); -chan_dist = chan_dist(chan_dist>0); -% allow for some tolerance in the clicking -chan_dist = median(chan_dist); -tolerance = 0.3*chan_dist; -dx = chan_x - x; -dy = chan_y - y; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION to assist in the selection of a single channel +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function select_channel_single(pos, callback) + +info = guidata(gcf); +x = info.x; +y = info.y; +label = info.label; + +% compute a tolerance measure +distance = dist([x y]'); +distance = triu(distance, 1); +distance = distance(:); +distance = distance(distance>0); +distance = median(distance); +tolerance = 0.3*distance; + +% compute the distance between the clicked point and all channels +dx = x - pos(1); +dy = y - pos(2); dd = sqrt(dx.^2 + dy.^2); [d, i] = min(dd); if d=range(i, 1) & x<=range(i, 2) & y>=range(i, 3) & y<=range(i, 4)); end +label = label(select); -uiresume; % ?? +% execute the original callback with the selected channels as input argument +if any(select) + if ~isempty(callback) + if isa(callback, 'cell') + % the callback specifies a function and additional arguments + funhandle = callback{1}; + funargs = callback(2:end); + feval(funhandle, label, funargs{:}); + else + % the callback only specifies a function + funhandle = callback; + feval(funhandle, label); + end + end +end diff --git a/external/fieldtrip/private/select_point.m b/external/fieldtrip/private/select_point.m new file mode 100644 index 0000000..d1ceaa3 --- /dev/null +++ b/external/fieldtrip/private/select_point.m @@ -0,0 +1,113 @@ +function [selected] = select_point(pos, varargin) + +% SELECT_POINT helper function for selecting a one or multiple points +% in the current figure using the mouse. +% +% Use as +% [selected] = select_point(pos, ...) +% +% It returns a list of the [x y] coordinates of the selected points. +% +% Optional input arguments should come in key-value pairs and can include +% 'multiple' true/false, make multiple selections, pressing "q" on the keyboard finalizes the selection (default = false) +% 'nearest' true/false (default = true) +% +% Example use +% pos = randn(10,2); +% figure +% plot(pos(:,1), pos(:,2), '.') +% select_point(pos) + +% $Log: select_point.m,v $ +% Revision 1.2 2009/06/30 11:46:15 roboos +% fixed docu +% +% Revision 1.1 2009/06/30 11:44:34 roboos +% renamed select_pointd into select_point for consistency with plot_topo +% +% Revision 1.7 2009/06/22 12:33:11 crimic +% minor change +% +% Revision 1.6 2009/06/16 08:17:40 crimic +% added check on input +% +% Revision 1.5 2009/06/15 15:46:45 roboos +% first implementation of point3d, multiple changes to point2d, still some work to be done to make them consistent +% +% Revision 1.4 2009/06/15 13:43:27 roboos +% reimplemented from scratch +% +% Revision 1.3 2009/06/04 10:51:09 roboos +% only whitespace +% +% Revision 1.2 2009/06/03 11:23:46 crimic +% first implementation +% + + +% get optional input arguments +nearest = keyval('nearest', varargin); if isempty(nearest), nearest = true; end +multiple = keyval('multiple', varargin); if isempty(multiple), multiple = false; end + +% ensure that it is boolean +nearest = istrue(nearest); +multiple = istrue(multiple); + +if multiple + fprintf('select multiple points by clicking in the figure, press "q" if you are done\n'); +end + +x = []; +y = []; +done = false; + +selected = zeros(0,3); + +% ensure that "q" is not the current character, which happens if you reuse the same figure +set(gcf, 'CurrentCharacter', 'x') + +while ~done + k = waitforbuttonpress; + point = get(gca,'CurrentPoint'); % button down detected + key = get(gcf,'CurrentCharacter'); % which key was pressed (if any)? + if strcmp(key, 'q') + % we are done with the clicking + done = true; + else + % add the current point + x(end+1) = point(1,1); + y(end+1) = point(1,2); + end + + if ~multiple + done = true; + end +end + +if nearest && ~isempty(pos) + % determine the points that are the nearest to the displayed points + selected = []; + + % compute the distance between the points to get an estimate of the tolerance + dp = dist(pos'); + dp = triu(dp, 1); + dp = dp(:); + dp = dp(dp>0); + % allow for some tolerance in the clicking + dp = median(dp); + tolerance = 0.3*dp; + + for i=1:length(x) + % compute the distance between the clicked position and all points + dx = pos(:,1) - x(i); + dy = pos(:,2) - y(i); + dd = sqrt(dx.^2 + dy.^2); + [d, i] = min(dd); + if d0); + % allow for some tolerance in the clicking + dp = median(dp); + tolerance = 0.3*dp; + + for i=1:length(x) + % compute the distance between the clicked position and all points + dx = pos(:,1) - x(i); + dy = pos(:,2) - y(i); + dd = sqrt(dx.^2 + dy.^2); + [d, i] = min(dd); + if d1 + warning('using the first patch object in the figure'); + h = h(1); +end + +selected = zeros(0,3); + +done = false; +while ~done + k = waitforbuttonpress; + [p v vi facev facei] = select3d(h); + key = get(gcf,'CurrentCharacter'); % which key was pressed (if any)? + + if strcmp(key, 'q') + % finished selecting points + done = true; + else + % a new point was selected + if nearest + selected(end+1,:) = v; + else + selected(end+1,:) = p; + end % if nearest + fprintf('selected point at [%f %f %f]\n', selected(end,1), selected(end,2), selected(end,3)); + end + + if ~multiple + done = true; + end +end + diff --git a/external/fieldtrip/private/select_range.m b/external/fieldtrip/private/select_range.m new file mode 100644 index 0000000..f1e535a --- /dev/null +++ b/external/fieldtrip/private/select_range.m @@ -0,0 +1,239 @@ +function select_range(handle, eventdata, varargin) + +% SELECT_RANGE is a helper function that can be used as callback function +% in a figure. It allows the user to select a horizontal or a vertical +% range, or one or multiple boxes. +% +% Example +% x = randn(10,1); +% y = randn(10,1); +% figure; plot(x, y, '.'); +% +% set(gcf, 'WindowButtonDownFcn', {@select_range, 'multiple', true, 'callback', @disp, 'event', 'WindowButtonDownFcn'}); +% set(gcf, 'WindowButtonUpFcn', {@select_range, 'multiple', true, 'callback', @disp, 'event', 'WindowButtonUpFcn'}); +% set(gcf, 'WindowButtonMotionFcn', {@select_range, 'multiple', true, 'callback', @disp, 'event', 'WindowButtonMotionFcn'}); +% +% set(gcf, 'WindowButtonDownFcn', {@select_range, 'multiple', false, 'xrange', false, 'yrange', false, 'callback', @disp, 'event', 'WindowButtonDownFcn'}); +% set(gcf, 'WindowButtonUpFcn', {@select_range, 'multiple', false, 'xrange', false, 'yrange', false, 'callback', @disp, 'event', 'WindowButtonUpFcn'}); +% set(gcf, 'WindowButtonMotionFcn', {@select_range, 'multiple', false, 'xrange', false, 'yrange', false, 'callback', @disp, 'event', 'WindowButtonMotionFcn'}); + +% Copyright (C) 2009, Robert Oostenveld +% +% $Log: select_range.m,v $ +% Revision 1.5 2009/08/04 11:56:55 roboos +% again a change in the handling user data +% added explicit option for clearing the user data in the figure +% +% Revision 1.4 2009/08/03 20:39:16 roboos +% reverted to revision 1.2 and changed the setappdata handling +% +% Revision 1.2 2009/07/30 19:10:25 ingnie +% deleted disp(callback) +% +% Revision 1.1 2009/07/14 13:17:41 roboos +% implemented new function, to be used as callback in interactive data selection +% + +% get the optional arguments +event = keyval('event', varargin); +callback = keyval('callback', varargin); +multiple = keyval('multiple', varargin); if isempty(multiple), multiple = false; end +xrange = keyval('xrange', varargin); if isempty(xrange), xrange = true; end +yrange = keyval('yrange', varargin); if isempty(yrange), yrange = true; end +clear = keyval('clear', varargin); if isempty(clear), clear = false; end + +% convert 'yes/no' string to boolean value +multiple = istrue(multiple); +xrange = istrue(xrange); +yrange = istrue(yrange); + +p = handle; +while ~isequal(p, 0) + handle = p; + p = get(handle, 'parent'); +end + +if ishandle(handle) + userData = getappdata(handle, 'select_range_m'); +else + userData = []; +end + +if isempty(userData) + userData.range = []; % this is a Nx4 matrix with the selection range + userData.box = []; % this is a Nx1 vector with the line handle +end + +p = get(gca, 'CurrentPoint'); +p = p(1,1:2); + +abc = axis; +xLim = abc(1:2); +yLim = abc(3:4); + +% limit cursor coordinates +if p(1)xLim(2), p(1)=xLim(2); end; +if p(2)yLim(2), p(2)=yLim(2); end; + +% determine whether the user is currently making a selection +selecting = numel(userData.range)>0 && any(isnan(userData.range(end,:))); +pointonly = ~xrange && ~yrange; + +if pointonly && multiple + warning('multiple selections are not possible for a point'); + multiple = false; +end + +switch event + case 'WindowButtonDownFcn' + if inSelection(p, userData.range) + % the user has clicked in one of the existing selections + evalCallback(callback, userData.range); + if clear + delete(userData.box(ishandle(userData.box))); + userData.range = []; + userData.box = []; + set(handle, 'Pointer', 'crosshair'); + end + + else + if ~multiple + % start with a new selection + delete(userData.box(ishandle(userData.box))); + userData.range = []; + userData.box = []; + end + + % add a new selection range + userData.range(end+1,1:4) = nan; + userData.range(end,1) = p(1); + userData.range(end,3) = p(2); + + % add a new selection box + xData = [nan nan nan nan nan]; + yData = [nan nan nan nan nan]; + userData.box(end+1) = line(xData, yData); + end + + case 'WindowButtonUpFcn' + if selecting + % select the other corner of the box + userData.range(end,2) = p(1); + userData.range(end,4) = p(2); + end + + if multiple && ~isempty(userData.range) && ~diff(userData.range(end,1:2)) && ~diff(userData.range(end,3:4)) + % start with a new selection + delete(userData.box(ishandle(userData.box))); + userData.range = []; + userData.box = []; + end + + if ~isempty(userData.range) + % ensure that the selection is sane + if diff(userData.range(end,1:2))<0 + userData.range(end,1:2) = userData.range(end,[2 1]); + end + if diff(userData.range(end,3:4))<0 + userData.range(end,3:4) = userData.range(end,[4 3]); + end + if pointonly + % only select a single point + userData.range(end,2) = userData.range(end,1); + userData.range(end,4) = userData.range(end,3); + elseif ~xrange + % only select along the y-axis + userData.range(end,1:2) = [-inf inf]; + elseif ~yrange + % only select along the x-axis + userData.range(end,3:4) = [-inf inf]; + end + end + + if pointonly && ~multiple + evalCallback(callback, userData.range); + if clear + delete(userData.box(ishandle(userData.box))); + userData.range = []; + userData.box = []; + set(handle, 'Pointer', 'crosshair'); + end + end + + case 'WindowButtonMotionFcn' + if selecting && ~pointonly + % update the selection box + if xrange + x1 = userData.range(end,1); + x2 = p(1); + else + x1 = xLim(1); + x2 = xLim(2); + end + if yrange + y1 = userData.range(end,3); + y2 = p(2); + else + y1 = yLim(1); + y2 = yLim(2); + end + + xData = [x1 x2 x2 x1 x1]; + yData = [y1 y1 y2 y2 y1]; + set(userData.box(end), 'xData', xData); + set(userData.box(end), 'yData', yData); + set(userData.box(end), 'Color', [0 0 0]); + set(userData.box(end), 'EraseMode', 'xor'); + set(userData.box(end), 'LineStyle', '--'); + set(userData.box(end), 'LineWidth', 1.5); + set(userData.box(end), 'Visible', 'on'); + + else + % update the cursor + if inSelection(p, userData.range) + set(handle, 'Pointer', 'hand'); + else + set(handle, 'Pointer', 'crosshair'); + end + end + + otherwise + error('unexpected event "%s"', event); + +end % switch event + +% put the modified selections back into the figure +if ishandle(handle) + setappdata(handle, 'select_range_m', userData); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function retval = inSelection(p, range) +if isempty(range) + retval = false; +else + retval = (p(1)>=range(:,1) & p(1)<=range(:,2) & p(2)>=range(:,3) & p(2)<=range(:,4)); + retval = any(retval); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function evalCallback(callback, val) +if ~isempty(callback) + if isa(callback, 'cell') + % the callback specifies a function and additional arguments + funhandle = callback{1}; + funargs = callback(2:end); + feval(funhandle, val, funargs{:}); + else + % the callback only specifies a function + funhandle = callback; + feval(funhandle, val); + end +end + diff --git a/external/fieldtrip/private/selectdata.m b/external/fieldtrip/private/selectdata.m index ace76a0..5850d28 100644 --- a/external/fieldtrip/private/selectdata.m +++ b/external/fieldtrip/private/selectdata.m @@ -29,6 +29,22 @@ % Copyright (C) 2009, Jan-Mathijs Schoffelen % % $Log: selectdata.m,v $ +% Revision 1.10 2009/08/18 09:55:46 jansch +% included possibility to concatenate over grid positions, allowing for cutting +% the dipole grid and glueing it together later on +% +% Revision 1.9 2009/08/17 08:41:19 jansch +% multiple changes +% +% Revision 1.8 2009/07/15 12:11:57 jansch +% fixed small bug +% +% Revision 1.7 2009/07/06 09:41:18 jansch +% multiple changes. allowing for selection of rpt in frequency data when input +% data has rpttap. allowing for grandaveraging functionality in the case of +% multiple inputs with the same dimensionalities. this is equivalent to the +% XXXgrandaverage functions with keepindividual = 'yes'. +% % Revision 1.6 2009/04/14 18:29:32 roboos % deleted the subfunction istrue, since it now is a seperate function % @@ -53,7 +69,7 @@ dimord = cell(1,length(data)); for k = 1:length(data) - data{k} = checkdata(data{k}, 'datatype', {'freq' 'timelock' 'source', 'volume'}); + data{k} = checkdata(data{k}, 'datatype', {'freq' 'timelock' 'source', 'volume', 'freqmvar'}); [dtype{k}, dimord{k}] = datatype(data{k}); end @@ -70,6 +86,7 @@ istlck = datatype(data{1},'timelock'); issource = datatype(data{1},'source'); isvolume = datatype(data{1},'volume'); +isfreqmvar = datatype(data{1},'freqmvar'); selchan = keyval('channel', kvp); selectchan = ~isempty(selchan); selfoi = keyval('foilim', kvp); selectfoi = ~isempty(selfoi); @@ -84,6 +101,9 @@ avgoverroi = keyval('avgoverroi', kvp); if isempty(avgoverroi), avgoverroi = false; end avgoverrpt = keyval('avgoverrpt', kvp); if isempty(avgoverrpt), avgoverrpt = false; end +% create anonymous function and apply it to the boolean input arguments +istrue = @(x)(ischar(x) && (strcmpi(x, 'yes') || strcmpi(x, 'true')) || (~isempty(x) && numel(x)==1 && x==1)); + % ensure that these are boolean arguments, optionally convert from "yes"/"no" to true/false avgoverchan = istrue(avgoverchan); avgoverfreq = istrue(avgoverfreq); @@ -96,7 +116,7 @@ end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% from here on the data is concatenated +% concatenate the data %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if length(data)>1, @@ -108,33 +128,39 @@ param = {param}; end - dimtok = tokenize(dimord{1}, '_'); + dimtok = tokenize(dimord{1}, '_'); dimtok(strmatch('chan', dimtok)) = {'label'}; % data.chan does not exist - dimmat = zeros(length(dimtok), length(data)); + dimmat = zeros(length(dimtok), length(data)); dimmat(:,1) = 1; for k = 1:length(dimtok) - try, + if isempty(strfind(dimtok{k},'rpt')), dimdat = getfield(data{1}, dimtok{k}); - catch - % dimtok is probably 'rpt' or so - dimdat = getsubfield(data{1}, param{1}); + else + % dimtok is 'rpt' or 'rpttap' + dimdat = size(getsubfield(data{1}, param{1}),1); end for m = 2:length(data) - try, + if isempty(strfind(dimtok{k},'rpt')), dimdat2 = getfield(data{m},dimtok{k}); - catch - % dimtok is probably 'rpt' or so - dimdat2 = getsubfield(data{m}, param{1}); + else + % dimtok is 'rpt' or 'rpttap' + dimdat2 = size(getsubfield(data{m}, param{1}),1); end try, dimmat(k,m) = all(dimdat(:)==dimdat2(:)); catch end; try, dimmat(k,m) = all(cellfun(@isequal,dimdat,dimdat2)); catch end; end end catdim = find(sum(dimmat,2)1, error('ambiguous dimensions for concatenation'); + elseif isempty(catdim) && isempty(strmatch('rpt',dimtok)) && isempty(strmatch('rpttap',dimtok)), + %treat as individual observations: prepend a first dimension 'rpt' + %(so this part should be able to cover the functionality of ...grandaverage) + catdim = 0; + elseif isempty(catdim) + error('don''t know how to concatenate the data'); end % concatenate the data @@ -143,28 +169,65 @@ for m = 1:length(tmp) tmp{m} = getsubfield(data{m},param{k}); end - data{1} = setsubfield(data{1}, param{k}, cat(catdim,tmp{:})); + if catdim==0, + ndim = length(size(tmp{1})); + data{1} = setsubfield(data{1}, param{k}, permute(cat(ndim+1,tmp{:}),[ndim+1 1:ndim])); + else + data{1} = setsubfield(data{1}, param{k}, cat(catdim,tmp{:})); + end end + if catdim==0, + %a dimension has been prepended + dimtok = ['rpt' dimtok]; + catdim = 1; + dimord{1} = ['rpt_',dimord{1}]; + if issubfield(data{1}, 'dim'), + dim = [length(data) data{1}.dim]; + end + else + if issubfield(data{1}, 'dim'), + dim = data{1}.dim; + end + end + % concatenate the relevant descriptive fields in the data-structure if ~strcmp(dimtok{catdim},'rpt') && ~strcmp(dimtok{catdim},'rpttap'), for k = 1:length(data) if k==1, tmp = getsubfield(data{k}, dimtok{catdim}); + if strcmp(dimtok{catdim},'pos') && isfield(data{k},'inside'), + tmpinside = getfield(data{k}, 'inside'); + tmpoutside = getfield(data{k}, 'outside'); + tmpnvox = numel(tmpinside)+numel(tmpoutside); + end else if strcmp(dimtok{catdim},'pos'), tmp = [tmp; getsubfield(data{k}, dimtok{catdim})]; sortflag = 0; - else + + %FIXME make this robust, now inside as vector is assumed + if exist('tmpinside', 'var') + tmpx = getfield(data{k}, 'inside'); + tmpx2 = getfield(data{k}, 'outside'); + tmpnvox = numel(tmpinside)+numel(tmpoutside); + tmpinside = [tmpinside(:)' tmpnvox(end)+tmpx(:)']; + tmpoutside = [tmpoutside(:)' tmpnvox(end)+tmpx2(:)']; + end + else tmp = [tmp getsubfield(data{k}, dimtok{catdim})]; sortflag = 1; end end end data{1} = setsubfield(data{1}, dimtok{catdim}, tmp); + if exist('tmpinside', 'var') + data{1} = setfield(data{1}, 'inside', tmpinside); + data{1} = setfield(data{1}, 'outside', tmpoutside); + end else % no such field as {'label','time','freq','pos'} has to be concatenated sortflag = 0; end - + % concatenate the relevant descriptive fields in the data-structure (continued) tryfields = {'cumsumcnt' 'cumtapcnt' 'dof'}; for k = 1:length(tryfields) @@ -182,9 +245,9 @@ end % FIXME this is ugly: solve it - if issource || isvolume, - data{1}.dim(catdim) = max(size(tmp)); - end + %if issource || isvolume, + % data{1}.dim(catdim) = max(size(tmp)); + %end % sort concatenated data FIXME this is also ugly and depends on tmp if sortflag && ~iscell(tmp), @@ -196,19 +259,24 @@ tmp = ipermute(tmp(ind,:,:,:,:), [catdim setdiff(1:length(size(tmp)), catdim)]); data{1} = setsubfield(data{1}, param{k}, tmp); end - elseif iscell(tmp) + elseif exist('tmp', 'var') && iscell(tmp) %in this case (ugly!) tmp is probably a cell-array containing functional data end - + % remove unspecified parameters - rmparam = setdiff(parameterselection('all',data{1}),param); + rmparam = setdiff(parameterselection('all',data{1}),[param 'pos' 'inside' 'outside']); for k = 1:length(rmparam) data{1} = rmsubfield(data{1}, rmparam{k}); end - + % keep the first structure only - data = data{1}; - dimord = dimord{1}; + data = data{1}; + dimord = dimord{1}; + data.dimord = dimord; + if isfield(data, 'dim'), + %data.dim = dim; + data.dim = size(data.(param{1})); + end else % nothing to do @@ -216,11 +284,25 @@ dimord = dimord{1}; end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% from here on the data is concatenated +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % determine the subselection in the data if selectrpt, dimtok = tokenize(data.dimord, '_'); if strcmp(dimtok{1}, 'rpttap'), - error('here you have to ensure the correct handling of tapers'); + %account for the tapers + sumtapcnt = [0;cumsum(data.cumtapcnt(:))]; + begtapcnt = sumtapcnt(1:end-1)+1; + endtapcnt = sumtapcnt(2:end); + begtapcnt = begtapcnt(selrpt); + endtapcnt = endtapcnt(selrpt); + tapers = zeros(1,sumtapcnt(end)); + for k = 1:length(begtapcnt) + tapers(begtapcnt(k):endtapcnt(k)) = 1; + end + selrpt = find(tapers); else % do nothing end @@ -232,7 +314,16 @@ if selectfoi, if length(selfoi)==1, selfoi(2) = selfoi; end; - selfoi = nearest(data.freq, selfoi(1)):nearest(data.freq, selfoi(2)); + if length(selfoi)==2, + %treat selfoi as lower limit and upper limit + selfoi = nearest(data.freq, selfoi(1)):nearest(data.freq, selfoi(2)); + else + %treat selfoi as a list of frequencies + for k=1:length(selfoi) + tmpfoi(k) = nearest(data.freq, selfoi(k)); + end + selfoi = tmpfoi; + end end if selecttoi, @@ -245,6 +336,24 @@ end if isfreq, + if isfield(data, 'labelcmb'), + %there is a crsspctrm field, this will only be selectdimmed + %if we apply a trick + tmpdata = data; + tmpdata.label = data.labelcmb; + if selectrpt, tmpdata = seloverdim(tmpdata, 'rpt', selrpt); end + if selectchan, tmpdata = seloverdim(tmpdata, 'chan', selchan); end + if selectfoi, tmpdata = seloverdim(tmpdata, 'freq', selfoi); end + if selecttoi, tmpdata = seloverdim(tmpdata, 'time', seltoi); end + % average over dimensions + if avgoverrpt, data = avgoverdim(data, 'rpt'); end + if avgoverchan, data = avgoverdim(data, 'chan'); end + if avgoverfreq, data = avgoverdim(data, 'freq'); end + if avgovertime, data = avgoverdim(data, 'time'); end + crsspctrm = tmpdata.crsspctrm; clear tmpdata; + else + crsspctrm = []; + end % make the subselection if selectrpt, data = seloverdim(data, 'rpt', selrpt); end if selectchan, data = seloverdim(data, 'chan', selchan); end @@ -255,6 +364,7 @@ if avgoverchan, data = avgoverdim(data, 'chan'); end if avgoverfreq, data = avgoverdim(data, 'freq'); end if avgovertime, data = avgoverdim(data, 'time'); end + if ~isempty(crsspctrm), data.crsspctrm = crsspctrm; end elseif istlck, % make the subselection @@ -269,9 +379,24 @@ if avgovertime, data = avgoverdim(data, 'time'); end elseif issource, - error('this is not yet implemented'); + %FIXME fill in everything + if selectrpt, data = seloverdim(data, 'rpt', selrpt); end + if selectfoi, data = seloverdim(data, 'freq', selfoi); end + if avgoverrpt, data = avgoverdim(data, 'rpt'); end + if avgoverfreq, data = avgoverdim(data, 'freq'); end elseif isvolume, error('this is not yet implemented'); +elseif isfreqmvar, + % make the subselection + if selectrpt, data = seloverdim(data, 'rpt', selrpt); end + if selectchan, data = seloverdim(data, 'chan', selchan); end + if selectfoi, data = seloverdim(data, 'freq', selfoi); end + if selecttoi, data = seloverdim(data, 'time', seltoi); end + % average over dimensions + if avgoverrpt, data = avgoverdim(data, 'rpt'); end + if avgoverchan, data = avgoverdim(data, 'chan'); end + if avgoverfreq, data = avgoverdim(data, 'freq'); end + if avgovertime, data = avgoverdim(data, 'time'); end end diff --git a/external/fieldtrip/private/senslabel.m b/external/fieldtrip/private/senslabel.m index 81db081..f75cc93 100644 --- a/external/fieldtrip/private/senslabel.m +++ b/external/fieldtrip/private/senslabel.m @@ -6,6 +6,7 @@ % label = senslabel(type) % % The input type can be any of the following +% 'biosemi64' % 'biosemi128' % 'biosemi256' % 'bti148' @@ -40,6 +41,12 @@ % Copyright (C) 2008, Vladimir Litvak % % $Log: senslabel.m,v $ +% Revision 1.4 2009/07/29 07:07:59 roboos +% use caching of input and output arguments to speed up the handling of multiple calls with the same input argument +% +% Revision 1.3 2009/06/19 16:51:50 vlalit +% Added biosemi64 system of Diane Whitmer, I don't know how generic it is. +% % Revision 1.2 2009/05/07 13:34:09 roboos % added ctf64 % @@ -59,6 +66,21 @@ % moved definition of channel label sets to seperate function % +% these are for remembering the type on subsequent calls with the same input arguments +persistent previous_argin previous_argout + +if nargin<1 + % ensure that all input arguments are defined + type = []; +end + +current_argin = {type}; +if isequal(current_argin, previous_argin) + % don't do the type detection again, but return the previous values from cache + label = previous_argout{1}; + return +end + % prevent defining all possible labels if not needed isbiosemi = ~isempty(regexp(type, '^biosemi', 'once')); isbti = ~isempty(regexp(type, '^bti', 'once')); @@ -69,1836 +91,1912 @@ isneuromag = ~isempty(regexp(type, '^neuromag', 'once')); if isbti - btiref = { - 'MRxA' - 'MRyA' - 'MRzA' - 'MLxA' - 'MLyA' - 'MLzA' - 'MCxA' - 'MCyA' - 'MCzA' - 'MRxaA' - 'MRyaA' - 'MRzaA' - 'MLxaA' - 'MLyaA' - 'MLzaA' - 'MCxaA' - 'MCyaA' - 'MCzaA' - 'GxxA' - 'GyxA' - 'GzxA' - 'GyyA' - 'GzyA' - }; + btiref = { + 'MRxA' + 'MRyA' + 'MRzA' + 'MLxA' + 'MLyA' + 'MLzA' + 'MCxA' + 'MCyA' + 'MCzA' + 'MRxaA' + 'MRyaA' + 'MRzaA' + 'MLxaA' + 'MLyaA' + 'MLzaA' + 'MCxaA' + 'MCyaA' + 'MCzaA' + 'GxxA' + 'GyxA' + 'GzxA' + 'GyyA' + 'GzyA' + }; - bti148 = cell(148,1); - for i=1:148 - bti148{i,1} = sprintf('A%d', i); - end + bti148 = cell(148,1); + for i=1:148 + bti148{i,1} = sprintf('A%d', i); + end - bti148_planar = cell(148,1); - for i=1:148 - bti148_planar{i,1} = sprintf('A%d_dH', i); - bti148_planar{i,2} = sprintf('A%d_dV', i); - end + bti148_planar = cell(148,1); + for i=1:148 + bti148_planar{i,1} = sprintf('A%d_dH', i); + bti148_planar{i,2} = sprintf('A%d_dV', i); + end - bti248 = cell(248,1); - for i=1:248 - bti248{i,1} = sprintf('A%d', i); - end + bti248 = cell(248,1); + for i=1:248 + bti248{i,1} = sprintf('A%d', i); + end - bti248_planar = cell(248,2); - for i=1:248 - bti248_planar{i,1} = sprintf('A%d_dH', i); - bti248_planar{i,2} = sprintf('A%d_dV', i); - end + bti248_planar = cell(248,2); + for i=1:248 + bti248_planar{i,1} = sprintf('A%d_dH', i); + bti248_planar{i,2} = sprintf('A%d_dV', i); + end end % if isbti if isctf - ctfref = { - 'BG1' - 'BG2' - 'BG3' - 'BP1' - 'BP2' - 'BP3' - 'BR1' - 'BR2' - 'BR3' - 'G11' - 'G12' - 'G13' - 'G22' - 'G23' - 'P11' - 'P12' - 'P13' - 'P22' - 'P23' - 'Q11' - 'Q12' - 'Q13' - 'Q22' - 'Q23' - 'R11' - 'R12' - 'R13' - 'R22' - 'R23' - }; + ctfref = { + 'BG1' + 'BG2' + 'BG3' + 'BP1' + 'BP2' + 'BP3' + 'BR1' + 'BR2' + 'BR3' + 'G11' + 'G12' + 'G13' + 'G22' + 'G23' + 'P11' + 'P12' + 'P13' + 'P22' + 'P23' + 'Q11' + 'Q12' + 'Q13' + 'Q22' + 'Q23' + 'R11' + 'R12' + 'R13' + 'R22' + 'R23' + }; - ctfheadloc = { - 'HLC0011' - 'HLC0012' - 'HLC0013' - 'HLC0021' - 'HLC0022' - 'HLC0023' - 'HLC0031' - 'HLC0032' - 'HLC0033' - 'HLC0018' - 'HLC0028' - 'HLC0038' - 'HLC0014' - 'HLC0015' - 'HLC0016' - 'HLC0017' - 'HLC0024' - 'HLC0025' - 'HLC0026' - 'HLC0027' - 'HLC0034' - 'HLC0035' - 'HLC0036' - 'HLC0037' - }; + ctfheadloc = { + 'HLC0011' + 'HLC0012' + 'HLC0013' + 'HLC0021' + 'HLC0022' + 'HLC0023' + 'HLC0031' + 'HLC0032' + 'HLC0033' + 'HLC0018' + 'HLC0028' + 'HLC0038' + 'HLC0014' + 'HLC0015' + 'HLC0016' + 'HLC0017' + 'HLC0024' + 'HLC0025' + 'HLC0026' + 'HLC0027' + 'HLC0034' + 'HLC0035' + 'HLC0036' + 'HLC0037' + }; - ctf64 = { - 'SL11' - 'SL12' - 'SL13' - 'SL14' - 'SL15' - 'SL16' - 'SL17' - 'SL18' - 'SL19' - 'SL21' - 'SL22' - 'SL23' - 'SL24' - 'SL25' - 'SL26' - 'SL27' - 'SL28' - 'SL29' - 'SL31' - 'SL32' - 'SL33' - 'SL34' - 'SL35' - 'SL41' - 'SL42' - 'SL43' - 'SL44' - 'SL45' - 'SL46' - 'SL47' - 'SL51' - 'SL52' - 'SR11' - 'SR12' - 'SR13' - 'SR14' - 'SR15' - 'SR16' - 'SR17' - 'SR18' - 'SR19' - 'SR21' - 'SR22' - 'SR23' - 'SR24' - 'SR25' - 'SR26' - 'SR27' - 'SR28' - 'SR29' - 'SR31' - 'SR32' - 'SR33' - 'SR34' - 'SR35' - 'SR41' - 'SR42' - 'SR43' - 'SR44' - 'SR45' - 'SR46' - 'SR47' - 'SR51' - 'SR52' - }; + ctf64 = { + 'SL11' + 'SL12' + 'SL13' + 'SL14' + 'SL15' + 'SL16' + 'SL17' + 'SL18' + 'SL19' + 'SL21' + 'SL22' + 'SL23' + 'SL24' + 'SL25' + 'SL26' + 'SL27' + 'SL28' + 'SL29' + 'SL31' + 'SL32' + 'SL33' + 'SL34' + 'SL35' + 'SL41' + 'SL42' + 'SL43' + 'SL44' + 'SL45' + 'SL46' + 'SL47' + 'SL51' + 'SL52' + 'SR11' + 'SR12' + 'SR13' + 'SR14' + 'SR15' + 'SR16' + 'SR17' + 'SR18' + 'SR19' + 'SR21' + 'SR22' + 'SR23' + 'SR24' + 'SR25' + 'SR26' + 'SR27' + 'SR28' + 'SR29' + 'SR31' + 'SR32' + 'SR33' + 'SR34' + 'SR35' + 'SR41' + 'SR42' + 'SR43' + 'SR44' + 'SR45' + 'SR46' + 'SR47' + 'SR51' + 'SR52' + }; - ctf151 = { - 'MLC11' - 'MLC12' - 'MLC13' - 'MLC14' - 'MLC15' - 'MLC21' - 'MLC22' - 'MLC23' - 'MLC24' - 'MLC31' - 'MLC32' - 'MLC33' - 'MLC41' - 'MLC42' - 'MLC43' - 'MLF11' - 'MLF12' - 'MLF21' - 'MLF22' - 'MLF23' - 'MLF31' - 'MLF32' - 'MLF33' - 'MLF34' - 'MLF41' - 'MLF42' - 'MLF43' - 'MLF44' - 'MLF45' - 'MLF51' - 'MLF52' - 'MLO11' - 'MLO12' - 'MLO21' - 'MLO22' - 'MLO31' - 'MLO32' - 'MLO33' - 'MLO41' - 'MLO42' - 'MLO43' - 'MLP11' - 'MLP12' - 'MLP13' - 'MLP21' - 'MLP22' - 'MLP31' - 'MLP32' - 'MLP33' - 'MLP34' - 'MLT11' - 'MLT12' - 'MLT13' - 'MLT14' - 'MLT15' - 'MLT16' - 'MLT21' - 'MLT22' - 'MLT23' - 'MLT24' - 'MLT25' - 'MLT26' - 'MLT31' - 'MLT32' - 'MLT33' - 'MLT34' - 'MLT35' - 'MLT41' - 'MLT42' - 'MLT43' - 'MLT44' - 'MRC11' - 'MRC12' - 'MRC13' - 'MRC14' - 'MRC15' - 'MRC21' - 'MRC22' - 'MRC23' - 'MRC24' - 'MRC31' - 'MRC32' - 'MRC33' - 'MRC41' - 'MRC42' - 'MRC43' - 'MRF11' - 'MRF12' - 'MRF21' - 'MRF22' - 'MRF23' - 'MRF31' - 'MRF32' - 'MRF33' - 'MRF34' - 'MRF41' - 'MRF42' - 'MRF43' - 'MRF44' - 'MRF45' - 'MRF51' - 'MRF52' - 'MRO11' - 'MRO12' - 'MRO21' - 'MRO22' - 'MRO31' - 'MRO32' - 'MRO33' - 'MRO41' - 'MRO42' - 'MRO43' - 'MRP11' - 'MRP12' - 'MRP13' - 'MRP21' - 'MRP22' - 'MRP31' - 'MRP32' - 'MRP33' - 'MRP34' - 'MRT11' - 'MRT12' - 'MRT13' - 'MRT14' - 'MRT15' - 'MRT16' - 'MRT21' - 'MRT22' - 'MRT23' - 'MRT24' - 'MRT25' - 'MRT26' - 'MRT31' - 'MRT32' - 'MRT33' - 'MRT34' - 'MRT35' - 'MRT41' - 'MRT42' - 'MRT43' - 'MRT44' - 'MZC01' - 'MZC02' - 'MZF01' - 'MZF02' - 'MZF03' - 'MZO01' - 'MZO02' - 'MZP01' - 'MZP02' - }; + ctf151 = { + 'MLC11' + 'MLC12' + 'MLC13' + 'MLC14' + 'MLC15' + 'MLC21' + 'MLC22' + 'MLC23' + 'MLC24' + 'MLC31' + 'MLC32' + 'MLC33' + 'MLC41' + 'MLC42' + 'MLC43' + 'MLF11' + 'MLF12' + 'MLF21' + 'MLF22' + 'MLF23' + 'MLF31' + 'MLF32' + 'MLF33' + 'MLF34' + 'MLF41' + 'MLF42' + 'MLF43' + 'MLF44' + 'MLF45' + 'MLF51' + 'MLF52' + 'MLO11' + 'MLO12' + 'MLO21' + 'MLO22' + 'MLO31' + 'MLO32' + 'MLO33' + 'MLO41' + 'MLO42' + 'MLO43' + 'MLP11' + 'MLP12' + 'MLP13' + 'MLP21' + 'MLP22' + 'MLP31' + 'MLP32' + 'MLP33' + 'MLP34' + 'MLT11' + 'MLT12' + 'MLT13' + 'MLT14' + 'MLT15' + 'MLT16' + 'MLT21' + 'MLT22' + 'MLT23' + 'MLT24' + 'MLT25' + 'MLT26' + 'MLT31' + 'MLT32' + 'MLT33' + 'MLT34' + 'MLT35' + 'MLT41' + 'MLT42' + 'MLT43' + 'MLT44' + 'MRC11' + 'MRC12' + 'MRC13' + 'MRC14' + 'MRC15' + 'MRC21' + 'MRC22' + 'MRC23' + 'MRC24' + 'MRC31' + 'MRC32' + 'MRC33' + 'MRC41' + 'MRC42' + 'MRC43' + 'MRF11' + 'MRF12' + 'MRF21' + 'MRF22' + 'MRF23' + 'MRF31' + 'MRF32' + 'MRF33' + 'MRF34' + 'MRF41' + 'MRF42' + 'MRF43' + 'MRF44' + 'MRF45' + 'MRF51' + 'MRF52' + 'MRO11' + 'MRO12' + 'MRO21' + 'MRO22' + 'MRO31' + 'MRO32' + 'MRO33' + 'MRO41' + 'MRO42' + 'MRO43' + 'MRP11' + 'MRP12' + 'MRP13' + 'MRP21' + 'MRP22' + 'MRP31' + 'MRP32' + 'MRP33' + 'MRP34' + 'MRT11' + 'MRT12' + 'MRT13' + 'MRT14' + 'MRT15' + 'MRT16' + 'MRT21' + 'MRT22' + 'MRT23' + 'MRT24' + 'MRT25' + 'MRT26' + 'MRT31' + 'MRT32' + 'MRT33' + 'MRT34' + 'MRT35' + 'MRT41' + 'MRT42' + 'MRT43' + 'MRT44' + 'MZC01' + 'MZC02' + 'MZF01' + 'MZF02' + 'MZF03' + 'MZO01' + 'MZO02' + 'MZP01' + 'MZP02' + }; - ctf151_planar = cell(151, 2); - for i=1:151 - ctf151_planar{i,1} = sprintf('%s_dH', ctf151{i}); - ctf151_planar{i,2} = sprintf('%s_dV', ctf151{i}); - end + ctf151_planar = cell(151, 2); + for i=1:151 + ctf151_planar{i,1} = sprintf('%s_dH', ctf151{i}); + ctf151_planar{i,2} = sprintf('%s_dV', ctf151{i}); + end - ctf275 = { - 'MLC11' - 'MLC12' - 'MLC13' - 'MLC14' - 'MLC15' - 'MLC16' - 'MLC17' - 'MLC21' - 'MLC22' - 'MLC23' - 'MLC24' - 'MLC25' - 'MLC31' - 'MLC32' - 'MLC41' - 'MLC42' - 'MLC51' - 'MLC52' - 'MLC53' - 'MLC54' - 'MLC55' - 'MLC61' - 'MLC62' - 'MLC63' - 'MLF11' - 'MLF12' - 'MLF13' - 'MLF14' - 'MLF21' - 'MLF22' - 'MLF23' - 'MLF24' - 'MLF25' - 'MLF31' - 'MLF32' - 'MLF33' - 'MLF34' - 'MLF35' - 'MLF41' - 'MLF42' - 'MLF43' - 'MLF44' - 'MLF45' - 'MLF46' - 'MLF51' - 'MLF52' - 'MLF53' - 'MLF54' - 'MLF55' - 'MLF56' - 'MLF61' - 'MLF62' - 'MLF63' - 'MLF64' - 'MLF65' - 'MLF66' - 'MLF67' - 'MLO11' - 'MLO12' - 'MLO13' - 'MLO14' - 'MLO21' - 'MLO22' - 'MLO23' - 'MLO24' - 'MLO31' - 'MLO32' - 'MLO33' - 'MLO34' - 'MLO41' - 'MLO42' - 'MLO43' - 'MLO44' - 'MLO51' - 'MLO52' - 'MLO53' - 'MLP11' - 'MLP12' - 'MLP21' - 'MLP22' - 'MLP23' - 'MLP31' - 'MLP32' - 'MLP33' - 'MLP34' - 'MLP35' - 'MLP41' - 'MLP42' - 'MLP43' - 'MLP44' - 'MLP45' - 'MLP51' - 'MLP52' - 'MLP53' - 'MLP54' - 'MLP55' - 'MLP56' - 'MLP57' - 'MLT11' - 'MLT12' - 'MLT13' - 'MLT14' - 'MLT15' - 'MLT16' - 'MLT21' - 'MLT22' - 'MLT23' - 'MLT24' - 'MLT25' - 'MLT26' - 'MLT27' - 'MLT31' - 'MLT32' - 'MLT33' - 'MLT34' - 'MLT35' - 'MLT36' - 'MLT37' - 'MLT41' - 'MLT42' - 'MLT43' - 'MLT44' - 'MLT45' - 'MLT46' - 'MLT47' - 'MLT51' - 'MLT52' - 'MLT53' - 'MLT54' - 'MLT55' - 'MLT56' - 'MLT57' - 'MRC11' - 'MRC12' - 'MRC13' - 'MRC14' - 'MRC15' - 'MRC16' - 'MRC17' - 'MRC21' - 'MRC22' - 'MRC23' - 'MRC24' - 'MRC25' - 'MRC31' - 'MRC32' - 'MRC41' - 'MRC42' - 'MRC51' - 'MRC52' - 'MRC53' - 'MRC54' - 'MRC55' - 'MRC61' - 'MRC62' - 'MRC63' - 'MRF11' - 'MRF12' - 'MRF13' - 'MRF14' - 'MRF21' - 'MRF22' - 'MRF23' - 'MRF24' - 'MRF25' - 'MRF31' - 'MRF32' - 'MRF33' - 'MRF34' - 'MRF35' - 'MRF41' - 'MRF42' - 'MRF43' - 'MRF44' - 'MRF45' - 'MRF46' - 'MRF51' - 'MRF52' - 'MRF53' - 'MRF54' - 'MRF55' - 'MRF56' - 'MRF61' - 'MRF62' - 'MRF63' - 'MRF64' - 'MRF65' - 'MRF66' - 'MRF67' - 'MRO11' - 'MRO12' - 'MRO13' - 'MRO14' - 'MRO21' - 'MRO22' - 'MRO23' - 'MRO24' - 'MRO31' - 'MRO32' - 'MRO33' - 'MRO34' - 'MRO41' - 'MRO42' - 'MRO43' - 'MRO44' - 'MRO51' - 'MRO52' - 'MRO53' - 'MRP11' - 'MRP12' - 'MRP21' - 'MRP22' - 'MRP23' - 'MRP32' - 'MRP33' - 'MRP34' - 'MRP35' - 'MRP41' - 'MRP42' - 'MRP43' - 'MRP44' - 'MRP45' - 'MRP51' - 'MRP52' - 'MRP53' - 'MRP54' - 'MRP55' - 'MRP56' - 'MRP57' - 'MRT11' - 'MRT12' - 'MRT13' - 'MRT14' - 'MRT15' - 'MRT16' - 'MRT21' - 'MRT22' - 'MRT23' - 'MRT24' - 'MRT25' - 'MRT26' - 'MRT27' - 'MRT31' - 'MRT32' - 'MRT33' - 'MRT34' - 'MRT35' - 'MRT36' - 'MRT37' - 'MRT41' - 'MRT42' - 'MRT43' - 'MRT44' - 'MRT45' - 'MRT46' - 'MRT47' - 'MRT51' - 'MRT52' - 'MRT53' - 'MRT54' - 'MRT55' - 'MRT56' - 'MRT57' - 'MZC01' - 'MZC02' - 'MZC03' - 'MZC04' - 'MZF01' - 'MZF02' - 'MZF03' - 'MZO01' - 'MZO02' - 'MZO03' - 'MZP01' - }; + ctf275 = { + 'MLC11' + 'MLC12' + 'MLC13' + 'MLC14' + 'MLC15' + 'MLC16' + 'MLC17' + 'MLC21' + 'MLC22' + 'MLC23' + 'MLC24' + 'MLC25' + 'MLC31' + 'MLC32' + 'MLC41' + 'MLC42' + 'MLC51' + 'MLC52' + 'MLC53' + 'MLC54' + 'MLC55' + 'MLC61' + 'MLC62' + 'MLC63' + 'MLF11' + 'MLF12' + 'MLF13' + 'MLF14' + 'MLF21' + 'MLF22' + 'MLF23' + 'MLF24' + 'MLF25' + 'MLF31' + 'MLF32' + 'MLF33' + 'MLF34' + 'MLF35' + 'MLF41' + 'MLF42' + 'MLF43' + 'MLF44' + 'MLF45' + 'MLF46' + 'MLF51' + 'MLF52' + 'MLF53' + 'MLF54' + 'MLF55' + 'MLF56' + 'MLF61' + 'MLF62' + 'MLF63' + 'MLF64' + 'MLF65' + 'MLF66' + 'MLF67' + 'MLO11' + 'MLO12' + 'MLO13' + 'MLO14' + 'MLO21' + 'MLO22' + 'MLO23' + 'MLO24' + 'MLO31' + 'MLO32' + 'MLO33' + 'MLO34' + 'MLO41' + 'MLO42' + 'MLO43' + 'MLO44' + 'MLO51' + 'MLO52' + 'MLO53' + 'MLP11' + 'MLP12' + 'MLP21' + 'MLP22' + 'MLP23' + 'MLP31' + 'MLP32' + 'MLP33' + 'MLP34' + 'MLP35' + 'MLP41' + 'MLP42' + 'MLP43' + 'MLP44' + 'MLP45' + 'MLP51' + 'MLP52' + 'MLP53' + 'MLP54' + 'MLP55' + 'MLP56' + 'MLP57' + 'MLT11' + 'MLT12' + 'MLT13' + 'MLT14' + 'MLT15' + 'MLT16' + 'MLT21' + 'MLT22' + 'MLT23' + 'MLT24' + 'MLT25' + 'MLT26' + 'MLT27' + 'MLT31' + 'MLT32' + 'MLT33' + 'MLT34' + 'MLT35' + 'MLT36' + 'MLT37' + 'MLT41' + 'MLT42' + 'MLT43' + 'MLT44' + 'MLT45' + 'MLT46' + 'MLT47' + 'MLT51' + 'MLT52' + 'MLT53' + 'MLT54' + 'MLT55' + 'MLT56' + 'MLT57' + 'MRC11' + 'MRC12' + 'MRC13' + 'MRC14' + 'MRC15' + 'MRC16' + 'MRC17' + 'MRC21' + 'MRC22' + 'MRC23' + 'MRC24' + 'MRC25' + 'MRC31' + 'MRC32' + 'MRC41' + 'MRC42' + 'MRC51' + 'MRC52' + 'MRC53' + 'MRC54' + 'MRC55' + 'MRC61' + 'MRC62' + 'MRC63' + 'MRF11' + 'MRF12' + 'MRF13' + 'MRF14' + 'MRF21' + 'MRF22' + 'MRF23' + 'MRF24' + 'MRF25' + 'MRF31' + 'MRF32' + 'MRF33' + 'MRF34' + 'MRF35' + 'MRF41' + 'MRF42' + 'MRF43' + 'MRF44' + 'MRF45' + 'MRF46' + 'MRF51' + 'MRF52' + 'MRF53' + 'MRF54' + 'MRF55' + 'MRF56' + 'MRF61' + 'MRF62' + 'MRF63' + 'MRF64' + 'MRF65' + 'MRF66' + 'MRF67' + 'MRO11' + 'MRO12' + 'MRO13' + 'MRO14' + 'MRO21' + 'MRO22' + 'MRO23' + 'MRO24' + 'MRO31' + 'MRO32' + 'MRO33' + 'MRO34' + 'MRO41' + 'MRO42' + 'MRO43' + 'MRO44' + 'MRO51' + 'MRO52' + 'MRO53' + 'MRP11' + 'MRP12' + 'MRP21' + 'MRP22' + 'MRP23' + 'MRP32' + 'MRP33' + 'MRP34' + 'MRP35' + 'MRP41' + 'MRP42' + 'MRP43' + 'MRP44' + 'MRP45' + 'MRP51' + 'MRP52' + 'MRP53' + 'MRP54' + 'MRP55' + 'MRP56' + 'MRP57' + 'MRT11' + 'MRT12' + 'MRT13' + 'MRT14' + 'MRT15' + 'MRT16' + 'MRT21' + 'MRT22' + 'MRT23' + 'MRT24' + 'MRT25' + 'MRT26' + 'MRT27' + 'MRT31' + 'MRT32' + 'MRT33' + 'MRT34' + 'MRT35' + 'MRT36' + 'MRT37' + 'MRT41' + 'MRT42' + 'MRT43' + 'MRT44' + 'MRT45' + 'MRT46' + 'MRT47' + 'MRT51' + 'MRT52' + 'MRT53' + 'MRT54' + 'MRT55' + 'MRT56' + 'MRT57' + 'MZC01' + 'MZC02' + 'MZC03' + 'MZC04' + 'MZF01' + 'MZF02' + 'MZF03' + 'MZO01' + 'MZO02' + 'MZO03' + 'MZP01' + }; - % f.ck, apparently one channel is missing - ctf275_planar = cell(274,2); - for i=1:274 - ctf275_planar{i,1} = sprintf('%s_dH', ctf275{i}); - ctf275_planar{i,2} = sprintf('%s_dV', ctf275{i}); - end + % f.ck, apparently one channel is missing + ctf275_planar = cell(274,2); + for i=1:274 + ctf275_planar{i,1} = sprintf('%s_dH', ctf275{i}); + ctf275_planar{i,2} = sprintf('%s_dV', ctf275{i}); + end end % if issctf if isneuromag - neuromag122 = { - 'MEG 001' 'MEG 002' - 'MEG 003' 'MEG 004' - 'MEG 005' 'MEG 006' - 'MEG 007' 'MEG 008' - 'MEG 009' 'MEG 010' - 'MEG 011' 'MEG 012' - 'MEG 013' 'MEG 014' - 'MEG 015' 'MEG 016' - 'MEG 017' 'MEG 018' - 'MEG 019' 'MEG 020' - 'MEG 021' 'MEG 022' - 'MEG 023' 'MEG 024' - 'MEG 025' 'MEG 026' - 'MEG 027' 'MEG 028' - 'MEG 029' 'MEG 030' - 'MEG 031' 'MEG 032' - 'MEG 033' 'MEG 034' - 'MEG 035' 'MEG 036' - 'MEG 037' 'MEG 038' - 'MEG 039' 'MEG 040' - 'MEG 041' 'MEG 042' - 'MEG 043' 'MEG 044' - 'MEG 045' 'MEG 046' - 'MEG 047' 'MEG 048' - 'MEG 049' 'MEG 050' - 'MEG 051' 'MEG 052' - 'MEG 053' 'MEG 054' - 'MEG 055' 'MEG 056' - 'MEG 057' 'MEG 058' - 'MEG 059' 'MEG 060' - 'MEG 061' 'MEG 062' - 'MEG 063' 'MEG 064' - 'MEG 065' 'MEG 066' - 'MEG 067' 'MEG 068' - 'MEG 069' 'MEG 070' - 'MEG 071' 'MEG 072' - 'MEG 073' 'MEG 074' - 'MEG 075' 'MEG 076' - 'MEG 077' 'MEG 078' - 'MEG 079' 'MEG 080' - 'MEG 081' 'MEG 082' - 'MEG 083' 'MEG 084' - 'MEG 085' 'MEG 086' - 'MEG 087' 'MEG 088' - 'MEG 089' 'MEG 090' - 'MEG 091' 'MEG 092' - 'MEG 093' 'MEG 094' - 'MEG 095' 'MEG 096' - 'MEG 097' 'MEG 098' - 'MEG 099' 'MEG 100' - 'MEG 101' 'MEG 102' - 'MEG 103' 'MEG 104' - 'MEG 105' 'MEG 106' - 'MEG 107' 'MEG 108' - 'MEG 109' 'MEG 110' - 'MEG 111' 'MEG 112' - 'MEG 113' 'MEG 114' - 'MEG 115' 'MEG 116' - 'MEG 117' 'MEG 118' - 'MEG 119' 'MEG 120' - 'MEG 121' 'MEG 122' - }; + neuromag122 = { + 'MEG 001' 'MEG 002' + 'MEG 003' 'MEG 004' + 'MEG 005' 'MEG 006' + 'MEG 007' 'MEG 008' + 'MEG 009' 'MEG 010' + 'MEG 011' 'MEG 012' + 'MEG 013' 'MEG 014' + 'MEG 015' 'MEG 016' + 'MEG 017' 'MEG 018' + 'MEG 019' 'MEG 020' + 'MEG 021' 'MEG 022' + 'MEG 023' 'MEG 024' + 'MEG 025' 'MEG 026' + 'MEG 027' 'MEG 028' + 'MEG 029' 'MEG 030' + 'MEG 031' 'MEG 032' + 'MEG 033' 'MEG 034' + 'MEG 035' 'MEG 036' + 'MEG 037' 'MEG 038' + 'MEG 039' 'MEG 040' + 'MEG 041' 'MEG 042' + 'MEG 043' 'MEG 044' + 'MEG 045' 'MEG 046' + 'MEG 047' 'MEG 048' + 'MEG 049' 'MEG 050' + 'MEG 051' 'MEG 052' + 'MEG 053' 'MEG 054' + 'MEG 055' 'MEG 056' + 'MEG 057' 'MEG 058' + 'MEG 059' 'MEG 060' + 'MEG 061' 'MEG 062' + 'MEG 063' 'MEG 064' + 'MEG 065' 'MEG 066' + 'MEG 067' 'MEG 068' + 'MEG 069' 'MEG 070' + 'MEG 071' 'MEG 072' + 'MEG 073' 'MEG 074' + 'MEG 075' 'MEG 076' + 'MEG 077' 'MEG 078' + 'MEG 079' 'MEG 080' + 'MEG 081' 'MEG 082' + 'MEG 083' 'MEG 084' + 'MEG 085' 'MEG 086' + 'MEG 087' 'MEG 088' + 'MEG 089' 'MEG 090' + 'MEG 091' 'MEG 092' + 'MEG 093' 'MEG 094' + 'MEG 095' 'MEG 096' + 'MEG 097' 'MEG 098' + 'MEG 099' 'MEG 100' + 'MEG 101' 'MEG 102' + 'MEG 103' 'MEG 104' + 'MEG 105' 'MEG 106' + 'MEG 107' 'MEG 108' + 'MEG 109' 'MEG 110' + 'MEG 111' 'MEG 112' + 'MEG 113' 'MEG 114' + 'MEG 115' 'MEG 116' + 'MEG 117' 'MEG 118' + 'MEG 119' 'MEG 120' + 'MEG 121' 'MEG 122' + }; - % this is an alternative set of labels without a space in them - neuromag122alt = { - 'MEG001' 'MEG002' - 'MEG003' 'MEG004' - 'MEG005' 'MEG006' - 'MEG007' 'MEG008' - 'MEG009' 'MEG010' - 'MEG011' 'MEG012' - 'MEG013' 'MEG014' - 'MEG015' 'MEG016' - 'MEG017' 'MEG018' - 'MEG019' 'MEG020' - 'MEG021' 'MEG022' - 'MEG023' 'MEG024' - 'MEG025' 'MEG026' - 'MEG027' 'MEG028' - 'MEG029' 'MEG030' - 'MEG031' 'MEG032' - 'MEG033' 'MEG034' - 'MEG035' 'MEG036' - 'MEG037' 'MEG038' - 'MEG039' 'MEG040' - 'MEG041' 'MEG042' - 'MEG043' 'MEG044' - 'MEG045' 'MEG046' - 'MEG047' 'MEG048' - 'MEG049' 'MEG050' - 'MEG051' 'MEG052' - 'MEG053' 'MEG054' - 'MEG055' 'MEG056' - 'MEG057' 'MEG058' - 'MEG059' 'MEG060' - 'MEG061' 'MEG062' - 'MEG063' 'MEG064' - 'MEG065' 'MEG066' - 'MEG067' 'MEG068' - 'MEG069' 'MEG070' - 'MEG071' 'MEG072' - 'MEG073' 'MEG074' - 'MEG075' 'MEG076' - 'MEG077' 'MEG078' - 'MEG079' 'MEG080' - 'MEG081' 'MEG082' - 'MEG083' 'MEG084' - 'MEG085' 'MEG086' - 'MEG087' 'MEG088' - 'MEG089' 'MEG090' - 'MEG091' 'MEG092' - 'MEG093' 'MEG094' - 'MEG095' 'MEG096' - 'MEG097' 'MEG098' - 'MEG099' 'MEG100' - 'MEG101' 'MEG102' - 'MEG103' 'MEG104' - 'MEG105' 'MEG106' - 'MEG107' 'MEG108' - 'MEG109' 'MEG110' - 'MEG111' 'MEG112' - 'MEG113' 'MEG114' - 'MEG115' 'MEG116' - 'MEG117' 'MEG118' - 'MEG119' 'MEG120' - 'MEG121' 'MEG122' - }; + % this is an alternative set of labels without a space in them + neuromag122alt = { + 'MEG001' 'MEG002' + 'MEG003' 'MEG004' + 'MEG005' 'MEG006' + 'MEG007' 'MEG008' + 'MEG009' 'MEG010' + 'MEG011' 'MEG012' + 'MEG013' 'MEG014' + 'MEG015' 'MEG016' + 'MEG017' 'MEG018' + 'MEG019' 'MEG020' + 'MEG021' 'MEG022' + 'MEG023' 'MEG024' + 'MEG025' 'MEG026' + 'MEG027' 'MEG028' + 'MEG029' 'MEG030' + 'MEG031' 'MEG032' + 'MEG033' 'MEG034' + 'MEG035' 'MEG036' + 'MEG037' 'MEG038' + 'MEG039' 'MEG040' + 'MEG041' 'MEG042' + 'MEG043' 'MEG044' + 'MEG045' 'MEG046' + 'MEG047' 'MEG048' + 'MEG049' 'MEG050' + 'MEG051' 'MEG052' + 'MEG053' 'MEG054' + 'MEG055' 'MEG056' + 'MEG057' 'MEG058' + 'MEG059' 'MEG060' + 'MEG061' 'MEG062' + 'MEG063' 'MEG064' + 'MEG065' 'MEG066' + 'MEG067' 'MEG068' + 'MEG069' 'MEG070' + 'MEG071' 'MEG072' + 'MEG073' 'MEG074' + 'MEG075' 'MEG076' + 'MEG077' 'MEG078' + 'MEG079' 'MEG080' + 'MEG081' 'MEG082' + 'MEG083' 'MEG084' + 'MEG085' 'MEG086' + 'MEG087' 'MEG088' + 'MEG089' 'MEG090' + 'MEG091' 'MEG092' + 'MEG093' 'MEG094' + 'MEG095' 'MEG096' + 'MEG097' 'MEG098' + 'MEG099' 'MEG100' + 'MEG101' 'MEG102' + 'MEG103' 'MEG104' + 'MEG105' 'MEG106' + 'MEG107' 'MEG108' + 'MEG109' 'MEG110' + 'MEG111' 'MEG112' + 'MEG113' 'MEG114' + 'MEG115' 'MEG116' + 'MEG117' 'MEG118' + 'MEG119' 'MEG120' + 'MEG121' 'MEG122' + }; - neuromag306 = { - 'MEG 0113' 'MEG 0112' 'MEG 0111' - 'MEG 0122' 'MEG 0123' 'MEG 0121' - 'MEG 0132' 'MEG 0133' 'MEG 0131' - 'MEG 0143' 'MEG 0142' 'MEG 0141' - 'MEG 0213' 'MEG 0212' 'MEG 0211' - 'MEG 0222' 'MEG 0223' 'MEG 0221' - 'MEG 0232' 'MEG 0233' 'MEG 0231' - 'MEG 0243' 'MEG 0242' 'MEG 0241' - 'MEG 0313' 'MEG 0312' 'MEG 0311' - 'MEG 0322' 'MEG 0323' 'MEG 0321' - 'MEG 0333' 'MEG 0332' 'MEG 0331' - 'MEG 0343' 'MEG 0342' 'MEG 0341' - 'MEG 0413' 'MEG 0412' 'MEG 0411' - 'MEG 0422' 'MEG 0423' 'MEG 0421' - 'MEG 0432' 'MEG 0433' 'MEG 0431' - 'MEG 0443' 'MEG 0442' 'MEG 0441' - 'MEG 0513' 'MEG 0512' 'MEG 0511' - 'MEG 0523' 'MEG 0522' 'MEG 0521' - 'MEG 0532' 'MEG 0533' 'MEG 0531' - 'MEG 0542' 'MEG 0543' 'MEG 0541' - 'MEG 0613' 'MEG 0612' 'MEG 0611' - 'MEG 0622' 'MEG 0623' 'MEG 0621' - 'MEG 0633' 'MEG 0632' 'MEG 0631' - 'MEG 0642' 'MEG 0643' 'MEG 0641' - 'MEG 0713' 'MEG 0712' 'MEG 0711' - 'MEG 0723' 'MEG 0722' 'MEG 0721' - 'MEG 0733' 'MEG 0732' 'MEG 0731' - 'MEG 0743' 'MEG 0742' 'MEG 0741' - 'MEG 0813' 'MEG 0812' 'MEG 0811' - 'MEG 0822' 'MEG 0823' 'MEG 0821' - 'MEG 0913' 'MEG 0912' 'MEG 0911' - 'MEG 0923' 'MEG 0922' 'MEG 0921' - 'MEG 0932' 'MEG 0933' 'MEG 0931' - 'MEG 0942' 'MEG 0943' 'MEG 0941' - 'MEG 1013' 'MEG 1012' 'MEG 1011' - 'MEG 1023' 'MEG 1022' 'MEG 1021' - 'MEG 1032' 'MEG 1033' 'MEG 1031' - 'MEG 1043' 'MEG 1042' 'MEG 1041' - 'MEG 1112' 'MEG 1113' 'MEG 1111' - 'MEG 1123' 'MEG 1122' 'MEG 1121' - 'MEG 1133' 'MEG 1132' 'MEG 1131' - 'MEG 1142' 'MEG 1143' 'MEG 1141' - 'MEG 1213' 'MEG 1212' 'MEG 1211' - 'MEG 1223' 'MEG 1222' 'MEG 1221' - 'MEG 1232' 'MEG 1233' 'MEG 1231' - 'MEG 1243' 'MEG 1242' 'MEG 1241' - 'MEG 1312' 'MEG 1313' 'MEG 1311' - 'MEG 1323' 'MEG 1322' 'MEG 1321' - 'MEG 1333' 'MEG 1332' 'MEG 1331' - 'MEG 1342' 'MEG 1343' 'MEG 1341' - 'MEG 1412' 'MEG 1413' 'MEG 1411' - 'MEG 1423' 'MEG 1422' 'MEG 1421' - 'MEG 1433' 'MEG 1432' 'MEG 1431' - 'MEG 1442' 'MEG 1443' 'MEG 1441' - 'MEG 1512' 'MEG 1513' 'MEG 1511' - 'MEG 1522' 'MEG 1523' 'MEG 1521' - 'MEG 1533' 'MEG 1532' 'MEG 1531' - 'MEG 1543' 'MEG 1542' 'MEG 1541' - 'MEG 1613' 'MEG 1612' 'MEG 1611' - 'MEG 1622' 'MEG 1623' 'MEG 1621' - 'MEG 1632' 'MEG 1633' 'MEG 1631' - 'MEG 1643' 'MEG 1642' 'MEG 1641' - 'MEG 1713' 'MEG 1712' 'MEG 1711' - 'MEG 1722' 'MEG 1723' 'MEG 1721' - 'MEG 1732' 'MEG 1733' 'MEG 1731' - 'MEG 1743' 'MEG 1742' 'MEG 1741' - 'MEG 1813' 'MEG 1812' 'MEG 1811' - 'MEG 1822' 'MEG 1823' 'MEG 1821' - 'MEG 1832' 'MEG 1833' 'MEG 1831' - 'MEG 1843' 'MEG 1842' 'MEG 1841' - 'MEG 1912' 'MEG 1913' 'MEG 1911' - 'MEG 1923' 'MEG 1922' 'MEG 1921' - 'MEG 1932' 'MEG 1933' 'MEG 1931' - 'MEG 1943' 'MEG 1942' 'MEG 1941' - 'MEG 2013' 'MEG 2012' 'MEG 2011' - 'MEG 2023' 'MEG 2022' 'MEG 2021' - 'MEG 2032' 'MEG 2033' 'MEG 2031' - 'MEG 2042' 'MEG 2043' 'MEG 2041' - 'MEG 2113' 'MEG 2112' 'MEG 2111' - 'MEG 2122' 'MEG 2123' 'MEG 2121' - 'MEG 2133' 'MEG 2132' 'MEG 2131' - 'MEG 2143' 'MEG 2142' 'MEG 2141' - 'MEG 2212' 'MEG 2213' 'MEG 2211' - 'MEG 2223' 'MEG 2222' 'MEG 2221' - 'MEG 2233' 'MEG 2232' 'MEG 2231' - 'MEG 2242' 'MEG 2243' 'MEG 2241' - 'MEG 2312' 'MEG 2313' 'MEG 2311' - 'MEG 2323' 'MEG 2322' 'MEG 2321' - 'MEG 2332' 'MEG 2333' 'MEG 2331' - 'MEG 2343' 'MEG 2342' 'MEG 2341' - 'MEG 2412' 'MEG 2413' 'MEG 2411' - 'MEG 2423' 'MEG 2422' 'MEG 2421' - 'MEG 2433' 'MEG 2432' 'MEG 2431' - 'MEG 2442' 'MEG 2443' 'MEG 2441' - 'MEG 2512' 'MEG 2513' 'MEG 2511' - 'MEG 2522' 'MEG 2523' 'MEG 2521' - 'MEG 2533' 'MEG 2532' 'MEG 2531' - 'MEG 2543' 'MEG 2542' 'MEG 2541' - 'MEG 2612' 'MEG 2613' 'MEG 2611' - 'MEG 2623' 'MEG 2622' 'MEG 2621' - 'MEG 2633' 'MEG 2632' 'MEG 2631' - 'MEG 2642' 'MEG 2643' 'MEG 2641' - }; + neuromag306 = { + 'MEG 0113' 'MEG 0112' 'MEG 0111' + 'MEG 0122' 'MEG 0123' 'MEG 0121' + 'MEG 0132' 'MEG 0133' 'MEG 0131' + 'MEG 0143' 'MEG 0142' 'MEG 0141' + 'MEG 0213' 'MEG 0212' 'MEG 0211' + 'MEG 0222' 'MEG 0223' 'MEG 0221' + 'MEG 0232' 'MEG 0233' 'MEG 0231' + 'MEG 0243' 'MEG 0242' 'MEG 0241' + 'MEG 0313' 'MEG 0312' 'MEG 0311' + 'MEG 0322' 'MEG 0323' 'MEG 0321' + 'MEG 0333' 'MEG 0332' 'MEG 0331' + 'MEG 0343' 'MEG 0342' 'MEG 0341' + 'MEG 0413' 'MEG 0412' 'MEG 0411' + 'MEG 0422' 'MEG 0423' 'MEG 0421' + 'MEG 0432' 'MEG 0433' 'MEG 0431' + 'MEG 0443' 'MEG 0442' 'MEG 0441' + 'MEG 0513' 'MEG 0512' 'MEG 0511' + 'MEG 0523' 'MEG 0522' 'MEG 0521' + 'MEG 0532' 'MEG 0533' 'MEG 0531' + 'MEG 0542' 'MEG 0543' 'MEG 0541' + 'MEG 0613' 'MEG 0612' 'MEG 0611' + 'MEG 0622' 'MEG 0623' 'MEG 0621' + 'MEG 0633' 'MEG 0632' 'MEG 0631' + 'MEG 0642' 'MEG 0643' 'MEG 0641' + 'MEG 0713' 'MEG 0712' 'MEG 0711' + 'MEG 0723' 'MEG 0722' 'MEG 0721' + 'MEG 0733' 'MEG 0732' 'MEG 0731' + 'MEG 0743' 'MEG 0742' 'MEG 0741' + 'MEG 0813' 'MEG 0812' 'MEG 0811' + 'MEG 0822' 'MEG 0823' 'MEG 0821' + 'MEG 0913' 'MEG 0912' 'MEG 0911' + 'MEG 0923' 'MEG 0922' 'MEG 0921' + 'MEG 0932' 'MEG 0933' 'MEG 0931' + 'MEG 0942' 'MEG 0943' 'MEG 0941' + 'MEG 1013' 'MEG 1012' 'MEG 1011' + 'MEG 1023' 'MEG 1022' 'MEG 1021' + 'MEG 1032' 'MEG 1033' 'MEG 1031' + 'MEG 1043' 'MEG 1042' 'MEG 1041' + 'MEG 1112' 'MEG 1113' 'MEG 1111' + 'MEG 1123' 'MEG 1122' 'MEG 1121' + 'MEG 1133' 'MEG 1132' 'MEG 1131' + 'MEG 1142' 'MEG 1143' 'MEG 1141' + 'MEG 1213' 'MEG 1212' 'MEG 1211' + 'MEG 1223' 'MEG 1222' 'MEG 1221' + 'MEG 1232' 'MEG 1233' 'MEG 1231' + 'MEG 1243' 'MEG 1242' 'MEG 1241' + 'MEG 1312' 'MEG 1313' 'MEG 1311' + 'MEG 1323' 'MEG 1322' 'MEG 1321' + 'MEG 1333' 'MEG 1332' 'MEG 1331' + 'MEG 1342' 'MEG 1343' 'MEG 1341' + 'MEG 1412' 'MEG 1413' 'MEG 1411' + 'MEG 1423' 'MEG 1422' 'MEG 1421' + 'MEG 1433' 'MEG 1432' 'MEG 1431' + 'MEG 1442' 'MEG 1443' 'MEG 1441' + 'MEG 1512' 'MEG 1513' 'MEG 1511' + 'MEG 1522' 'MEG 1523' 'MEG 1521' + 'MEG 1533' 'MEG 1532' 'MEG 1531' + 'MEG 1543' 'MEG 1542' 'MEG 1541' + 'MEG 1613' 'MEG 1612' 'MEG 1611' + 'MEG 1622' 'MEG 1623' 'MEG 1621' + 'MEG 1632' 'MEG 1633' 'MEG 1631' + 'MEG 1643' 'MEG 1642' 'MEG 1641' + 'MEG 1713' 'MEG 1712' 'MEG 1711' + 'MEG 1722' 'MEG 1723' 'MEG 1721' + 'MEG 1732' 'MEG 1733' 'MEG 1731' + 'MEG 1743' 'MEG 1742' 'MEG 1741' + 'MEG 1813' 'MEG 1812' 'MEG 1811' + 'MEG 1822' 'MEG 1823' 'MEG 1821' + 'MEG 1832' 'MEG 1833' 'MEG 1831' + 'MEG 1843' 'MEG 1842' 'MEG 1841' + 'MEG 1912' 'MEG 1913' 'MEG 1911' + 'MEG 1923' 'MEG 1922' 'MEG 1921' + 'MEG 1932' 'MEG 1933' 'MEG 1931' + 'MEG 1943' 'MEG 1942' 'MEG 1941' + 'MEG 2013' 'MEG 2012' 'MEG 2011' + 'MEG 2023' 'MEG 2022' 'MEG 2021' + 'MEG 2032' 'MEG 2033' 'MEG 2031' + 'MEG 2042' 'MEG 2043' 'MEG 2041' + 'MEG 2113' 'MEG 2112' 'MEG 2111' + 'MEG 2122' 'MEG 2123' 'MEG 2121' + 'MEG 2133' 'MEG 2132' 'MEG 2131' + 'MEG 2143' 'MEG 2142' 'MEG 2141' + 'MEG 2212' 'MEG 2213' 'MEG 2211' + 'MEG 2223' 'MEG 2222' 'MEG 2221' + 'MEG 2233' 'MEG 2232' 'MEG 2231' + 'MEG 2242' 'MEG 2243' 'MEG 2241' + 'MEG 2312' 'MEG 2313' 'MEG 2311' + 'MEG 2323' 'MEG 2322' 'MEG 2321' + 'MEG 2332' 'MEG 2333' 'MEG 2331' + 'MEG 2343' 'MEG 2342' 'MEG 2341' + 'MEG 2412' 'MEG 2413' 'MEG 2411' + 'MEG 2423' 'MEG 2422' 'MEG 2421' + 'MEG 2433' 'MEG 2432' 'MEG 2431' + 'MEG 2442' 'MEG 2443' 'MEG 2441' + 'MEG 2512' 'MEG 2513' 'MEG 2511' + 'MEG 2522' 'MEG 2523' 'MEG 2521' + 'MEG 2533' 'MEG 2532' 'MEG 2531' + 'MEG 2543' 'MEG 2542' 'MEG 2541' + 'MEG 2612' 'MEG 2613' 'MEG 2611' + 'MEG 2623' 'MEG 2622' 'MEG 2621' + 'MEG 2633' 'MEG 2632' 'MEG 2631' + 'MEG 2642' 'MEG 2643' 'MEG 2641' + }; - % this is an alternative set of labels without a space in them - neuromag306alt = { - 'MEG0113' 'MEG0112' 'MEG0111' - 'MEG0122' 'MEG0123' 'MEG0121' - 'MEG0132' 'MEG0133' 'MEG0131' - 'MEG0143' 'MEG0142' 'MEG0141' - 'MEG0213' 'MEG0212' 'MEG0211' - 'MEG0222' 'MEG0223' 'MEG0221' - 'MEG0232' 'MEG0233' 'MEG0231' - 'MEG0243' 'MEG0242' 'MEG0241' - 'MEG0313' 'MEG0312' 'MEG0311' - 'MEG0322' 'MEG0323' 'MEG0321' - 'MEG0333' 'MEG0332' 'MEG0331' - 'MEG0343' 'MEG0342' 'MEG0341' - 'MEG0413' 'MEG0412' 'MEG0411' - 'MEG0422' 'MEG0423' 'MEG0421' - 'MEG0432' 'MEG0433' 'MEG0431' - 'MEG0443' 'MEG0442' 'MEG0441' - 'MEG0513' 'MEG0512' 'MEG0511' - 'MEG0523' 'MEG0522' 'MEG0521' - 'MEG0532' 'MEG0533' 'MEG0531' - 'MEG0542' 'MEG0543' 'MEG0541' - 'MEG0613' 'MEG0612' 'MEG0611' - 'MEG0622' 'MEG0623' 'MEG0621' - 'MEG0633' 'MEG0632' 'MEG0631' - 'MEG0642' 'MEG0643' 'MEG0641' - 'MEG0713' 'MEG0712' 'MEG0711' - 'MEG0723' 'MEG0722' 'MEG0721' - 'MEG0733' 'MEG0732' 'MEG0731' - 'MEG0743' 'MEG0742' 'MEG0741' - 'MEG0813' 'MEG0812' 'MEG0811' - 'MEG0822' 'MEG0823' 'MEG0821' - 'MEG0913' 'MEG0912' 'MEG0911' - 'MEG0923' 'MEG0922' 'MEG0921' - 'MEG0932' 'MEG0933' 'MEG0931' - 'MEG0942' 'MEG0943' 'MEG0941' - 'MEG1013' 'MEG1012' 'MEG1011' - 'MEG1023' 'MEG1022' 'MEG1021' - 'MEG1032' 'MEG1033' 'MEG1031' - 'MEG1043' 'MEG1042' 'MEG1041' - 'MEG1112' 'MEG1113' 'MEG1111' - 'MEG1123' 'MEG1122' 'MEG1121' - 'MEG1133' 'MEG1132' 'MEG1131' - 'MEG1142' 'MEG1143' 'MEG1141' - 'MEG1213' 'MEG1212' 'MEG1211' - 'MEG1223' 'MEG1222' 'MEG1221' - 'MEG1232' 'MEG1233' 'MEG1231' - 'MEG1243' 'MEG1242' 'MEG1241' - 'MEG1312' 'MEG1313' 'MEG1311' - 'MEG1323' 'MEG1322' 'MEG1321' - 'MEG1333' 'MEG1332' 'MEG1331' - 'MEG1342' 'MEG1343' 'MEG1341' - 'MEG1412' 'MEG1413' 'MEG1411' - 'MEG1423' 'MEG1422' 'MEG1421' - 'MEG1433' 'MEG1432' 'MEG1431' - 'MEG1442' 'MEG1443' 'MEG1441' - 'MEG1512' 'MEG1513' 'MEG1511' - 'MEG1522' 'MEG1523' 'MEG1521' - 'MEG1533' 'MEG1532' 'MEG1531' - 'MEG1543' 'MEG1542' 'MEG1541' - 'MEG1613' 'MEG1612' 'MEG1611' - 'MEG1622' 'MEG1623' 'MEG1621' - 'MEG1632' 'MEG1633' 'MEG1631' - 'MEG1643' 'MEG1642' 'MEG1641' - 'MEG1713' 'MEG1712' 'MEG1711' - 'MEG1722' 'MEG1723' 'MEG1721' - 'MEG1732' 'MEG1733' 'MEG1731' - 'MEG1743' 'MEG1742' 'MEG1741' - 'MEG1813' 'MEG1812' 'MEG1811' - 'MEG1822' 'MEG1823' 'MEG1821' - 'MEG1832' 'MEG1833' 'MEG1831' - 'MEG1843' 'MEG1842' 'MEG1841' - 'MEG1912' 'MEG1913' 'MEG1911' - 'MEG1923' 'MEG1922' 'MEG1921' - 'MEG1932' 'MEG1933' 'MEG1931' - 'MEG1943' 'MEG1942' 'MEG1941' - 'MEG2013' 'MEG2012' 'MEG2011' - 'MEG2023' 'MEG2022' 'MEG2021' - 'MEG2032' 'MEG2033' 'MEG2031' - 'MEG2042' 'MEG2043' 'MEG2041' - 'MEG2113' 'MEG2112' 'MEG2111' - 'MEG2122' 'MEG2123' 'MEG2121' - 'MEG2133' 'MEG2132' 'MEG2131' - 'MEG2143' 'MEG2142' 'MEG2141' - 'MEG2212' 'MEG2213' 'MEG2211' - 'MEG2223' 'MEG2222' 'MEG2221' - 'MEG2233' 'MEG2232' 'MEG2231' - 'MEG2242' 'MEG2243' 'MEG2241' - 'MEG2312' 'MEG2313' 'MEG2311' - 'MEG2323' 'MEG2322' 'MEG2321' - 'MEG2332' 'MEG2333' 'MEG2331' - 'MEG2343' 'MEG2342' 'MEG2341' - 'MEG2412' 'MEG2413' 'MEG2411' - 'MEG2423' 'MEG2422' 'MEG2421' - 'MEG2433' 'MEG2432' 'MEG2431' - 'MEG2442' 'MEG2443' 'MEG2441' - 'MEG2512' 'MEG2513' 'MEG2511' - 'MEG2522' 'MEG2523' 'MEG2521' - 'MEG2533' 'MEG2532' 'MEG2531' - 'MEG2543' 'MEG2542' 'MEG2541' - 'MEG2612' 'MEG2613' 'MEG2611' - 'MEG2623' 'MEG2622' 'MEG2621' - 'MEG2633' 'MEG2632' 'MEG2631' - 'MEG2642' 'MEG2643' 'MEG2641' - }; + % this is an alternative set of labels without a space in them + neuromag306alt = { + 'MEG0113' 'MEG0112' 'MEG0111' + 'MEG0122' 'MEG0123' 'MEG0121' + 'MEG0132' 'MEG0133' 'MEG0131' + 'MEG0143' 'MEG0142' 'MEG0141' + 'MEG0213' 'MEG0212' 'MEG0211' + 'MEG0222' 'MEG0223' 'MEG0221' + 'MEG0232' 'MEG0233' 'MEG0231' + 'MEG0243' 'MEG0242' 'MEG0241' + 'MEG0313' 'MEG0312' 'MEG0311' + 'MEG0322' 'MEG0323' 'MEG0321' + 'MEG0333' 'MEG0332' 'MEG0331' + 'MEG0343' 'MEG0342' 'MEG0341' + 'MEG0413' 'MEG0412' 'MEG0411' + 'MEG0422' 'MEG0423' 'MEG0421' + 'MEG0432' 'MEG0433' 'MEG0431' + 'MEG0443' 'MEG0442' 'MEG0441' + 'MEG0513' 'MEG0512' 'MEG0511' + 'MEG0523' 'MEG0522' 'MEG0521' + 'MEG0532' 'MEG0533' 'MEG0531' + 'MEG0542' 'MEG0543' 'MEG0541' + 'MEG0613' 'MEG0612' 'MEG0611' + 'MEG0622' 'MEG0623' 'MEG0621' + 'MEG0633' 'MEG0632' 'MEG0631' + 'MEG0642' 'MEG0643' 'MEG0641' + 'MEG0713' 'MEG0712' 'MEG0711' + 'MEG0723' 'MEG0722' 'MEG0721' + 'MEG0733' 'MEG0732' 'MEG0731' + 'MEG0743' 'MEG0742' 'MEG0741' + 'MEG0813' 'MEG0812' 'MEG0811' + 'MEG0822' 'MEG0823' 'MEG0821' + 'MEG0913' 'MEG0912' 'MEG0911' + 'MEG0923' 'MEG0922' 'MEG0921' + 'MEG0932' 'MEG0933' 'MEG0931' + 'MEG0942' 'MEG0943' 'MEG0941' + 'MEG1013' 'MEG1012' 'MEG1011' + 'MEG1023' 'MEG1022' 'MEG1021' + 'MEG1032' 'MEG1033' 'MEG1031' + 'MEG1043' 'MEG1042' 'MEG1041' + 'MEG1112' 'MEG1113' 'MEG1111' + 'MEG1123' 'MEG1122' 'MEG1121' + 'MEG1133' 'MEG1132' 'MEG1131' + 'MEG1142' 'MEG1143' 'MEG1141' + 'MEG1213' 'MEG1212' 'MEG1211' + 'MEG1223' 'MEG1222' 'MEG1221' + 'MEG1232' 'MEG1233' 'MEG1231' + 'MEG1243' 'MEG1242' 'MEG1241' + 'MEG1312' 'MEG1313' 'MEG1311' + 'MEG1323' 'MEG1322' 'MEG1321' + 'MEG1333' 'MEG1332' 'MEG1331' + 'MEG1342' 'MEG1343' 'MEG1341' + 'MEG1412' 'MEG1413' 'MEG1411' + 'MEG1423' 'MEG1422' 'MEG1421' + 'MEG1433' 'MEG1432' 'MEG1431' + 'MEG1442' 'MEG1443' 'MEG1441' + 'MEG1512' 'MEG1513' 'MEG1511' + 'MEG1522' 'MEG1523' 'MEG1521' + 'MEG1533' 'MEG1532' 'MEG1531' + 'MEG1543' 'MEG1542' 'MEG1541' + 'MEG1613' 'MEG1612' 'MEG1611' + 'MEG1622' 'MEG1623' 'MEG1621' + 'MEG1632' 'MEG1633' 'MEG1631' + 'MEG1643' 'MEG1642' 'MEG1641' + 'MEG1713' 'MEG1712' 'MEG1711' + 'MEG1722' 'MEG1723' 'MEG1721' + 'MEG1732' 'MEG1733' 'MEG1731' + 'MEG1743' 'MEG1742' 'MEG1741' + 'MEG1813' 'MEG1812' 'MEG1811' + 'MEG1822' 'MEG1823' 'MEG1821' + 'MEG1832' 'MEG1833' 'MEG1831' + 'MEG1843' 'MEG1842' 'MEG1841' + 'MEG1912' 'MEG1913' 'MEG1911' + 'MEG1923' 'MEG1922' 'MEG1921' + 'MEG1932' 'MEG1933' 'MEG1931' + 'MEG1943' 'MEG1942' 'MEG1941' + 'MEG2013' 'MEG2012' 'MEG2011' + 'MEG2023' 'MEG2022' 'MEG2021' + 'MEG2032' 'MEG2033' 'MEG2031' + 'MEG2042' 'MEG2043' 'MEG2041' + 'MEG2113' 'MEG2112' 'MEG2111' + 'MEG2122' 'MEG2123' 'MEG2121' + 'MEG2133' 'MEG2132' 'MEG2131' + 'MEG2143' 'MEG2142' 'MEG2141' + 'MEG2212' 'MEG2213' 'MEG2211' + 'MEG2223' 'MEG2222' 'MEG2221' + 'MEG2233' 'MEG2232' 'MEG2231' + 'MEG2242' 'MEG2243' 'MEG2241' + 'MEG2312' 'MEG2313' 'MEG2311' + 'MEG2323' 'MEG2322' 'MEG2321' + 'MEG2332' 'MEG2333' 'MEG2331' + 'MEG2343' 'MEG2342' 'MEG2341' + 'MEG2412' 'MEG2413' 'MEG2411' + 'MEG2423' 'MEG2422' 'MEG2421' + 'MEG2433' 'MEG2432' 'MEG2431' + 'MEG2442' 'MEG2443' 'MEG2441' + 'MEG2512' 'MEG2513' 'MEG2511' + 'MEG2522' 'MEG2523' 'MEG2521' + 'MEG2533' 'MEG2532' 'MEG2531' + 'MEG2543' 'MEG2542' 'MEG2541' + 'MEG2612' 'MEG2613' 'MEG2611' + 'MEG2623' 'MEG2622' 'MEG2621' + 'MEG2633' 'MEG2632' 'MEG2631' + 'MEG2642' 'MEG2643' 'MEG2641' + }; end % if isneuromag if iseeg || isext - eeg1020 = { - 'Fp1' - 'Fpz' - 'Fp2' - 'F7' - 'F3' - 'Fz' - 'F4' - 'F8' - 'T7' - 'C3' - 'Cz' - 'C4' - 'T8' - 'P7' - 'P3' - 'Pz' - 'P4' - 'P8' - 'O1' - 'Oz' - 'O2'}; + eeg1020 = { + 'Fp1' + 'Fpz' + 'Fp2' + 'F7' + 'F3' + 'Fz' + 'F4' + 'F8' + 'T7' + 'C3' + 'Cz' + 'C4' + 'T8' + 'P7' + 'P3' + 'Pz' + 'P4' + 'P8' + 'O1' + 'Oz' + 'O2'}; - eeg1010 = { - 'Fp1' - 'Fpz' - 'Fp2' - 'AF9' - 'AF7' - 'AF5' - 'AF3' - 'AF1' - 'AFz' - 'AF2' - 'AF4' - 'AF6' - 'AF8' - 'AF10' - 'F9' - 'F7' - 'F5' - 'F3' - 'F1' - 'Fz' - 'F2' - 'F4' - 'F6' - 'F8' - 'F10' - 'FT9' - 'FT7' - 'FC5' - 'FC3' - 'FC1' - 'FCz' - 'FC2' - 'FC4' - 'FC6' - 'FT8' - 'FT10' - 'T9' - 'T7' - 'C5' - 'C3' - 'C1' - 'Cz' - 'C2' - 'C4' - 'C6' - 'T8' - 'T10' - 'TP9' - 'TP7' - 'CP5' - 'CP3' - 'CP1' - 'CPz' - 'CP2' - 'CP4' - 'CP6' - 'TP8' - 'TP10' - 'P9' - 'P7' - 'P5' - 'P3' - 'P1' - 'Pz' - 'P2' - 'P4' - 'P6' - 'P8' - 'P10' - 'PO9' - 'PO7' - 'PO5' - 'PO3' - 'PO1' - 'POz' - 'PO2' - 'PO4' - 'PO6' - 'PO8' - 'PO10' - 'O1' - 'Oz' - 'O2' - 'I1' - 'Iz' - 'I2' - }; + eeg1010 = { + 'Fp1' + 'Fpz' + 'Fp2' + 'AF9' + 'AF7' + 'AF5' + 'AF3' + 'AF1' + 'AFz' + 'AF2' + 'AF4' + 'AF6' + 'AF8' + 'AF10' + 'F9' + 'F7' + 'F5' + 'F3' + 'F1' + 'Fz' + 'F2' + 'F4' + 'F6' + 'F8' + 'F10' + 'FT9' + 'FT7' + 'FC5' + 'FC3' + 'FC1' + 'FCz' + 'FC2' + 'FC4' + 'FC6' + 'FT8' + 'FT10' + 'T9' + 'T7' + 'C5' + 'C3' + 'C1' + 'Cz' + 'C2' + 'C4' + 'C6' + 'T8' + 'T10' + 'TP9' + 'TP7' + 'CP5' + 'CP3' + 'CP1' + 'CPz' + 'CP2' + 'CP4' + 'CP6' + 'TP8' + 'TP10' + 'P9' + 'P7' + 'P5' + 'P3' + 'P1' + 'Pz' + 'P2' + 'P4' + 'P6' + 'P8' + 'P10' + 'PO9' + 'PO7' + 'PO5' + 'PO3' + 'PO1' + 'POz' + 'PO2' + 'PO4' + 'PO6' + 'PO8' + 'PO10' + 'O1' + 'Oz' + 'O2' + 'I1' + 'Iz' + 'I2' + }; - eeg1005 = { - 'Fp1' - 'Fpz' - 'Fp2' - 'AF9' - 'AF7' - 'AF5' - 'AF3' - 'AF1' - 'AFz' - 'AF2' - 'AF4' - 'AF6' - 'AF8' - 'AF10' - 'F9' - 'F7' - 'F5' - 'F3' - 'F1' - 'Fz' - 'F2' - 'F4' - 'F6' - 'F8' - 'F10' - 'FT9' - 'FT7' - 'FC5' - 'FC3' - 'FC1' - 'FCz' - 'FC2' - 'FC4' - 'FC6' - 'FT8' - 'FT10' - 'T9' - 'T7' - 'C5' - 'C3' - 'C1' - 'Cz' - 'C2' - 'C4' - 'C6' - 'T8' - 'T10' - 'TP9' - 'TP7' - 'CP5' - 'CP3' - 'CP1' - 'CPz' - 'CP2' - 'CP4' - 'CP6' - 'TP8' - 'TP10' - 'P9' - 'P7' - 'P5' - 'P3' - 'P1' - 'Pz' - 'P2' - 'P4' - 'P6' - 'P8' - 'P10' - 'PO9' - 'PO7' - 'PO5' - 'PO3' - 'PO1' - 'POz' - 'PO2' - 'PO4' - 'PO6' - 'PO8' - 'PO10' - 'O1' - 'Oz' - 'O2' - 'I1' - 'Iz' - 'I2' - 'AFp9h' - 'AFp7h' - 'AFp5h' - 'AFp3h' - 'AFp1h' - 'AFp2h' - 'AFp4h' - 'AFp6h' - 'AFp8h' - 'AFp10h' - 'AFF9h' - 'AFF7h' - 'AFF5h' - 'AFF3h' - 'AFF1h' - 'AFF2h' - 'AFF4h' - 'AFF6h' - 'AFF8h' - 'AFF10h' - 'FFT9h' - 'FFT7h' - 'FFC5h' - 'FFC3h' - 'FFC1h' - 'FFC2h' - 'FFC4h' - 'FFC6h' - 'FFT8h' - 'FFT10h' - 'FTT9h' - 'FTT7h' - 'FCC5h' - 'FCC3h' - 'FCC1h' - 'FCC2h' - 'FCC4h' - 'FCC6h' - 'FTT8h' - 'FTT10h' - 'TTP9h' - 'TTP7h' - 'CCP5h' - 'CCP3h' - 'CCP1h' - 'CCP2h' - 'CCP4h' - 'CCP6h' - 'TTP8h' - 'TTP10h' - 'TPP9h' - 'TPP7h' - 'CPP5h' - 'CPP3h' - 'CPP1h' - 'CPP2h' - 'CPP4h' - 'CPP6h' - 'TPP8h' - 'TPP10h' - 'PPO9h' - 'PPO7h' - 'PPO5h' - 'PPO3h' - 'PPO1h' - 'PPO2h' - 'PPO4h' - 'PPO6h' - 'PPO8h' - 'PPO10h' - 'POO9h' - 'POO7h' - 'POO5h' - 'POO3h' - 'POO1h' - 'POO2h' - 'POO4h' - 'POO6h' - 'POO8h' - 'POO10h' - 'OI1h' - 'OI2h' - 'Fp1h' - 'Fp2h' - 'AF9h' - 'AF7h' - 'AF5h' - 'AF3h' - 'AF1h' - 'AF2h' - 'AF4h' - 'AF6h' - 'AF8h' - 'AF10h' - 'F9h' - 'F7h' - 'F5h' - 'F3h' - 'F1h' - 'F2h' - 'F4h' - 'F6h' - 'F8h' - 'F10h' - 'FT9h' - 'FT7h' - 'FC5h' - 'FC3h' - 'FC1h' - 'FC2h' - 'FC4h' - 'FC6h' - 'FT8h' - 'FT10h' - 'T9h' - 'T7h' - 'C5h' - 'C3h' - 'C1h' - 'C2h' - 'C4h' - 'C6h' - 'T8h' - 'T10h' - 'TP9h' - 'TP7h' - 'CP5h' - 'CP3h' - 'CP1h' - 'CP2h' - 'CP4h' - 'CP6h' - 'TP8h' - 'TP10h' - 'P9h' - 'P7h' - 'P5h' - 'P3h' - 'P1h' - 'P2h' - 'P4h' - 'P6h' - 'P8h' - 'P10h' - 'PO9h' - 'PO7h' - 'PO5h' - 'PO3h' - 'PO1h' - 'PO2h' - 'PO4h' - 'PO6h' - 'PO8h' - 'PO10h' - 'O1h' - 'O2h' - 'I1h' - 'I2h' - 'AFp9' - 'AFp7' - 'AFp5' - 'AFp3' - 'AFp1' - 'AFpz' - 'AFp2' - 'AFp4' - 'AFp6' - 'AFp8' - 'AFp10' - 'AFF9' - 'AFF7' - 'AFF5' - 'AFF3' - 'AFF1' - 'AFFz' - 'AFF2' - 'AFF4' - 'AFF6' - 'AFF8' - 'AFF10' - 'FFT9' - 'FFT7' - 'FFC5' - 'FFC3' - 'FFC1' - 'FFCz' - 'FFC2' - 'FFC4' - 'FFC6' - 'FFT8' - 'FFT10' - 'FTT9' - 'FTT7' - 'FCC5' - 'FCC3' - 'FCC1' - 'FCCz' - 'FCC2' - 'FCC4' - 'FCC6' - 'FTT8' - 'FTT10' - 'TTP9' - 'TTP7' - 'CCP5' - 'CCP3' - 'CCP1' - 'CCPz' - 'CCP2' - 'CCP4' - 'CCP6' - 'TTP8' - 'TTP10' - 'TPP9' - 'TPP7' - 'CPP5' - 'CPP3' - 'CPP1' - 'CPPz' - 'CPP2' - 'CPP4' - 'CPP6' - 'TPP8' - 'TPP10' - 'PPO9' - 'PPO7' - 'PPO5' - 'PPO3' - 'PPO1' - 'PPOz' - 'PPO2' - 'PPO4' - 'PPO6' - 'PPO8' - 'PPO10' - 'POO9' - 'POO7' - 'POO5' - 'POO3' - 'POO1' - 'POOz' - 'POO2' - 'POO4' - 'POO6' - 'POO8' - 'POO10' - 'OI1' - 'OIz' - 'OI2' - }; + eeg1005 = { + 'Fp1' + 'Fpz' + 'Fp2' + 'AF9' + 'AF7' + 'AF5' + 'AF3' + 'AF1' + 'AFz' + 'AF2' + 'AF4' + 'AF6' + 'AF8' + 'AF10' + 'F9' + 'F7' + 'F5' + 'F3' + 'F1' + 'Fz' + 'F2' + 'F4' + 'F6' + 'F8' + 'F10' + 'FT9' + 'FT7' + 'FC5' + 'FC3' + 'FC1' + 'FCz' + 'FC2' + 'FC4' + 'FC6' + 'FT8' + 'FT10' + 'T9' + 'T7' + 'C5' + 'C3' + 'C1' + 'Cz' + 'C2' + 'C4' + 'C6' + 'T8' + 'T10' + 'TP9' + 'TP7' + 'CP5' + 'CP3' + 'CP1' + 'CPz' + 'CP2' + 'CP4' + 'CP6' + 'TP8' + 'TP10' + 'P9' + 'P7' + 'P5' + 'P3' + 'P1' + 'Pz' + 'P2' + 'P4' + 'P6' + 'P8' + 'P10' + 'PO9' + 'PO7' + 'PO5' + 'PO3' + 'PO1' + 'POz' + 'PO2' + 'PO4' + 'PO6' + 'PO8' + 'PO10' + 'O1' + 'Oz' + 'O2' + 'I1' + 'Iz' + 'I2' + 'AFp9h' + 'AFp7h' + 'AFp5h' + 'AFp3h' + 'AFp1h' + 'AFp2h' + 'AFp4h' + 'AFp6h' + 'AFp8h' + 'AFp10h' + 'AFF9h' + 'AFF7h' + 'AFF5h' + 'AFF3h' + 'AFF1h' + 'AFF2h' + 'AFF4h' + 'AFF6h' + 'AFF8h' + 'AFF10h' + 'FFT9h' + 'FFT7h' + 'FFC5h' + 'FFC3h' + 'FFC1h' + 'FFC2h' + 'FFC4h' + 'FFC6h' + 'FFT8h' + 'FFT10h' + 'FTT9h' + 'FTT7h' + 'FCC5h' + 'FCC3h' + 'FCC1h' + 'FCC2h' + 'FCC4h' + 'FCC6h' + 'FTT8h' + 'FTT10h' + 'TTP9h' + 'TTP7h' + 'CCP5h' + 'CCP3h' + 'CCP1h' + 'CCP2h' + 'CCP4h' + 'CCP6h' + 'TTP8h' + 'TTP10h' + 'TPP9h' + 'TPP7h' + 'CPP5h' + 'CPP3h' + 'CPP1h' + 'CPP2h' + 'CPP4h' + 'CPP6h' + 'TPP8h' + 'TPP10h' + 'PPO9h' + 'PPO7h' + 'PPO5h' + 'PPO3h' + 'PPO1h' + 'PPO2h' + 'PPO4h' + 'PPO6h' + 'PPO8h' + 'PPO10h' + 'POO9h' + 'POO7h' + 'POO5h' + 'POO3h' + 'POO1h' + 'POO2h' + 'POO4h' + 'POO6h' + 'POO8h' + 'POO10h' + 'OI1h' + 'OI2h' + 'Fp1h' + 'Fp2h' + 'AF9h' + 'AF7h' + 'AF5h' + 'AF3h' + 'AF1h' + 'AF2h' + 'AF4h' + 'AF6h' + 'AF8h' + 'AF10h' + 'F9h' + 'F7h' + 'F5h' + 'F3h' + 'F1h' + 'F2h' + 'F4h' + 'F6h' + 'F8h' + 'F10h' + 'FT9h' + 'FT7h' + 'FC5h' + 'FC3h' + 'FC1h' + 'FC2h' + 'FC4h' + 'FC6h' + 'FT8h' + 'FT10h' + 'T9h' + 'T7h' + 'C5h' + 'C3h' + 'C1h' + 'C2h' + 'C4h' + 'C6h' + 'T8h' + 'T10h' + 'TP9h' + 'TP7h' + 'CP5h' + 'CP3h' + 'CP1h' + 'CP2h' + 'CP4h' + 'CP6h' + 'TP8h' + 'TP10h' + 'P9h' + 'P7h' + 'P5h' + 'P3h' + 'P1h' + 'P2h' + 'P4h' + 'P6h' + 'P8h' + 'P10h' + 'PO9h' + 'PO7h' + 'PO5h' + 'PO3h' + 'PO1h' + 'PO2h' + 'PO4h' + 'PO6h' + 'PO8h' + 'PO10h' + 'O1h' + 'O2h' + 'I1h' + 'I2h' + 'AFp9' + 'AFp7' + 'AFp5' + 'AFp3' + 'AFp1' + 'AFpz' + 'AFp2' + 'AFp4' + 'AFp6' + 'AFp8' + 'AFp10' + 'AFF9' + 'AFF7' + 'AFF5' + 'AFF3' + 'AFF1' + 'AFFz' + 'AFF2' + 'AFF4' + 'AFF6' + 'AFF8' + 'AFF10' + 'FFT9' + 'FFT7' + 'FFC5' + 'FFC3' + 'FFC1' + 'FFCz' + 'FFC2' + 'FFC4' + 'FFC6' + 'FFT8' + 'FFT10' + 'FTT9' + 'FTT7' + 'FCC5' + 'FCC3' + 'FCC1' + 'FCCz' + 'FCC2' + 'FCC4' + 'FCC6' + 'FTT8' + 'FTT10' + 'TTP9' + 'TTP7' + 'CCP5' + 'CCP3' + 'CCP1' + 'CCPz' + 'CCP2' + 'CCP4' + 'CCP6' + 'TTP8' + 'TTP10' + 'TPP9' + 'TPP7' + 'CPP5' + 'CPP3' + 'CPP1' + 'CPPz' + 'CPP2' + 'CPP4' + 'CPP6' + 'TPP8' + 'TPP10' + 'PPO9' + 'PPO7' + 'PPO5' + 'PPO3' + 'PPO1' + 'PPOz' + 'PPO2' + 'PPO4' + 'PPO6' + 'PPO8' + 'PPO10' + 'POO9' + 'POO7' + 'POO5' + 'POO3' + 'POO1' + 'POOz' + 'POO2' + 'POO4' + 'POO6' + 'POO8' + 'POO10' + 'OI1' + 'OIz' + 'OI2' + }; - % Add also alternative labels that are used in some systems - ext1020 = cat(1, eeg1005, {'A1' 'A2' 'M1' 'M2' 'T3' 'T4' 'T5' 'T6'}'); + % Add also alternative labels that are used in some systems + ext1020 = cat(1, eeg1005, {'A1' 'A2' 'M1' 'M2' 'T3' 'T4' 'T5' 'T6'}'); - % This is to account for all variants of case in 1020 systems - ext1020 = unique(cat(1, ext1020, upper(ext1020), lower(ext1020))); + % This is to account for all variants of case in 1020 systems + ext1020 = unique(cat(1, ext1020, upper(ext1020), lower(ext1020))); end % if iseeg || isext if isbiosemi - biosemi128 = { - 'A1' - 'A2' - 'A3' - 'A4' - 'A5' - 'A6' - 'A7' - 'A8' - 'A9' - 'A10' - 'A11' - 'A12' - 'A13' - 'A14' - 'A15' - 'A16' - 'A17' - 'A18' - 'A19' - 'A20' - 'A21' - 'A22' - 'A23' - 'A24' - 'A25' - 'A26' - 'A27' - 'A28' - 'A29' - 'A30' - 'A31' - 'A32' - 'B1' - 'B2' - 'B3' - 'B4' - 'B5' - 'B6' - 'B7' - 'B8' - 'B9' - 'B10' - 'B11' - 'B12' - 'B13' - 'B14' - 'B15' - 'B16' - 'B17' - 'B18' - 'B19' - 'B20' - 'B21' - 'B22' - 'B23' - 'B24' - 'B25' - 'B26' - 'B27' - 'B28' - 'B29' - 'B30' - 'B31' - 'B32' - 'C1' - 'C2' - 'C3' - 'C4' - 'C5' - 'C6' - 'C7' - 'C8' - 'C9' - 'C10' - 'C11' - 'C12' - 'C13' - 'C14' - 'C15' - 'C16' - 'C17' - 'C18' - 'C19' - 'C20' - 'C21' - 'C22' - 'C23' - 'C24' - 'C25' - 'C26' - 'C27' - 'C28' - 'C29' - 'C30' - 'C31' - 'C32' - 'D1' - 'D2' - 'D3' - 'D4' - 'D5' - 'D6' - 'D7' - 'D8' - 'D9' - 'D10' - 'D11' - 'D12' - 'D13' - 'D14' - 'D15' - 'D16' - 'D17' - 'D18' - 'D19' - 'D20' - 'D21' - 'D22' - 'D23' - 'D24' - 'D25' - 'D26' - 'D27' - 'D28' - 'D29' - 'D30' - 'D31' - 'D32' - }; + biosemi64 = { + 'A1' + 'A2' + 'A3' + 'A4' + 'A5' + 'A6' + 'A7' + 'A8' + 'A9' + 'A10' + 'A11' + 'A12' + 'A13' + 'A14' + 'A15' + 'A16' + 'A17' + 'A18' + 'A19' + 'A20' + 'A21' + 'A22' + 'A23' + 'A24' + 'A25' + 'A26' + 'A27' + 'A28' + 'A29' + 'A30' + 'A31' + 'A32' + 'B1' + 'B2' + 'B3' + 'B4' + 'B5' + 'B6' + 'B7' + 'B8' + 'B9' + 'B10' + 'B11' + 'B12' + 'B13' + 'B14' + 'B15' + 'B16' + 'B17' + 'B18' + 'B19' + 'B20' + 'B21' + 'B22' + 'B23' + 'B24' + 'B25' + 'B26' + 'B27' + 'B28' + 'B29' + 'B30' + 'B31' + 'B32' + }; + + biosemi128 = { + 'A1' + 'A2' + 'A3' + 'A4' + 'A5' + 'A6' + 'A7' + 'A8' + 'A9' + 'A10' + 'A11' + 'A12' + 'A13' + 'A14' + 'A15' + 'A16' + 'A17' + 'A18' + 'A19' + 'A20' + 'A21' + 'A22' + 'A23' + 'A24' + 'A25' + 'A26' + 'A27' + 'A28' + 'A29' + 'A30' + 'A31' + 'A32' + 'B1' + 'B2' + 'B3' + 'B4' + 'B5' + 'B6' + 'B7' + 'B8' + 'B9' + 'B10' + 'B11' + 'B12' + 'B13' + 'B14' + 'B15' + 'B16' + 'B17' + 'B18' + 'B19' + 'B20' + 'B21' + 'B22' + 'B23' + 'B24' + 'B25' + 'B26' + 'B27' + 'B28' + 'B29' + 'B30' + 'B31' + 'B32' + 'C1' + 'C2' + 'C3' + 'C4' + 'C5' + 'C6' + 'C7' + 'C8' + 'C9' + 'C10' + 'C11' + 'C12' + 'C13' + 'C14' + 'C15' + 'C16' + 'C17' + 'C18' + 'C19' + 'C20' + 'C21' + 'C22' + 'C23' + 'C24' + 'C25' + 'C26' + 'C27' + 'C28' + 'C29' + 'C30' + 'C31' + 'C32' + 'D1' + 'D2' + 'D3' + 'D4' + 'D5' + 'D6' + 'D7' + 'D8' + 'D9' + 'D10' + 'D11' + 'D12' + 'D13' + 'D14' + 'D15' + 'D16' + 'D17' + 'D18' + 'D19' + 'D20' + 'D21' + 'D22' + 'D23' + 'D24' + 'D25' + 'D26' + 'D27' + 'D28' + 'D29' + 'D30' + 'D31' + 'D32' + }; + + biosemi256 = { + 'A1' + 'A2' + 'A3' + 'A4' + 'A5' + 'A6' + 'A7' + 'A8' + 'A9' + 'A10' + 'A11' + 'A12' + 'A13' + 'A14' + 'A15' + 'A16' + 'A17' + 'A18' + 'A19' + 'A20' + 'A21' + 'A22' + 'A23' + 'A24' + 'A25' + 'A26' + 'A27' + 'A28' + 'A29' + 'A30' + 'A31' + 'A32' + 'B1' + 'B2' + 'B3' + 'B4' + 'B5' + 'B6' + 'B7' + 'B8' + 'B9' + 'B10' + 'B11' + 'B12' + 'B13' + 'B14' + 'B15' + 'B16' + 'B17' + 'B18' + 'B19' + 'B20' + 'B21' + 'B22' + 'B23' + 'B24' + 'B25' + 'B26' + 'B27' + 'B28' + 'B29' + 'B30' + 'B31' + 'B32' + 'C1' + 'C2' + 'C3' + 'C4' + 'C5' + 'C6' + 'C7' + 'C8' + 'C9' + 'C10' + 'C11' + 'C12' + 'C13' + 'C14' + 'C15' + 'C16' + 'C17' + 'C18' + 'C19' + 'C20' + 'C21' + 'C22' + 'C23' + 'C24' + 'C25' + 'C26' + 'C27' + 'C28' + 'C29' + 'C30' + 'C31' + 'C32' + 'D1' + 'D2' + 'D3' + 'D4' + 'D5' + 'D6' + 'D7' + 'D8' + 'D9' + 'D10' + 'D11' + 'D12' + 'D13' + 'D14' + 'D15' + 'D16' + 'D17' + 'D18' + 'D19' + 'D20' + 'D21' + 'D22' + 'D23' + 'D24' + 'D25' + 'D26' + 'D27' + 'D28' + 'D29' + 'D30' + 'D31' + 'D32' + 'E1' + 'E2' + 'E3' + 'E4' + 'E5' + 'E6' + 'E7' + 'E8' + 'E9' + 'E10' + 'E11' + 'E12' + 'E13' + 'E14' + 'E15' + 'E16' + 'E17' + 'E18' + 'E19' + 'E20' + 'E21' + 'E22' + 'E23' + 'E24' + 'E25' + 'E26' + 'E27' + 'E28' + 'E29' + 'E30' + 'E31' + 'E32' + 'F1' + 'F2' + 'F3' + 'F4' + 'F5' + 'F6' + 'F7' + 'F8' + 'F9' + 'F10' + 'F11' + 'F12' + 'F13' + 'F14' + 'F15' + 'F16' + 'F17' + 'F18' + 'F19' + 'F20' + 'F21' + 'F22' + 'F23' + 'F24' + 'F25' + 'F26' + 'F27' + 'F28' + 'F29' + 'F30' + 'F31' + 'F32' + 'G1' + 'G2' + 'G3' + 'G4' + 'G5' + 'G6' + 'G7' + 'G8' + 'G9' + 'G10' + 'G11' + 'G12' + 'G13' + 'G14' + 'G15' + 'G16' + 'G17' + 'G18' + 'G19' + 'G20' + 'G21' + 'G22' + 'G23' + 'G24' + 'G25' + 'G26' + 'G27' + 'G28' + 'G29' + 'G30' + 'G31' + 'G32' + 'H1' + 'H2' + 'H3' + 'H4' + 'H5' + 'H6' + 'H7' + 'H8' + 'H9' + 'H10' + 'H11' + 'H12' + 'H13' + 'H14' + 'H15' + 'H16' + 'H17' + 'H18' + 'H19' + 'H20' + 'H21' + 'H22' + 'H23' + 'H24' + 'H25' + 'H26' + 'H27' + 'H28' + 'H29' + 'H30' + 'H31' + 'H32' + }; - biosemi256 = { - 'A1' - 'A2' - 'A3' - 'A4' - 'A5' - 'A6' - 'A7' - 'A8' - 'A9' - 'A10' - 'A11' - 'A12' - 'A13' - 'A14' - 'A15' - 'A16' - 'A17' - 'A18' - 'A19' - 'A20' - 'A21' - 'A22' - 'A23' - 'A24' - 'A25' - 'A26' - 'A27' - 'A28' - 'A29' - 'A30' - 'A31' - 'A32' - 'B1' - 'B2' - 'B3' - 'B4' - 'B5' - 'B6' - 'B7' - 'B8' - 'B9' - 'B10' - 'B11' - 'B12' - 'B13' - 'B14' - 'B15' - 'B16' - 'B17' - 'B18' - 'B19' - 'B20' - 'B21' - 'B22' - 'B23' - 'B24' - 'B25' - 'B26' - 'B27' - 'B28' - 'B29' - 'B30' - 'B31' - 'B32' - 'C1' - 'C2' - 'C3' - 'C4' - 'C5' - 'C6' - 'C7' - 'C8' - 'C9' - 'C10' - 'C11' - 'C12' - 'C13' - 'C14' - 'C15' - 'C16' - 'C17' - 'C18' - 'C19' - 'C20' - 'C21' - 'C22' - 'C23' - 'C24' - 'C25' - 'C26' - 'C27' - 'C28' - 'C29' - 'C30' - 'C31' - 'C32' - 'D1' - 'D2' - 'D3' - 'D4' - 'D5' - 'D6' - 'D7' - 'D8' - 'D9' - 'D10' - 'D11' - 'D12' - 'D13' - 'D14' - 'D15' - 'D16' - 'D17' - 'D18' - 'D19' - 'D20' - 'D21' - 'D22' - 'D23' - 'D24' - 'D25' - 'D26' - 'D27' - 'D28' - 'D29' - 'D30' - 'D31' - 'D32' - 'E1' - 'E2' - 'E3' - 'E4' - 'E5' - 'E6' - 'E7' - 'E8' - 'E9' - 'E10' - 'E11' - 'E12' - 'E13' - 'E14' - 'E15' - 'E16' - 'E17' - 'E18' - 'E19' - 'E20' - 'E21' - 'E22' - 'E23' - 'E24' - 'E25' - 'E26' - 'E27' - 'E28' - 'E29' - 'E30' - 'E31' - 'E32' - 'F1' - 'F2' - 'F3' - 'F4' - 'F5' - 'F6' - 'F7' - 'F8' - 'F9' - 'F10' - 'F11' - 'F12' - 'F13' - 'F14' - 'F15' - 'F16' - 'F17' - 'F18' - 'F19' - 'F20' - 'F21' - 'F22' - 'F23' - 'F24' - 'F25' - 'F26' - 'F27' - 'F28' - 'F29' - 'F30' - 'F31' - 'F32' - 'G1' - 'G2' - 'G3' - 'G4' - 'G5' - 'G6' - 'G7' - 'G8' - 'G9' - 'G10' - 'G11' - 'G12' - 'G13' - 'G14' - 'G15' - 'G16' - 'G17' - 'G18' - 'G19' - 'G20' - 'G21' - 'G22' - 'G23' - 'G24' - 'G25' - 'G26' - 'G27' - 'G28' - 'G29' - 'G30' - 'G31' - 'G32' - 'H1' - 'H2' - 'H3' - 'H4' - 'H5' - 'H6' - 'H7' - 'H8' - 'H9' - 'H10' - 'H11' - 'H12' - 'H13' - 'H14' - 'H15' - 'H16' - 'H17' - 'H18' - 'H19' - 'H20' - 'H21' - 'H22' - 'H23' - 'H24' - 'H25' - 'H26' - 'H27' - 'H28' - 'H29' - 'H30' - 'H31' - 'H32' - }; end % if isbiosemi if isegi - egi256 = cell(256, 1); - for i = 1:256 - egi256{i} = sprintf('e%d', i); - end + egi256 = cell(256, 1); + for i = 1:256 + egi256{i} = sprintf('e%d', i); + end - % the others are subsets - egi32 = egi256(1:32); - egi64 = egi256(1:64); - egi128 = egi256(1:128); + % the others are subsets + egi32 = egi256(1:32); + egi64 = egi256(1:64); + egi128 = egi256(1:128); end % if isegi % search for the requested definition of channel labels if exist(type, 'var') - label = eval(type); - label = label(:); + label = eval(type); + label = label(:); else - error('the requested sensor type is not supported'); + error('the requested sensor type is not supported'); +end + +% remember the current input and output arguments, so that they can be +% reused on a subsequent call in case the same input argument is given +current_argout = {label}; +if isempty(previous_argin) + previous_argin = current_argin; + previous_argout = current_argout; end diff --git a/external/fieldtrip/private/senstype.m b/external/fieldtrip/private/senstype.m index a00eaba..df3a355 100644 --- a/external/fieldtrip/private/senstype.m +++ b/external/fieldtrip/private/senstype.m @@ -12,6 +12,7 @@ % The output type can be any of the following % 'electrode' % 'magnetometer' +% 'biosemi64' % 'biosemi128' % 'biosemi256' % 'bti148' @@ -58,6 +59,21 @@ % Copyright (C) 2007-2008, Robert Oostenveld % % $Log: senstype.m,v $ +% Revision 1.19 2009/07/29 08:04:38 roboos +% cleaned up the code, no functional change +% +% Revision 1.18 2009/07/28 11:16:23 roboos +% removed keyboard statement, thanks to Jurrian +% +% Revision 1.17 2009/07/28 10:17:41 roboos +% make distinction between only label, label+pnt and label+pnt+ori +% +% Revision 1.16 2009/07/27 16:04:51 roboos +% improved distinction between eeg and meg, fixes problem with biosemi-eeg being detected as "ctf" due to reference channel match +% +% Revision 1.15 2009/06/19 16:51:50 vlalit +% Added biosemi64 system of Diane Whitmer, I don't know how generic it is. +% % Revision 1.14 2009/05/07 13:34:09 roboos % added ctf64 % @@ -138,14 +154,20 @@ % preferably the structure specifies its own type type = sens.type; +elseif issubfield(sens, 'orig.FileHeader') && issubfield(sens, 'orig.VarHeader') + % this is a complete header that was read from a Plexon *.nex file using read_plexon_nex + type = 'plexon'; + else % start with unknown, then try to determine the proper type by looking at the labels type = 'unknown'; - if isfield(sens, 'label') + if isfield(sens, 'label') && isfield(sens, 'pnt') && isfield(sens, 'ori') % probably this is MEG, determine the type of magnetometer/gradiometer system % note that the order here is important: first check whether it matches a 275 channel system, then a 151 channel system, since the 151 channels are a subset of the 275 - if (mean(ismember(senslabel('ctf275'), sens.label)) > 0.8) || (mean(ismember(senslabel('ctfheadloc'), sens.label)) > 0.8) + if (mean(ismember(senslabel('ctf275'), sens.label)) > 0.8) + type = 'ctf275'; + elseif (mean(ismember(senslabel('ctfheadloc'), sens.label)) > 0.8) % look at the head localization channels type = 'ctf275'; elseif (mean(ismember(senslabel('ctf151'), sens.label)) > 0.8) type = 'ctf151'; @@ -177,12 +199,18 @@ type = 'ctf'; % it might be 151 or 275 channels elseif isfield(sens, 'pnt') && isfield(sens, 'ori') && numel(sens.label)==numel(sens.pnt) type = 'magnetometer'; - elseif isfield(sens, 'pnt') && isfield(sens, 'ori') + else type = 'meg'; - elseif (mean(ismember(senslabel('biosemi256'), sens.label)) > 0.8) + end + + elseif isfield(sens, 'label') && isfield(sens, 'pnt') && ~isfield(sens, 'ori') + % probably this is EEG + if (mean(ismember(senslabel('biosemi256'), sens.label)) > 0.8) type = 'biosemi256'; elseif (mean(ismember(senslabel('biosemi128'), sens.label)) > 0.8) type = 'biosemi128'; + elseif (mean(ismember(senslabel('biosemi64'), sens.label)) > 0.8) + type = 'biosemi64'; elseif (mean(ismember(senslabel('egi256'), sens.label)) > 0.8) type = 'egi256'; elseif (mean(ismember(senslabel('egi128'), sens.label)) > 0.8) @@ -193,26 +221,72 @@ type = 'egi32'; elseif (sum(ismember(sens.label, senslabel('eeg1005'))) > 10) % Otherwise it's not even worth recognizing type = 'ext1020'; - elseif isfield(sens, 'label') && isfield(sens, 'pnt') && ~isfield(sens, 'ori') % looks like EEG + else type = 'electrode'; end - end + elseif isfield(sens, 'label') && ~isfield(sens, 'pnt') && ~isfield(sens, 'ori') + % look only at the channel labels + if (mean(ismember(senslabel('ctf275'), sens.label)) > 0.8) + type = 'ctf275'; + elseif (mean(ismember(senslabel('ctfheadloc'), sens.label)) > 0.8) % look at the head localization channels + type = 'ctf275'; + elseif (mean(ismember(senslabel('ctf151'), sens.label)) > 0.8) + type = 'ctf151'; + elseif (mean(ismember(senslabel('ctf64'), sens.label)) > 0.8) + type = 'ctf64'; + elseif (mean(ismember(senslabel('ctf275_planar'), sens.label)) > 0.8) + type = 'ctf275_planar'; + elseif (mean(ismember(senslabel('ctf151_planar'), sens.label)) > 0.8) + type = 'ctf151_planar'; + elseif (mean(ismember(senslabel('bti248'), sens.label)) > 0.8) + type = 'bti248'; + elseif (mean(ismember(senslabel('bti148'), sens.label)) > 0.8) + type = 'bti148'; + elseif (mean(ismember(senslabel('bti248_planar'), sens.label)) > 0.8) + type = 'bti248_planar'; + elseif (mean(ismember(senslabel('bti148_planar'), sens.label)) > 0.8) + type = 'bti148_planar'; + elseif (mean(ismember(senslabel('neuromag306'), sens.label)) > 0.8) + type = 'neuromag306'; + elseif (mean(ismember(senslabel('neuromag306alt'),sens.label)) > 0.8) % an alternative set without spaces in the name + type = 'neuromag306'; + elseif (mean(ismember(senslabel('neuromag122'), sens.label)) > 0.8) + type = 'neuromag122'; + elseif (mean(ismember(senslabel('neuromag122alt'),sens.label)) > 0.8) % an alternative set without spaces in the name + type = 'neuromag122'; + elseif (mean(ismember(senslabel('biosemi256'), sens.label)) > 0.8) + type = 'biosemi256'; + elseif (mean(ismember(senslabel('biosemi128'), sens.label)) > 0.8) + type = 'biosemi128'; + elseif (mean(ismember(senslabel('biosemi64'), sens.label)) > 0.8) + type = 'biosemi64'; + elseif (mean(ismember(senslabel('egi256'), sens.label)) > 0.8) + type = 'egi256'; + elseif (mean(ismember(senslabel('egi128'), sens.label)) > 0.8) + type = 'egi128'; + elseif (mean(ismember(senslabel('egi64'), sens.label)) > 0.8) + type = 'egi64'; + elseif (mean(ismember(senslabel('egi32'), sens.label)) > 0.8) + type = 'egi32'; + elseif (sum(ismember(sens.label, senslabel('eeg1005'))) > 10) % Otherwise it's not even worth recognizing + type = 'ext1020'; + elseif any(ismember(senslabel('btiref'), sens.label)) + type = 'bti'; % it might be 148 or 248 channels + elseif any(ismember(senslabel('ctfref'), sens.label)) + type = 'ctf'; % it might be 151 or 275 channels + end + + end % look at label, ori and/or pnt end % if isfield(sens, 'type') -% use an alternative approach if it is still unknown -if strcmp(type, 'unknown') && issubfield(sens, 'orig.FileHeader') && issubfield(sens, 'orig.VarHeader') - % this is a complete header that was read from a Plexon *.nex file using read_plexon_nex - type = 'plexon'; -end - if ~isempty(desired) % return a boolean flag switch desired case 'eeg' - type = any(strcmp(type, {'eeg' 'electrode' 'biosemi128' 'biosemi256' 'egi32' 'egi64' 'egi128' 'egi256' 'ext1020'})); + type = any(strcmp(type, {'eeg' 'electrode' 'biosemi64' 'biosemi128' 'biosemi256' 'egi32' 'egi64' 'egi128' 'egi256' 'ext1020'})); case 'biosemi' - type = any(strcmp(type, {'biosemi128' 'biosemi256'})); + type = any(strcmp(type, {'biosemi64' 'biosemi128' 'biosemi256'})); case 'egi' type = any(strcmp(type, {'egi64' 'egi128' 'egi256'})); case 'meg' @@ -233,8 +307,8 @@ type = any(strcmp(type, {'neuromag122' 'neuromag306' 'ctf151_planar' 'ctf275_planar' 'bti148_planar' 'bti248_planar' 'yokogawa160_planar'})); otherwise type = any(strcmp(type, desired)); - end -end + end % switch desired +end % detemine the correspondence to the desired type % remember the current input and output arguments, so that they can be % reused on a subsequent call in case the same input argument is given @@ -244,4 +318,4 @@ previous_argout = current_argout; end -return % voltype main() +return % senstype main() diff --git a/external/fieldtrip/private/singleplotER.m b/external/fieldtrip/private/singleplotER.m index 440a576..fd94614 100644 --- a/external/fieldtrip/private/singleplotER.m +++ b/external/fieldtrip/private/singleplotER.m @@ -22,7 +22,7 @@ % cfg.ylim = 'maxmin' or [ymin ymax] (default = 'maxmin') % cfg.channel = Nx1 cell-array with selection of channels (default = 'all'), % see CHANNELSELECTION for details -% cfg.cohrefchannel = Name of reference-channel, only for visualizing coherence +% cfg.cohrefchannel = name of reference channel for visualising coherence, can be 'gui' % cfg.baseline = 'yes','no' or [time1 time2] (default = 'no'), see TIMELOCKBASELINE or FREQBASELINE % cfg.baselinetype = 'absolute' or 'relative' (default = 'absolute') % cfg.trials = 'all' or a selection given as a 1xN vector (default = 'all') @@ -54,6 +54,18 @@ % Copyright (C) 2003-2006, Ole Jensen % % $Log: singleplotER.m,v $ +% Revision 1.41 2009/07/14 13:51:31 roboos +% some small changes related to the new interactive data selection +% +% Revision 1.40 2009/07/14 13:21:09 roboos +% changed the interactive plotting: instead of using plotSelection it now uses the selection function from the new plotting module (select_range and select_channel) and uses a local subfunction to update the cfg and call the next figure +% +% Revision 1.39 2009/07/13 13:25:52 crimic +% inserted new plotting tool functions +% +% Revision 1.38 2009/06/17 13:44:52 roboos +% cleaned up help +% % Revision 1.37 2009/05/12 18:21:37 roboos % added handling of cfg.cohrefchannel='gui' for manual/interactive selection % @@ -288,12 +300,13 @@ cfg.layout = lay; plot_lay(cfg.layout, 'box', false); title('Select the reference channel by clicking on it...'); - info = []; + % add the channel information to the figure + info = guidata(h); info.x = lay.pos(:,1); info.y = lay.pos(:,2); info.label = lay.label; guidata(h, info); - set(gcf, 'WindowButtonUpFcn', {@select_channel, 'callback', {@select_cohrefchannel, cfg, varargin{:}}}); + set(gcf, 'WindowButtonUpFcn', {@select_channel, 'callback', {@select_singleplotER, cfg, varargin{:}}}); return end @@ -369,17 +382,17 @@ chansel = match_str(varargin{k-1}.label, cfg.channel); end - % cfg.maskparameter only possible for single channel - if length(chansel) > 1 && ~isempty(cfg.maskparameter) - warning('no masking possible for average over multiple channels') - masking = 0; - elseif length(chansel) == 1 && ~isempty(cfg.maskparameter) - masking = 1; - end - % Average across selected channels: P = squeeze(mean(P(chansel,:), 1)); + % select mask + if ~isempty(cfg.maskparameter) %&& masking + M = varargin{1}.(cfg.maskparameter); % mask always from only first dataset + M = squeeze(mean(M(chansel,:), 1)); + else + M = []; + end + % Update ymin and ymax for the current data set: if strcmp(cfg.ylim, 'maxmin') ind_xmin = nearest(varargin{k-1}.(cfg.xparam), xmin); @@ -392,96 +405,27 @@ end style = GRAPHCOLOR(k); - plot(varargin{k-1}.(cfg.xparam), P, style); -end - -% select mask -if ~isempty(cfg.maskparameter) && masking - M = varargin{1}.(cfg.maskparameter); % mask always from only first dataset - mask = squeeze(M(chansel,:)); -end - -% Add mask box -if ~isempty(cfg.maskparameter) && masking - % determine how many boxes - foundbeg = 0; - foundend = 0; - beg = []; - eind = []; - for i = 1:length(mask) - if ~foundbeg && mask(i) == 1 - beg(length(beg)+1) = i; - foundbeg = 1; - foundend = 0; - elseif ~foundbeg && mask(i) == 0 - %next - elseif ~foundend && mask(i) == 1 - %next - elseif ~foundend && mask(i) == 0 - eind(length(eind)+1) = i-1; - foundend = 1; - foundbeg = 0; - end - end - if length(eind) == length(beg)-1 - eind(length(eind)+1) = length(mask); - end - numbox = length(beg); - for i = 1:numbox - xmaskmin = varargin{1}.(cfg.xparam)(beg(i)); - xmaskmax = varargin{1}.(cfg.xparam)(eind(i)); - hs = patch([xmaskmin xmaskmax xmaskmax xmaskmin xmaskmin],[ymin ymin ymax ymax ymin], [.6 .6 .6]); - set(hs, 'EdgeColor', 'none'); - end + plot_vector(varargin{k-1}.(cfg.xparam), P, 'style', style, 'highlight', M); end % Set xlim and ylim: xlim([xmin xmax]); ylim([ymin ymax]); -% Make the figure interactive: +% Make the figure interactive if strcmp(cfg.interactive, 'yes') - userData.hFigure = gcf; - userData.hAxes = gca; - for i=1:1 % no multiple selection regions - userData.hSelection{i} = plot(0,0); - set(userData.hSelection{i}, 'XData', mean([xmin xmax])); - set(userData.hSelection{i}, 'YData', mean([ymin ymax])); - set(userData.hSelection{i}, 'Color', [0 0 0]); - set(userData.hSelection{i}, 'EraseMode', 'xor'); - set(userData.hSelection{i}, 'LineStyle', '--'); - set(userData.hSelection{i}, 'LineWidth', 1.5); - set(userData.hSelection{i}, 'Visible', 'on'); - userData.range{i} = []; - end - userData.iSelection = 0; - userData.plotType = 'singleplot'; - userData.selecting = 0; - userData.selectionType = ''; - userData.selectAxes = 'x'; - userData.lastClick = []; - userData.cfg = cfg; - userData.data = varargin; - userData.chanX = []; - userData.chanY = []; - userData.chanLabels = []; - tag = sprintf('%.5f', 10000 * rand(1)); - set(gcf, 'Renderer', cfg.renderer); - set(gcf, 'Tag', tag); - set(gcf, 'UserData', userData); - set(gcf, 'WindowButtonMotionFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 0);']); - set(gcf, 'WindowButtonDownFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 1);']); - set(gcf, 'WindowButtonUpFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 2);']); + set(gcf, 'WindowButtonUpFcn', {@select_range, 'multiple', false, 'yrange', false, 'callback', {@select_topoplotER, cfg, varargin{:}}, 'event', 'WindowButtonUpFcn'}); + set(gcf, 'WindowButtonDownFcn', {@select_range, 'multiple', false, 'yrange', false, 'callback', {@select_topoplotER, cfg, varargin{:}}, 'event', 'WindowButtonDownFcn'}); + set(gcf, 'WindowButtonMotionFcn', {@select_range, 'multiple', false, 'yrange', false, 'callback', {@select_topoplotER, cfg, varargin{:}}, 'event', 'WindowButtonMotionFcn'}); end % Create title text containing channel name(s) and channel number(s): if length(chansel) == 1 - t=[char(cfg.channel) ' / ' num2str(chansel) ]; + t = [char(cfg.channel) ' / ' num2str(chansel) ]; else - t=sprintf('mean(%0s)', join(',',cfg.channel)); + t = sprintf('mean(%0s)', join(',', cfg.channel)); end - -h=title(t,'fontsize', cfg.fontsize); +h = title(t,'fontsize', cfg.fontsize); % get the output cfg cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); @@ -490,7 +434,7 @@ % SUBFUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function t = join(separator,cells) -if length(cells)==0 +if isempty(cells) t = ''; return; end @@ -501,23 +445,25 @@ end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function l = cellstrmatch(str,strlist) -l = []; -for k=1:length(strlist) - if strcmp(char(str),char(strlist(k))) - l = [l k]; - end -end - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -% this function is called by select_channel +% SUBFUNCTION which is called by select_channel in case cfg.cohrefchannel='gui' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function select_cohrefchannel(label, cfg, varargin) -fprintf('selected "%s" as reference channel\n', label); +function select_singleplotER(label, cfg, varargin) cfg.cohrefchannel = label; -figure +fprintf('selected cfg.cohrefchannel = ''%s''\n', cfg.cohrefchannel); +p = get(gcf, 'Position'); +f = figure; +set(f, 'Position', p); singleplotER(cfg, varargin{:}); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION which is called after selecting a time range +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function select_topoplotER(range, cfg, varargin) +cfg.comment = 'auto'; +cfg.yparam = []; +cfg.xlim = range(1:2); +fprintf('selected cfg.xlim = [%f %f]\n', cfg.xlim(1), cfg.xlim(2)); +p = get(gcf, 'Position'); +f = figure; +set(f, 'Position', p); +topoplotER(cfg, varargin{:}); diff --git a/external/fieldtrip/private/singleplotTFR.m b/external/fieldtrip/private/singleplotTFR.m index c3a6479..d8ac05b 100644 --- a/external/fieldtrip/private/singleplotTFR.m +++ b/external/fieldtrip/private/singleplotTFR.m @@ -7,13 +7,13 @@ % singleplotTFR(cfg,data) % % The data can be a time-frequency representation of power that was -% computed using the FREQANALYSIS function. +% computed using the FREQANALYSIS function. % % The configuration can have the following parameters: % cfg.xparam = field to be plotted on x-axis, e.g. 'time' (default depends on data.dimord) % cfg.yparam = field to be plotted on y-axis, e.g. 'freq' (default depends on data.dimord) % cfg.zparam = field to be plotted on y-axis, e.g. 'powspcrtrm' (default depends on data.dimord) -% cfg.maskparameter = field in the data to be used for opacity masking of data +% cfg.maskparameter = field in the data to be used for opacity masking of data % (not possible for mean over multiple channels) % cfg.xlim = 'maxmin' or [xmin xmax] (default = 'maxmin') % cfg.ylim = 'maxmin' or [ymin ymax] (default = 'maxmin') @@ -23,15 +23,15 @@ % cfg.trials = 'all' or a selection given as a 1xN vector (default = 'all') % cfg.channel = Nx1 cell-array with selection of channels (default = 'all'), % see CHANNELSELECTION for details -% cfg.cohrefchannel = Name of reference-channel, only for visualizing coherence +% cfg.cohrefchannel = name of reference channel for visualising coherence, can be 'gui' % cfg.fontsize = font size of title (default = 8) % cfg.colormap = any sized colormap, see COLORMAP % cfg.colorbar = 'yes', 'no' (default = 'yes') % cfg.interactive = Interactive plot 'yes' or 'no' (default = 'no') % In a interactive plot you can select areas and produce a new -% interactive plot when a selected area is clicked. Multiple areas +% interactive plot when a selected area is clicked. Multiple areas % can be selected by holding down the SHIFT key. -% cfg.renderer = 'painters', 'zbuffer',' opengl' or 'none' (default = 'opengl') +% cfg.renderer = 'painters', 'zbuffer',' opengl' or 'none' (default = []) % cfg.masknans = 'yes' or 'no' (default = 'yes') % % See also: @@ -44,6 +44,15 @@ % Copyright (C) 2005-2006, F.C. Donders Centre % % $Log: singleplotTFR.m,v $ +% Revision 1.37 2009/07/14 13:52:16 roboos +% changed the interactive plotting: instead of using plotSelection it now uses the selection function from the new plotting module (select_range and select_channel) and uses a local subfunction to update the cfg and call the next figure +% +% Revision 1.36 2009/07/14 13:27:25 roboos +% consistent handling of cfg.renderer, default is to let matlab decide +% +% Revision 1.35 2009/06/17 13:44:52 roboos +% cleaned up help +% % Revision 1.34 2009/05/12 18:47:23 roboos % added general suppoprt for cfg.cohrefchannel and % added handling of cfg.cohrefchannel='gui' for manual/interactive selection @@ -144,7 +153,7 @@ % % Revision 1.5 2006/03/14 08:09:22 roboos % added copyrigth and cvs log statement -% +% fieldtripdefs @@ -165,7 +174,7 @@ if ~isfield(cfg,'fontsize'), cfg.fontsize = 8; end if ~isfield(cfg,'colorbar'), cfg.colorbar = 'yes'; end if ~isfield(cfg,'interactive'), cfg.interactive = 'no'; end -if ~isfield(cfg,'renderer'), cfg.renderer = 'opengl'; end +if ~isfield(cfg,'renderer'), cfg.renderer = []; end if ~isfield(cfg,'masknans'), cfg.masknans = 'yes'; end if ~isfield(cfg,'maskparameter'), cfg.maskparameter = []; end @@ -209,39 +218,40 @@ cfg = checkconfig(cfg, 'renamed', {'channelname', 'channel'}); end - % Check for unconverted coherence spectrum data - if (strcmp(cfg.zparam,'cohspctrm')) && (isfield(data, 'labelcmb')) - % A reference channel is required: - if ~isfield(cfg,'cohrefchannel'), - error('no reference channel specified'); - end - - if strcmp(cfg.cohrefchannel, 'gui') - % Open a single figure with the channel layout, the user can click on a reference channel - h = clf; - lay = prepare_layout(cfg, data) - cfg.layout = lay; - plot_lay(lay, 'box', false); - title('Select the reference channel by clicking on it...'); - info = []; - info.x = lay.pos(:,1); - info.y = lay.pos(:,2); - info.label = lay.label; - guidata(h, info); - set(gcf, 'WindowButtonUpFcn', {@select_channel, 'callback', {@select_cohrefchannel, cfg, data}}); - return - end - - % Convert 2-dimensional channel matrix to a single dimension: - sel1 = strmatch(cfg.cohrefchannel, data.labelcmb(:,2)); - sel2 = strmatch(cfg.cohrefchannel, data.labelcmb(:,1)); - fprintf('selected %d channels for coherence\n', length(sel1)+length(sel2)); - data.cohspctrm = data.cohspctrm([sel1;sel2],:,:); - data.label = [data.labelcmb(sel1,1);data.labelcmb(sel2,2)]; - data.labelcmb = data.labelcmb([sel1;sel2],:); - data = rmfield(data, 'labelcmb'); +% Check for unconverted coherence spectrum data +if (strcmp(cfg.zparam,'cohspctrm')) && (isfield(data, 'labelcmb')) + % A reference channel is required: + if ~isfield(cfg,'cohrefchannel'), + error('no reference channel specified'); end + if strcmp(cfg.cohrefchannel, 'gui') + % Open a single figure with the channel layout, the user can click on a reference channel + h = clf; + lay = prepare_layout(cfg, data); + cfg.layout = lay; + plot_lay(lay, 'box', false); + title('Select the reference channel by clicking on it...'); + % add the channel information to the figure + info = guidata(h); + info.x = lay.pos(:,1); + info.y = lay.pos(:,2); + info.label = lay.label; + guidata(h, info); + set(gcf, 'WindowButtonUpFcn', {@select_channel, 'callback', {@select_singleplotTFR, cfg, data}}); + return + end + + % Convert 2-dimensional channel matrix to a single dimension: + sel1 = strmatch(cfg.cohrefchannel, data.labelcmb(:,2)); + sel2 = strmatch(cfg.cohrefchannel, data.labelcmb(:,1)); + fprintf('selected %d channels for coherence\n', length(sel1)+length(sel2)); + data.cohspctrm = data.cohspctrm([sel1;sel2],:,:); + data.label = [data.labelcmb(sel1,1);data.labelcmb(sel2,2)]; + data.labelcmb = data.labelcmb([sel1;sel2],:); + data = rmfield(data, 'labelcmb'); +end + cfg.channel = channelselection(cfg.channel, data.label); if isempty(cfg.channel) error('no channels selected'); @@ -360,72 +370,36 @@ end; if isequal(cfg.colorbar,'yes') -colorbar; + colorbar; end % Make the figure interactive: if strcmp(cfg.interactive, 'yes') - userData.hFigure = gcf; - userData.hAxes = gca; - for i=1:1 % no multiple selection regions - userData.hSelection{i} = plot(mean([xmin xmax]),mean([ymin ymax])); - set(userData.hSelection{i}, 'XData', mean([xmin xmax])); - set(userData.hSelection{i}, 'YData', mean([ymin ymax])); - set(userData.hSelection{i}, 'Color', [0 0 0]); - set(userData.hSelection{i}, 'EraseMode', 'xor'); - set(userData.hSelection{i}, 'LineStyle', '--'); - set(userData.hSelection{i}, 'LineWidth', 1.5); - set(userData.hSelection{i}, 'Visible', 'on'); - userData.range{i} = []; - end - userData.iSelection = 0; - userData.plotType = 'singleplot'; - userData.selecting = 0; - userData.selectionType = ''; - userData.selectAxes = 'xy'; - userData.lastClick = []; - userData.cfg = cfg; - userData.data = data; - userData.chanX = []; - userData.chanY = []; - userData.chanLabels = []; - tag = sprintf('%.5f', 10000 * rand(1)); - set(gcf, 'Renderer', cfg.renderer); - set(gcf, 'Tag', tag); - set(gcf, 'UserData', userData); - set(gcf, 'WindowButtonMotionFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 0);']); - set(gcf, 'WindowButtonDownFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 1);']); - set(gcf, 'WindowButtonUpFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 2);']); + set(gcf, 'WindowButtonUpFcn', {@select_range, 'multiple', false, 'callback', {@select_topoplotTFR, cfg, data}, 'event', 'WindowButtonUpFcn'}); + set(gcf, 'WindowButtonDownFcn', {@select_range, 'multiple', false, 'callback', {@select_topoplotTFR, cfg, data}, 'event', 'WindowButtonDownFcn'}); + set(gcf, 'WindowButtonMotionFcn', {@select_range, 'multiple', false, 'callback', {@select_topoplotTFR, cfg, data}, 'event', 'WindowButtonMotionFcn'}); end +% Create title text containing channel name(s) and channel number(s): if length(chansel) == 1 - title([char(cfg.channel) ' / ' num2str(chansel)]) + t = [char(cfg.channel) ' / ' num2str(chansel) ]; else - title(sprintf('mean(%0s)', join(',',cfg.channel))); + t = sprintf('mean(%0s)', join(',', cfg.channel)); end +h = title(t,'fontsize', cfg.fontsize); axis tight; hold off; % get the output cfg -cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); +cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function l = cellstrmatch(str,strlist) -l = []; -for k=1:length(strlist) - if strcmp(char(str),char(strlist(k))) - l = [l k]; - end -end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function t = join(separator,cells) -if length(cells)==0 +if isempty(cells) t = ''; return; end @@ -436,12 +410,26 @@ end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -% this function is called by select_channel +% SUBFUNCTION which is called by select_channel in case cfg.cohrefchannel='gui' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function select_cohrefchannel(label, cfg, varargin) -fprintf('selected "%s" as reference channel\n', label); +function select_singleplotTFR(label, cfg, varargin) cfg.cohrefchannel = label; -figure +fprintf('selected cfg.cohrefchannel = ''%s''\n', cfg.cohrefchannel); +p = get(gcf, 'Position'); +f = figure; +set(f, 'Position', p); singleplotTFR(cfg, varargin{:}); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION which is called after selecting a time range +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function select_topoplotTFR(range, cfg, varargin) +cfg.comment = 'auto'; +cfg.xlim = range(1:2); +cfg.ylim = range(3:4); +fprintf('selected cfg.xlim = [%f %f]\n', cfg.xlim(1), cfg.xlim(2)); +fprintf('selected cfg.ylim = [%f %f]\n', cfg.ylim(1), cfg.ylim(2)); +p = get(gcf, 'Position'); +f = figure; +set(f, 'Position', p); +topoplotER(cfg, varargin{:}); diff --git a/external/fieldtrip/private/sourceanalysis.m b/external/fieldtrip/private/sourceanalysis.m index 9d10d5c..8a1572f 100644 --- a/external/fieldtrip/private/sourceanalysis.m +++ b/external/fieldtrip/private/sourceanalysis.m @@ -22,7 +22,7 @@ % 'loreta' minimum norm estimation with smoothness constraint % 'rv' scan residual variance with single dipole % 'music' multiple signal classification -% 'mvlap' multivariate Laplace source localization +% 'mvl' multivariate Laplace source localization % The DICS and PCC methods are for frequency domain data, all other methods % are for time domain data. % @@ -167,6 +167,9 @@ % Copyright (c) 2003-2008, Robert Oostenveld, F.C. Donders Centre % % $Log: sourceanalysis.m,v $ +% Revision 1.140 2009/06/04 13:37:28 marvger +% changed mvlap name to mvl to make it consistent with literature +% % Revision 1.139 2009/05/20 16:53:30 marvger % added support for mvlap method (tentative) % @@ -916,7 +919,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % do time domain source reconstruction %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -elseif istimelock && any(strcmp(cfg.method, {'lcmv', 'sam', 'mne', 'loreta', 'rv', 'music', 'pcc', 'mvlap'})) +elseif istimelock && any(strcmp(cfg.method, {'lcmv', 'sam', 'mne', 'loreta', 'rv', 'music', 'pcc', 'mvl'})) % determine the size of the data Nsamples = size(data.avg,2); @@ -1156,7 +1159,7 @@ dip(i) = music(grid, sens, vol, squeeze(avg(i,:,:)), optarg{:}); end end - elseif strcmp(cfg.method, 'mvlap') + elseif strcmp(cfg.method, 'mvl') for i=1:Nrepetitions fprintf('estimating current density distribution for repetition %d\n', i); fns = fieldnames(cfg); @@ -1167,7 +1170,7 @@ optarg{n+1} = cfg.(fns{c}); n=n+2; end - dip(i) = mvlapestimate(grid, sens, vol, squeeze(avg(i,:,:)), optarg{:}); + dip(i) = mvlestimate(grid, sens, vol, squeeze(avg(i,:,:)), optarg{:}); end else error(sprintf('method ''%s'' is unsupported for source reconstruction in the time domain', cfg.method)); @@ -1311,7 +1314,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: sourceanalysis.m,v 1.139 2009/05/20 16:53:30 marvger Exp $'; +cfg.version.id = '$Id: sourceanalysis.m,v 1.140 2009/06/04 13:37:28 marvger Exp $'; % remember the configuration details of the input data if nargin==2 try, cfg.previous = data.cfg; end diff --git a/external/fieldtrip/private/sourceinterpolate.m b/external/fieldtrip/private/sourceinterpolate.m index 364c37a..b14baaa 100644 --- a/external/fieldtrip/private/sourceinterpolate.m +++ b/external/fieldtrip/private/sourceinterpolate.m @@ -33,6 +33,9 @@ % Copyright (C) 2003-2007, Robert Oostenveld % % $Log: sourceinterpolate.m,v $ +% Revision 1.53 2009/07/09 16:08:22 vlalit +% replaced read_fcdc_mri with read_mri to avoid warning +% % Revision 1.52 2009/03/11 11:30:45 roboos % use autodetection of source and MRI units % @@ -192,7 +195,7 @@ if ischar(anatomical) % read the anatomical MRI data from file fprintf('reading MRI from file\n'); - anatomical = read_fcdc_mri(anatomical); + anatomical = read_mri(anatomical); end % check if the input data is valid for this function and ensure that the structures correctly describes a volume @@ -329,7 +332,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: sourceinterpolate.m,v 1.52 2009/03/11 11:30:45 roboos Exp $'; +cfg.version.id = '$Id: sourceinterpolate.m,v 1.53 2009/07/09 16:08:22 vlalit Exp $'; % remember the configuration details of the input data cfg.previous = []; try, cfg.previous{1} = functional.cfg; end diff --git a/external/fieldtrip/private/sourceplot.m b/external/fieldtrip/private/sourceplot.m index dbb6391..0ce2179 100644 --- a/external/fieldtrip/private/sourceplot.m +++ b/external/fieldtrip/private/sourceplot.m @@ -105,7 +105,7 @@ % cfg.distmat = precomputed distance matrix (default = []) % cfg.camlight = 'yes' or 'no' (default = 'yes') % cfg.renderer = 'painters', 'zbuffer',' opengl' or 'none' (default = 'opengl') -% When using opacity the OpenGL renderer is required. +% When using opacity the OpenGL renderer is required. % TODO have to be built in: % cfg.marker = [Nx3] array defining N marker positions to display (orig: from sliceinterp) @@ -121,6 +121,29 @@ % Copyright (C) 2007-2008, Robert Oostenveld, Ingrid Nieuwenhuis % % $Log: sourceplot.m,v $ +% Revision 1.75 2009/08/30 14:58:42 crimic +% typo corrected +% +% Revision 1.74 2009/08/24 09:15:07 jansch +% included (experimental and undocumented) possibility to use image, rather than +% imagesc for the plotting, allowing for coding different aspects of the data in +% rgb-space +% +% Revision 1.73 2009/07/14 07:27:30 roboos +% replaced read_fcdc_mri with read_mri to avoid warning +% +% Revision 1.72 2009/07/08 08:10:29 roboos +% also detect int8 and int16 anatomical MRI and convert to double +% +% Revision 1.71 2009/06/17 14:05:25 roboos +% use ischar instead of isstr +% +% Revision 1.70 2009/06/17 14:03:41 roboos +% consistent handling of ginput in case figure is closed +% +% Revision 1.69 2009/05/29 14:12:32 ingnie +% only do intelligent masking (meaning opacity=0.5 when anatomy present) if not method = surface +% % Revision 1.68 2009/05/14 12:03:59 jansch % some changes to experimentally plot 4D and 5D volumes, undocumented % @@ -225,14 +248,14 @@ %%% checkdata see below!!! %%% % set the common defaults -if ~isfield(cfg, 'method'), cfg.method = 'ortho'; end +if ~isfield(cfg, 'method'), cfg.method = 'ortho'; end if ~isfield(cfg, 'anaparameter'), if isfield(data, 'anatomy'), cfg.anaparameter = 'anatomy'; else cfg.anaparameter = []; end -end +end % all methods if ~isfield(cfg, 'funparameter'), cfg.funparameter = []; end @@ -241,16 +264,16 @@ if ~isfield(cfg, 'title'), cfg.title = ''; end if ~isfield(cfg, 'atlas'), cfg.atlas = []; end if ~isfield(cfg, 'marker'), cfg.marker = []; end %TODO implement marker -if ~isfield(cfg, 'markersize'), cfg.markersize = 5; end +if ~isfield(cfg, 'markersize'), cfg.markersize = 5; end if ~isfield(cfg, 'markercolor'), cfg.markercolor = [1,1,1]; end % set the common defaults for the functional data -if ~isfield(cfg, 'funcolormap'), cfg.funcolormap = 'auto'; end -if ~isfield(cfg, 'funcolorlim'), cfg.funcolorlim = 'auto'; end; +if ~isfield(cfg, 'funcolormap'), cfg.funcolormap = 'auto'; end +if ~isfield(cfg, 'funcolorlim'), cfg.funcolorlim = 'auto'; end; % set the common defaults for the statistical data -if ~isfield(cfg, 'opacitymap'), cfg.opacitymap = 'auto'; end; -if ~isfield(cfg, 'opacitylim'), cfg.opacitylim = 'auto'; end; +if ~isfield(cfg, 'opacitymap'), cfg.opacitymap = 'auto'; end; +if ~isfield(cfg, 'opacitylim'), cfg.opacitylim = 'auto'; end; if ~isfield(cfg, 'roi'), cfg.roi = []; end; % set the defaults per method @@ -264,7 +287,7 @@ if ~isfield(cfg, 'queryrange'); cfg.queryrange = 3; end if ~isfield(cfg, 'inputcoord'); cfg.inputcoord = []; end if isfield(cfg, 'TTlookup'), - error('TTlookup is old; now specify cfg.atlas, see help!'); + error('TTlookup is old; now specify cfg.atlas, see help!'); end % slice if ~isfield(cfg, 'nslices'); cfg.nslices = 20; end @@ -290,11 +313,11 @@ end %%%%%%% -if isstr(data) +if ischar(data) % read the anatomical MRI data from file filename = data; fprintf('reading MRI from file\n'); - data = read_fcdc_mri(filename); + data = read_mri(filename); end % check if the input data is valid for this function @@ -334,19 +357,17 @@ hasroi = 1; tmpcfg.roi = cfg.roi; tmpcfg.atlas = cfg.atlas; - tmpcfg.inputcoord = cfg.inputcoord; + tmpcfg.inputcoord = cfg.inputcoord; roi = volumelookup(tmpcfg,data); end end - + %%% anaparameter if isequal(cfg.anaparameter,'anatomy') if isfield(data, 'anatomy') hasana = 1; - mri8 = isa(data.anatomy, 'uint8'); - mri16 = isa(data.anatomy, 'uint16'); % convert integers to single precision float if neccessary - if mri8 || mri16 + if isa(data.anatomy, 'uint8') || isa(data.anatomy, 'uint16') || isa(data.anatomy, 'int8') || isa(data.anatomy, 'int16') fprintf('converting anatomy to double\n'); ana = double(data.anatomy); else @@ -379,12 +400,31 @@ end % handle fun -if hasfun +if hasfun && issubfield(data, 'dimord') && strcmp(data.dimord(end-2:end),'rgb') + % treat functional data as rgb values + if any(fun(:)>1 | fun(:)<0) + %scale + tmpdim = size(fun); + nvox = prod(tmpdim(1:end-1)); + tmpfun = reshape(fun,[nvox tmpdim(end)]); + m1 = max(tmpfun,[],1); + m2 = min(tmpfun,[],1); + tmpfun = (tmpfun-m2(ones(nvox,1),:))./(m1(ones(nvox,1),:)-m2(ones(nvox,1),:)); + fun = reshape(tmpfun, tmpdim); + end + qi = 1; + hasfreq = 0; + hastime = 0; + + doimage = 1; + fcolmin = 0; + fcolmax = 1; +elseif hasfun % determine scaling min and max (fcolmin fcolmax) and funcolormap funmin = min(fun(:)); funmax = max(fun(:)); % smart lims: make from auto other string - if isequal(cfg.funcolorlim,'auto') + if isequal(cfg.funcolorlim,'auto') if sign(funmin)>-1 && sign(funmax)>-1 cfg.funcolorlim = 'zeromax'; elseif sign(funmin)<1 && sign(funmax)<1 @@ -415,7 +455,7 @@ fcolmin = cfg.funcolorlim(1); fcolmax = cfg.funcolorlim(2); % smart colormap - if isequal(cfg.funcolormap,'auto') + if isequal(cfg.funcolormap,'auto') if sign(fcolmin) == -1 && sign(fcolmax) == 1 cfg.funcolormap = 'jet'; else @@ -433,7 +473,7 @@ fprintf('taking absolute value of complex data\n'); fun = abs(fun); end - + %what if fun is 4D? if ndims(fun)>3, if isfield(data, 'time') && isfield(data, 'freq'), @@ -458,10 +498,14 @@ hasfreq = 0; hastime = 0; end + + doimage = 0; else qi = 1; hasfreq = 0; hastime = 0; + + doimage = 0; end % handle fun %%% maskparameter @@ -521,7 +565,7 @@ if isequal(cfg.opacitymap,'auto'), cfg.opacitymap = 'vdown'; end; otherwise error('incorrect specification of cfg.opacitylim'); - end + end % switch opacitylim else % limits are numeric opacmin = cfg.opacitylim(1); @@ -537,10 +581,10 @@ end end % handling opacitylim and opacitymap clear mskmin mskmax; -end +end -% prevent outside fun from being plotted -if hasfun && isfield(data,'inside') && ~hasmsk +% prevent outside fun from being plotted in slice and orthoplot +if hasfun && isfield(data,'inside') && ~hasmsk && ~isequal(cfg.method,'surface') hasmsk = 1; msk = zeros(dim); cfg.opacitymap = 'rampup'; @@ -579,7 +623,7 @@ %%% determine what has to be plotted, depends on method if isequal(cfg.method,'ortho') - if ~isstr(cfg.location) + if ~ischar(cfg.location) if strcmp(cfg.locationcoordinates, 'head') % convert the headcoordinates location into voxel coordinates loc = inv(data.transform) * [cfg.location(:); 1]; @@ -611,29 +655,29 @@ end % determine the initial intersection of the cursor (xi yi zi) - if isstr(loc) && strcmp(loc, 'min') + if ischar(loc) && strcmp(loc, 'min') if isempty(cfg.funparameter) error('cfg.location is min, but no functional parameter specified'); end [minval, minindx] = min(fun(:)); [xi, yi, zi] = ind2sub(dim, minindx); - elseif isstr(loc) && strcmp(loc, 'max') + elseif ischar(loc) && strcmp(loc, 'max') if isempty(cfg.funparameter) error('cfg.location is max, but no functional parameter specified'); end [maxval, maxindx] = max(fun(:)); [xi, yi, zi] = ind2sub(dim, maxindx); - elseif isstr(loc) && strcmp(loc, 'center') + elseif ischar(loc) && strcmp(loc, 'center') xi = round(dim(1)/2); yi = round(dim(2)/2); zi = round(dim(3)/2); - elseif ~isstr(loc) + elseif ~ischar(loc) % using nearest instead of round ensures that the position remains within the volume xi = nearest(1:dim(1), loc(1)); yi = nearest(1:dim(2), loc(2)); zi = nearest(1:dim(3), loc(3)); end - + %% do the actual plotting %% nas = []; lpa = []; @@ -658,7 +702,7 @@ if hasfun && ~hasatlas val = fun(xi, yi, zi, qi); if ~hasfreq && ~hastime, - fprintf('voxel %d, indices [%d %d %d], location [%.1f %.1f %.1f], value %f\n', sub2ind(dim, xi, yi, zi), ijk(1:3), xyz(1:3), val); + fprintf('voxel %d, indices [%d %d %d], location [%.1f %.1f %.1f], value %f\n', sub2ind(dim, xi, yi, zi), ijk(1:3), xyz(1:3), val); elseif hastime && hasfreq, val = fun(xi, yi, zi, qi(1), qi(2)); fprintf('voxel %d, indices [%d %d %d %d %d], %s coordinates [%.1f %.1f %.1f %.1f %.1f], value %f\n', [sub2ind(dim(1:3), xi, yi, zi), ijk(1:3)', qi], cfg.inputcoord, [xyz(1:3)' data.freq(qi(1)) data.time(qi(2))], val); @@ -677,7 +721,7 @@ end if hasatlas - % determine the anatomical label of the current position + % determine the anatomical label of the current position lab = atlas_lookup(atlas, (xyz(1:3)), 'inputcoord', cfg.inputcoord, 'queryrange', cfg.queryrange); if isempty(lab) fprintf([f,' labels: not found\n']); @@ -690,7 +734,7 @@ fprintf('\n'); end end - + % make vols and scales, containes volumes to be plotted (fun, ana, msk) vols = {}; if hasana; vols{1} = ana; scales{1} = []; end; % needed when only plotting ana @@ -701,22 +745,22 @@ % this seems to be a problem that people often have error('no anatomy is present and no functional data is selected, please check your cfg.funparameter'); end - + h1 = subplot(2,2,1); - [vols2D] = handle_ortho(vols, [xi yi zi qi], 2, dim); - plot2D(vols2D, scales); + [vols2D] = handle_ortho(vols, [xi yi zi qi], 2, dim, doimage); + plot2D(vols2D, scales, doimage); xlabel('i'); ylabel('k'); axis(cfg.axis); if strcmp(cfg.crosshair, 'yes'), crosshair([xi zi]); end - + h2 = subplot(2,2,2); - [vols2D] = handle_ortho(vols, [xi yi zi qi], 1, dim); - plot2D(vols2D, scales); + [vols2D] = handle_ortho(vols, [xi yi zi qi], 1, dim, doimage); + plot2D(vols2D, scales, doimage); xlabel('j'); ylabel('k'); axis(cfg.axis); if strcmp(cfg.crosshair, 'yes'), crosshair([yi zi]); end h3 = subplot(2,2,3); - [vols2D] = handle_ortho(vols, [xi yi zi qi], 3, dim); - plot2D(vols2D, scales); + [vols2D] = handle_ortho(vols, [xi yi zi qi], 3, dim, doimage); + plot2D(vols2D, scales, doimage); xlabel('i'); ylabel('j'); axis(cfg.axis); if strcmp(cfg.crosshair, 'yes'), crosshair([xi yi]); end @@ -730,7 +774,7 @@ caxis([-1 1].*max(abs(caxis))); colorbar; %caxis([fcolmin fcolmax]); - %set(gca, 'Visible', 'off'); + %set(gca, 'Visible', 'off'); elseif hasfreq && hasfun, subplot(2,2,4); plot(data.freq, squeeze(vols{2}(xi,yi,zi,:))); xlabel('freq'); @@ -756,66 +800,76 @@ set(gcf, 'renderer', cfg.renderer); % ensure that this is done in interactive mode drawnow; - + if interactive_flag - try, [d1, d2, key] = ginput(1); catch, key='q'; end + try + [d1, d2, key] = ginput(1); + catch + % this happens if the figure is closed + key='q'; + end + if isempty(key) % this happens if you press the apple key - % do nothing - elseif key=='q' - break; - elseif key=='l' - lpa = [xi yi zi]; - elseif key=='r' - rpa = [xi yi zi]; - elseif key=='n' - nas = [xi yi zi]; - elseif key=='i' || key=='j' || key=='k' || key=='m' - % update the view to a new position - if l1=='i' && l2=='k' && key=='i', zi = zi+1; - elseif l1=='i' && l2=='k' && key=='j', xi = xi-1; - elseif l1=='i' && l2=='k' && key=='k', xi = xi+1; - elseif l1=='i' && l2=='k' && key=='m', zi = zi-1; - elseif l1=='i' && l2=='j' && key=='i', yi = yi+1; - elseif l1=='i' && l2=='j' && key=='j', xi = xi-1; - elseif l1=='i' && l2=='j' && key=='k', xi = xi+1; - elseif l1=='i' && l2=='j' && key=='m', yi = yi-1; - elseif l1=='j' && l2=='k' && key=='i', zi = zi+1; - elseif l1=='j' && l2=='k' && key=='j', yi = yi-1; - elseif l1=='j' && l2=='k' && key=='k', yi = yi+1; - elseif l1=='j' && l2=='k' && key=='m', zi = zi-1; - end; - else - % update the view to a new position - l1 = get(get(gca, 'xlabel'), 'string'); - l2 = get(get(gca, 'ylabel'), 'string'); - switch l1, - case 'i' - xi = d1; - case 'j' - yi = d1; - case 'k' - zi = d1; - case 'freq' - qi = nearest(data.freq,d1); - case 'time' - qi = nearest(data.time,d1); - end - switch l2, - case 'i' - xi = d2; - case 'j' - yi = d2; - case 'k' - zi = d2; - case 'freq' - qi = [nearest(data.freq,d2) qi(1)]; - end + key = ''; end + switch key + case '' + % do nothing + case 'q' + break; + case 'l' + lpa = [xi yi zi]; + case 'r' + rpa = [xi yi zi]; + case 'n' + nas = [xi yi zi]; + case {'i' 'j''k' 'm'} + % update the view to a new position + if l1=='i' && l2=='k' && key=='i', zi = zi+1; + elseif l1=='i' && l2=='k' && key=='j', xi = xi-1; + elseif l1=='i' && l2=='k' && key=='k', xi = xi+1; + elseif l1=='i' && l2=='k' && key=='m', zi = zi-1; + elseif l1=='i' && l2=='j' && key=='i', yi = yi+1; + elseif l1=='i' && l2=='j' && key=='j', xi = xi-1; + elseif l1=='i' && l2=='j' && key=='k', xi = xi+1; + elseif l1=='i' && l2=='j' && key=='m', yi = yi-1; + elseif l1=='j' && l2=='k' && key=='i', zi = zi+1; + elseif l1=='j' && l2=='k' && key=='j', yi = yi-1; + elseif l1=='j' && l2=='k' && key=='k', yi = yi+1; + elseif l1=='j' && l2=='k' && key=='m', zi = zi-1; + end; + otherwise + % update the view to a new position + l1 = get(get(gca, 'xlabel'), 'string'); + l2 = get(get(gca, 'ylabel'), 'string'); + switch l1, + case 'i' + xi = d1; + case 'j' + yi = d1; + case 'k' + zi = d1; + case 'freq' + qi = nearest(data.freq,d1); + case 'time' + qi = nearest(data.time,d1); + end + switch l2, + case 'i' + xi = d2; + case 'j' + yi = d2; + case 'k' + zi = d2; + case 'freq' + qi = [nearest(data.freq,d2) qi(1)]; + end + end % switch key end % if interactive_flag if ~isempty(nas), fprintf('nas = [%f %f %f]\n', nas); cfg.fiducial.nas = nas; else fprintf('nas = undefined\n'); end if ~isempty(lpa), fprintf('lpa = [%f %f %f]\n', lpa); cfg.fiducial.lpa = lpa; else fprintf('lpa = undefined\n'); end - if ~isempty(rpa), fprintf('rpa = [%f %f %f]\n', rpa); cfg.fiducial.rpa = rpa; else fprintf('rpa = undefined\n'); end + if ~isempty(rpa), fprintf('rpa = [%f %f %f]\n', rpa); cfg.fiducial.rpa = rpa; else fprintf('rpa = undefined\n'); end end % while interactive_flag elseif isequal(cfg.method,'glassbrain') @@ -830,14 +884,14 @@ tmpcfg.maskparameter = 'inside'; tmpcfg.axis = cfg.axis; tmpcfg.renderer = cfg.renderer; - if hasfun, + if hasfun, fun = getsubfield(data, cfg.funparameter); fun(1,:,:) = max(fun, [], 1); fun(:,1,:) = max(fun, [], 2); fun(:,:,1) = max(fun, [], 3); data = setsubfield(data, cfg.funparameter, fun); end - + if hasana, ana = getsubfield(data, 'anatomy'); %ana(1,:,:) = max(ana, [], 1); @@ -845,7 +899,7 @@ %ana(:,:,1) = max(ana, [], 3); data = setsubfield(data, 'anatomy', ana); end - + if hasmsk, msk = getsubfield(data, 'inside'); msk(1,:,:) = squeeze(fun(1,:,:))>0 & imfill(abs(squeeze(ana(1,:,:))-1))>0; @@ -855,7 +909,7 @@ end sourceplot(tmpcfg, data); - + elseif isequal(cfg.method,'surface') % read the triangulated cortical surface from file @@ -883,7 +937,7 @@ fprintf('%d voxels in functional data\n', prod(dim)); fprintf('%d vertices in cortical surface\n', size(surf.pnt,1)); - if hasfun + if hasfun [interpmat, cfg.distmat] = interp_gridded(data.transform, fun, surf.pnt, 'projmethod', cfg.projmethod, 'distmat', cfg.distmat, 'sphereradius', cfg.sphereradius, 'inside', data.inside); % interpolate the functional data val = interpmat * fun(data.inside(:)); @@ -953,7 +1007,7 @@ elseif isequal(cfg.method,'slice') % white BG => mskana - + %% TODO: HERE THE FUNCTION THAT MAKES TO SLICE DIMENSION ALWAYS THE THIRD %% DIMENSION, AND ALSO KEEP TRANSFORMATION MATRIX UP TO DATE % zoiets @@ -961,7 +1015,7 @@ %if hasfun; fun = shiftdim(fun,cfg.slicedim-1); end; %if hasmsk; msk = shiftdim(msk,cfg.slicedim-1); end; %%%%% select slices - if ~isstr(cfg.slicerange) + if ~ischar(cfg.slicerange) ind_fslice = cfg.slicerange(1); ind_lslice = cfg.slicerange(2); elseif isequal(cfg.slicerange, 'auto') @@ -992,7 +1046,7 @@ % update the dimensions of the volume if hasana; dim=size(ana); else dim=size(fun); end; - + %%%%% make "quilts", that contain all slices on 2D patched sheet % Number of patches along sides of Quilt (M and N) % Size (in voxels) of side of patches of Quilt (m and n) @@ -1032,10 +1086,8 @@ if hasana; vols2D{1} = quilt_ana; scales{1} = []; end; % needed when only plotting ana if hasfun; vols2D{2} = quilt_fun; scales{2} = [fcolmin fcolmax]; end; if hasmsk; vols2D{3} = quilt_msk; scales{3} = [opacmin opacmax]; end; - - plot2D(vols2D, scales); + plot2D(vols2D, scales, doimage); axis off - if strcmp(cfg.colorbar, 'yes'), if hasfun % use a normal Matlab coorbar @@ -1052,13 +1104,13 @@ set(gcf, 'renderer', cfg.renderer); % get the output cfg -cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); +cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % handle_ortho makes an overlay of 3D anatomical, functional and probability % volumes. The three volumes must be scaled between 0 and 1. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function [vols2D] = handle_ortho(vols, indx, slicedir, dim); +function [vols2D] = handle_ortho(vols, indx, slicedir, dim, doimage) % put 2Dvolumes in fun, ana and msk if length(vols)>=1 && isempty(vols{1}); hasana=0; else ana=vols{1}; hasana=1; end; @@ -1094,8 +1146,18 @@ % cut out the slice of interest if hasana; ana = squeeze(ana(xi,yi,zi)); end; -if hasfun; fun = squeeze(fun(xi,yi,zi,qi(1),qi(2))); end; -if hasmsk; msk = squeeze(msk(xi,yi,zi)); end; +if hasfun; + if doimage + fun = squeeze(fun(xi,yi,zi,:)); + else + fun = squeeze(fun(xi,yi,zi,qi(1),qi(2))); + end +end +if hasmsk && length(size(msk))>3 + msk = squeeze(msk(xi,yi,zi,qi(1),qi(2))); +elseif hasmsk + msk = squeeze(msk(xi,yi,zi)); +end; %put fun, ana and msk in vols2D if hasana; vols2D{1} = ana; end; @@ -1105,7 +1167,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % plot2D plots a two dimensional plot, used in ortho and slice %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function plot2D(vols2D, scales); +function plot2D(vols2D, scales, doimage); cla; % put 2D volumes in fun, ana and msk hasana = length(vols2D)>0 && ~isempty(vols2D{1}); @@ -1114,7 +1176,8 @@ % the transpose is needed for displaying the matrix using the Matlab image() function if hasana; ana = vols2D{1}'; end; -if hasfun; fun = vols2D{2}'; end; +if hasfun && ~doimage; fun = vols2D{2}'; end; +if hasfun && doimage; fun = permute(vols2D{2},[2 1 3]); end; if hasmsk; msk = vols2D{3}'; end; @@ -1132,16 +1195,22 @@ hold on if hasfun - hf = imagesc(fun); - caxis(scales{2}); - % apply the opacity mask to the functional data - if hasmsk - % set the opacity - set(hf, 'AlphaData', msk) - set(hf, 'AlphaDataMapping', 'scaled') - alim(scales{3}); - elseif hasana - set(hf, 'AlphaData', 0.5) + + if doimage + hf = image(fun); + else + hf = imagesc(fun); + caxis(scales{2}); + % apply the opacity mask to the functional data + if hasmsk + % set the opacity + set(hf, 'AlphaData', msk) + set(hf, 'AlphaDataMapping', 'scaled') + alim(scales{3}); + elseif hasana + set(hf, 'AlphaData', 0.5) + end + end end diff --git a/external/fieldtrip/private/splint_gh.mexmaci b/external/fieldtrip/private/splint_gh.mexmaci index 10660af..4f4ccb9 100755 Binary files a/external/fieldtrip/private/splint_gh.mexmaci and b/external/fieldtrip/private/splint_gh.mexmaci differ diff --git a/external/fieldtrip/private/standardise.m b/external/fieldtrip/private/standardise.m index 7c1654d..335f71a 100644 --- a/external/fieldtrip/private/standardise.m +++ b/external/fieldtrip/private/standardise.m @@ -1,4 +1,4 @@ -function x = standardise(x,dim) +function [x,mx,sx] = standardise(x,dim) % X = STANDARDISE(X, DIM) computes the zscore of a matrix along dimension dim % has extended functionality as compared to the stats-toolbox's zscore function @@ -6,10 +6,16 @@ % Copyright (C) 2009, Jan-Mathijs Schoffelen % % $Log: standardise.m,v $ +% Revision 1.2 2009/06/16 15:44:29 jansch +% added automatic dim detection (first non singular dimension) for nargin==1. +% added mean and std to output +% % Revision 1.1 2009/05/19 15:59:11 jansch % first commitment into cvs % +if nargin == 1, dim = find(size(x)>1,1,'first'); end + n = size(x,dim); mx = mean(x,dim); %sx = std(x,0,dim); diff --git a/external/fieldtrip/private/statistics_montecarlo.m b/external/fieldtrip/private/statistics_montecarlo.m index 3448633..0647590 100644 --- a/external/fieldtrip/private/statistics_montecarlo.m +++ b/external/fieldtrip/private/statistics_montecarlo.m @@ -81,6 +81,9 @@ % Copyright (C) 2005-2007, Robert Oostenveld % % $Log: statistics_montecarlo.m,v $ +% Revision 1.31 2009/08/06 08:31:31 roboos +% renamed some internal variables +% % Revision 1.30 2009/04/23 07:48:23 roboos % added cfg.randomseed option, default is yes % @@ -381,8 +384,8 @@ % compare each data element with the maximum statistic prb_pos = prb_pos + (statobsmin(statrand(:))); - ref_pos(i) = max(statrand(:)); - ref_neg(i) = min(statrand(:)); + posdistribution(i) = max(statrand(:)); + negdistribution(i) = min(statrand(:)); else % compare each data element with its own statistic prb_pos = prb_pos + (statobs %d)\n',k, nref*nchan, i, j); + + if widthparam(i,j)>0 + xbeg = lay.pos(refindx(i),1); + ybeg = lay.pos(refindx(i),2); + xend = lay.pos(chanindx(j),1); + yend = lay.pos(chanindx(j),2); + x = [xbeg xend]'; + y = [ybeg yend]'; + % h = line(x, y); + h = patch(x, y, 1); + + if ~isempty(widthparam) + set(h, 'LineWidth', widthparam(i,j)); + end + + if ~isempty(alphaparam) + set(h, 'EdgeAlpha', alphaparam(i,j)); + end + + if ~isempty(colorparam) + set(h, 'EdgeColor', rgb(colorparam(i,j),:)); + end + + end + end +end +progress('close'); + +% also plot the position of the electrodes +plot(lay.pos(:,1), lay.pos(:,2), 'k.'); + +% also plot the outline, i.e. head shape or sulci +if isfield(lay, 'outline') + fprintf('solid lines indicate the outline, e.g. head shape or sulci\n'); + for i=1:length(lay.outline) + if ~isempty(lay.outline{i}) + X = lay.outline{i}(:,1); + Y = lay.outline{i}(:,2); + h = line(X, Y); + set(h, 'color', 'k'); + set(h, 'linewidth', 1.5); + set(h, 'linestyle', '-'); + end + end +end + +% also plot the mask, i.e. global outline for masking the topoplot +if isfield(lay, 'mask') + fprintf('dashed lines indicate the mask for topograpic interpolation\n'); + for i=1:length(lay.mask) + if ~isempty(lay.mask{i}) + X = lay.mask{i}(:,1); + Y = lay.mask{i}(:,2); + % the polygon representing the mask should be closed + X(end+1) = X(1); + Y(end+1) = Y(1); + h = line(X, Y); + set(h, 'color', 'k'); + set(h, 'linewidth', 1.5); + set(h, 'linestyle', '-'); + end + end +end + +% get the output cfg +cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); + +if nargout<1 + clear cfg +end diff --git a/external/fieldtrip/private/topoplotER.m b/external/fieldtrip/private/topoplotER.m index 3fc7b86..98fdd8d 100644 --- a/external/fieldtrip/private/topoplotER.m +++ b/external/fieldtrip/private/topoplotER.m @@ -14,7 +14,7 @@ % 'avg', 'powspctrm' or 'cohspctrm' (default depends on data.dimord) % cfg.xlim = 'maxmin' or [xmin xmax] (default = 'maxmin') % cfg.zlim = 'maxmin', 'absmax' or [zmin zmax] (default = 'maxmin') -% cfg.cohrefchannel = Name of reference-channel, only for visualizing coherence +% cfg.cohrefchannel = name of reference channel for visualising coherence, can be 'gui' % cfg.baseline = 'yes','no' or [time1 time2] (default = 'no'), see TIMELOCKBASELINE or FREQBASELINE % cfg.baselinetype = 'absolute' or 'relative' (default = 'absolute') % cfg.trials = 'all' or a selection given as a 1xN vector (default = 'all') @@ -71,6 +71,18 @@ % Copyright (C) 2005-2006, F.C. Donders Centre % % $Log: topoplotER.m,v $ +% Revision 1.58 2009/07/14 13:51:32 roboos +% some small changes related to the new interactive data selection +% +% Revision 1.57 2009/07/14 13:21:09 roboos +% changed the interactive plotting: instead of using plotSelection it now uses the selection function from the new plotting module (select_range and select_channel) and uses a local subfunction to update the cfg and call the next figure +% +% Revision 1.56 2009/06/17 14:05:25 roboos +% use ischar instead of isstr +% +% Revision 1.55 2009/06/17 13:44:52 roboos +% cleaned up help +% % Revision 1.54 2009/05/12 18:58:49 roboos % added handling of cfg.cohrefchannel='gui' for manual/interactive selection % @@ -186,6 +198,7 @@ fieldtripdefs cfg = checkconfig(cfg, 'trackconfig', 'on'); +cfg = checkconfig(cfg, 'unused', {'cohtargetchannel'}); cla @@ -299,9 +312,6 @@ if ~isfield(cfg, 'zparam'), cfg.zparam='topo'; end end -% Old style coherence plotting with cohtargetchannel is no longer supported: -cfg = checkconfig(cfg, 'unused', {'cohtargetchannel'}); - % Read or create the layout that will be used for plotting: lay = prepare_layout(cfg, data); cfg.layout = lay; @@ -332,12 +342,13 @@ h = clf; plot_lay(lay, 'box', false); title('Select the reference channel by clicking on it...'); - info = []; + % add the channel information to the figure + info = guidata(gcf); info.x = lay.pos(:,1); info.y = lay.pos(:,2); info.label = lay.label; guidata(h, info); - set(gcf, 'WindowButtonUpFcn', {@select_channel, 'callback', {@select_cohrefchannel, cfg, data}}); + set(gcf, 'WindowButtonUpFcn', {@select_channel, 'callback', {@select_topoplotER, cfg, data}}); return end @@ -460,7 +471,7 @@ comment = sprintf('%0s=[%.3g %.3g]', cfg.xparam, cfg.xlim(1), cfg.xlim(2)); end cfg.comment = comment; -elseif ~isstr(cfg.comment) +elseif ~ischar(cfg.comment) error('cfg.comment must be string'); end @@ -470,39 +481,26 @@ % The remainder of the code is meant to make the figure interactive hold on; +% Make the figure interactive if strcmp(cfg.interactive, 'yes') - userData.hFigure = gcf; - userData.hAxes = gca; - for i=1:10 - userData.hSelection{i} = plot(0,0); - set(userData.hSelection{i}, ... - 'XData', [0], ... - 'YData', [0], ... - 'Color', [0 0 0], ... - 'EraseMode', 'xor', ... - 'LineStyle', '--', ... - 'LineWidth', 1.5, ... - 'Visible', 'on'); - userData.range{i} = []; + % add the channel information to the figure + info = guidata(gcf); + info.x = lay.pos(:,1); + info.y = lay.pos(:,2); + info.label = lay.label; + guidata(gcf, info); + + if any(strcmp(data.dimord, {'chan_time', 'chan_freq', 'subj_chan_time', 'rpt_chan_time'})) + set(gcf, 'WindowButtonUpFcn', {@select_channel, 'multiple', true, 'callback', {@select_singleplotER, cfg, varargin{:}}, 'event', 'WindowButtonUpFcn'}); + set(gcf, 'WindowButtonDownFcn', {@select_channel, 'multiple', true, 'callback', {@select_singleplotER, cfg, varargin{:}}, 'event', 'WindowButtonDownFcn'}); + set(gcf, 'WindowButtonMotionFcn', {@select_channel, 'multiple', true, 'callback', {@select_singleplotER, cfg, varargin{:}}, 'event', 'WindowButtonMotionFcn'}); + elseif any(strcmp(data.dimord, {'chan_freq_time', 'subj_chan_freq_time', 'rpt_chan_freq_time', 'rpttap_chan_freq_time'})) + set(gcf, 'WindowButtonUpFcn', {@select_channel, 'multiple', true, 'callback', {@select_singleplotTFR, cfg, varargin{:}}, 'event', 'WindowButtonUpFcn'}); + set(gcf, 'WindowButtonDownFcn', {@select_channel, 'multiple', true, 'callback', {@select_singleplotTFR, cfg, varargin{:}}, 'event', 'WindowButtonDownFcn'}); + set(gcf, 'WindowButtonMotionFcn', {@select_channel, 'multiple', true, 'callback', {@select_singleplotTFR, cfg, varargin{:}}, 'event', 'WindowButtonMotionFcn'}); + else + error('unsupported dimord "%" for interactive plotting', data.dimord); end - userData.iSelection = 0; - userData.plotType = 'topoplot'; - userData.selecting = 0; - userData.selectionType = ''; - userData.selectAxes = 'z'; - userData.lastClick = []; - userData.cfg = cfg; - userData.data = data; - userData.chanX = chanX; - userData.chanY = chanY; - userData.chanLabels = chanLabels; - tag = sprintf('%.5f', 10000 * rand(1)); - set(gcf, 'Renderer', cfg.renderer); - set(gcf, 'Tag', tag); - set(gcf, 'UserData', userData); - set(gcf, 'WindowButtonMotionFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 0);']); - set(gcf, 'WindowButtonDownFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 1);']); - set(gcf, 'WindowButtonUpFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 2);']); end axis off; @@ -512,12 +510,49 @@ cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes'); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -% this function is called by select_channel +% SUBFUNCTION which is called after selecting channels in case of cfg.cohrefchannel='gui' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function select_cohrefchannel(label, cfg, varargin) -fprintf('selected "%s" as reference channel\n', label); +function select_topoplotER(label, cfg, varargin) cfg.cohrefchannel = label; -figure +fprintf('selected cfg.cohrefchannel = ''%s''\n', cfg.cohrefchannel); +p = get(gcf, 'Position'); +f = figure; +set(f, 'Position', p); topoplotER(cfg, varargin{:}); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION which is called after selecting channels in case of cfg.interactive='yes' +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function select_singleplotER(label, cfg, varargin) +if ~isempty(label) + cfg.xlim = 'maxmin'; + cfg.channel = label; + fprintf('selected cfg.channel = {'); + for i=1:(length(cfg.channel)-1) + fprintf('''%s'', ', cfg.channel{i}); + end + fprintf('''%s''}\n', cfg.channel{end}); + p = get(gcf, 'Position'); + f = figure; + set(f, 'Position', p); + singleplotER(cfg, varargin{:}); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION which is called after selecting channels in case of cfg.interactive='yes' +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function select_singleplotTFR(label, cfg, varargin) +if ~isempty(label) + cfg.xlim = 'maxmin'; + cfg.ylim = 'maxmin'; + cfg.channel = label; + fprintf('selected cfg.channel = {'); + for i=1:(length(cfg.channel)-1) + fprintf('''%s'', ', cfg.channel{i}); + end + fprintf('''%s''}\n', cfg.channel{end}); + p = get(gcf, 'Position'); + f = figure; + set(f, 'Position', p); + singleplotTFR(cfg, varargin{:}); +end diff --git a/external/fieldtrip/private/topoplotTFR.m b/external/fieldtrip/private/topoplotTFR.m index 4f5bd06..f24d692 100644 --- a/external/fieldtrip/private/topoplotTFR.m +++ b/external/fieldtrip/private/topoplotTFR.m @@ -18,7 +18,7 @@ % cfg.xlim = 'maxmin' or [xmin xmax] (default = 'maxmin') % cfg.ylim = 'maxmin' or [ymin ymax] (default = 'maxmin') % cfg.zlim = 'maxmin', 'absmax' or [zmin zmax] (default = 'maxmin') -% cfg.cohrefchannel = Name of reference-channel, only for visualizing coherence +% cfg.cohrefchannel = name of reference channel for visualising coherence, can be 'gui' % cfg.baseline = 'yes','no' or [time1 time2] (default = 'no'), see FREQBASELINE % cfg.baselinetype = 'absolute' or 'relative' (default = 'absolute') % cfg.trials = 'all' or a selection given as a 1xN vector (default = 'all') @@ -57,6 +57,9 @@ % Copyright (C) 2005-2006, F.C. Donders Centre % % $Log: topoplotTFR.m,v $ +% Revision 1.21 2009/06/17 13:44:52 roboos +% cleaned up help +% % Revision 1.20 2008/12/16 15:31:42 sashae % plot functions can now give cfg as output % added checkconfig to start and end of function, configtracking possible diff --git a/external/fieldtrip/private/trialfun_twoclass_classification.m b/external/fieldtrip/private/trialfun_twoclass_classification.m new file mode 100644 index 0000000..2e877ee --- /dev/null +++ b/external/fieldtrip/private/trialfun_twoclass_classification.m @@ -0,0 +1,79 @@ +function [trl] = trialfun_Subject01(cfg) + +% This trial function can be used to train and test a real-time +% classifier in offline and online mode. It selects pieces of data +% in the two classes based on two trigger values. The first N occurences +% in each class are marked as training items. All subsequent occurrences +% are marked as test items. +% +% This function can be used in conjunction with rt_classification and uses the options +% cfg.trialdef.numtrain = numberof training items, e.g. 20 +% cfg.trialdef.eventvalue1 = trigger value for the 1st class +% cfg.trialdef.eventvalue2 = trigger value for the 2nd class +% cfg.trialdef.eventtype = string, e.g. 'trigger' +% cfg.trialdef.prestim = latency in seconds, e.g. 0.3 +% cfg.trialdef.poststim = latency in seconds, e.g. 0.7 + +% $Log: trialfun_twoclass_classification.m,v $ +% Revision 1.2 2009/06/04 15:36:21 roboos +% some small fixes +% +% Revision 1.1 2009/06/04 14:17:43 roboos +% first version, not yet tested +% + +% these are used to count the number of training items in each class +persistent numtrain1 +persistent numtrain2 + +if isempty(numtrain1) + numtrain1 = 0; +end +if isempty(numtrain2) + numtrain2 = 0; +end + +if isfield(cfg, 'hdr') + hdr = cfg.hdr; +else + hdr = read_header(cfg.headerfile); +end + +if isfield(cfg, 'event') + event = cfg.event; +else + event = read_event(cfg.headerfile); +end + +baseline = round(cfg.trialdef.prestim*hdr.Fs); +duration = round(cfg.trialdef.prestim*hdr.Fs + cfg.trialdef.poststim*hdr.Fs); + +% make a subset of the interesting events +sel = strcmp(cfg.trialdef.eventtype, {event.type}); +event = event(sel); +num = length(event); +trl = zeros(num,5); + +for i=1:num + % determine the location of this trial in the data stream + begsample = event(i).sample - baseline; + endsample = begsample + duration - 1; + offset = baseline; + % determine the class and wether this trial is eligeable for training + if event(i).value==cfg.trialdef.eventvalue1 + class = 1; + train = (numtrain1 < cfg.trialdef.numtrain); % boolean + numtrain1 = numtrain1 + train; % increment the counter + elseif event(i).value==cfg.trialdef.eventvalue2 + class = 2; + train = (numtrain2 < cfg.trialdef.numtrain); % boolean + numtrain2 = numtrain2 + train; % increment the counter + else + % the class is unknown and therefore irrelevant + class = nan; + train = false; + end + % remember this trial, the class and whether it should be used for training + trl(i,:) = [begsample endsample offset class train]; +end + diff --git a/external/fieldtrip/private/uilayout.m b/external/fieldtrip/private/uilayout.m new file mode 100644 index 0000000..934ef32 --- /dev/null +++ b/external/fieldtrip/private/uilayout.m @@ -0,0 +1,163 @@ +function uilayout(h, varargin) + +% UILAYOUT is a helper function to facilitate the layout of multiple +% usercontrol elements + +% Copyright (C) 2009, Robert Oostenveld +% +% $Log: uilayout.m,v $ +% Revision 1.3 2009/08/04 12:42:34 ingnie +% fixed typo +% +% Revision 1.2 2009/08/04 12:29:29 ingnie +% fixed small typo +% +% Revision 1.1 2009/08/04 11:57:47 roboos +% implemented helper function for consistent setting of uicontrol details and for consistent and/or automatic layouting +% + +% these are used to make a selection of uicontrol elements +tag = keyval('tag', varargin{:}); +style = keyval('style', varargin{:}); + +% determine all children +if ~isempty(tag) && ~isempty(style) + c = findall(h, 'tag', tag, 'style', style); +elseif ~isempty(tag) + c = findall(h, 'tag', tag); +elseif ~isempty(style) + c = findall(h, 'style', style); +else + c = findall(h); +end +c = flipud(c); +% fprintf('selected %d elements\n', numel(c)); + +% these are the normal features of an uicontrol +BackgroundColor = keyval('BackgroundColor', varargin{:}); +CallBack = keyval('CallBack', varargin{:}); +Clipping = keyval('Clipping', varargin{:}); +Enable = keyval('Enable', varargin{:}); +FontAngle = keyval('FontAngle', varargin{:}); +FontName = keyval('FontName', varargin{:}); +FontSize = keyval('FontSize', varargin{:}); +FontUnits = keyval('FontUnits', varargin{:}); +FontWeight = keyval('FontWeight', varargin{:}); +ForegroundColor = keyval('ForegroundColor', varargin{:}); +HorizontalAlignment = keyval('HorizontalAlignment', varargin{:}); +Max = keyval('Max', varargin{:}); +Min = keyval('Min', varargin{:}); +Position = keyval('Position', varargin{:}); +Selected = keyval('Selected', varargin{:}); +String = keyval('String', varargin{:}); +Units = keyval('Units', varargin{:}); +Value = keyval('Value', varargin{:}); +Visible = keyval('Visible', varargin{:}); +Tag = keyval('retag', varargin{:}); % this is to change the tag on the selected items +Style = keyval('restyle', varargin{:}); % this is to change the style on the selected items + +feature = { + 'BackgroundColor' + 'CallBack' + 'Clipping' + 'Enable' + 'FontAngle' + 'FontName' + 'FontSize' + 'FontUnits' + 'FontWeight' + 'ForegroundColor' + 'HorizontalAlignment' + 'Max' + 'Min' + 'Position' + 'Selected' + 'String' + 'Units' + 'Value' + 'Visible' + 'Tag' + 'Style' + }; + +for i=1:length(feature) + val = eval(feature{i}); + if ~isempty(val) + % fprintf('setting %s\n', feature{i}); + for j=1:length(c) + set(c(j), feature{i}, val) + end + end +end + +% these are special features to help with the positioning of the elements +hpos = keyval('hpos', varargin{:}); +vpos = keyval('vpos', varargin{:}); +width = keyval('width', varargin{:}); +height = keyval('height', varargin{:}); + +if isempty(hpos) && isempty(vpos) && isempty(width) && isempty(height) + % re-positioning of the elements is not needed + return +end + +pos = zeros(length(c), 4); +for i=1:length(c) + pos(i,:) = get(c(i), 'position'); +end + +if ~isempty(width) + pos(:,3) = width; +end +width = pos(:,3); + +if ~isempty(height) + pos(:,4) = height; +end +height = pos(:,4); + +if ~isempty(hpos) + if isequal(hpos, 'auto') + scale = (1 - 0.01 - 0.01*length(c)) / sum(width); + if scale>0 && scale<1 + % fprintf('adjusting width with %f\n', scale); + width = width*scale; + pos(:,3) = width; + end + hpos = cumsum([0.01; width+0.01]); + hpos = hpos(1:end-1); + elseif isequal(hpos, 'align') + hpos = pos(1,2); % the position of the first element + elseif isequal(hpos, 'distribute') + minpos = min(pos(:,1)); + maxpos = max(pos(:,1)); + hpos = linspace(minpos, maxpos, length(c)); + end + pos(:,1) = hpos; + pos(:,3) = width; +end % hpos + +if ~isempty(vpos) + if isequal(vpos, 'auto') + scale = (1 - 0.01 - 0.01*length(c)) / sum(height); + if scale>0 && scale<1 + % fprintf('adjusting height with %f\n', scale); + height = height*scale; + pos(:,4) = height; + end + vpos = cumsum([0.01; width]); + vpos = vpos(1:end-1); + elseif isequal(vpos, 'align') + vpos = pos(1,2); % the position of the first element + elseif isequal(vpos, 'distribute') + minpos = min(pos(:,2)); + maxpos = max(pos(:,2)); + vpos = linspace(minpos, maxpos, length(c)); + end + pos(:,2) = vpos; +end % vpos + +% assign the new/automatic position to each of the elements +for i=1:length(c) + set(c(i), 'position', pos(i,:)); +end diff --git a/external/fieldtrip/private/volumelookup.m b/external/fieldtrip/private/volumelookup.m index 98e87e8..543ce9e 100755 --- a/external/fieldtrip/private/volumelookup.m +++ b/external/fieldtrip/private/volumelookup.m @@ -27,7 +27,7 @@ % cfg.box = Nx3 vector, size of each box in cm/mm dep on unit of input % cfg.round2nearestvoxel = 'yes' or 'no' (default = 'no'), voxel closest to point of interest is calculated % and box/sphere is centered around coordinates of that voxel -% configuration options for labels to a mask: +% configuration options for labels from a mask: % cfg.inputcoord = 'mni' or 'tal', coordinate system of the mri/source/stat % cfg.atlas = string, filename of atlas to use, either the AFNI % brik file that is available from http://afni.nimh.nih.gov/afni/doc/misc/ttatlas_tlrc, @@ -42,6 +42,8 @@ % for j = 1:length(sel) % found_areas{j,1} = [num2str(labels.count(ind(j))) ': ' labels.name{ind(j)}]; % end +% in found_areas you can then see how many times which labels are found +% NB in the AFNI brick one location can have 2 labels! % % Dependent on the input coordinates and the coordinates of the atlas, the % input MRI is transformed betweem MNI and Talairach-Tournoux coordinates @@ -50,6 +52,9 @@ % Copyright (C) 2008, Robert Oostenveld, Ingrid Nieuwenhuis % % $Log: volumelookup.m,v $ +% Revision 1.5 2009/08/03 15:20:19 ingnie +% updated help +% % Revision 1.4 2009/01/29 13:54:57 ingnie % changed help % diff --git a/external/fieldtrip/private/volumenormalise.m b/external/fieldtrip/private/volumenormalise.m index b52a92e..3ed4ae7 100644 --- a/external/fieldtrip/private/volumenormalise.m +++ b/external/fieldtrip/private/volumenormalise.m @@ -38,6 +38,9 @@ % Copyright (C) 2004-2006, Jan-Mathijs Schoffelen % % $Log: volumenormalise.m,v $ +% Revision 1.21 2009/07/14 07:27:30 roboos +% replaced read_fcdc_mri with read_mri to avoid warning +% % Revision 1.20 2009/01/20 13:01:31 sashae % changed configtracking such that it is only enabled when BOTH explicitly allowed at start % of the fieldtrip function AND requested by the user @@ -137,7 +140,7 @@ if ischar(interp), fprintf('reading source MRI from file\n'); filename = interp; - interp = read_fcdc_mri(filename); + interp = read_mri(filename); if filetype(filename, 'ctf_mri') % based on the filetype assume that the coordinates correspond with CTF convention source_coordinates = 'ctf'; @@ -312,7 +315,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: volumenormalise.m,v 1.20 2009/01/20 13:01:31 sashae Exp $'; +cfg.version.id = '$Id: volumenormalise.m,v 1.21 2009/07/14 07:27:30 roboos Exp $'; % remember the configuration details of the input data try, cfg.previous = interp.cfg; end % remember the exact configuration details in the output diff --git a/external/fieldtrip/private/volumerealign.m b/external/fieldtrip/private/volumerealign.m index b748a52..b77bf78 100644 --- a/external/fieldtrip/private/volumerealign.m +++ b/external/fieldtrip/private/volumerealign.m @@ -17,6 +17,8 @@ % an MRI). % % The configuration can contain the following options +% cfg.clim = [min max], scaling of the anatomy color (default +% is to adjust to the minimum and maximum) % cfg.method = different methods for aligning the electrodes % 'realignfiducial' realign the volume to the fiducials % 'interactive' manually using graphical user interface @@ -34,11 +36,25 @@ % - the Y-axis goes approximately towards LPA, orthogonal to X and in the plane spanned by the fiducials % - the Z-axis goes approximately towards the vertex, orthogonal to X and Y % -% See also READ_FCDC_MRI, ELECTRODEREALIGN +% +% See also READ_MRI, ELECTRODEREALIGN -% Copyright (C) 2006, Robert Oostenveld +% Copyright (C) 2006-2009, Robert Oostenveld % % $Log: volumerealign.m,v $ +% Revision 1.12 2009/07/31 13:43:36 jansch +% now really fixed a bug (unlike last time) +% +% Revision 1.11 2009/07/30 14:22:00 jansch +% fixed bug in input arguments for volplot +% +% Revision 1.10 2009/07/29 13:53:49 roboos +% added cfg.clim for color scaling, thanks to Hanneke +% +% Revision 2 2009/07/29 13:00:00 hanvdij +% Added colorscaling option in the volplot function at the end of this +% script. +% % Revision 1.9 2009/01/20 13:01:31 sashae % changed configtracking such that it is only enabled when BOTH explicitly allowed at start % of the fieldtrip function AND requested by the user @@ -79,6 +95,7 @@ % set the defaults if ~isfield(cfg, 'fiducial'), cfg.fiducial = []; end if ~isfield(cfg, 'parameter'), cfg.parameter = 'anatomy'; end +if ~isfield(cfg, 'clim'), cfg.clim = []; end if ~isfield(cfg, 'method') if ~isempty(cfg.fiducial) @@ -116,7 +133,7 @@ xc = round(xc); yc = round(yc); zc = round(zc); - volplot(x, y, z, dat, [xc yc zc]); + volplot(x, y, z, dat, [xc yc zc], cfg.clim); drawnow; try, [d1, d2, key] = ginput(1); catch, key='q'; end if key=='q' @@ -170,7 +187,11 @@ mri.transform = vox2head; else warning('removing old transformation matrix'); - mri.transform = vox2head; + scale = eye(4); + %FIXME check whether the following is ever necessary + %origvox2head = mri.transform; + %scale(1:3,1:3) = diag(sqrt(sum(origvox2head(1:3,1:3).^2,2))); + mri.transform = scale*vox2head; end % get the output cfg @@ -185,7 +206,7 @@ [st, i] = dbstack; cfg.version.name = st(i); end -cfg.version.id = '$Id: volumerealign.m,v 1.9 2009/01/20 13:01:31 sashae Exp $'; +cfg.version.id = '$Id: volumerealign.m,v 1.12 2009/07/31 13:43:36 jansch Exp $'; % remember the configuration mri.cfg = cfg; @@ -193,12 +214,19 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % helper function to show three orthogonal slices %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function volplot(x, y, z, dat, c); +function volplot(x, y, z, dat, c, cscale); xi = c(1); yi = c(2); zi = c(3); -cmin = min(dat(:)); -cmax = max(dat(:)); + +% manual color scaling of anatomy data is usefull in case of some pixel noise +if nargin<6 || isempty(cscale) + cmin = min(dat(:)); + cmax = max(dat(:)); +else + cmin = cscale(1); + cmax = cscale(2); +end h1 = subplot(2,2,1); h2 = subplot(2,2,2); diff --git a/external/fieldtrip/private/volumesegment.m b/external/fieldtrip/private/volumesegment.m index 45d715a..406c5a2 100644 --- a/external/fieldtrip/private/volumesegment.m +++ b/external/fieldtrip/private/volumesegment.m @@ -54,6 +54,9 @@ % cfg.segment = 'yes' or 'no' % $Log: volumesegment.m,v $ +% Revision 1.14 2009/07/14 07:27:30 roboos +% replaced read_fcdc_mri with read_mri to avoid warning +% % Revision 1.13 2009/01/20 13:01:31 sashae % changed configtracking such that it is only enabled when BOTH explicitly allowed at start % of the fieldtrip function AND requested by the user @@ -176,7 +179,7 @@ % read the anatomical MRI data from file filename = mri; fprintf('reading MRI from file\n'); - mri = read_fcdc_mri(filename); + mri = read_mri(filename); if filetype(filename, 'ctf_mri') && isempty(cfg.coordinates) % based on the filetype assume that the coordinates correspond with CTF convention cfg.coordinates = 'ctf'; @@ -333,7 +336,7 @@ [st, i] = dbstack; cfg.version.name = st(i).name; end -cfg.version.id = '$Id: volumesegment.m,v 1.13 2009/01/20 13:01:31 sashae Exp $'; +cfg.version.id = '$Id: volumesegment.m,v 1.14 2009/07/14 07:27:30 roboos Exp $'; % remember the configuration details of the input data try, cfg.previous = mri.cfg; end % remember the exact configuration details in the output diff --git a/external/fieldtrip/private/write_data.m b/external/fieldtrip/private/write_data.m index abbaf7e..7b2f15b 100644 --- a/external/fieldtrip/private/write_data.m +++ b/external/fieldtrip/private/write_data.m @@ -24,12 +24,16 @@ function write_data(filename, dat, varargin) % fcdc_buffer % plexon_nex % neuralynx_ncs +% neuralynx_sdma % % See also READ_HEADER, READ_DATA, READ_EVEN, WRITE_EVENT % Copyright (C) 2007-2008, Robert Oostenveld % % $Log: write_data.m,v $ +% Revision 1.23 2009/06/17 10:15:43 roboos +% added support for neuralynx_sdma, only for 32 bit integer data +% % Revision 1.22 2009/05/19 18:29:48 roboos % give instructive error if the fieldtrip buffer mex file is not on the path % @@ -126,7 +130,7 @@ function write_data(filename, dat, varargin) case 'disp' % display it on screen, this is only for debugging disp('new data arived'); - + case 'fcdc_global' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % store it in a global variable, this is only for debugging @@ -139,21 +143,21 @@ function write_data(filename, dat, varargin) else data_queue = cat(2, data_queue, dat); end - + case 'fcdc_buffer' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % network transparent buffer %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - + if ~isempty(chanindx) % assume that the header corresponds to the original multichannel % file and that the data represents a subset of channels hdr.label = hdr.label(chanindx); hdr.nChans = length(chanindx); end - + [host, port] = filetype_check_uri(filename); - + type = { 'char' 'uint8' @@ -167,7 +171,7 @@ function write_data(filename, dat, varargin) 'single' 'double' }; - + wordsize = { 1 % 'char' 1 % 'uint8' @@ -181,7 +185,7 @@ function write_data(filename, dat, varargin) 4 % 'single' 8 % 'double' }; - + % this should only be done the first time if ~append && ~isempty(hdr) % reformat the header into a buffer-compatible format @@ -190,25 +194,25 @@ function write_data(filename, dat, varargin) packet.nsamples = 0; packet.nevents = 0; packet.data_type = find(strcmp(type, class(dat))) - 1; % zero-offset - + % try to put_hdr and initialize if necessary try % try writing the packet buffer('put_hdr', packet, host, port); - + catch if ~isempty(strfind(lasterr, 'Buffer size N must be an integer-valued scalar double.')) % this happens if the MATLAB75/toolbox/signal/signal/buffer % function is used instead of the fieldtrip buffer error('the FieldTrip buffer mex file was not found on your path, it should be in fieldtrip/fileio/private'); - + elseif ~isempty(strfind(lasterr, 'failed to create socket')) && (strcmp(host, 'localhost') || strcmp(host, '127.0.0.1')) - + % start a local instance of the TCP server warning('starting fieldtrip buffer on %s:%d', host, port); buffer('tcpserver', 'init', host, port); pause(1); - + % rewrite the packet until success success = false; while ~success @@ -222,11 +226,11 @@ function write_data(filename, dat, varargin) end end end % if strfind... - + end % try - + end % writing header - + if ~isempty(dat) max_nsamples = 32556; if size(dat,2)>max_nsamples @@ -257,7 +261,7 @@ function write_data(filename, dat, varargin) buffer('put_dat', packet, host, port); end % if data larger than chuncksize end - + case 'brainvision_eeg' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % combination of *.eeg and *.vhdr file @@ -265,7 +269,7 @@ function write_data(filename, dat, varargin) if append error('appending data is not yet supported for this data format'); end - + if nchans~=hdr.nChans && length(chanindx)==nchans % assume that the header corresponds to the original multichannel % file and that the data represents a subset of channels @@ -277,7 +281,7 @@ function write_data(filename, dat, varargin) % hdr.nChans % hdr.Fs write_brainvision_eeg(filename, hdr, dat); - + case 'fcdc_matbin' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % multiplexed data in a *.bin file (ieee-le, 64 bit floating point values), @@ -286,7 +290,7 @@ function write_data(filename, dat, varargin) if append error('appending data is not yet supported for this data format'); end - + [path, file, ext] = fileparts(filename); headerfile = fullfile(path, [file '.mat']); datafile = fullfile(path, [file '.bin']); @@ -302,7 +306,7 @@ function write_data(filename, dat, varargin) [fid,message] = fopen(datafile,'wb','ieee-le'); fwrite(fid, dat, 'double'); fclose(fid); - + case 'fcdc_mysql' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % write to a MySQL server listening somewhere else on the network @@ -329,7 +333,7 @@ function write_data(filename, dat, varargin) end db_insert('fieldtrip.header', s); end - + elseif isempty(hdr) && ~isempty(dat) dim = size(dat); if numel(dim)==2 @@ -356,11 +360,11 @@ function write_data(filename, dat, varargin) db_insert('fieldtrip.data', s); end end - + else error('you should specify either the header or the data when writing to a MySQL database'); end - + case 'matlab' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % plain matlab file @@ -385,7 +389,104 @@ function write_data(filename, dat, varargin) % file does not yet exist, which is not a problem end save(filename, 'dat', 'hdr'); - + + case 'neuralynx_sdma' + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % The first version of this file format contained in the first 8 bytes the + % channel label as string. Subsequently it contained 32 bit integer values. + % + % The second version of this file format starts with 8 bytes describing (as + % a space-padded string) the data type. The channel label is contained in + % the filename as dataset.chanlabel.bin. + % + % The third version of this file format starts with 7 bytes describing (as + % a zero-padded string) the data type, followed by the 8th byte which + % describes the downscaling for the 8 and 16 bit integer representations. + % The downscaling itself is represented as uint8 and should be interpreted as + % the number of bits to shift. The channel label is contained in the + % filename as dataset.chanlabel.bin. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + statuschannel = { + 'stx' + 'pid' + 'siz' + 'tsh' + 'tsl' + 'cpu' + 'ttl' + 'x01' + 'x02' + 'x03' + 'x04' + 'x05' + 'x06' + 'x07' + 'x08' + 'x09' + 'x10' + 'crc' + }; + + dirname = filename; + clear filename + [path, file] = fileparts(dirname); + for i=1:hdr.nChans + downscale(i) = 0; + if ~isempty(strmatch(hdr.label{i}, statuschannel)) + format{i} = 'uint32'; + else + format{i} = 'int32'; + end + filename{i} = fullfile(dirname, [file '.' hdr.label{i} '.bin']); + end + + if ~isdir(dirname) + mkdir(dirname); + end + + % open and write to the output files, one for each selected channel + fid = zeros(hdr.nChans,1); + for j=1:hdr.nChans + + if append==false + fid(j) = fopen(filename{j}, 'wb', 'ieee-le'); % open the file + magic = format{j}; % this used to be the channel name + magic((end+1):8) = 0; % pad with zeros + magic(8) = downscale(j); % number of bits to shift + fwrite(fid(j), magic(1:8)); % write the 8-byte file header + else + fid(j) = fopen(filename{j}, 'ab', 'ieee-le'); % open the file for appending + end % if append + + % convert the data into the correct class + buf = dat(j,:); + if ~strcmp(class(buf), format{j}) + switch format{j} + case 'int16' + buf = int16(buf); + case 'int32' + buf = int32(buf); + case 'single' + buf = single(buf); + case 'double' + buf = double(buf); + case 'uint32' + buf = uint32(buf); + otherwise + error('unsupported format conversion'); + end + end + + % apply the scaling, this corresponds to bit shifting + buf = buf ./ (2^downscale(j)); + + % write the segment of data to the output file + fwrite(fid(j), buf, format{j}, 'ieee-le'); + + fclose(fid(j)); + end % for each channel + case 'riff_wave' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % This writes data Y to a Windows WAVE file specified by the file name @@ -396,7 +497,7 @@ function write_data(filename, dat, varargin) if append error('appending data is not yet supported for this data format'); end - + if nchans~=hdr.nChans && length(chanindx)==nchans % assume that the header corresponds to the original multichannel % file and that the data represents a subset of channels @@ -407,7 +508,7 @@ function write_data(filename, dat, varargin) error('this format only supports single channel continuous data'); end wavwrite(dat, hdr.Fs, nbits, filename); - + case 'plexon_nex' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % single or mulitple channel Plexon NEX file @@ -415,8 +516,8 @@ function write_data(filename, dat, varargin) if append error('appending data is not yet supported for this data format'); end - - [path, file, ext] = fileparts(filename); + + [path, file] = fileparts(filename); filename = fullfile(path, [file, '.nex']); if nchans~=1 error('only supported for single-channel data'); @@ -435,15 +536,15 @@ function write_data(filename, dat, varargin) end nex.var.indx = 0; nex.var.dat = dat; - + write_plexon_nex(filename, nex); - + if 0 % the following code snippet can be used for testing nex2 = []; [nex2.var, nex2.hdr] = read_plexon_nex(filename, 'channel', 1); end - + case 'neuralynx_ncs' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % single channel Neuralynx NCS file @@ -451,14 +552,14 @@ function write_data(filename, dat, varargin) if append error('appending data is not yet supported for this data format'); end - + if nchans>1 error('only supported for single-channel data'); end - + [path, file, ext] = fileparts(filename); filename = fullfile(path, [file, '.ncs']); - + if nchans~=hdr.nChans && length(chanindx)==nchans % assume that the header corresponds to the original multichannel % file and that the data represents a subset of channels @@ -472,35 +573,35 @@ function write_data(filename, dat, varargin) else error('cannot determine channel label'); end - + FSAMPLE = hdr.Fs; RECORDNSMP = 512; RECORDSIZE = 1044; - + % cut the downsampled LFP data into record-size pieces nrecords = ceil(nsamples/RECORDNSMP); fprintf('construct ncs with %d records\n', nrecords); - + % construct a ncs structure with all header details and the data in it ncs = []; ncs.NumValidSamp = ones(1,nrecords) * RECORDNSMP; % except for the last block ncs.ChanNumber = ones(1,nrecords) * ADCHANNEL; ncs.SampFreq = ones(1,nrecords) * FSAMPLE; ncs.TimeStamp = zeros(1,nrecords,'uint64'); - + if rem(nsamples, RECORDNSMP)>0 % the data length is not an integer number of records, pad the last record with zeros dat = cat(2, dat, zeros(nchans, nrecords*RECORDNSMP-nsamples)); ncs.NumValidSamp(end) = rem(nsamples, RECORDNSMP); end - + ncs.dat = reshape(dat, RECORDNSMP, nrecords); - + for i=1:nrecords % timestamps should be 64 bit unsigned integers ncs.TimeStamp(i) = uint64(hdr.FirstTimeStamp) + uint64((i-1)*RECORDNSMP*hdr.TimeStampPerSample); end - + % add the elements that will go into the ascii header ncs.hdr.CheetahRev = '4.23.0'; ncs.hdr.NLX_Base_Class_Type = 'CscAcqEnt'; @@ -508,16 +609,16 @@ function write_data(filename, dat, varargin) ncs.hdr.RecordSize = RECORDSIZE; ncs.hdr.ADChannel = ADCHANNEL; ncs.hdr.SamplingFrequency = FSAMPLE; - + % write it to a file fprintf('writing to %s\n', filename); write_neuralynx_ncs(filename, ncs); - + if 0 % the following code snippet can be used for testing ncs2 = read_neuralynx_ncs(filename, 1, inf); end - + otherwise error('unsupported data format'); end % switch dataformat diff --git a/external/fileio/private/apply_montage.m b/external/fileio/private/apply_montage.m index e7a0f07..ccf0716 100644 --- a/external/fileio/private/apply_montage.m +++ b/external/fileio/private/apply_montage.m @@ -7,7 +7,11 @@ % forward computation and source reconstruction of the data. % % Use as -% [sens] = apply_montage(sens, montage, ...) +% [sens] = apply_montage(sens, montage, ...) +% [data] = apply_montage(data, montage, ...) +% [montage] = apply_montage(montage1, montage2, ...) +% where the input is a FieldTrip sensor definition as obtained from READ_SENS +% or a FieldTrip raw data structure as obtained from PREPROCESSING. % % A montage is specified as a structure with the fields % montage.tra = MxN matrix @@ -18,11 +22,35 @@ % 'keepunused' string, 'yes' or 'no' (default = 'no') % 'inverse' string, 'yes' or 'no' (default = 'no') % +% If the first input is a montage, then the second input montage will be +% applied to the first. In effect the resulting montage will first do +% montage1, then montage2. +% % See also READ_SENS, TRANSFORM_SENS % Copyright (C) 2008, Robert Oostenveld % % $Log: apply_montage.m,v $ +% Revision 1.19 2009/07/02 16:14:15 roboos +% allow to apply one montage on another one +% +% Revision 1.18 2009/07/02 09:18:41 vlalit +% Fixing a bug in building an inverse montage (confusion between 'montage' and 'tra') +% +% Revision 1.17 2009/07/01 09:21:37 roboos +% changed handling of rank-reduced montage for inversion, give warning +% removed the modification by vladimir for the sequential application of montages +% +% Revision 1.16 2009/06/27 13:24:54 vlalit +% Additional fixes to make custom balancing work. +% +% Revision 1.15 2009/06/26 17:39:17 vlalit +% Added the possiblity to handle custom montages applied to MEG sensors (for removing +% spatial confounds). Hopefully there won't be major side effects. +% +% Revision 1.14 2009/06/17 10:13:05 roboos +% improved documentation +% % Revision 1.13 2009/03/26 11:07:40 roboos % start with an explicit check on the channel number % @@ -70,6 +98,12 @@ keepunused = keyval('keepunused', varargin{:}); if isempty(keepunused), keepunused = 'no'; end inverse = keyval('inverse', varargin{:}); if isempty(inverse), inverse = 'no'; end +% check the consistency of the input sensor array or data +if isfield(sens, 'labelorg') && isfield(sens, 'labelnew') + % the input data structure is also a montage, i.e. apply the montages sequentially + sens.label = sens.labelnew; +end + % check the consistency of the montage if size(montage.tra,1)~=length(montage.labelnew) error('the number of channels in the montage is inconsistent'); @@ -79,9 +113,15 @@ if strcmp(inverse, 'yes') % apply the inverse montage, i.e. undo a previously applied montage - tmp.labelnew = montage.labelorg; - tmp.labelorg = montage.labelnew; - tmp.tra = inv(montage.tra); + tmp.labelnew = montage.labelorg; % swap around + tmp.labelorg = montage.labelnew; % swap around + tmp.tra = full(montage.tra); + if rank(tmp.tra) < length(tmp.tra) + warning('the linear projection for the montage is not full-rank, the resulting data will have reduced dimensionality'); + tmp.tra = pinv(tmp.tra); + else + tmp.tra = inv(tmp.tra); + end montage = tmp; end @@ -150,10 +190,17 @@ montage.tra = sparse(montage.tra(:,selmont)); montage.labelorg = montage.labelorg(selmont); -if isfield(sens, 'tra') +if isfield(sens, 'labelorg') && isfield(sens, 'labelnew') + % apply the montage on top of the other montage + sens = rmfield(sens, 'label'); + sens.tra = montage.tra * sens.tra; + sens.labelnew = montage.labelnew; + +elseif isfield(sens, 'tra') % apply the montage to the sensor array sens.tra = montage.tra * sens.tra; sens.label = montage.labelnew; + elseif isfield(sens, 'trial') % apply the montage to the data that was preprocessed using fieldtrip Ntrials = numel(sens.trial); @@ -177,5 +224,3 @@ function y = indx2logical(x, n) y = false(1,n); y(x) = true; - - diff --git a/external/fileio/private/ctf2grad.m b/external/fileio/private/ctf2grad.m index 1910b30..7a8138d 100644 --- a/external/fileio/private/ctf2grad.m +++ b/external/fileio/private/ctf2grad.m @@ -14,6 +14,9 @@ % Copyright (C) 2004, Robert Oostenveld % % $Log: ctf2grad.m,v $ +% Revision 1.4 2009/08/05 08:36:57 roboos +% preallocate the memory that will hold the coil positions, orientations and weights -> significant speedup +% % Revision 1.3 2009/03/23 21:16:03 roboos % don't give error if balancing fails, but only warning and remove the balancing information % @@ -100,6 +103,16 @@ selREF = selREF(:)'; numMEG = length(selMEG); numREF = length(selREF); + + % determine the number of channels and coils + coilcount = 0; + coilcount = coilcount + sum([hdr.res4.senres(selREF).numCoils]); + coilcount = coilcount + sum([hdr.res4.senres(selMEG).numCoils]); + chancount = numMEG + numREF; + % preallocate the memory + grad.pnt = zeros(coilcount, 3); % this will hold the position of each coil + grad.ori = zeros(coilcount, 3); % this will hold the orientation of each coil + grad.tra = zeros(chancount, coilcount); % this describes how each coil contributes to each channel % combine the bottom and top coil of each MEG channel for i=1:numMEG @@ -123,6 +136,10 @@ grad.tra(i,i ) = 1; grad.tra(i,i+numMEG) = 1; end + + % the MEG channels always have 2 coils, the reference channels vary in the number of coils + chancount = 1*numMEG; + coilcount = 2*numMEG; % combine the coils of each reference channel for i=1:numREF @@ -136,12 +153,14 @@ ori = hdr.res4.senres(n).ori'; end % determine the number of coils for this channel - numcoils = sum(sum(pos.^2, 2)~=0); + numcoils = hdr.res4.senres(n).numCoils; % add the coils of this channel to the gradiometer array + chancount = chancount+1; for j=1:numcoils - grad.pnt(end+1, :) = pos(j,:); - grad.ori(end+1, :) = ori(j,:) .* -sign(hdr.res4.senres(n).properGain); - grad.tra(i+numMEG, end+1) = 1; + coilcount = coilcount+1; + grad.pnt(coilcount, :) = pos(j,:); + grad.ori(coilcount, :) = ori(j,:) .* -sign(hdr.res4.senres(n).properGain); + grad.tra(chancount, coilcount) = 1; end end @@ -260,9 +279,9 @@ numcoils = sum(sum(pos.^2, 2)~=0); % add the coils of this channel to the gradiometer array for j=1:numcoils - grad.pnt(end+1, :) = pos(j,:); - grad.ori(end+1, :) = ori(j,:) .* -sign(hdr.gainV(n)); - grad.tra(i+numMEG, end+1) = 1; + grad.pnt(numMEG+i, :) = pos(j,:); + grad.ori(numMEG+i, :) = ori(j,:) .* -sign(hdr.gainV(n)); + grad.tra(numMEG+i, 2*numMEG+i) = 1; end end diff --git a/external/fileio/private/filetype.m b/external/fileio/private/filetype.m index b454bfd..25515ab 100644 --- a/external/fileio/private/filetype.m +++ b/external/fileio/private/filetype.m @@ -54,6 +54,9 @@ % Copyright (C) 2003-2007 Robert Oostenveld % % $Log: filetype.m,v $ +% Revision 1.98 2009/07/07 10:38:38 roboos +% added header check for dicom +% % Revision 1.97 2009/05/06 15:42:06 roboos % also remember/check previous directory and don't do filetype caching in case type=unknown % @@ -903,7 +906,7 @@ type = 'layout'; manufacturer = 'Ole Jensen'; content = 'layout of channels for plotting'; -elseif filetype_check_extension(filename, '.dcm') || filetype_check_extension(filename, '.ima') +elseif filetype_check_extension(filename, '.dcm') || filetype_check_extension(filename, '.ima') || filetype_check_header(filename, 'DICM', 128) type = 'dicom'; manufacturer = 'Dicom'; content = 'image data'; diff --git a/external/fileio/private/hastoolbox.m b/external/fileio/private/hastoolbox.m index 8289f0d..79640d3 100644 --- a/external/fileio/private/hastoolbox.m +++ b/external/fileio/private/hastoolbox.m @@ -10,6 +10,9 @@ % Copyright (C) 2005-2008, Robert Oostenveld % % $Log: hastoolbox.m,v $ +% Revision 1.36 2009/09/08 14:34:01 roboos +% also detect 64 bit windows version (thanks to arno) +% % Revision 1.35 2009/04/21 09:54:15 roboos % added prtools % @@ -318,7 +321,7 @@ % for windows computers in the F.C. Donders Centre prefix = 'h:\common\matlab'; - if ~status && strcmp(computer, 'PCWIN') + if ~status && (strcmp(computer, 'PCWIN') || strcmp(computer, 'PCWIN64')) status = myaddpath(fullfile(prefix, lower(toolbox)), silent); end diff --git a/external/fileio/private/issubfield.m b/external/fileio/private/issubfield.m index f494642..60d94f4 100644 --- a/external/fileio/private/issubfield.m +++ b/external/fileio/private/issubfield.m @@ -1,4 +1,4 @@ -function [r] = issubfield(s, f); +function [r] = issubfield(s, f) % ISSUBFIELD tests for the presence of a field in a structure just like the standard % Matlab ISFIELD function, except that you can also specify nested fields @@ -17,6 +17,9 @@ % Copyright (C) 2005, Robert Oostenveld % % $Log: issubfield.m,v $ +% Revision 1.2 2009/07/30 20:11:44 ingnie +% made output boolian +% % Revision 1.1 2008/11/13 09:55:36 roboos % moved from fieldtrip/private, fileio or from roboos/misc to new location at fieldtrip/public % @@ -26,7 +29,7 @@ try getsubfield(s, f); % if this works, then the subfield must be present - r = 1; + r = true; catch - r = 0; % apparently the subfield is not present + r = false; % apparently the subfield is not present end \ No newline at end of file diff --git a/external/fileio/private/keyval.m b/external/fileio/private/keyval.m index b9fefe7..106cf02 100644 --- a/external/fileio/private/keyval.m +++ b/external/fileio/private/keyval.m @@ -11,6 +11,12 @@ % Copyright (C) 2005-2007, Robert Oostenveld % % $Log: keyval.m,v $ +% Revision 1.5 2009/08/04 11:58:28 roboos +% perform a case-insensitive string comparison for keys +% +% Revision 1.4 2009/07/14 16:11:02 roboos +% speed up the input checks +% % Revision 1.3 2009/05/14 19:24:02 roboos % removed ; at end of function declaration % @@ -35,22 +41,20 @@ error('optional input arguments should come in key-value pairs, i.e. there should be an even number'); end -for i=1:2:length(varargin) - if ~ischar(varargin{i}) - % the 1st, 3rd, etc. contain the keys, the 2nd, 4th, etc. contain the values - error('optional input arguments should come in key-value pairs, the optional input argument %d is invalid (should be a string)', i); - end -end - +% the 1st, 3rd, etc. contain the keys, the 2nd, 4th, etc. contain the values keys = varargin(1:2:end); vals = varargin(2:2:end); -hit = find(strcmp(key, keys)); +if ~all(cellfun(@ischar, keys)) + error('optional input arguments should come in key-value pairs, the optional input argument %d is invalid (should be a string)', i); +end + +hit = find(strcmpi(key, keys)); if isempty(hit) % the requested key was not found val = []; elseif length(hit)==1 - % the requested key was found + % the requested key was found val = vals{hit}; else error('multiple input arguments with the same name'); diff --git a/external/fileio/private/read_24bit.c b/external/fileio/private/read_24bit.c index 95c8998..fdb3b16 100644 --- a/external/fileio/private/read_24bit.c +++ b/external/fileio/private/read_24bit.c @@ -19,6 +19,9 @@ * * * $Log: read_24bit.c,v $ + * Revision 1.4 2009/06/08 16:33:08 roboos + * include sys/types.h for all platforms, not only WIN32/64, since also required for OSX 10.4 + * * Revision 1.3 2009/03/30 07:23:03 roboos * changed ifdef syntax * @@ -59,11 +62,11 @@ #define _LARGEFILE_SOURCE #include +#include #include "mex.h" #include "matrix.h" #if defined(_WIN32) || defined(_WIN64) -#include #define int32_t INT32_T #define int64_t INT64_T #define fseeko fseek diff --git a/external/fileio/private/read_combined_ds.m b/external/fileio/private/read_combined_ds.m new file mode 100644 index 0000000..96d9c1c --- /dev/null +++ b/external/fileio/private/read_combined_ds.m @@ -0,0 +1,147 @@ +function [dat] = read_combined_ds(dirname, hdr, begsample, endsample, chanindx) + +% READ_COMBINED_DS +% +% Use as +% hdr = read_combined_ds(dirname) +% dat = read_combined_ds(dirname, hdr, begsample, endsample, chanindx) + +% Copyright (C) 2008, Robert Oostenveld +% +% $Log: read_combined_ds.m,v $ +% Revision 1.1 2009/01/14 09:24:45 roboos +% moved even more files from fileio to fileio/privtae, see previous log entry +% +% Revision 1.1 2008/07/01 16:23:02 roboos +% added read_combined_data (new implementation) +% + +needhdr = nargin==1 || isempty(hdr); +needdat = nargin>1; + +supported = { + 'plexon_nex' + 'neuralynx_bin' + 'neuralynx_ncs' + 'fcdc_matbin' + }; + +if needhdr + + ls = dir(dirname); % get the list of filenames + ls = ls(~cell2mat({ls.isdir})); % remove the subdirs + + nfiles = length(ls); + fname = cell(nfiles,1); + ftype = cell(nfiles,1); + sel = false(nfiles,1); + + for i=1:nfiles + fname{i} = fullfile(dirname, ls(i).name); + ftype{i} = filetype(fname{i}); + sel(i) = any(strcmp(ftype{i}, supported)); + [p, f, x] = fileparts(fname{i}); + if filetype_check_extension(fname{i}, '.mat') + % select only the *.bin and not the *.mat of each pair + sel(i) = false; + end + end + + if ~any(sel) + error('no supported files were found'); + end + + fname = fname(sel); + ftype = ftype(sel); + nfiles = length(fname); + + clear filehdr + for i=1:nfiles + filehdr(i) = read_header(fname{i}, 'headerformat', ftype{i}); + end + + if any([filehdr.nChans]~=1) + error('more than one channel per file not supported'); + else + hdr.nChans = sum([filehdr.nChans]); + end + + if length(unique([filehdr.label]))~=nfiles + error('not all channels have a unique name'); + else + hdr.label = [filehdr.label]'; + end + + if any(diff([filehdr.Fs])) + error('different sampling frequenties per file not supported'); + else + hdr.Fs = filehdr(1).Fs; + end + + if any(diff([filehdr.nSamples])) + error('different nSamples per file not supported'); + else + hdr.nSamples = filehdr(1).nSamples; + end + + if any(diff([filehdr.nSamplesPre])) + error('different nSamplesPre per file not supported'); + else + hdr.nSamplesPre = filehdr(1).nSamplesPre; + end + + if any(diff([filehdr.nTrials])) + error('different nTrials per file not supported'); + else + hdr.nTrials = filehdr(1).nTrials; + end + + if isfield(filehdr, 'TimeStampPerSample') && any(diff([filehdr.TimeStampPerSample])) + error('different TimeStampPerSample per file not supported'); + else + hdr.TimeStampPerSample = filehdr(1).TimeStampPerSample; + end + + if isfield(filehdr, 'FirstTimeStamp') && any(diff([filehdr.FirstTimeStamp])) + error('different FirstTimeStamp per file not supported'); + else + hdr.FirstTimeStamp = filehdr(1).FirstTimeStamp; + end + + % remember the original header details + hdr.orig.header = filehdr; + hdr.orig.fname = fname; + hdr.orig.ftype = ftype; +end + +if needdat + filehdr = hdr.orig.header; + fname = hdr.orig.fname; + ftype = hdr.orig.ftype; + + if nargin<3 + begsample=1; + end + if nargin<4 + endsample = hdr.nSamples*hdr.nTrials; + end + if nargin<5 + chanindx = 1:hdr.nChans; + end + + nsamples = endsample-begsample+1; + nchans = length(chanindx); + dat = zeros(nchans, nsamples); + + for i=1:nchans + thischan = chanindx(i); + tmp = read_data(fname{thischan}, 'header', filehdr(thischan), 'dataformat', ftype{thischan}, 'begsample', begsample, 'endsample', endsample); + dat(i,:) = tmp; + end +end + +if ~needdat + % return the header + dat = hdr; +end + diff --git a/external/fileio/private/read_eeglabdata.m b/external/fileio/private/read_eeglabdata.m index 730adbf..4c2f13e 100644 --- a/external/fileio/private/read_eeglabdata.m +++ b/external/fileio/private/read_eeglabdata.m @@ -36,6 +36,10 @@ % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA % $Log: read_eeglabdata.m,v $ +% Revision 1.3 2009/07/01 15:35:19 vlalit +% Try several different ways of looking for the data file +% before giving up (fix suggested by Jakob Scherer) +% % Revision 1.2 2009/02/27 12:03:09 vlalit % Arno's fix for a bug reported by Antanas Spokas % @@ -72,7 +76,20 @@ if strcmpi(header.orig.data(end-2:end), 'set'), header.ori = load('-mat', filename); else - fid = fopen(header.orig.data); + + % assuming that the data file is in the current directory + fid = fopen(header.orig.data); + + % assuming the .dat and .set files are located in the same directory + if fid == -1 + pathstr = fileparts(filename); + fid = fopen(fullfile(pathstr, header.orig.data)); + end + + if fid == -1 + fid = fopen(fullfile(header.orig.filepath, header.orig.data)); % + end + if fid == -1, error('Cannot not find data file'); end; % only read the desired trials diff --git a/external/fileio/private/read_eeglabevent.m b/external/fileio/private/read_eeglabevent.m index 1c438b7..28c0f6a 100644 --- a/external/fileio/private/read_eeglabevent.m +++ b/external/fileio/private/read_eeglabevent.m @@ -33,6 +33,12 @@ % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA % $Log: read_eeglabevent.m,v $ +% Revision 1.4 2009/08/29 05:11:31 josdie +% After consultation with Arno, changed so that value (when present) and type fields in EEGlab .set files are treated as being reversed from value and type fields in FieldTrip files. +% +% Revision 1.3 2009/08/09 01:45:24 josdie +% Changed event value to equal EEGlab's event value rather than type. +% % Revision 1.2 2009/02/02 20:45:34 josdie % FieldTrip's .value field now set to EEGlab's .type field. FieldTrip's .type field set to 'trigger'. FieldTrip's .duration field set to 0 rather than empty. % @@ -64,8 +70,14 @@ event = []; oldevent = header.orig.event; for index = 1:length(oldevent) - event(index).value = num2str( oldevent(index).type ); - event(index).type = 'trigger'; + event(index).value = num2str( oldevent(index).type ); + if isfield(oldevent,'code') + event(index).type = oldevent(index).code; + elseif isfield(oldevent,'value') + event(index).type = oldevent(index).value; + else + event(index).type = 'trigger'; + end; if header.nTrials > 1 event(index).sample = oldevent(index).latency-header.nSamplesPre; event(index).offset = header.nSamplesPre; diff --git a/external/fileio/private/read_eeglabheader.m b/external/fileio/private/read_eeglabheader.m index 65845cd..5ab7e60 100644 --- a/external/fileio/private/read_eeglabheader.m +++ b/external/fileio/private/read_eeglabheader.m @@ -30,6 +30,18 @@ % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA % $Log: read_eeglabheader.m,v $ +% Revision 1.6 2009/08/08 04:07:02 josdie +% Bug in Joe's brain fixed. Change to header.nSamplesPre calculation changed back. +% +% Revision 1.5 2009/08/08 03:17:26 josdie +% Fixed bug that was causing hdr.label to have as many labels as there are time points rather than matching the number of channels. +% +% Revision 1.4 2009/08/08 03:05:29 josdie +% Fixed bug in calculation of header.nSamplesPre. +% +% Revision 1.3 2009/07/01 16:08:21 vlalit +% Fixing a bug in converting channel locations to elec struct (reproted by Jakib Scherer) +% % Revision 1.2 2009/01/23 15:35:46 roboos % create default channel names if EEG.chanlocs.labels is missing % @@ -70,18 +82,20 @@ header.label = { EEG.chanlocs.labels }'; catch warning('creating default channel names'); - for i=1:header.nSamples + for i=1:header.nChans header.label{i} = sprintf('chan%03d', i); end end -for ind = 1:length( EEG.chanlocs ) - header.elec.label{ind} = EEG.chanlocs(ind).labels; - if ~isempty(EEG.chanlocs(ind).X) - % this channel has a position - header.elec.pnt(ind,1) = EEG.chanlocs(ind).X; - header.elec.pnt(ind,2) = EEG.chanlocs(ind).Y; - header.elec.pnt(ind,3) = EEG.chanlocs(ind).Z; - end; +ind = 1; +for i = 1:length( EEG.chanlocs ) + if ~isempty(EEG.chanlocs(i).X) + header.elec.label{ind, 1} = EEG.chanlocs(i).labels; + % this channel has a position + header.elec.pnt(ind,1) = EEG.chanlocs(i).X; + header.elec.pnt(ind,2) = EEG.chanlocs(i).Y; + header.elec.pnt(ind,3) = EEG.chanlocs(i).Z; + ind = ind+1; + end; end; % remove data diff --git a/external/fileio/private/read_event.m b/external/fileio/private/read_event.m index 4e1748b..bb88b15 100644 --- a/external/fileio/private/read_event.m +++ b/external/fileio/private/read_event.m @@ -59,6 +59,19 @@ % Copyright (C) 2004-2008, Robert Oostenveld % % $Log: read_event.m,v $ +% Revision 1.103 2009/08/21 12:03:42 vlalit +% Fixed a bug with handling of 16 and 32-bit Neuroscan cnt variants. +% +% Revision 1.102 2009/08/09 03:34:55 josdie +% Modified egi_egia so that combined subject average files have the cell names start with a four character subject code (e.g., S001) so that other software can decode the subject number more reliably. +% +% Revision 1.101 2009/07/28 11:22:54 roboos +% improved detection of binary trigger channels for neuromag +% +% Revision 1.100 2009/06/09 13:54:30 marvger +% removed warning for empty events; interferes with continuous pooling in +% realtime applications +% % Revision 1.99 2009/05/22 09:02:29 marvger % changed tcp handling % @@ -897,7 +910,7 @@ event(eventCount).sample = (eventCount-1)*hdr.nSamples + 1; event(eventCount).offset = -hdr.nSamplesPre; event(eventCount).duration = hdr.nSamples; - event(eventCount).value = ['S' num2str(subject) cnames{cell}]; + event(eventCount).value = ['S' sprintf('%03d',subject) cnames{cell}]; end end @@ -1221,9 +1234,13 @@ end if iscontinuous - binary = {'STI 014', 'STI 015', 'STI 016'}; - binaryindx = match_str(hdr.label, binary); analogindx = strmatch('STI', hdr.label); + binaryindx = find(strcmp(chantype(hdr), 'trigger')); + if isempty(binaryindx) + % use a predefined set of channel names + binary = {'STI 014', 'STI 015', 'STI 016'}; + binaryindx = match_str(hdr.label, binary); + end if ~isempty(binaryindx) % add triggers based on the binary trigger channel, this is based on @@ -1518,10 +1535,10 @@ event(end ).offset = -hdr.nSamplesPre; event(end ).value = []; - case 'ns_cnt' + case {'ns_cnt', 'ns_cnt16', 'ns_cnt32'} % read the header, the original header includes the event table if isempty(hdr) - hdr = read_header(filename); + hdr = read_header(filename, 'headerformat', eventformat); end % translate the event table into known FieldTrip event types for i=1:hdr.orig.nevent @@ -1618,8 +1635,8 @@ % this has the side effect that events without a sample number are discarded [dum, indx] = sort([event.sample]); event = event(indx); -else - warning(sprintf('no events found in %s', filename)); +% else +% warning(sprintf('no events found in %s', filename)); end % apply the optional filters diff --git a/external/fileio/private/read_header.m b/external/fileio/private/read_header.m index 7b72f3e..37afb3b 100644 --- a/external/fileio/private/read_header.m +++ b/external/fileio/private/read_header.m @@ -55,6 +55,18 @@ % Copyright (C) 2003-2008, Robert Oostenveld, F.C. Donders Centre % % $Log: read_header.m,v $ +% Revision 1.98 2009/08/05 00:26:38 josdie +% Workaround for defect in number of subjects field in EGIS average files generated by NetStation. +% +% Revision 1.97 2009/07/19 19:49:21 josdie +% Deleted egi_egis changing a zero hdr.nSamplesPre to a one. +% +% Revision 1.96 2009/07/09 10:00:45 roboos +% no leading spaces for fake channel names in fcdc_buffer +% +% Revision 1.95 2009/06/17 13:43:20 roboos +% don't include LastTimeStamp in output +% % Revision 1.94 2009/04/20 17:19:01 vlalit % Changed the MNE reader not to set hdr.elec when there are no EEG channels. % @@ -841,7 +853,8 @@ for i = 1:hdr.nChans hdr.label{i} = ['e' num2str(i)]; end; - hdr.nTrials = fhdr(11)*fhdr(18); %number of trials is numSubjects * numCells + %since NetStation does not properly set the fhdr(11) field, use the number of subjects from the chdr instead + hdr.nTrials = chdr(1,2)*fhdr(18); %number of trials is numSubjects * numCells hdr.nSamplesPre = ceil(fhdr(14)/(1000/hdr.Fs)); if any(chdr(:,3)-chdr(1,3)) @@ -878,10 +891,6 @@ % actually allocated to the age of the subject, although NetStation % does not use it when generating an EGIS session file. - if hdr.nSamplesPre == 0 - hdr.nSamplesPre = 1; %If baseline was left as zero, then change to "1" to avoid possible issues with software expecting a non-zero baseline. - end; - if any(chdr(:,3)-chdr(1,3)) error('Number of samples not the same for all cells.'); end; @@ -945,7 +954,7 @@ hdr.nTrials = 1; % since continuous warning('creating fake channel names'); for i=1:hdr.nChans - hdr.label{i} = sprintf('%3d', i); + hdr.label{i} = sprintf('%d', i); end % this should be a column vector hdr.label = hdr.label(:); @@ -1006,7 +1015,6 @@ LastTimeStamp = ncs.hdr.LastTimeStamp; % this is the first timestamp of the last block, i.e. not the timestamp of the last sample hdr.TimeStampPerSample = double(LastTimeStamp - FirstTimeStamp) ./ ((ncs.NRecords-1)*512); hdr.FirstTimeStamp = FirstTimeStamp; - hdr.LastTimeStamp = LastTimeStamp + uint64((512-1)*hdr.TimeStampPerSample); case 'neuralynx_nse' nse = read_neuralynx_nse(filename, 1, 0); @@ -1018,7 +1026,7 @@ hdr.nSamples = 32; % there are 32 samples in each waveform hdr.nSamplesPre = 0; hdr.orig = nse.hdr; - % FIXME add hdr.FirstTimeStamp and hdr.LastTimeStamp + % FIXME add hdr.FirstTimeStamp and hdr.TimeStampPerSample case {'neuralynx_ttl', 'neuralynx_tsl', 'neuralynx_tsh'} % these are hardcoded, they contain an 8-byte header and int32 values for a single channel @@ -1361,7 +1369,6 @@ % the timestamps indicate the beginning of each block, hence the timestamp of the last block corresponds with the end of the previous block hdr.TimeStampPerSample = double(ts(end)-ts(1))/sum(num(1:(end-1))); hdr.FirstTimeStamp = ts(1); % the timestamp of the first continuous sample - hdr.LastTimeStamp = ts(end) + uint64(hdr.TimeStampPerSample * num(end)); % the timestamp of the last sample (not the beginning of the last block) % also make the spike channels visible for i=1:length(orig.ChannelHeader) diff --git a/external/fileio/private/read_mri.m b/external/fileio/private/read_mri.m index eabfeaa..ecf08de 100644 --- a/external/fileio/private/read_mri.m +++ b/external/fileio/private/read_mri.m @@ -16,6 +16,22 @@ % Copyright (C) 2004-2009, Robert Oostenveld % % $Log: read_mri.m,v $ +% Revision 1.11 2009/07/16 12:57:12 roboos +% fixed fprintf feedback for dicom reader +% +% Revision 1.10 2009/07/09 15:07:59 roboos +% use another coordinate transformation matrix for fif MRI +% +% Revision 1.9 2009/07/08 08:08:45 roboos +% also support mne toolbox fiff_read_mri function +% +% Revision 1.8 2009/07/07 10:40:37 roboos +% Improved handling of sequence of files, also when the files don't have nice names. +% Use SeriesNumber from the DICOM header to figure out which slices go together. +% +% Revision 1.7 2009/07/02 15:04:09 roboos +% construct transformation matrix for dicom files +% % Revision 1.6 2009/03/11 15:02:18 vlalit % Use either SPM5 or SPM8 to read *.nii files. % @@ -185,43 +201,102 @@ transform(2,4) = -dim(2) - transform(2,4); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -elseif filetype(filename, 'neuromag_fif') - % use the neuromag functions to read the Neuromag MRI - % FIXME this needs to be checked to ensure consistency with the FieldTrip definition of volume data +elseif filetype(filename, 'neuromag_fif') && hastoolbox('mne') + % use the mne functions to read the Neuromag MRI + hdr = fiff_read_mri(filename); + img = cat(3, hdr.slices.data); + hdr.slices = rmfield(hdr.slices, 'data'); % remove the image data to save memory + % hmm, which transformation matrix should I use? + if issubfield(hdr.voxel_trans, 'trans') + transform = hdr.voxel_trans.trans; + elseif issubfield(hdr.trans, 'trans') + transform = hdr.trans.trans; + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +elseif filetype(filename, 'neuromag_fif') && hastoolbox('meg_pd') + % use the meg_pd functions to read the Neuromag MRI [img,coords] = loadmri(filename); dev = loadtrans(filename,'MRI','HEAD'); - transform = dev*coords; + transform = dev*coords; hdr.coords = coords; - hdr.dev = dev; + hdr.dev = dev; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +elseif filetype(filename, 'neuromag_fif') + error('reading MRI data from a fif file requires either the MNE toolbox or the meg_pd toolbox to be installed'); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% elseif filetype(filename, 'dicom') % this uses the Image processing toolbox - % the DICOM file probably represents a stack of slices + % the DICOM file probably represents a stack of slices, possibly even multiple volumes + orig = dicominfo(filename); + dim(1) = orig.Rows; + dim(2) = orig.Columns; + [p, f] = fileparts(filename); + + % this works for the Siemens scanners at the FCDC tok = tokenize(f, '.'); for i=5:length(tok) - tok{i} = '*'; % this works for the Siemens scanners at the FCDC + tok{i} = '*'; end filename = sprintf('%s.', tok{:}); % reconstruct the filename with wildcards and '.' between the segments filename = filename(1:end-1); % remove the last '.' dirlist = dir(fullfile(p, filename)); dirlist = {dirlist.name}; + + if length(dirlist)==1 + % try something else to get a list of all the slices + dirlist = dir(fullfile(p, '*')); + dirlist = {dirlist(~[dirlist.isdir]).name}; + end + + keep = false(1, length(dirlist)); for i=1:length(dirlist) filename = char(fullfile(p, dirlist{i})); - fprintf('reading ''%s''\n', filename); - info = dicominfo(filename); - img(:,:,i) = dicomread(info); - hdr(i) = info; - if i==1 - % this pre-allocates enough space for the other slices - img(1,1,length(dirlist)) = 0; + if ~filetype(filename, 'dicom') + keep(i) = false; + fprintf('skipping ''%s'' because of incorrect filetype\n', filename); + end + % read the header information + info = dicominfo(filename); + if info.SeriesNumber~=orig.SeriesNumber + keep(i) = false; + fprintf('skipping ''%s'' because of different SeriesNumber\n', filename); + else + keep(i) = true; + hdr(i) = info; end end + % remove the files that were skipped + hdr = hdr(keep); + dirlist = dirlist(keep); + + % pre-allocate enough space for the subsequent slices + dim(3) = length(dirlist); + img = zeros(dim(1), dim(2), dim(3)); + for i=1:length(dirlist) + filename = char(fullfile(p, dirlist{i})); + fprintf('reading image data from ''%s''\n', filename); + img(:,:,i) = dicomread(hdr(i)); + end + % reorder the slices [z, indx] = sort(cell2mat({hdr.SliceLocation})); hdr = hdr(indx); img = img(:,:,indx); + + try + % construct a homgenous transformation matrix that performs the scaling from voxels to mm + dx = hdr(1).PixelSpacing(1); + dy = hdr(1).PixelSpacing(2); + dz = hdr(2).SliceLocation - hdr(1).SliceLocation; + transform = eye(4); + transform(1,1) = dx; + transform(2,2) = dy; + transform(3,3) = dz; + end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% else diff --git a/external/fileio/private/read_plexon_ddt.m b/external/fileio/private/read_plexon_ddt.m new file mode 100644 index 0000000..e625acb --- /dev/null +++ b/external/fileio/private/read_plexon_ddt.m @@ -0,0 +1,138 @@ +function [dat] = read_plexon_ddt(filename, begsample, endsample) + +% READ_PLEXON_DDT reads header or data from a Plexon *.ddt file, +% which is a Plexon continuous data file optimized for continuous +% (streaming) recording where every channel is continuously recorded +% without gaps and the recording includes any dead time between spikes. +% +% Use as +% [hdr] = read_plexon_ddt(filename) +% [dat] = read_plexon_ddt(filename, begsample, endsample) +% +% samples start counting at 1 +% returned values are in mV + +% Copyright (C) 2005-2007, Robert Oostenveld +% +% $Log: read_plexon_ddt.m,v $ +% Revision 1.1 2009/01/14 09:24:45 roboos +% moved even more files from fileio to fileio/privtae, see previous log entry +% +% Revision 1.3 2007/01/03 16:57:58 roboos +% improved documentation +% +% Revision 1.2 2005/09/08 16:52:51 roboos +% added support for subformat versions 101, 102 and 103 +% +% Revision 1.1 2005/09/06 12:40:39 roboos +% renamed plextor into plexon (i.e. it had an incorrect company name) +% +% Revision 1.1 2005/09/02 16:36:04 roboos +% new implementation, only for version 100 +% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Version 100: Samples are assumed to be 12 bits. All channels have the same NIDAQ gain, and preamp +% gain is not saved. +% int Version; // =100 +% int DataOffset; // Offset into the file where the data starts +% double Freq; // Digitization frequency +% int NChannels; // Number of channels +% int Year; // Time/date when the data was acquired +% int Month; +% int Day; +% int Hour; +% int Minute; +% int Second; +% int Gain; // NIDAQ gain (the same gain for all channels +% char Comment[128]; // User-supplied comment +% unsigned char Padding[256]; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +dat = []; +fid = fopen(filename, 'rb', 'ieee-le'); + +Version = fread(fid, 1, 'int' ); +dat.Version = Version; + +% this is common for all subformats +dat.DataOffset = fread(fid, 1, 'int' ); +dat.Freq = fread(fid, 1, 'double' ); +dat.NChannels = fread(fid, 1, 'int' ); +dat.Year = fread(fid, 1, 'int' ); +dat.Month = fread(fid, 1, 'int' ); +dat.Day = fread(fid, 1, 'int' ); +dat.Hour = fread(fid, 1, 'int' ); +dat.Minute = fread(fid, 1, 'int' ); +dat.Second = fread(fid, 1, 'int' ); +dat.Gain = fread(fid, 1, 'int' ); +dat.Comment = char(fread(fid, 128, 'char')'); + +if Version==100 + % the remainder of the header contains bytes for padding + dat.Padding = fread(fid, 256, 'char' ); +elseif Version==101 + % Version 101: A field was added to indicate bits-per-sample (12 or 16). All channels have the same NIDAQ + % gain, and preamp gain is not saved. + dat.BitsPerSample = fread(fid, 1, 'char'); + dat.Padding = fread(fid, 255, 'char'); +elseif Version==102 + % Version 102: A byte array of per-channel NIDAQ gains was added. The Gain field now indicates the + % preamp gain, a value of 1 usually indicating that no preamp gain was specified in the application that + % recorded the DDT. + dat.BitsPerSample = fread(fid, 1, 'char'); + dat.ChannelGain = fread(fid, 64, 'char'); + dat.Padding = fread(fid, 191, 'char'); +elseif Version==103 + % Version 103: A field was added to specify ADC maximal input voltage. The value is integer number in + % millivolts. + dat.BitsPerSample = fread(fid, 1, 'char'); + dat.ChannelGain = fread(fid, 64, 'char'); + dat.MaxMagnitudeMV = fread(fid, 1, 'short'); + dat.Padding = fread(fid, 189, 'char'); +else + error('unsupported version of ddt file'); +end + +% determine the number of samples by looking at the length of the datafile +offset = ftell(fid); +fseek(fid, 0, 'eof'); +dat.NSamples = (ftell(fid) - offset) / (2 * dat.NChannels); + +if nargin==1 + % do not read any data, only return the header + fclose(fid); + return +end + +% A DDT file contains a file header followed by an array of A/D samples stored as 16-bit integers regardless +% of whether values are 12 or 16-bits. The samples are multiplexed. + +% read the desired samples of the data +offset = dat.DataOffset + (begsample-1)*dat.NChannels; +nsampl = endsample - begsample + 1; +fseek(fid, offset, 'bof'); +dat.data = fread(fid, [dat.NChannels nsampl], 'int16'); + +fclose(fid); + +% apply the calibration to obtain the signal in physical units +if Version==100 + PreAmpGain = 1000; + dat.data = dat.data * 5000 / (2048 * dat.Gain * PreAmpGain); +elseif Version==101 + PreAmpGain = 1000; + dat.data = dat.data * 5000 / (0.5 * (2^dat.BitsPerSample) * dat.Gain * PreAmpGain); +elseif Version==102 + for i=1:dat.NChannels + dat.data(i,:) = dat.data(i,:) * 5000 ./ (0.5 * (2^dat.BitsPerSample) * dat.ChannelGain(i)); + end +elseif Version==103 + % I am not sure whether the calibration like this is correct, since the + % Plexon documentation does not explicitely specify how to do it for the + % 104 file format. I presume that it is identical to the 103 format. + for i=1:dat.NChannels + dat.data(i,:) = dat.data(i,:) * 5000 ./ (0.5 * (2^dat.BitsPerSample) * dat.ChannelGain(i)); + end +end + diff --git a/external/fileio/private/read_plexon_ds.m b/external/fileio/private/read_plexon_ds.m new file mode 100644 index 0000000..a1162c1 --- /dev/null +++ b/external/fileio/private/read_plexon_ds.m @@ -0,0 +1,155 @@ +function [dat] = read_plexon_ds(dirname, hdr, begsample, endsample, chanindx) + +% READ_PLEXON_DS reads multiple single-channel Plexon files that are +% all contained in a single directory. Each file is treated as a single +% channel of a combined multi-channel dataset. +% +% Use as +% hdr = read_plexon_ds(dirname) +% dat = read_plexon_ds(dirname, hdr, begsample, endsample, chanindx) +% +% See also READ_PLEXON_NEX, READ_PLEXON_PLX, READ_PLEXON_DDT + +% Copyright (C) 2007, Robert Oostenveld +% +% $Log: read_plexon_ds.m,v $ +% Revision 1.1 2009/01/14 09:24:45 roboos +% moved even more files from fileio to fileio/privtae, see previous log entry +% +% Revision 1.1 2007/03/21 16:58:51 roboos +% first implementation only for continuous plexon_nex, based on the skeleton from read_neuralynx_ds +% + +needhdr = (nargin==1); +needdat = (nargin>=2); + +if needhdr + % get the list of filenames + ls = dir(dirname); + ls = ls(~cell2mat({ls.isdir})); + fname = {}; + for i=1:length(ls) + fname{i} = fullfile(dirname, ls(i).name); + end + + ftype = zeros(length(fname), 1); + for i=1:length(fname) + if filetype(fname{i}, 'plexon_nex') + ftype(i) = 1; + elseif filetype(fname{i}, 'plexon_plx') + ftype(i) = 2; + elseif filetype(fname{i}, 'plexon_ddt') + ftype(i) = 3; + end + end + + % only remember the filenames that are relevant + fname = fname(ftype>0); + ftype = ftype(ftype>0); + + if length(fname)==0 + error('the directory contains no supported files'); + elseif any(ftype~=1) + error('only nex files are supported in a plexon dataset directory'); + end + + for i=1:length(fname) + % this will only work if all files within a dataset return a similar header structure + switch ftype(i) + case 1 + orig(i) = read_plexon_nex(fname{i}); + case 'plexon_plx' + error('plx files are not supported in plexon dataset directory'); + case 'plexon_ddt' + error('ddt files are not supported in plexon dataset directory'); + otherwise + error('unsupported file in plexon dataset directory'); + end + end + + for i=1:length(fname) + if length(orig(i).VarHeader)>1 + error('multiple channels in a single NEX file not supported'); + else + % combine the information from the different files in a single header + label{i} = deblank(orig(i).VarHeader.Name); + Type(i) = orig(i).VarHeader.Type; + WFrequency(i) = orig(i).VarHeader.WFrequency; % of the waveform + ADBitVolts(i) = orig(i).VarHeader.ADtoMV; + NPointsWave(i) = orig(i).VarHeader.NPointsWave; + Beg(i) = orig(i).FileHeader.Beg; + End(i) = orig(i).FileHeader.End; + Frequency(i) = orig(i).FileHeader.Frequency; % of the timestamps + end + end + + if any(Type~=5) + error('not all channels contain continuous data'); + end + + if any(WFrequency~=WFrequency(1)) + warning('not all channels have the same sampling rate'); + end + + if any(Frequency~=Frequency(1)) + warning('not all channels have the same timestamp rate'); + end + + if any(Beg~=Beg(1)) + warning('not all channels start at the same time'); + end + + if any(End~=End(1)) + warning('not all channels end at the same time'); + end + + if any(NPointsWave~=NPointsWave(1)) + warning('not all channels have the same number of samples'); + end + + % construct the header that applies to all channels combined + hdr.label = label; + hdr.nChans = length(label); + hdr.nSamples = NPointsWave(1); + hdr.nSamplesPre = 0; % it is continuous + hdr.nTrials = 1; % it is continuous + hdr.Fs = WFrequency(1); + hdr.FirstTimeStamp = Beg(1); + hdr.LastTimeStamp = End(1); % FIXME this is often not correct + hdr.TimeStampPerSample = Frequency(1)/WFrequency(1); + hdr.filename = fname; + + % remember the original header details + hdr.orig = orig(:); + + % return the header + dat = hdr; + +else + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % read the data of the selected channels (i.e. files) + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if nargin<5 + % select all channels + chanindx = 1:length(hdr.label); + end + nchan = length(chanindx); + nsample = endsample-begsample+1; + dat = zeros(nchan, nsample); + + for i=1:nchan + thischan = chanindx(i); + thisfile = hdr.filename{thischan}; + switch filetype(thisfile) + case 'plexon_nex' + buf = read_plexon_nex(thisfile, 'header', hdr.orig(thischan), 'channel', 1); % always read the first and only channel + dat(i,:) = buf.dat(begsample:endsample); + case 'plexon_plx' + error('plx files are not supported in plexon dataset directory'); + case 'plexon_ddt' + error('ddt files are not supported in plexon dataset directory'); + otherwise + error('unsupported file in plexon dataset directory'); + end + end +end diff --git a/external/fileio/private/read_plexon_plx.m b/external/fileio/private/read_plexon_plx.m new file mode 100644 index 0000000..7ca346e --- /dev/null +++ b/external/fileio/private/read_plexon_plx.m @@ -0,0 +1,431 @@ +function [varargout] = read_plexon_plx(filename, varargin) + +% READ_PLEXON_PLX reads header or data from a Plexon *.plx file, which +% is a file containing action-potential (spike) timestamps and waveforms +% (spike channels), event timestamps (event channels), and continuous +% variable data (continuous A/D channels). +% +% Use as +% [hdr] = read_plexon_plx(filename) +% [dat] = read_plexon_plx(filename, ...) +% [dat1, dat2, dat3, hdr] = read_plexon_plx(filename, ...) +% +% Optional input arguments should be specified in key-value pairs +% 'header' = structure with header information +% 'memmap' = 0 or 1 +% 'feedback' = 0 or 1 +% 'ChannelIndex' = number, or list of numbers (that will result in multiple outputs) +% 'SlowChannelIndex' = number, or list of numbers (that will result in multiple outputs) + +% Copyright (C) 2007, Robert Oostenveld +% +% $Log: read_plexon_plx.m,v $ +% Revision 1.1 2009/01/14 09:24:45 roboos +% moved even more files from fileio to fileio/privtae, see previous log entry +% +% Revision 1.8 2008/09/30 08:01:04 roboos +% replaced all fread(char=>char) into uint8=>char to ensure that the +% chars are read as 8 bits and not as extended 16 bit characters. The +% 16 bit handling causes problems on some internationalized OS/Matlab +% combinations. +% +% the help of fread specifies "If the precision is 'char' or 'char*1', MATLAB +% reads characters using the encoding scheme associated with the file. +% See FOPEN for more information". +% +% Revision 1.7 2007/03/26 12:32:13 roboos +% changed some documentation, whitespace and comments +% output in multiple output arguments instead of a single cell-array (for consistency with nex reader) +% +% Revision 1.6 2007/03/19 17:11:54 roboos +% made the datablock header reader the same for regular and memmapped files +% fixed the timestamps for memmapped files +% +% Revision 1.5 2007/03/18 22:05:28 roboos +% avoid one call to double() +% fixed bug in variable name (mixed up spike and slow channel, only in feedback) +% also deal with spike channels that do not contain any data +% +% Revision 1.4 2007/03/14 16:10:54 roboos +% do re-allocation in larger chuncks +% +% Revision 1.3 2007/01/09 09:44:10 roboos +% use multiple output arguments, made feedback optional, represent values in DataBlockHeader as int16 if possible (save some memory) +% +% Revision 1.2 2007/01/04 12:08:43 roboos +% implemented memory mapped reading of block header info, implented direct reading of data, timestamps as uint16 and uint32 +% +% Revision 1.1 2007/01/03 16:58:15 roboos +% new implementation +% + +% parse the optional input arguments +hdr = keyval('header', varargin); +memmap = keyval('memmap', varargin); +feedback = keyval('feedback', varargin); +ChannelIndex = keyval('ChannelIndex', varargin); +SlowChannelIndex = keyval('SlowChannelIndex', varargin); +EventIndex = keyval('EventIndex', varargin); % not yet used + +% set the defaults +if isempty(memmap) + memmap=0; +end +if isempty(feedback) + feedback=1; +end + +needhdr = isempty(hdr); + +% start with empty return values +varargout = {}; + +% the datafile is little endian, hence it may be neccessary to swap bytes in +% the memory mapped data stream depending on the CPU type of this computer +if littleendian + swapFcn = @(x) x; +else + swapFcn = @(x) swapbytes(x); +end + +% read header info from file, use Matlabs for automatic byte-ordering +fid = fopen(filename, 'r', 'ieee-le'); +fseek(fid, 0, 'eof'); +siz = ftell(fid); +fseek(fid, 0, 'bof'); + +if needhdr + if feedback, fprintf('reading header from %s\n', filename); end + % a PLX file consists of a file header, channel headers, and data blocks + hdr = PL_FileHeader(fid); + for i=1:hdr.NumDSPChannels + hdr.ChannelHeader(i) = PL_ChannelHeader(fid); + end + for i=1:hdr.NumEventChannels + hdr.EventHeader(i) = PL_EventHeader(fid); + end + for i=1:hdr.NumSlowChannels + hdr.SlowChannelHeader(i) = PL_SlowChannelHeader(fid); + end + hdr.DataOffset = ftell(fid); + + if memmap + % open the file as meory mapped object, note that byte swapping may be needed + mm = memmapfile(filename, 'offset', hdr.DataOffset, 'format', 'int16'); + end + + dum = struct(... + 'Type', [],... + 'UpperByteOf5ByteTimestamp', [],... + 'TimeStamp', [],... + 'Channel', [],... + 'Unit', [],... + 'NumberOfWaveforms', [],... + 'NumberOfWordsInWaveform', [] ... + ); + + % read the header of each data block and remember its data offset in bytes + Nblocks = 0; + offset = hdr.DataOffset; % only used when reading from memmapped file + hdr.DataBlockOffset = []; + hdr.DataBlockHeader = dum; + while offset=length(hdr.DataBlockOffset); + % allocate another 1000 elements, this prevents continuous reallocation + hdr.DataBlockOffset(Nblocks+10000) = 0; + hdr.DataBlockHeader(Nblocks+10000) = dum; + if feedback, fprintf('reading DataBlockHeader %4.1f%%\n', 100*(offset-hdr.DataOffset)/(siz-hdr.DataOffset)); end + end + Nblocks = Nblocks+1; + if memmap + % get the header information from the memory mapped file + hdr.DataBlockOffset(Nblocks) = offset; + hdr.DataBlockHeader(Nblocks) = PL_DataBlockHeader(mm, offset-hdr.DataOffset, swapFcn); + % skip the header (16 bytes) and the data (int16 words) + offset = offset + 16 + 2 * double(hdr.DataBlockHeader(Nblocks).NumberOfWordsInWaveform * hdr.DataBlockHeader(Nblocks).NumberOfWaveforms); + else + % read the header information from the file the traditional way + hdr.DataBlockOffset(Nblocks) = offset; + hdr.DataBlockHeader(Nblocks) = PL_DataBlockHeader(fid, [], swapFcn); + fseek(fid, 2 * double(hdr.DataBlockHeader(Nblocks).NumberOfWordsInWaveform * hdr.DataBlockHeader(Nblocks).NumberOfWaveforms), 'cof'); % data consists of short integers + offset = ftell(fid); + end % if memmap + end + % this prints the final 100% + if feedback, fprintf('reading DataBlockHeader %4.1f%%\n', 100*(offset-hdr.DataOffset)/(siz-hdr.DataOffset)); end + % remove the allocated space that was not needed + hdr.DataBlockOffset = hdr.DataBlockOffset(1:Nblocks); + hdr.DataBlockHeader = hdr.DataBlockHeader(1:Nblocks); + +end % if needhdr + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% read the spike channel data +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +if ~isempty(ChannelIndex) + if feedback, fprintf('reading spike data from %s\n', filename); end + + if memmap + % open the file as meory mapped object, note that byte swapping may be needed + mm = memmapfile(filename, 'offset', hdr.DataOffset, 'format', 'int16'); + end + + type = [hdr.DataBlockHeader.Type]; + chan = [hdr.DataBlockHeader.Channel]; + ts = [hdr.DataBlockHeader.TimeStamp]; + + for i=1:length(ChannelIndex) + % determine the data blocks with continuous data belonging to this channel + sel = (type==1 & chan==hdr.ChannelHeader(ChannelIndex(i)).Channel); + sel = find(sel); + + if isempty(sel) + warning(sprintf('spike channel %d contains no data', ChannelIndex(i))); + varargin{end+1} = []; + continue; + end + + % the number of samples can potentially be different in each block + num = double([hdr.DataBlockHeader(sel).NumberOfWordsInWaveform]) .* double([hdr.DataBlockHeader(sel).NumberOfWaveforms]); + + % check whether the number of samples per block makes sense + if any(num~=num(1)) + error('spike channel blocks with diffent number of samples'); + end + + % allocate memory to hold the data + buf = zeros(num(1), length(sel), 'int16'); + + if memmap + % get the header information from the memory mapped file + datbeg = double(hdr.DataBlockOffset(sel) - hdr.DataOffset)/2 + 8 + 1; % expressed in 2-byte words, minus the file header, skip the 16 byte block header + datend = datbeg + num - 1; + for j=1:length(sel) + buf(:,j) = mm.Data(datbeg(j):datend(j)); + end + % optionally swap the bytes to correct for the endianness + buf = swapFcn(buf); + else + % read the data from the file in the traditional way + offset = double(hdr.DataBlockOffset(sel)) + 16; % expressed in bytes, skip the 16 byte block header + for j=1:length(sel) + fseek(fid, offset(j), 'bof'); + buf(:,j) = fread(fid, num(j), 'int16'); + end + end % if memmap + + % remember the data for this channel + varargout{i} = buf; + + end %for ChannelIndex +end % if ChannelIndex + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% read the continuous channel data +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +if ~isempty(SlowChannelIndex) + if feedback, fprintf('reading continuous data from %s\n', filename); end + dat = {}; + + if memmap + % open the file as meory mapped object, note that byte swapping may be needed + mm = memmapfile(filename, 'offset', hdr.DataOffset, 'format', 'int16'); + end + + type = [hdr.DataBlockHeader.Type]; + chan = [hdr.DataBlockHeader.Channel]; + ts = [hdr.DataBlockHeader.TimeStamp]; + + for i=1:length(SlowChannelIndex) + % determine the data blocks with continuous data belonging to this channel + sel = (type==5 & chan==hdr.SlowChannelHeader(SlowChannelIndex(i)).Channel); + sel = find(sel); + + if isempty(sel) + error(sprintf('Continuous channel %d contains no data', SlowChannelIndex(i))); + % warning(sprintf('Continuous channel %d contains no data', SlowChannelIndex(i))); + % varargin{end+1} = []; + % continue; + end + + % the number of samples can be different in each block + num = double([hdr.DataBlockHeader(sel).NumberOfWordsInWaveform]) .* double([hdr.DataBlockHeader(sel).NumberOfWaveforms]); + cumnum = cumsum([0 num]); + + % allocate memory to hold the data + buf = zeros(1, cumnum(end), 'int16'); + if memmap + % get the header information from the memory mapped file + datbeg = double(hdr.DataBlockOffset(sel) - hdr.DataOffset)/2 + 8 + 1; % expressed in 2-byte words, minus the file header, skip the 16 byte block header + datend = datbeg + num - 1; + for j=1:length(sel) + bufbeg = cumnum(j)+1; + bufend = cumnum(j+1); + % copy the data from the memory mapped file into the continuous buffer + buf(bufbeg:bufend) = mm.Data(datbeg(j):datend(j)); + end + % optionally swap the bytes to correct for the endianness + buf = swapFcn(buf); + else + % read the data from the file in the traditional way + offset = double(hdr.DataBlockOffset(sel)) + 16; % expressed in bytes, skip the 16 byte block header + for j=1:length(sel) + bufbeg = cumnum(j)+1; + bufend = cumnum(j+1); + % copy the data from the file into the continuous buffer + fseek(fid, offset(j), 'bof'); + buf(bufbeg:bufend) = fread(fid, num(j), 'int16'); + end + end % if memmap + + % remember the data for this channel + varargout{i} = buf; + + end %for SlowChannelIndex +end % if SlowChannelIndex + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% read the event channel data +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +if ~isempty(EventIndex) + type = [hdr.DataBlockHeader.Type]; + chan = [hdr.DataBlockHeader.Channel]; + ts = [hdr.DataBlockHeader.TimeStamp]; + + for i=1:length(EventIndex) + % determine the data blocks with continuous data belonging to this channel + sel = (type==0 & chan==hdr.EventHeader(EventIndex(i)).Channel); + sel = find(sel); + + if isempty(sel) + warning(sprintf('event channel %d contains no data', EventIndex(i))); + varargin{end+1} = []; + continue; + end + + % this still has to be implemented + keyboard + + end % for EventIndex +end % if EventIndex + +fclose(fid); + +% always return the header as last +varargout{end+1} = hdr; +return + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTIONS for reading the different header elements +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function hdr = PL_FileHeader(fid) +hdr.MagicNumber = fread(fid, 1, 'uint32=>uint32'); % = 0x58454c50; +hdr.Version = fread(fid, 1, 'int32' ); % Version of the data format; determines which data items are valid +hdr.Comment = fread(fid, [1 128], 'uint8=>char' ); % User-supplied comment +hdr.ADFrequency = fread(fid, 1, 'int32' ); % Timestamp frequency in hertz +hdr.NumDSPChannels = fread(fid, 1, 'int32' ); % Number of DSP channel headers in the file +hdr.NumEventChannels = fread(fid, 1, 'int32' ); % Number of Event channel headers in the file +hdr.NumSlowChannels = fread(fid, 1, 'int32' ); % Number of A/D channel headers in the file +hdr.NumPointsWave = fread(fid, 1, 'int32' ); % Number of data points in waveform +hdr.NumPointsPreThr = fread(fid, 1, 'int32' ); % Number of data points before crossing the threshold +hdr.Year = fread(fid, 1, 'int32' ); % Time/date when the data was acquired +hdr.Month = fread(fid, 1, 'int32' ); +hdr.Day = fread(fid, 1, 'int32' ); +hdr.Hour = fread(fid, 1, 'int32' ); +hdr.Minute = fread(fid, 1, 'int32' ); +hdr.Second = fread(fid, 1, 'int32' ); +hdr.FastRead = fread(fid, 1, 'int32' ); % reserved +hdr.WaveformFreq = fread(fid, 1, 'int32' ); % waveform sampling rate; ADFrequency above is timestamp freq +hdr.LastTimestamp = fread(fid, 1, 'double'); % duration of the experimental session, in ticks +% The following 6 items are only valid if Version >= 103 +hdr.Trodalness = fread(fid, 1, 'char' ); % 1 for single, 2 for stereotrode, 4 for tetrode +hdr.DataTrodalness = fread(fid, 1, 'char' ); % trodalness of the data representation +hdr.BitsPerSpikeSample = fread(fid, 1, 'char' ); % ADC resolution for spike waveforms in bits (usually 12) +hdr.BitsPerSlowSample = fread(fid, 1, 'char' ); % ADC resolution for slow-channel data in bits (usually 12) +hdr.SpikeMaxMagnitudeMV = fread(fid, 1, 'uint16'); % the zero-to-peak voltage in mV for spike waveform adc values (usually 3000) +hdr.SlowMaxMagnitudeMV = fread(fid, 1, 'uint16'); % the zero-to-peak voltage in mV for slow-channel waveform adc values (usually 5000); Only valid if Version >= 105 (usually either 1000 or 500) +% The following item is only valid if Version >= 105 +hdr.SpikePreAmpGain = fread(fid, 1, 'uint16'); % so that this part of the header is 256 bytes +hdr.Padding = fread(fid, 46, 'char' ); % so that this part of the header is 256 bytes +% Counters for the number of timestamps and waveforms in each channel and unit. +% Note that these only record the counts for the first 4 units in each channel. +% channel numbers are 1-based - array entry at [0] is unused +hdr.TSCounts = fread(fid, [5 130], 'int32' ); % number of timestamps[channel][unit] +hdr.WFCounts = fread(fid, [5 130], 'int32' ); % number of waveforms[channel][unit] +% Starting at index 300, the next array also records the number of samples for the +% continuous channels. Note that since EVCounts has only 512 entries, continuous +% channels above channel 211 do not have sample counts. +hdr.EVCounts = fread(fid, 512, 'int32' ); % number of timestamps[event_number] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function hdr = PL_ChannelHeader(fid) +hdr.Name = fread(fid, [1 32], 'uint8=>char' ); % Name given to the DSP channel +hdr.SIGName = fread(fid, [1 32], 'uint8=>char' ); % Name given to the corresponding SIG channel +hdr.Channel = fread(fid, 1, 'int32' ); % DSP channel number, 1-based +hdr.WFRate = fread(fid, 1, 'int32' ); % When MAP is doing waveform rate limiting, this is limit w/f per sec divided by 10 +hdr.SIG = fread(fid, 1, 'int32' ); % SIG channel associated with this DSP channel 1 - based +hdr.Ref = fread(fid, 1, 'int32' ); % SIG channel used as a Reference signal, 1- based +hdr.Gain = fread(fid, 1, 'int32' ); % actual gain divided by SpikePreAmpGain. For pre version 105, actual gain divided by 1000. +hdr.Filter = fread(fid, 1, 'int32' ); % 0 or 1 +hdr.Threshold = fread(fid, 1, 'int32' ); % Threshold for spike detection in a/d values +hdr.Method = fread(fid, 1, 'int32' ); % Method used for sorting units, 1 - boxes, 2 - templates +hdr.NUnits = fread(fid, 1, 'int32' ); % number of sorted units +hdr.Template = fread(fid, [64 5], 'int16' ); % Templates used for template sorting, in a/d values +hdr.Fit = fread(fid, 5, 'int32' ); % Template fit +hdr.SortWidth = fread(fid, 1, 'int32' ); % how many points to use in template sorting (template only) +hdr.Boxes = reshape(fread(fid, 4*2*5, 'int16' ), [4 2 5]); % the boxes used in boxes sorting +hdr.SortBeg = fread(fid, 1, 'int32' ); % beginning of the sorting window to use in template sorting (width defined by SortWidth) +hdr.Comment = fread(fid, [1 128], 'uint8=>char' ); +hdr.Padding = fread(fid, 11, 'int32' ); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function hdr = PL_EventHeader(fid) +hdr.Name = fread(fid, [1 32], 'uint8=>char' ); % name given to this event +hdr.Channel = fread(fid, 1, 'int32' ); % event number, 1-based +hdr.Comment = fread(fid, [1 128], 'uint8=>char' ); +hdr.Padding = fread(fid, 33, 'int32' ); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function hdr = PL_SlowChannelHeader(fid) +hdr.Name = fread(fid, [1 32], 'uint8=>char' ); % name given to this channel +hdr.Channel = fread(fid, 1, 'int32' ); % channel number, 0-based +hdr.ADFreq = fread(fid, 1, 'int32' ); % digitization frequency +hdr.Gain = fread(fid, 1, 'int32' ); % gain at the adc card +hdr.Enabled = fread(fid, 1, 'int32' ); % whether this channel is enabled for taking data, 0 or 1 +hdr.PreAmpGain = fread(fid, 1, 'int32' ); % gain at the preamp +% As of Version 104, this indicates the spike channel (PL_ChannelHeader.Channel) of +% a spike channel corresponding to this continuous data channel. +% <=0 means no associated spike channel. +hdr.SpikeChannel = fread(fid, 1, 'int32' ); +hdr.Comment = fread(fid, [1 128], 'uint8=>char' ); +hdr.Padding = fread(fid, 28, 'int32' ); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function hdr = PL_DataBlockHeader(fid, offset, swapFcn) +% % this is the conventional code, it has been replaced by code that works +% % with both regular and memmapped files +% hdr.Type = fread(fid, 1, 'int16=>int16' ); % Data type; 1=spike, 4=Event, 5=continuous +% hdr.UpperByteOf5ByteTimestamp = fread(fid, 1, 'uint16=>uint16' ); % Upper 8 bits of the 40 bit timestamp +% hdr.TimeStamp = fread(fid, 1, 'uint32=>uint32' ); % Lower 32 bits of the 40 bit timestamp +% hdr.Channel = fread(fid, 1, 'int16=>int16' ); % Channel number +% hdr.Unit = fread(fid, 1, 'int16=>int16' ); % Sorted unit number; 0=unsorted +% hdr.NumberOfWaveforms = fread(fid, 1, 'int16=>int16' ); % Number of waveforms in the data to folow, usually 0 or 1 +% hdr.NumberOfWordsInWaveform = fread(fid, 1, 'int16=>int16' ); % Number of samples per waveform in the data to follow +if isa(fid, 'memmapfile') + mm = fid; + datbeg = offset/2 + 1; % the offset is in bytes (minus the file header), the memory mapped file is indexed in int16 words + datend = offset/2 + 8; + buf = mm.Data(datbeg:datend); +else + buf = fread(fid, 8, 'int16=>int16'); +end +hdr.Type = swapFcn(buf(1)); +hdr.UpperByteOf5ByteTimestamp = swapFcn(uint16(buf(2))); +hdr.TimeStamp = swapFcn(typecast(buf([3 4]), 'uint32')); +hdr.Channel = swapFcn(buf(5)); +hdr.Unit = swapFcn(buf(6)); +hdr.NumberOfWaveforms = swapFcn(buf(7)); +hdr.NumberOfWordsInWaveform = swapFcn(buf(8)); diff --git a/external/fileio/private/read_sbin_events.m b/external/fileio/private/read_sbin_events.m index 1d3c278..69c4de2 100644 --- a/external/fileio/private/read_sbin_events.m +++ b/external/fileio/private/read_sbin_events.m @@ -17,6 +17,9 @@ % % $Log: read_sbin_events.m,v $ +% Revision 1.4 2009/09/04 02:44:49 josdie +% Fixed crash for segmented files due to typo in last revision. +% % Revision 1.3 2009/04/29 10:55:16 jansch % incorporated handling of unsegmented files % @@ -120,7 +123,7 @@ nsmp = 1; else %data are organized in segments - nsmp = Nsamples; + nsmp = NSamples; end eventData = zeros(NEvent,NSegments*NSamples); diff --git a/external/fileio/private/read_sens.m b/external/fileio/private/read_sens.m index c21a80e..e30027b 100644 --- a/external/fileio/private/read_sens.m +++ b/external/fileio/private/read_sens.m @@ -32,6 +32,13 @@ % Copyright (C) 2005-2008, Robert Oostenveld % % $Log: read_sens.m,v $ +% Revision 1.14 2009/07/02 10:34:21 vlalit +% Added eeglab_set to the list of formats where electrode locations can be found in +% the header. +% +% Revision 1.13 2009/06/03 09:52:15 roboos +% added zebris_sfp +% % Revision 1.12 2009/02/02 16:10:15 vlalit % Provide the 'headertype' argument to the internal read_header call. % @@ -171,7 +178,7 @@ % This is for EEG formats where electrode positions can be stored with the data %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - case {'spmeeg_mat'} + case {'spmeeg_mat', 'eeglab_set'} % check the availability of the required low-level toolbox % this is required because the read_sens function is also on itself included in the forwinv toolbox hastoolbox('fileio'); @@ -218,6 +225,13 @@ else error('no electrodes or gradiometers found in Matlab file'); end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % these are created by a Zebris tracker, at CRC in Liege at least. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + case 'zebris_sfp' + [sens.fid, sens.pnt, sens.fid_label, sens.label] = read_zebris(filename, 0); otherwise error('unknown fileformat for electrodes or gradiometers'); diff --git a/external/fileio/private/senslabel.m b/external/fileio/private/senslabel.m index 81db081..f75cc93 100644 --- a/external/fileio/private/senslabel.m +++ b/external/fileio/private/senslabel.m @@ -6,6 +6,7 @@ % label = senslabel(type) % % The input type can be any of the following +% 'biosemi64' % 'biosemi128' % 'biosemi256' % 'bti148' @@ -40,6 +41,12 @@ % Copyright (C) 2008, Vladimir Litvak % % $Log: senslabel.m,v $ +% Revision 1.4 2009/07/29 07:07:59 roboos +% use caching of input and output arguments to speed up the handling of multiple calls with the same input argument +% +% Revision 1.3 2009/06/19 16:51:50 vlalit +% Added biosemi64 system of Diane Whitmer, I don't know how generic it is. +% % Revision 1.2 2009/05/07 13:34:09 roboos % added ctf64 % @@ -59,6 +66,21 @@ % moved definition of channel label sets to seperate function % +% these are for remembering the type on subsequent calls with the same input arguments +persistent previous_argin previous_argout + +if nargin<1 + % ensure that all input arguments are defined + type = []; +end + +current_argin = {type}; +if isequal(current_argin, previous_argin) + % don't do the type detection again, but return the previous values from cache + label = previous_argout{1}; + return +end + % prevent defining all possible labels if not needed isbiosemi = ~isempty(regexp(type, '^biosemi', 'once')); isbti = ~isempty(regexp(type, '^bti', 'once')); @@ -69,1836 +91,1912 @@ isneuromag = ~isempty(regexp(type, '^neuromag', 'once')); if isbti - btiref = { - 'MRxA' - 'MRyA' - 'MRzA' - 'MLxA' - 'MLyA' - 'MLzA' - 'MCxA' - 'MCyA' - 'MCzA' - 'MRxaA' - 'MRyaA' - 'MRzaA' - 'MLxaA' - 'MLyaA' - 'MLzaA' - 'MCxaA' - 'MCyaA' - 'MCzaA' - 'GxxA' - 'GyxA' - 'GzxA' - 'GyyA' - 'GzyA' - }; + btiref = { + 'MRxA' + 'MRyA' + 'MRzA' + 'MLxA' + 'MLyA' + 'MLzA' + 'MCxA' + 'MCyA' + 'MCzA' + 'MRxaA' + 'MRyaA' + 'MRzaA' + 'MLxaA' + 'MLyaA' + 'MLzaA' + 'MCxaA' + 'MCyaA' + 'MCzaA' + 'GxxA' + 'GyxA' + 'GzxA' + 'GyyA' + 'GzyA' + }; - bti148 = cell(148,1); - for i=1:148 - bti148{i,1} = sprintf('A%d', i); - end + bti148 = cell(148,1); + for i=1:148 + bti148{i,1} = sprintf('A%d', i); + end - bti148_planar = cell(148,1); - for i=1:148 - bti148_planar{i,1} = sprintf('A%d_dH', i); - bti148_planar{i,2} = sprintf('A%d_dV', i); - end + bti148_planar = cell(148,1); + for i=1:148 + bti148_planar{i,1} = sprintf('A%d_dH', i); + bti148_planar{i,2} = sprintf('A%d_dV', i); + end - bti248 = cell(248,1); - for i=1:248 - bti248{i,1} = sprintf('A%d', i); - end + bti248 = cell(248,1); + for i=1:248 + bti248{i,1} = sprintf('A%d', i); + end - bti248_planar = cell(248,2); - for i=1:248 - bti248_planar{i,1} = sprintf('A%d_dH', i); - bti248_planar{i,2} = sprintf('A%d_dV', i); - end + bti248_planar = cell(248,2); + for i=1:248 + bti248_planar{i,1} = sprintf('A%d_dH', i); + bti248_planar{i,2} = sprintf('A%d_dV', i); + end end % if isbti if isctf - ctfref = { - 'BG1' - 'BG2' - 'BG3' - 'BP1' - 'BP2' - 'BP3' - 'BR1' - 'BR2' - 'BR3' - 'G11' - 'G12' - 'G13' - 'G22' - 'G23' - 'P11' - 'P12' - 'P13' - 'P22' - 'P23' - 'Q11' - 'Q12' - 'Q13' - 'Q22' - 'Q23' - 'R11' - 'R12' - 'R13' - 'R22' - 'R23' - }; + ctfref = { + 'BG1' + 'BG2' + 'BG3' + 'BP1' + 'BP2' + 'BP3' + 'BR1' + 'BR2' + 'BR3' + 'G11' + 'G12' + 'G13' + 'G22' + 'G23' + 'P11' + 'P12' + 'P13' + 'P22' + 'P23' + 'Q11' + 'Q12' + 'Q13' + 'Q22' + 'Q23' + 'R11' + 'R12' + 'R13' + 'R22' + 'R23' + }; - ctfheadloc = { - 'HLC0011' - 'HLC0012' - 'HLC0013' - 'HLC0021' - 'HLC0022' - 'HLC0023' - 'HLC0031' - 'HLC0032' - 'HLC0033' - 'HLC0018' - 'HLC0028' - 'HLC0038' - 'HLC0014' - 'HLC0015' - 'HLC0016' - 'HLC0017' - 'HLC0024' - 'HLC0025' - 'HLC0026' - 'HLC0027' - 'HLC0034' - 'HLC0035' - 'HLC0036' - 'HLC0037' - }; + ctfheadloc = { + 'HLC0011' + 'HLC0012' + 'HLC0013' + 'HLC0021' + 'HLC0022' + 'HLC0023' + 'HLC0031' + 'HLC0032' + 'HLC0033' + 'HLC0018' + 'HLC0028' + 'HLC0038' + 'HLC0014' + 'HLC0015' + 'HLC0016' + 'HLC0017' + 'HLC0024' + 'HLC0025' + 'HLC0026' + 'HLC0027' + 'HLC0034' + 'HLC0035' + 'HLC0036' + 'HLC0037' + }; - ctf64 = { - 'SL11' - 'SL12' - 'SL13' - 'SL14' - 'SL15' - 'SL16' - 'SL17' - 'SL18' - 'SL19' - 'SL21' - 'SL22' - 'SL23' - 'SL24' - 'SL25' - 'SL26' - 'SL27' - 'SL28' - 'SL29' - 'SL31' - 'SL32' - 'SL33' - 'SL34' - 'SL35' - 'SL41' - 'SL42' - 'SL43' - 'SL44' - 'SL45' - 'SL46' - 'SL47' - 'SL51' - 'SL52' - 'SR11' - 'SR12' - 'SR13' - 'SR14' - 'SR15' - 'SR16' - 'SR17' - 'SR18' - 'SR19' - 'SR21' - 'SR22' - 'SR23' - 'SR24' - 'SR25' - 'SR26' - 'SR27' - 'SR28' - 'SR29' - 'SR31' - 'SR32' - 'SR33' - 'SR34' - 'SR35' - 'SR41' - 'SR42' - 'SR43' - 'SR44' - 'SR45' - 'SR46' - 'SR47' - 'SR51' - 'SR52' - }; + ctf64 = { + 'SL11' + 'SL12' + 'SL13' + 'SL14' + 'SL15' + 'SL16' + 'SL17' + 'SL18' + 'SL19' + 'SL21' + 'SL22' + 'SL23' + 'SL24' + 'SL25' + 'SL26' + 'SL27' + 'SL28' + 'SL29' + 'SL31' + 'SL32' + 'SL33' + 'SL34' + 'SL35' + 'SL41' + 'SL42' + 'SL43' + 'SL44' + 'SL45' + 'SL46' + 'SL47' + 'SL51' + 'SL52' + 'SR11' + 'SR12' + 'SR13' + 'SR14' + 'SR15' + 'SR16' + 'SR17' + 'SR18' + 'SR19' + 'SR21' + 'SR22' + 'SR23' + 'SR24' + 'SR25' + 'SR26' + 'SR27' + 'SR28' + 'SR29' + 'SR31' + 'SR32' + 'SR33' + 'SR34' + 'SR35' + 'SR41' + 'SR42' + 'SR43' + 'SR44' + 'SR45' + 'SR46' + 'SR47' + 'SR51' + 'SR52' + }; - ctf151 = { - 'MLC11' - 'MLC12' - 'MLC13' - 'MLC14' - 'MLC15' - 'MLC21' - 'MLC22' - 'MLC23' - 'MLC24' - 'MLC31' - 'MLC32' - 'MLC33' - 'MLC41' - 'MLC42' - 'MLC43' - 'MLF11' - 'MLF12' - 'MLF21' - 'MLF22' - 'MLF23' - 'MLF31' - 'MLF32' - 'MLF33' - 'MLF34' - 'MLF41' - 'MLF42' - 'MLF43' - 'MLF44' - 'MLF45' - 'MLF51' - 'MLF52' - 'MLO11' - 'MLO12' - 'MLO21' - 'MLO22' - 'MLO31' - 'MLO32' - 'MLO33' - 'MLO41' - 'MLO42' - 'MLO43' - 'MLP11' - 'MLP12' - 'MLP13' - 'MLP21' - 'MLP22' - 'MLP31' - 'MLP32' - 'MLP33' - 'MLP34' - 'MLT11' - 'MLT12' - 'MLT13' - 'MLT14' - 'MLT15' - 'MLT16' - 'MLT21' - 'MLT22' - 'MLT23' - 'MLT24' - 'MLT25' - 'MLT26' - 'MLT31' - 'MLT32' - 'MLT33' - 'MLT34' - 'MLT35' - 'MLT41' - 'MLT42' - 'MLT43' - 'MLT44' - 'MRC11' - 'MRC12' - 'MRC13' - 'MRC14' - 'MRC15' - 'MRC21' - 'MRC22' - 'MRC23' - 'MRC24' - 'MRC31' - 'MRC32' - 'MRC33' - 'MRC41' - 'MRC42' - 'MRC43' - 'MRF11' - 'MRF12' - 'MRF21' - 'MRF22' - 'MRF23' - 'MRF31' - 'MRF32' - 'MRF33' - 'MRF34' - 'MRF41' - 'MRF42' - 'MRF43' - 'MRF44' - 'MRF45' - 'MRF51' - 'MRF52' - 'MRO11' - 'MRO12' - 'MRO21' - 'MRO22' - 'MRO31' - 'MRO32' - 'MRO33' - 'MRO41' - 'MRO42' - 'MRO43' - 'MRP11' - 'MRP12' - 'MRP13' - 'MRP21' - 'MRP22' - 'MRP31' - 'MRP32' - 'MRP33' - 'MRP34' - 'MRT11' - 'MRT12' - 'MRT13' - 'MRT14' - 'MRT15' - 'MRT16' - 'MRT21' - 'MRT22' - 'MRT23' - 'MRT24' - 'MRT25' - 'MRT26' - 'MRT31' - 'MRT32' - 'MRT33' - 'MRT34' - 'MRT35' - 'MRT41' - 'MRT42' - 'MRT43' - 'MRT44' - 'MZC01' - 'MZC02' - 'MZF01' - 'MZF02' - 'MZF03' - 'MZO01' - 'MZO02' - 'MZP01' - 'MZP02' - }; + ctf151 = { + 'MLC11' + 'MLC12' + 'MLC13' + 'MLC14' + 'MLC15' + 'MLC21' + 'MLC22' + 'MLC23' + 'MLC24' + 'MLC31' + 'MLC32' + 'MLC33' + 'MLC41' + 'MLC42' + 'MLC43' + 'MLF11' + 'MLF12' + 'MLF21' + 'MLF22' + 'MLF23' + 'MLF31' + 'MLF32' + 'MLF33' + 'MLF34' + 'MLF41' + 'MLF42' + 'MLF43' + 'MLF44' + 'MLF45' + 'MLF51' + 'MLF52' + 'MLO11' + 'MLO12' + 'MLO21' + 'MLO22' + 'MLO31' + 'MLO32' + 'MLO33' + 'MLO41' + 'MLO42' + 'MLO43' + 'MLP11' + 'MLP12' + 'MLP13' + 'MLP21' + 'MLP22' + 'MLP31' + 'MLP32' + 'MLP33' + 'MLP34' + 'MLT11' + 'MLT12' + 'MLT13' + 'MLT14' + 'MLT15' + 'MLT16' + 'MLT21' + 'MLT22' + 'MLT23' + 'MLT24' + 'MLT25' + 'MLT26' + 'MLT31' + 'MLT32' + 'MLT33' + 'MLT34' + 'MLT35' + 'MLT41' + 'MLT42' + 'MLT43' + 'MLT44' + 'MRC11' + 'MRC12' + 'MRC13' + 'MRC14' + 'MRC15' + 'MRC21' + 'MRC22' + 'MRC23' + 'MRC24' + 'MRC31' + 'MRC32' + 'MRC33' + 'MRC41' + 'MRC42' + 'MRC43' + 'MRF11' + 'MRF12' + 'MRF21' + 'MRF22' + 'MRF23' + 'MRF31' + 'MRF32' + 'MRF33' + 'MRF34' + 'MRF41' + 'MRF42' + 'MRF43' + 'MRF44' + 'MRF45' + 'MRF51' + 'MRF52' + 'MRO11' + 'MRO12' + 'MRO21' + 'MRO22' + 'MRO31' + 'MRO32' + 'MRO33' + 'MRO41' + 'MRO42' + 'MRO43' + 'MRP11' + 'MRP12' + 'MRP13' + 'MRP21' + 'MRP22' + 'MRP31' + 'MRP32' + 'MRP33' + 'MRP34' + 'MRT11' + 'MRT12' + 'MRT13' + 'MRT14' + 'MRT15' + 'MRT16' + 'MRT21' + 'MRT22' + 'MRT23' + 'MRT24' + 'MRT25' + 'MRT26' + 'MRT31' + 'MRT32' + 'MRT33' + 'MRT34' + 'MRT35' + 'MRT41' + 'MRT42' + 'MRT43' + 'MRT44' + 'MZC01' + 'MZC02' + 'MZF01' + 'MZF02' + 'MZF03' + 'MZO01' + 'MZO02' + 'MZP01' + 'MZP02' + }; - ctf151_planar = cell(151, 2); - for i=1:151 - ctf151_planar{i,1} = sprintf('%s_dH', ctf151{i}); - ctf151_planar{i,2} = sprintf('%s_dV', ctf151{i}); - end + ctf151_planar = cell(151, 2); + for i=1:151 + ctf151_planar{i,1} = sprintf('%s_dH', ctf151{i}); + ctf151_planar{i,2} = sprintf('%s_dV', ctf151{i}); + end - ctf275 = { - 'MLC11' - 'MLC12' - 'MLC13' - 'MLC14' - 'MLC15' - 'MLC16' - 'MLC17' - 'MLC21' - 'MLC22' - 'MLC23' - 'MLC24' - 'MLC25' - 'MLC31' - 'MLC32' - 'MLC41' - 'MLC42' - 'MLC51' - 'MLC52' - 'MLC53' - 'MLC54' - 'MLC55' - 'MLC61' - 'MLC62' - 'MLC63' - 'MLF11' - 'MLF12' - 'MLF13' - 'MLF14' - 'MLF21' - 'MLF22' - 'MLF23' - 'MLF24' - 'MLF25' - 'MLF31' - 'MLF32' - 'MLF33' - 'MLF34' - 'MLF35' - 'MLF41' - 'MLF42' - 'MLF43' - 'MLF44' - 'MLF45' - 'MLF46' - 'MLF51' - 'MLF52' - 'MLF53' - 'MLF54' - 'MLF55' - 'MLF56' - 'MLF61' - 'MLF62' - 'MLF63' - 'MLF64' - 'MLF65' - 'MLF66' - 'MLF67' - 'MLO11' - 'MLO12' - 'MLO13' - 'MLO14' - 'MLO21' - 'MLO22' - 'MLO23' - 'MLO24' - 'MLO31' - 'MLO32' - 'MLO33' - 'MLO34' - 'MLO41' - 'MLO42' - 'MLO43' - 'MLO44' - 'MLO51' - 'MLO52' - 'MLO53' - 'MLP11' - 'MLP12' - 'MLP21' - 'MLP22' - 'MLP23' - 'MLP31' - 'MLP32' - 'MLP33' - 'MLP34' - 'MLP35' - 'MLP41' - 'MLP42' - 'MLP43' - 'MLP44' - 'MLP45' - 'MLP51' - 'MLP52' - 'MLP53' - 'MLP54' - 'MLP55' - 'MLP56' - 'MLP57' - 'MLT11' - 'MLT12' - 'MLT13' - 'MLT14' - 'MLT15' - 'MLT16' - 'MLT21' - 'MLT22' - 'MLT23' - 'MLT24' - 'MLT25' - 'MLT26' - 'MLT27' - 'MLT31' - 'MLT32' - 'MLT33' - 'MLT34' - 'MLT35' - 'MLT36' - 'MLT37' - 'MLT41' - 'MLT42' - 'MLT43' - 'MLT44' - 'MLT45' - 'MLT46' - 'MLT47' - 'MLT51' - 'MLT52' - 'MLT53' - 'MLT54' - 'MLT55' - 'MLT56' - 'MLT57' - 'MRC11' - 'MRC12' - 'MRC13' - 'MRC14' - 'MRC15' - 'MRC16' - 'MRC17' - 'MRC21' - 'MRC22' - 'MRC23' - 'MRC24' - 'MRC25' - 'MRC31' - 'MRC32' - 'MRC41' - 'MRC42' - 'MRC51' - 'MRC52' - 'MRC53' - 'MRC54' - 'MRC55' - 'MRC61' - 'MRC62' - 'MRC63' - 'MRF11' - 'MRF12' - 'MRF13' - 'MRF14' - 'MRF21' - 'MRF22' - 'MRF23' - 'MRF24' - 'MRF25' - 'MRF31' - 'MRF32' - 'MRF33' - 'MRF34' - 'MRF35' - 'MRF41' - 'MRF42' - 'MRF43' - 'MRF44' - 'MRF45' - 'MRF46' - 'MRF51' - 'MRF52' - 'MRF53' - 'MRF54' - 'MRF55' - 'MRF56' - 'MRF61' - 'MRF62' - 'MRF63' - 'MRF64' - 'MRF65' - 'MRF66' - 'MRF67' - 'MRO11' - 'MRO12' - 'MRO13' - 'MRO14' - 'MRO21' - 'MRO22' - 'MRO23' - 'MRO24' - 'MRO31' - 'MRO32' - 'MRO33' - 'MRO34' - 'MRO41' - 'MRO42' - 'MRO43' - 'MRO44' - 'MRO51' - 'MRO52' - 'MRO53' - 'MRP11' - 'MRP12' - 'MRP21' - 'MRP22' - 'MRP23' - 'MRP32' - 'MRP33' - 'MRP34' - 'MRP35' - 'MRP41' - 'MRP42' - 'MRP43' - 'MRP44' - 'MRP45' - 'MRP51' - 'MRP52' - 'MRP53' - 'MRP54' - 'MRP55' - 'MRP56' - 'MRP57' - 'MRT11' - 'MRT12' - 'MRT13' - 'MRT14' - 'MRT15' - 'MRT16' - 'MRT21' - 'MRT22' - 'MRT23' - 'MRT24' - 'MRT25' - 'MRT26' - 'MRT27' - 'MRT31' - 'MRT32' - 'MRT33' - 'MRT34' - 'MRT35' - 'MRT36' - 'MRT37' - 'MRT41' - 'MRT42' - 'MRT43' - 'MRT44' - 'MRT45' - 'MRT46' - 'MRT47' - 'MRT51' - 'MRT52' - 'MRT53' - 'MRT54' - 'MRT55' - 'MRT56' - 'MRT57' - 'MZC01' - 'MZC02' - 'MZC03' - 'MZC04' - 'MZF01' - 'MZF02' - 'MZF03' - 'MZO01' - 'MZO02' - 'MZO03' - 'MZP01' - }; + ctf275 = { + 'MLC11' + 'MLC12' + 'MLC13' + 'MLC14' + 'MLC15' + 'MLC16' + 'MLC17' + 'MLC21' + 'MLC22' + 'MLC23' + 'MLC24' + 'MLC25' + 'MLC31' + 'MLC32' + 'MLC41' + 'MLC42' + 'MLC51' + 'MLC52' + 'MLC53' + 'MLC54' + 'MLC55' + 'MLC61' + 'MLC62' + 'MLC63' + 'MLF11' + 'MLF12' + 'MLF13' + 'MLF14' + 'MLF21' + 'MLF22' + 'MLF23' + 'MLF24' + 'MLF25' + 'MLF31' + 'MLF32' + 'MLF33' + 'MLF34' + 'MLF35' + 'MLF41' + 'MLF42' + 'MLF43' + 'MLF44' + 'MLF45' + 'MLF46' + 'MLF51' + 'MLF52' + 'MLF53' + 'MLF54' + 'MLF55' + 'MLF56' + 'MLF61' + 'MLF62' + 'MLF63' + 'MLF64' + 'MLF65' + 'MLF66' + 'MLF67' + 'MLO11' + 'MLO12' + 'MLO13' + 'MLO14' + 'MLO21' + 'MLO22' + 'MLO23' + 'MLO24' + 'MLO31' + 'MLO32' + 'MLO33' + 'MLO34' + 'MLO41' + 'MLO42' + 'MLO43' + 'MLO44' + 'MLO51' + 'MLO52' + 'MLO53' + 'MLP11' + 'MLP12' + 'MLP21' + 'MLP22' + 'MLP23' + 'MLP31' + 'MLP32' + 'MLP33' + 'MLP34' + 'MLP35' + 'MLP41' + 'MLP42' + 'MLP43' + 'MLP44' + 'MLP45' + 'MLP51' + 'MLP52' + 'MLP53' + 'MLP54' + 'MLP55' + 'MLP56' + 'MLP57' + 'MLT11' + 'MLT12' + 'MLT13' + 'MLT14' + 'MLT15' + 'MLT16' + 'MLT21' + 'MLT22' + 'MLT23' + 'MLT24' + 'MLT25' + 'MLT26' + 'MLT27' + 'MLT31' + 'MLT32' + 'MLT33' + 'MLT34' + 'MLT35' + 'MLT36' + 'MLT37' + 'MLT41' + 'MLT42' + 'MLT43' + 'MLT44' + 'MLT45' + 'MLT46' + 'MLT47' + 'MLT51' + 'MLT52' + 'MLT53' + 'MLT54' + 'MLT55' + 'MLT56' + 'MLT57' + 'MRC11' + 'MRC12' + 'MRC13' + 'MRC14' + 'MRC15' + 'MRC16' + 'MRC17' + 'MRC21' + 'MRC22' + 'MRC23' + 'MRC24' + 'MRC25' + 'MRC31' + 'MRC32' + 'MRC41' + 'MRC42' + 'MRC51' + 'MRC52' + 'MRC53' + 'MRC54' + 'MRC55' + 'MRC61' + 'MRC62' + 'MRC63' + 'MRF11' + 'MRF12' + 'MRF13' + 'MRF14' + 'MRF21' + 'MRF22' + 'MRF23' + 'MRF24' + 'MRF25' + 'MRF31' + 'MRF32' + 'MRF33' + 'MRF34' + 'MRF35' + 'MRF41' + 'MRF42' + 'MRF43' + 'MRF44' + 'MRF45' + 'MRF46' + 'MRF51' + 'MRF52' + 'MRF53' + 'MRF54' + 'MRF55' + 'MRF56' + 'MRF61' + 'MRF62' + 'MRF63' + 'MRF64' + 'MRF65' + 'MRF66' + 'MRF67' + 'MRO11' + 'MRO12' + 'MRO13' + 'MRO14' + 'MRO21' + 'MRO22' + 'MRO23' + 'MRO24' + 'MRO31' + 'MRO32' + 'MRO33' + 'MRO34' + 'MRO41' + 'MRO42' + 'MRO43' + 'MRO44' + 'MRO51' + 'MRO52' + 'MRO53' + 'MRP11' + 'MRP12' + 'MRP21' + 'MRP22' + 'MRP23' + 'MRP32' + 'MRP33' + 'MRP34' + 'MRP35' + 'MRP41' + 'MRP42' + 'MRP43' + 'MRP44' + 'MRP45' + 'MRP51' + 'MRP52' + 'MRP53' + 'MRP54' + 'MRP55' + 'MRP56' + 'MRP57' + 'MRT11' + 'MRT12' + 'MRT13' + 'MRT14' + 'MRT15' + 'MRT16' + 'MRT21' + 'MRT22' + 'MRT23' + 'MRT24' + 'MRT25' + 'MRT26' + 'MRT27' + 'MRT31' + 'MRT32' + 'MRT33' + 'MRT34' + 'MRT35' + 'MRT36' + 'MRT37' + 'MRT41' + 'MRT42' + 'MRT43' + 'MRT44' + 'MRT45' + 'MRT46' + 'MRT47' + 'MRT51' + 'MRT52' + 'MRT53' + 'MRT54' + 'MRT55' + 'MRT56' + 'MRT57' + 'MZC01' + 'MZC02' + 'MZC03' + 'MZC04' + 'MZF01' + 'MZF02' + 'MZF03' + 'MZO01' + 'MZO02' + 'MZO03' + 'MZP01' + }; - % f.ck, apparently one channel is missing - ctf275_planar = cell(274,2); - for i=1:274 - ctf275_planar{i,1} = sprintf('%s_dH', ctf275{i}); - ctf275_planar{i,2} = sprintf('%s_dV', ctf275{i}); - end + % f.ck, apparently one channel is missing + ctf275_planar = cell(274,2); + for i=1:274 + ctf275_planar{i,1} = sprintf('%s_dH', ctf275{i}); + ctf275_planar{i,2} = sprintf('%s_dV', ctf275{i}); + end end % if issctf if isneuromag - neuromag122 = { - 'MEG 001' 'MEG 002' - 'MEG 003' 'MEG 004' - 'MEG 005' 'MEG 006' - 'MEG 007' 'MEG 008' - 'MEG 009' 'MEG 010' - 'MEG 011' 'MEG 012' - 'MEG 013' 'MEG 014' - 'MEG 015' 'MEG 016' - 'MEG 017' 'MEG 018' - 'MEG 019' 'MEG 020' - 'MEG 021' 'MEG 022' - 'MEG 023' 'MEG 024' - 'MEG 025' 'MEG 026' - 'MEG 027' 'MEG 028' - 'MEG 029' 'MEG 030' - 'MEG 031' 'MEG 032' - 'MEG 033' 'MEG 034' - 'MEG 035' 'MEG 036' - 'MEG 037' 'MEG 038' - 'MEG 039' 'MEG 040' - 'MEG 041' 'MEG 042' - 'MEG 043' 'MEG 044' - 'MEG 045' 'MEG 046' - 'MEG 047' 'MEG 048' - 'MEG 049' 'MEG 050' - 'MEG 051' 'MEG 052' - 'MEG 053' 'MEG 054' - 'MEG 055' 'MEG 056' - 'MEG 057' 'MEG 058' - 'MEG 059' 'MEG 060' - 'MEG 061' 'MEG 062' - 'MEG 063' 'MEG 064' - 'MEG 065' 'MEG 066' - 'MEG 067' 'MEG 068' - 'MEG 069' 'MEG 070' - 'MEG 071' 'MEG 072' - 'MEG 073' 'MEG 074' - 'MEG 075' 'MEG 076' - 'MEG 077' 'MEG 078' - 'MEG 079' 'MEG 080' - 'MEG 081' 'MEG 082' - 'MEG 083' 'MEG 084' - 'MEG 085' 'MEG 086' - 'MEG 087' 'MEG 088' - 'MEG 089' 'MEG 090' - 'MEG 091' 'MEG 092' - 'MEG 093' 'MEG 094' - 'MEG 095' 'MEG 096' - 'MEG 097' 'MEG 098' - 'MEG 099' 'MEG 100' - 'MEG 101' 'MEG 102' - 'MEG 103' 'MEG 104' - 'MEG 105' 'MEG 106' - 'MEG 107' 'MEG 108' - 'MEG 109' 'MEG 110' - 'MEG 111' 'MEG 112' - 'MEG 113' 'MEG 114' - 'MEG 115' 'MEG 116' - 'MEG 117' 'MEG 118' - 'MEG 119' 'MEG 120' - 'MEG 121' 'MEG 122' - }; + neuromag122 = { + 'MEG 001' 'MEG 002' + 'MEG 003' 'MEG 004' + 'MEG 005' 'MEG 006' + 'MEG 007' 'MEG 008' + 'MEG 009' 'MEG 010' + 'MEG 011' 'MEG 012' + 'MEG 013' 'MEG 014' + 'MEG 015' 'MEG 016' + 'MEG 017' 'MEG 018' + 'MEG 019' 'MEG 020' + 'MEG 021' 'MEG 022' + 'MEG 023' 'MEG 024' + 'MEG 025' 'MEG 026' + 'MEG 027' 'MEG 028' + 'MEG 029' 'MEG 030' + 'MEG 031' 'MEG 032' + 'MEG 033' 'MEG 034' + 'MEG 035' 'MEG 036' + 'MEG 037' 'MEG 038' + 'MEG 039' 'MEG 040' + 'MEG 041' 'MEG 042' + 'MEG 043' 'MEG 044' + 'MEG 045' 'MEG 046' + 'MEG 047' 'MEG 048' + 'MEG 049' 'MEG 050' + 'MEG 051' 'MEG 052' + 'MEG 053' 'MEG 054' + 'MEG 055' 'MEG 056' + 'MEG 057' 'MEG 058' + 'MEG 059' 'MEG 060' + 'MEG 061' 'MEG 062' + 'MEG 063' 'MEG 064' + 'MEG 065' 'MEG 066' + 'MEG 067' 'MEG 068' + 'MEG 069' 'MEG 070' + 'MEG 071' 'MEG 072' + 'MEG 073' 'MEG 074' + 'MEG 075' 'MEG 076' + 'MEG 077' 'MEG 078' + 'MEG 079' 'MEG 080' + 'MEG 081' 'MEG 082' + 'MEG 083' 'MEG 084' + 'MEG 085' 'MEG 086' + 'MEG 087' 'MEG 088' + 'MEG 089' 'MEG 090' + 'MEG 091' 'MEG 092' + 'MEG 093' 'MEG 094' + 'MEG 095' 'MEG 096' + 'MEG 097' 'MEG 098' + 'MEG 099' 'MEG 100' + 'MEG 101' 'MEG 102' + 'MEG 103' 'MEG 104' + 'MEG 105' 'MEG 106' + 'MEG 107' 'MEG 108' + 'MEG 109' 'MEG 110' + 'MEG 111' 'MEG 112' + 'MEG 113' 'MEG 114' + 'MEG 115' 'MEG 116' + 'MEG 117' 'MEG 118' + 'MEG 119' 'MEG 120' + 'MEG 121' 'MEG 122' + }; - % this is an alternative set of labels without a space in them - neuromag122alt = { - 'MEG001' 'MEG002' - 'MEG003' 'MEG004' - 'MEG005' 'MEG006' - 'MEG007' 'MEG008' - 'MEG009' 'MEG010' - 'MEG011' 'MEG012' - 'MEG013' 'MEG014' - 'MEG015' 'MEG016' - 'MEG017' 'MEG018' - 'MEG019' 'MEG020' - 'MEG021' 'MEG022' - 'MEG023' 'MEG024' - 'MEG025' 'MEG026' - 'MEG027' 'MEG028' - 'MEG029' 'MEG030' - 'MEG031' 'MEG032' - 'MEG033' 'MEG034' - 'MEG035' 'MEG036' - 'MEG037' 'MEG038' - 'MEG039' 'MEG040' - 'MEG041' 'MEG042' - 'MEG043' 'MEG044' - 'MEG045' 'MEG046' - 'MEG047' 'MEG048' - 'MEG049' 'MEG050' - 'MEG051' 'MEG052' - 'MEG053' 'MEG054' - 'MEG055' 'MEG056' - 'MEG057' 'MEG058' - 'MEG059' 'MEG060' - 'MEG061' 'MEG062' - 'MEG063' 'MEG064' - 'MEG065' 'MEG066' - 'MEG067' 'MEG068' - 'MEG069' 'MEG070' - 'MEG071' 'MEG072' - 'MEG073' 'MEG074' - 'MEG075' 'MEG076' - 'MEG077' 'MEG078' - 'MEG079' 'MEG080' - 'MEG081' 'MEG082' - 'MEG083' 'MEG084' - 'MEG085' 'MEG086' - 'MEG087' 'MEG088' - 'MEG089' 'MEG090' - 'MEG091' 'MEG092' - 'MEG093' 'MEG094' - 'MEG095' 'MEG096' - 'MEG097' 'MEG098' - 'MEG099' 'MEG100' - 'MEG101' 'MEG102' - 'MEG103' 'MEG104' - 'MEG105' 'MEG106' - 'MEG107' 'MEG108' - 'MEG109' 'MEG110' - 'MEG111' 'MEG112' - 'MEG113' 'MEG114' - 'MEG115' 'MEG116' - 'MEG117' 'MEG118' - 'MEG119' 'MEG120' - 'MEG121' 'MEG122' - }; + % this is an alternative set of labels without a space in them + neuromag122alt = { + 'MEG001' 'MEG002' + 'MEG003' 'MEG004' + 'MEG005' 'MEG006' + 'MEG007' 'MEG008' + 'MEG009' 'MEG010' + 'MEG011' 'MEG012' + 'MEG013' 'MEG014' + 'MEG015' 'MEG016' + 'MEG017' 'MEG018' + 'MEG019' 'MEG020' + 'MEG021' 'MEG022' + 'MEG023' 'MEG024' + 'MEG025' 'MEG026' + 'MEG027' 'MEG028' + 'MEG029' 'MEG030' + 'MEG031' 'MEG032' + 'MEG033' 'MEG034' + 'MEG035' 'MEG036' + 'MEG037' 'MEG038' + 'MEG039' 'MEG040' + 'MEG041' 'MEG042' + 'MEG043' 'MEG044' + 'MEG045' 'MEG046' + 'MEG047' 'MEG048' + 'MEG049' 'MEG050' + 'MEG051' 'MEG052' + 'MEG053' 'MEG054' + 'MEG055' 'MEG056' + 'MEG057' 'MEG058' + 'MEG059' 'MEG060' + 'MEG061' 'MEG062' + 'MEG063' 'MEG064' + 'MEG065' 'MEG066' + 'MEG067' 'MEG068' + 'MEG069' 'MEG070' + 'MEG071' 'MEG072' + 'MEG073' 'MEG074' + 'MEG075' 'MEG076' + 'MEG077' 'MEG078' + 'MEG079' 'MEG080' + 'MEG081' 'MEG082' + 'MEG083' 'MEG084' + 'MEG085' 'MEG086' + 'MEG087' 'MEG088' + 'MEG089' 'MEG090' + 'MEG091' 'MEG092' + 'MEG093' 'MEG094' + 'MEG095' 'MEG096' + 'MEG097' 'MEG098' + 'MEG099' 'MEG100' + 'MEG101' 'MEG102' + 'MEG103' 'MEG104' + 'MEG105' 'MEG106' + 'MEG107' 'MEG108' + 'MEG109' 'MEG110' + 'MEG111' 'MEG112' + 'MEG113' 'MEG114' + 'MEG115' 'MEG116' + 'MEG117' 'MEG118' + 'MEG119' 'MEG120' + 'MEG121' 'MEG122' + }; - neuromag306 = { - 'MEG 0113' 'MEG 0112' 'MEG 0111' - 'MEG 0122' 'MEG 0123' 'MEG 0121' - 'MEG 0132' 'MEG 0133' 'MEG 0131' - 'MEG 0143' 'MEG 0142' 'MEG 0141' - 'MEG 0213' 'MEG 0212' 'MEG 0211' - 'MEG 0222' 'MEG 0223' 'MEG 0221' - 'MEG 0232' 'MEG 0233' 'MEG 0231' - 'MEG 0243' 'MEG 0242' 'MEG 0241' - 'MEG 0313' 'MEG 0312' 'MEG 0311' - 'MEG 0322' 'MEG 0323' 'MEG 0321' - 'MEG 0333' 'MEG 0332' 'MEG 0331' - 'MEG 0343' 'MEG 0342' 'MEG 0341' - 'MEG 0413' 'MEG 0412' 'MEG 0411' - 'MEG 0422' 'MEG 0423' 'MEG 0421' - 'MEG 0432' 'MEG 0433' 'MEG 0431' - 'MEG 0443' 'MEG 0442' 'MEG 0441' - 'MEG 0513' 'MEG 0512' 'MEG 0511' - 'MEG 0523' 'MEG 0522' 'MEG 0521' - 'MEG 0532' 'MEG 0533' 'MEG 0531' - 'MEG 0542' 'MEG 0543' 'MEG 0541' - 'MEG 0613' 'MEG 0612' 'MEG 0611' - 'MEG 0622' 'MEG 0623' 'MEG 0621' - 'MEG 0633' 'MEG 0632' 'MEG 0631' - 'MEG 0642' 'MEG 0643' 'MEG 0641' - 'MEG 0713' 'MEG 0712' 'MEG 0711' - 'MEG 0723' 'MEG 0722' 'MEG 0721' - 'MEG 0733' 'MEG 0732' 'MEG 0731' - 'MEG 0743' 'MEG 0742' 'MEG 0741' - 'MEG 0813' 'MEG 0812' 'MEG 0811' - 'MEG 0822' 'MEG 0823' 'MEG 0821' - 'MEG 0913' 'MEG 0912' 'MEG 0911' - 'MEG 0923' 'MEG 0922' 'MEG 0921' - 'MEG 0932' 'MEG 0933' 'MEG 0931' - 'MEG 0942' 'MEG 0943' 'MEG 0941' - 'MEG 1013' 'MEG 1012' 'MEG 1011' - 'MEG 1023' 'MEG 1022' 'MEG 1021' - 'MEG 1032' 'MEG 1033' 'MEG 1031' - 'MEG 1043' 'MEG 1042' 'MEG 1041' - 'MEG 1112' 'MEG 1113' 'MEG 1111' - 'MEG 1123' 'MEG 1122' 'MEG 1121' - 'MEG 1133' 'MEG 1132' 'MEG 1131' - 'MEG 1142' 'MEG 1143' 'MEG 1141' - 'MEG 1213' 'MEG 1212' 'MEG 1211' - 'MEG 1223' 'MEG 1222' 'MEG 1221' - 'MEG 1232' 'MEG 1233' 'MEG 1231' - 'MEG 1243' 'MEG 1242' 'MEG 1241' - 'MEG 1312' 'MEG 1313' 'MEG 1311' - 'MEG 1323' 'MEG 1322' 'MEG 1321' - 'MEG 1333' 'MEG 1332' 'MEG 1331' - 'MEG 1342' 'MEG 1343' 'MEG 1341' - 'MEG 1412' 'MEG 1413' 'MEG 1411' - 'MEG 1423' 'MEG 1422' 'MEG 1421' - 'MEG 1433' 'MEG 1432' 'MEG 1431' - 'MEG 1442' 'MEG 1443' 'MEG 1441' - 'MEG 1512' 'MEG 1513' 'MEG 1511' - 'MEG 1522' 'MEG 1523' 'MEG 1521' - 'MEG 1533' 'MEG 1532' 'MEG 1531' - 'MEG 1543' 'MEG 1542' 'MEG 1541' - 'MEG 1613' 'MEG 1612' 'MEG 1611' - 'MEG 1622' 'MEG 1623' 'MEG 1621' - 'MEG 1632' 'MEG 1633' 'MEG 1631' - 'MEG 1643' 'MEG 1642' 'MEG 1641' - 'MEG 1713' 'MEG 1712' 'MEG 1711' - 'MEG 1722' 'MEG 1723' 'MEG 1721' - 'MEG 1732' 'MEG 1733' 'MEG 1731' - 'MEG 1743' 'MEG 1742' 'MEG 1741' - 'MEG 1813' 'MEG 1812' 'MEG 1811' - 'MEG 1822' 'MEG 1823' 'MEG 1821' - 'MEG 1832' 'MEG 1833' 'MEG 1831' - 'MEG 1843' 'MEG 1842' 'MEG 1841' - 'MEG 1912' 'MEG 1913' 'MEG 1911' - 'MEG 1923' 'MEG 1922' 'MEG 1921' - 'MEG 1932' 'MEG 1933' 'MEG 1931' - 'MEG 1943' 'MEG 1942' 'MEG 1941' - 'MEG 2013' 'MEG 2012' 'MEG 2011' - 'MEG 2023' 'MEG 2022' 'MEG 2021' - 'MEG 2032' 'MEG 2033' 'MEG 2031' - 'MEG 2042' 'MEG 2043' 'MEG 2041' - 'MEG 2113' 'MEG 2112' 'MEG 2111' - 'MEG 2122' 'MEG 2123' 'MEG 2121' - 'MEG 2133' 'MEG 2132' 'MEG 2131' - 'MEG 2143' 'MEG 2142' 'MEG 2141' - 'MEG 2212' 'MEG 2213' 'MEG 2211' - 'MEG 2223' 'MEG 2222' 'MEG 2221' - 'MEG 2233' 'MEG 2232' 'MEG 2231' - 'MEG 2242' 'MEG 2243' 'MEG 2241' - 'MEG 2312' 'MEG 2313' 'MEG 2311' - 'MEG 2323' 'MEG 2322' 'MEG 2321' - 'MEG 2332' 'MEG 2333' 'MEG 2331' - 'MEG 2343' 'MEG 2342' 'MEG 2341' - 'MEG 2412' 'MEG 2413' 'MEG 2411' - 'MEG 2423' 'MEG 2422' 'MEG 2421' - 'MEG 2433' 'MEG 2432' 'MEG 2431' - 'MEG 2442' 'MEG 2443' 'MEG 2441' - 'MEG 2512' 'MEG 2513' 'MEG 2511' - 'MEG 2522' 'MEG 2523' 'MEG 2521' - 'MEG 2533' 'MEG 2532' 'MEG 2531' - 'MEG 2543' 'MEG 2542' 'MEG 2541' - 'MEG 2612' 'MEG 2613' 'MEG 2611' - 'MEG 2623' 'MEG 2622' 'MEG 2621' - 'MEG 2633' 'MEG 2632' 'MEG 2631' - 'MEG 2642' 'MEG 2643' 'MEG 2641' - }; + neuromag306 = { + 'MEG 0113' 'MEG 0112' 'MEG 0111' + 'MEG 0122' 'MEG 0123' 'MEG 0121' + 'MEG 0132' 'MEG 0133' 'MEG 0131' + 'MEG 0143' 'MEG 0142' 'MEG 0141' + 'MEG 0213' 'MEG 0212' 'MEG 0211' + 'MEG 0222' 'MEG 0223' 'MEG 0221' + 'MEG 0232' 'MEG 0233' 'MEG 0231' + 'MEG 0243' 'MEG 0242' 'MEG 0241' + 'MEG 0313' 'MEG 0312' 'MEG 0311' + 'MEG 0322' 'MEG 0323' 'MEG 0321' + 'MEG 0333' 'MEG 0332' 'MEG 0331' + 'MEG 0343' 'MEG 0342' 'MEG 0341' + 'MEG 0413' 'MEG 0412' 'MEG 0411' + 'MEG 0422' 'MEG 0423' 'MEG 0421' + 'MEG 0432' 'MEG 0433' 'MEG 0431' + 'MEG 0443' 'MEG 0442' 'MEG 0441' + 'MEG 0513' 'MEG 0512' 'MEG 0511' + 'MEG 0523' 'MEG 0522' 'MEG 0521' + 'MEG 0532' 'MEG 0533' 'MEG 0531' + 'MEG 0542' 'MEG 0543' 'MEG 0541' + 'MEG 0613' 'MEG 0612' 'MEG 0611' + 'MEG 0622' 'MEG 0623' 'MEG 0621' + 'MEG 0633' 'MEG 0632' 'MEG 0631' + 'MEG 0642' 'MEG 0643' 'MEG 0641' + 'MEG 0713' 'MEG 0712' 'MEG 0711' + 'MEG 0723' 'MEG 0722' 'MEG 0721' + 'MEG 0733' 'MEG 0732' 'MEG 0731' + 'MEG 0743' 'MEG 0742' 'MEG 0741' + 'MEG 0813' 'MEG 0812' 'MEG 0811' + 'MEG 0822' 'MEG 0823' 'MEG 0821' + 'MEG 0913' 'MEG 0912' 'MEG 0911' + 'MEG 0923' 'MEG 0922' 'MEG 0921' + 'MEG 0932' 'MEG 0933' 'MEG 0931' + 'MEG 0942' 'MEG 0943' 'MEG 0941' + 'MEG 1013' 'MEG 1012' 'MEG 1011' + 'MEG 1023' 'MEG 1022' 'MEG 1021' + 'MEG 1032' 'MEG 1033' 'MEG 1031' + 'MEG 1043' 'MEG 1042' 'MEG 1041' + 'MEG 1112' 'MEG 1113' 'MEG 1111' + 'MEG 1123' 'MEG 1122' 'MEG 1121' + 'MEG 1133' 'MEG 1132' 'MEG 1131' + 'MEG 1142' 'MEG 1143' 'MEG 1141' + 'MEG 1213' 'MEG 1212' 'MEG 1211' + 'MEG 1223' 'MEG 1222' 'MEG 1221' + 'MEG 1232' 'MEG 1233' 'MEG 1231' + 'MEG 1243' 'MEG 1242' 'MEG 1241' + 'MEG 1312' 'MEG 1313' 'MEG 1311' + 'MEG 1323' 'MEG 1322' 'MEG 1321' + 'MEG 1333' 'MEG 1332' 'MEG 1331' + 'MEG 1342' 'MEG 1343' 'MEG 1341' + 'MEG 1412' 'MEG 1413' 'MEG 1411' + 'MEG 1423' 'MEG 1422' 'MEG 1421' + 'MEG 1433' 'MEG 1432' 'MEG 1431' + 'MEG 1442' 'MEG 1443' 'MEG 1441' + 'MEG 1512' 'MEG 1513' 'MEG 1511' + 'MEG 1522' 'MEG 1523' 'MEG 1521' + 'MEG 1533' 'MEG 1532' 'MEG 1531' + 'MEG 1543' 'MEG 1542' 'MEG 1541' + 'MEG 1613' 'MEG 1612' 'MEG 1611' + 'MEG 1622' 'MEG 1623' 'MEG 1621' + 'MEG 1632' 'MEG 1633' 'MEG 1631' + 'MEG 1643' 'MEG 1642' 'MEG 1641' + 'MEG 1713' 'MEG 1712' 'MEG 1711' + 'MEG 1722' 'MEG 1723' 'MEG 1721' + 'MEG 1732' 'MEG 1733' 'MEG 1731' + 'MEG 1743' 'MEG 1742' 'MEG 1741' + 'MEG 1813' 'MEG 1812' 'MEG 1811' + 'MEG 1822' 'MEG 1823' 'MEG 1821' + 'MEG 1832' 'MEG 1833' 'MEG 1831' + 'MEG 1843' 'MEG 1842' 'MEG 1841' + 'MEG 1912' 'MEG 1913' 'MEG 1911' + 'MEG 1923' 'MEG 1922' 'MEG 1921' + 'MEG 1932' 'MEG 1933' 'MEG 1931' + 'MEG 1943' 'MEG 1942' 'MEG 1941' + 'MEG 2013' 'MEG 2012' 'MEG 2011' + 'MEG 2023' 'MEG 2022' 'MEG 2021' + 'MEG 2032' 'MEG 2033' 'MEG 2031' + 'MEG 2042' 'MEG 2043' 'MEG 2041' + 'MEG 2113' 'MEG 2112' 'MEG 2111' + 'MEG 2122' 'MEG 2123' 'MEG 2121' + 'MEG 2133' 'MEG 2132' 'MEG 2131' + 'MEG 2143' 'MEG 2142' 'MEG 2141' + 'MEG 2212' 'MEG 2213' 'MEG 2211' + 'MEG 2223' 'MEG 2222' 'MEG 2221' + 'MEG 2233' 'MEG 2232' 'MEG 2231' + 'MEG 2242' 'MEG 2243' 'MEG 2241' + 'MEG 2312' 'MEG 2313' 'MEG 2311' + 'MEG 2323' 'MEG 2322' 'MEG 2321' + 'MEG 2332' 'MEG 2333' 'MEG 2331' + 'MEG 2343' 'MEG 2342' 'MEG 2341' + 'MEG 2412' 'MEG 2413' 'MEG 2411' + 'MEG 2423' 'MEG 2422' 'MEG 2421' + 'MEG 2433' 'MEG 2432' 'MEG 2431' + 'MEG 2442' 'MEG 2443' 'MEG 2441' + 'MEG 2512' 'MEG 2513' 'MEG 2511' + 'MEG 2522' 'MEG 2523' 'MEG 2521' + 'MEG 2533' 'MEG 2532' 'MEG 2531' + 'MEG 2543' 'MEG 2542' 'MEG 2541' + 'MEG 2612' 'MEG 2613' 'MEG 2611' + 'MEG 2623' 'MEG 2622' 'MEG 2621' + 'MEG 2633' 'MEG 2632' 'MEG 2631' + 'MEG 2642' 'MEG 2643' 'MEG 2641' + }; - % this is an alternative set of labels without a space in them - neuromag306alt = { - 'MEG0113' 'MEG0112' 'MEG0111' - 'MEG0122' 'MEG0123' 'MEG0121' - 'MEG0132' 'MEG0133' 'MEG0131' - 'MEG0143' 'MEG0142' 'MEG0141' - 'MEG0213' 'MEG0212' 'MEG0211' - 'MEG0222' 'MEG0223' 'MEG0221' - 'MEG0232' 'MEG0233' 'MEG0231' - 'MEG0243' 'MEG0242' 'MEG0241' - 'MEG0313' 'MEG0312' 'MEG0311' - 'MEG0322' 'MEG0323' 'MEG0321' - 'MEG0333' 'MEG0332' 'MEG0331' - 'MEG0343' 'MEG0342' 'MEG0341' - 'MEG0413' 'MEG0412' 'MEG0411' - 'MEG0422' 'MEG0423' 'MEG0421' - 'MEG0432' 'MEG0433' 'MEG0431' - 'MEG0443' 'MEG0442' 'MEG0441' - 'MEG0513' 'MEG0512' 'MEG0511' - 'MEG0523' 'MEG0522' 'MEG0521' - 'MEG0532' 'MEG0533' 'MEG0531' - 'MEG0542' 'MEG0543' 'MEG0541' - 'MEG0613' 'MEG0612' 'MEG0611' - 'MEG0622' 'MEG0623' 'MEG0621' - 'MEG0633' 'MEG0632' 'MEG0631' - 'MEG0642' 'MEG0643' 'MEG0641' - 'MEG0713' 'MEG0712' 'MEG0711' - 'MEG0723' 'MEG0722' 'MEG0721' - 'MEG0733' 'MEG0732' 'MEG0731' - 'MEG0743' 'MEG0742' 'MEG0741' - 'MEG0813' 'MEG0812' 'MEG0811' - 'MEG0822' 'MEG0823' 'MEG0821' - 'MEG0913' 'MEG0912' 'MEG0911' - 'MEG0923' 'MEG0922' 'MEG0921' - 'MEG0932' 'MEG0933' 'MEG0931' - 'MEG0942' 'MEG0943' 'MEG0941' - 'MEG1013' 'MEG1012' 'MEG1011' - 'MEG1023' 'MEG1022' 'MEG1021' - 'MEG1032' 'MEG1033' 'MEG1031' - 'MEG1043' 'MEG1042' 'MEG1041' - 'MEG1112' 'MEG1113' 'MEG1111' - 'MEG1123' 'MEG1122' 'MEG1121' - 'MEG1133' 'MEG1132' 'MEG1131' - 'MEG1142' 'MEG1143' 'MEG1141' - 'MEG1213' 'MEG1212' 'MEG1211' - 'MEG1223' 'MEG1222' 'MEG1221' - 'MEG1232' 'MEG1233' 'MEG1231' - 'MEG1243' 'MEG1242' 'MEG1241' - 'MEG1312' 'MEG1313' 'MEG1311' - 'MEG1323' 'MEG1322' 'MEG1321' - 'MEG1333' 'MEG1332' 'MEG1331' - 'MEG1342' 'MEG1343' 'MEG1341' - 'MEG1412' 'MEG1413' 'MEG1411' - 'MEG1423' 'MEG1422' 'MEG1421' - 'MEG1433' 'MEG1432' 'MEG1431' - 'MEG1442' 'MEG1443' 'MEG1441' - 'MEG1512' 'MEG1513' 'MEG1511' - 'MEG1522' 'MEG1523' 'MEG1521' - 'MEG1533' 'MEG1532' 'MEG1531' - 'MEG1543' 'MEG1542' 'MEG1541' - 'MEG1613' 'MEG1612' 'MEG1611' - 'MEG1622' 'MEG1623' 'MEG1621' - 'MEG1632' 'MEG1633' 'MEG1631' - 'MEG1643' 'MEG1642' 'MEG1641' - 'MEG1713' 'MEG1712' 'MEG1711' - 'MEG1722' 'MEG1723' 'MEG1721' - 'MEG1732' 'MEG1733' 'MEG1731' - 'MEG1743' 'MEG1742' 'MEG1741' - 'MEG1813' 'MEG1812' 'MEG1811' - 'MEG1822' 'MEG1823' 'MEG1821' - 'MEG1832' 'MEG1833' 'MEG1831' - 'MEG1843' 'MEG1842' 'MEG1841' - 'MEG1912' 'MEG1913' 'MEG1911' - 'MEG1923' 'MEG1922' 'MEG1921' - 'MEG1932' 'MEG1933' 'MEG1931' - 'MEG1943' 'MEG1942' 'MEG1941' - 'MEG2013' 'MEG2012' 'MEG2011' - 'MEG2023' 'MEG2022' 'MEG2021' - 'MEG2032' 'MEG2033' 'MEG2031' - 'MEG2042' 'MEG2043' 'MEG2041' - 'MEG2113' 'MEG2112' 'MEG2111' - 'MEG2122' 'MEG2123' 'MEG2121' - 'MEG2133' 'MEG2132' 'MEG2131' - 'MEG2143' 'MEG2142' 'MEG2141' - 'MEG2212' 'MEG2213' 'MEG2211' - 'MEG2223' 'MEG2222' 'MEG2221' - 'MEG2233' 'MEG2232' 'MEG2231' - 'MEG2242' 'MEG2243' 'MEG2241' - 'MEG2312' 'MEG2313' 'MEG2311' - 'MEG2323' 'MEG2322' 'MEG2321' - 'MEG2332' 'MEG2333' 'MEG2331' - 'MEG2343' 'MEG2342' 'MEG2341' - 'MEG2412' 'MEG2413' 'MEG2411' - 'MEG2423' 'MEG2422' 'MEG2421' - 'MEG2433' 'MEG2432' 'MEG2431' - 'MEG2442' 'MEG2443' 'MEG2441' - 'MEG2512' 'MEG2513' 'MEG2511' - 'MEG2522' 'MEG2523' 'MEG2521' - 'MEG2533' 'MEG2532' 'MEG2531' - 'MEG2543' 'MEG2542' 'MEG2541' - 'MEG2612' 'MEG2613' 'MEG2611' - 'MEG2623' 'MEG2622' 'MEG2621' - 'MEG2633' 'MEG2632' 'MEG2631' - 'MEG2642' 'MEG2643' 'MEG2641' - }; + % this is an alternative set of labels without a space in them + neuromag306alt = { + 'MEG0113' 'MEG0112' 'MEG0111' + 'MEG0122' 'MEG0123' 'MEG0121' + 'MEG0132' 'MEG0133' 'MEG0131' + 'MEG0143' 'MEG0142' 'MEG0141' + 'MEG0213' 'MEG0212' 'MEG0211' + 'MEG0222' 'MEG0223' 'MEG0221' + 'MEG0232' 'MEG0233' 'MEG0231' + 'MEG0243' 'MEG0242' 'MEG0241' + 'MEG0313' 'MEG0312' 'MEG0311' + 'MEG0322' 'MEG0323' 'MEG0321' + 'MEG0333' 'MEG0332' 'MEG0331' + 'MEG0343' 'MEG0342' 'MEG0341' + 'MEG0413' 'MEG0412' 'MEG0411' + 'MEG0422' 'MEG0423' 'MEG0421' + 'MEG0432' 'MEG0433' 'MEG0431' + 'MEG0443' 'MEG0442' 'MEG0441' + 'MEG0513' 'MEG0512' 'MEG0511' + 'MEG0523' 'MEG0522' 'MEG0521' + 'MEG0532' 'MEG0533' 'MEG0531' + 'MEG0542' 'MEG0543' 'MEG0541' + 'MEG0613' 'MEG0612' 'MEG0611' + 'MEG0622' 'MEG0623' 'MEG0621' + 'MEG0633' 'MEG0632' 'MEG0631' + 'MEG0642' 'MEG0643' 'MEG0641' + 'MEG0713' 'MEG0712' 'MEG0711' + 'MEG0723' 'MEG0722' 'MEG0721' + 'MEG0733' 'MEG0732' 'MEG0731' + 'MEG0743' 'MEG0742' 'MEG0741' + 'MEG0813' 'MEG0812' 'MEG0811' + 'MEG0822' 'MEG0823' 'MEG0821' + 'MEG0913' 'MEG0912' 'MEG0911' + 'MEG0923' 'MEG0922' 'MEG0921' + 'MEG0932' 'MEG0933' 'MEG0931' + 'MEG0942' 'MEG0943' 'MEG0941' + 'MEG1013' 'MEG1012' 'MEG1011' + 'MEG1023' 'MEG1022' 'MEG1021' + 'MEG1032' 'MEG1033' 'MEG1031' + 'MEG1043' 'MEG1042' 'MEG1041' + 'MEG1112' 'MEG1113' 'MEG1111' + 'MEG1123' 'MEG1122' 'MEG1121' + 'MEG1133' 'MEG1132' 'MEG1131' + 'MEG1142' 'MEG1143' 'MEG1141' + 'MEG1213' 'MEG1212' 'MEG1211' + 'MEG1223' 'MEG1222' 'MEG1221' + 'MEG1232' 'MEG1233' 'MEG1231' + 'MEG1243' 'MEG1242' 'MEG1241' + 'MEG1312' 'MEG1313' 'MEG1311' + 'MEG1323' 'MEG1322' 'MEG1321' + 'MEG1333' 'MEG1332' 'MEG1331' + 'MEG1342' 'MEG1343' 'MEG1341' + 'MEG1412' 'MEG1413' 'MEG1411' + 'MEG1423' 'MEG1422' 'MEG1421' + 'MEG1433' 'MEG1432' 'MEG1431' + 'MEG1442' 'MEG1443' 'MEG1441' + 'MEG1512' 'MEG1513' 'MEG1511' + 'MEG1522' 'MEG1523' 'MEG1521' + 'MEG1533' 'MEG1532' 'MEG1531' + 'MEG1543' 'MEG1542' 'MEG1541' + 'MEG1613' 'MEG1612' 'MEG1611' + 'MEG1622' 'MEG1623' 'MEG1621' + 'MEG1632' 'MEG1633' 'MEG1631' + 'MEG1643' 'MEG1642' 'MEG1641' + 'MEG1713' 'MEG1712' 'MEG1711' + 'MEG1722' 'MEG1723' 'MEG1721' + 'MEG1732' 'MEG1733' 'MEG1731' + 'MEG1743' 'MEG1742' 'MEG1741' + 'MEG1813' 'MEG1812' 'MEG1811' + 'MEG1822' 'MEG1823' 'MEG1821' + 'MEG1832' 'MEG1833' 'MEG1831' + 'MEG1843' 'MEG1842' 'MEG1841' + 'MEG1912' 'MEG1913' 'MEG1911' + 'MEG1923' 'MEG1922' 'MEG1921' + 'MEG1932' 'MEG1933' 'MEG1931' + 'MEG1943' 'MEG1942' 'MEG1941' + 'MEG2013' 'MEG2012' 'MEG2011' + 'MEG2023' 'MEG2022' 'MEG2021' + 'MEG2032' 'MEG2033' 'MEG2031' + 'MEG2042' 'MEG2043' 'MEG2041' + 'MEG2113' 'MEG2112' 'MEG2111' + 'MEG2122' 'MEG2123' 'MEG2121' + 'MEG2133' 'MEG2132' 'MEG2131' + 'MEG2143' 'MEG2142' 'MEG2141' + 'MEG2212' 'MEG2213' 'MEG2211' + 'MEG2223' 'MEG2222' 'MEG2221' + 'MEG2233' 'MEG2232' 'MEG2231' + 'MEG2242' 'MEG2243' 'MEG2241' + 'MEG2312' 'MEG2313' 'MEG2311' + 'MEG2323' 'MEG2322' 'MEG2321' + 'MEG2332' 'MEG2333' 'MEG2331' + 'MEG2343' 'MEG2342' 'MEG2341' + 'MEG2412' 'MEG2413' 'MEG2411' + 'MEG2423' 'MEG2422' 'MEG2421' + 'MEG2433' 'MEG2432' 'MEG2431' + 'MEG2442' 'MEG2443' 'MEG2441' + 'MEG2512' 'MEG2513' 'MEG2511' + 'MEG2522' 'MEG2523' 'MEG2521' + 'MEG2533' 'MEG2532' 'MEG2531' + 'MEG2543' 'MEG2542' 'MEG2541' + 'MEG2612' 'MEG2613' 'MEG2611' + 'MEG2623' 'MEG2622' 'MEG2621' + 'MEG2633' 'MEG2632' 'MEG2631' + 'MEG2642' 'MEG2643' 'MEG2641' + }; end % if isneuromag if iseeg || isext - eeg1020 = { - 'Fp1' - 'Fpz' - 'Fp2' - 'F7' - 'F3' - 'Fz' - 'F4' - 'F8' - 'T7' - 'C3' - 'Cz' - 'C4' - 'T8' - 'P7' - 'P3' - 'Pz' - 'P4' - 'P8' - 'O1' - 'Oz' - 'O2'}; + eeg1020 = { + 'Fp1' + 'Fpz' + 'Fp2' + 'F7' + 'F3' + 'Fz' + 'F4' + 'F8' + 'T7' + 'C3' + 'Cz' + 'C4' + 'T8' + 'P7' + 'P3' + 'Pz' + 'P4' + 'P8' + 'O1' + 'Oz' + 'O2'}; - eeg1010 = { - 'Fp1' - 'Fpz' - 'Fp2' - 'AF9' - 'AF7' - 'AF5' - 'AF3' - 'AF1' - 'AFz' - 'AF2' - 'AF4' - 'AF6' - 'AF8' - 'AF10' - 'F9' - 'F7' - 'F5' - 'F3' - 'F1' - 'Fz' - 'F2' - 'F4' - 'F6' - 'F8' - 'F10' - 'FT9' - 'FT7' - 'FC5' - 'FC3' - 'FC1' - 'FCz' - 'FC2' - 'FC4' - 'FC6' - 'FT8' - 'FT10' - 'T9' - 'T7' - 'C5' - 'C3' - 'C1' - 'Cz' - 'C2' - 'C4' - 'C6' - 'T8' - 'T10' - 'TP9' - 'TP7' - 'CP5' - 'CP3' - 'CP1' - 'CPz' - 'CP2' - 'CP4' - 'CP6' - 'TP8' - 'TP10' - 'P9' - 'P7' - 'P5' - 'P3' - 'P1' - 'Pz' - 'P2' - 'P4' - 'P6' - 'P8' - 'P10' - 'PO9' - 'PO7' - 'PO5' - 'PO3' - 'PO1' - 'POz' - 'PO2' - 'PO4' - 'PO6' - 'PO8' - 'PO10' - 'O1' - 'Oz' - 'O2' - 'I1' - 'Iz' - 'I2' - }; + eeg1010 = { + 'Fp1' + 'Fpz' + 'Fp2' + 'AF9' + 'AF7' + 'AF5' + 'AF3' + 'AF1' + 'AFz' + 'AF2' + 'AF4' + 'AF6' + 'AF8' + 'AF10' + 'F9' + 'F7' + 'F5' + 'F3' + 'F1' + 'Fz' + 'F2' + 'F4' + 'F6' + 'F8' + 'F10' + 'FT9' + 'FT7' + 'FC5' + 'FC3' + 'FC1' + 'FCz' + 'FC2' + 'FC4' + 'FC6' + 'FT8' + 'FT10' + 'T9' + 'T7' + 'C5' + 'C3' + 'C1' + 'Cz' + 'C2' + 'C4' + 'C6' + 'T8' + 'T10' + 'TP9' + 'TP7' + 'CP5' + 'CP3' + 'CP1' + 'CPz' + 'CP2' + 'CP4' + 'CP6' + 'TP8' + 'TP10' + 'P9' + 'P7' + 'P5' + 'P3' + 'P1' + 'Pz' + 'P2' + 'P4' + 'P6' + 'P8' + 'P10' + 'PO9' + 'PO7' + 'PO5' + 'PO3' + 'PO1' + 'POz' + 'PO2' + 'PO4' + 'PO6' + 'PO8' + 'PO10' + 'O1' + 'Oz' + 'O2' + 'I1' + 'Iz' + 'I2' + }; - eeg1005 = { - 'Fp1' - 'Fpz' - 'Fp2' - 'AF9' - 'AF7' - 'AF5' - 'AF3' - 'AF1' - 'AFz' - 'AF2' - 'AF4' - 'AF6' - 'AF8' - 'AF10' - 'F9' - 'F7' - 'F5' - 'F3' - 'F1' - 'Fz' - 'F2' - 'F4' - 'F6' - 'F8' - 'F10' - 'FT9' - 'FT7' - 'FC5' - 'FC3' - 'FC1' - 'FCz' - 'FC2' - 'FC4' - 'FC6' - 'FT8' - 'FT10' - 'T9' - 'T7' - 'C5' - 'C3' - 'C1' - 'Cz' - 'C2' - 'C4' - 'C6' - 'T8' - 'T10' - 'TP9' - 'TP7' - 'CP5' - 'CP3' - 'CP1' - 'CPz' - 'CP2' - 'CP4' - 'CP6' - 'TP8' - 'TP10' - 'P9' - 'P7' - 'P5' - 'P3' - 'P1' - 'Pz' - 'P2' - 'P4' - 'P6' - 'P8' - 'P10' - 'PO9' - 'PO7' - 'PO5' - 'PO3' - 'PO1' - 'POz' - 'PO2' - 'PO4' - 'PO6' - 'PO8' - 'PO10' - 'O1' - 'Oz' - 'O2' - 'I1' - 'Iz' - 'I2' - 'AFp9h' - 'AFp7h' - 'AFp5h' - 'AFp3h' - 'AFp1h' - 'AFp2h' - 'AFp4h' - 'AFp6h' - 'AFp8h' - 'AFp10h' - 'AFF9h' - 'AFF7h' - 'AFF5h' - 'AFF3h' - 'AFF1h' - 'AFF2h' - 'AFF4h' - 'AFF6h' - 'AFF8h' - 'AFF10h' - 'FFT9h' - 'FFT7h' - 'FFC5h' - 'FFC3h' - 'FFC1h' - 'FFC2h' - 'FFC4h' - 'FFC6h' - 'FFT8h' - 'FFT10h' - 'FTT9h' - 'FTT7h' - 'FCC5h' - 'FCC3h' - 'FCC1h' - 'FCC2h' - 'FCC4h' - 'FCC6h' - 'FTT8h' - 'FTT10h' - 'TTP9h' - 'TTP7h' - 'CCP5h' - 'CCP3h' - 'CCP1h' - 'CCP2h' - 'CCP4h' - 'CCP6h' - 'TTP8h' - 'TTP10h' - 'TPP9h' - 'TPP7h' - 'CPP5h' - 'CPP3h' - 'CPP1h' - 'CPP2h' - 'CPP4h' - 'CPP6h' - 'TPP8h' - 'TPP10h' - 'PPO9h' - 'PPO7h' - 'PPO5h' - 'PPO3h' - 'PPO1h' - 'PPO2h' - 'PPO4h' - 'PPO6h' - 'PPO8h' - 'PPO10h' - 'POO9h' - 'POO7h' - 'POO5h' - 'POO3h' - 'POO1h' - 'POO2h' - 'POO4h' - 'POO6h' - 'POO8h' - 'POO10h' - 'OI1h' - 'OI2h' - 'Fp1h' - 'Fp2h' - 'AF9h' - 'AF7h' - 'AF5h' - 'AF3h' - 'AF1h' - 'AF2h' - 'AF4h' - 'AF6h' - 'AF8h' - 'AF10h' - 'F9h' - 'F7h' - 'F5h' - 'F3h' - 'F1h' - 'F2h' - 'F4h' - 'F6h' - 'F8h' - 'F10h' - 'FT9h' - 'FT7h' - 'FC5h' - 'FC3h' - 'FC1h' - 'FC2h' - 'FC4h' - 'FC6h' - 'FT8h' - 'FT10h' - 'T9h' - 'T7h' - 'C5h' - 'C3h' - 'C1h' - 'C2h' - 'C4h' - 'C6h' - 'T8h' - 'T10h' - 'TP9h' - 'TP7h' - 'CP5h' - 'CP3h' - 'CP1h' - 'CP2h' - 'CP4h' - 'CP6h' - 'TP8h' - 'TP10h' - 'P9h' - 'P7h' - 'P5h' - 'P3h' - 'P1h' - 'P2h' - 'P4h' - 'P6h' - 'P8h' - 'P10h' - 'PO9h' - 'PO7h' - 'PO5h' - 'PO3h' - 'PO1h' - 'PO2h' - 'PO4h' - 'PO6h' - 'PO8h' - 'PO10h' - 'O1h' - 'O2h' - 'I1h' - 'I2h' - 'AFp9' - 'AFp7' - 'AFp5' - 'AFp3' - 'AFp1' - 'AFpz' - 'AFp2' - 'AFp4' - 'AFp6' - 'AFp8' - 'AFp10' - 'AFF9' - 'AFF7' - 'AFF5' - 'AFF3' - 'AFF1' - 'AFFz' - 'AFF2' - 'AFF4' - 'AFF6' - 'AFF8' - 'AFF10' - 'FFT9' - 'FFT7' - 'FFC5' - 'FFC3' - 'FFC1' - 'FFCz' - 'FFC2' - 'FFC4' - 'FFC6' - 'FFT8' - 'FFT10' - 'FTT9' - 'FTT7' - 'FCC5' - 'FCC3' - 'FCC1' - 'FCCz' - 'FCC2' - 'FCC4' - 'FCC6' - 'FTT8' - 'FTT10' - 'TTP9' - 'TTP7' - 'CCP5' - 'CCP3' - 'CCP1' - 'CCPz' - 'CCP2' - 'CCP4' - 'CCP6' - 'TTP8' - 'TTP10' - 'TPP9' - 'TPP7' - 'CPP5' - 'CPP3' - 'CPP1' - 'CPPz' - 'CPP2' - 'CPP4' - 'CPP6' - 'TPP8' - 'TPP10' - 'PPO9' - 'PPO7' - 'PPO5' - 'PPO3' - 'PPO1' - 'PPOz' - 'PPO2' - 'PPO4' - 'PPO6' - 'PPO8' - 'PPO10' - 'POO9' - 'POO7' - 'POO5' - 'POO3' - 'POO1' - 'POOz' - 'POO2' - 'POO4' - 'POO6' - 'POO8' - 'POO10' - 'OI1' - 'OIz' - 'OI2' - }; + eeg1005 = { + 'Fp1' + 'Fpz' + 'Fp2' + 'AF9' + 'AF7' + 'AF5' + 'AF3' + 'AF1' + 'AFz' + 'AF2' + 'AF4' + 'AF6' + 'AF8' + 'AF10' + 'F9' + 'F7' + 'F5' + 'F3' + 'F1' + 'Fz' + 'F2' + 'F4' + 'F6' + 'F8' + 'F10' + 'FT9' + 'FT7' + 'FC5' + 'FC3' + 'FC1' + 'FCz' + 'FC2' + 'FC4' + 'FC6' + 'FT8' + 'FT10' + 'T9' + 'T7' + 'C5' + 'C3' + 'C1' + 'Cz' + 'C2' + 'C4' + 'C6' + 'T8' + 'T10' + 'TP9' + 'TP7' + 'CP5' + 'CP3' + 'CP1' + 'CPz' + 'CP2' + 'CP4' + 'CP6' + 'TP8' + 'TP10' + 'P9' + 'P7' + 'P5' + 'P3' + 'P1' + 'Pz' + 'P2' + 'P4' + 'P6' + 'P8' + 'P10' + 'PO9' + 'PO7' + 'PO5' + 'PO3' + 'PO1' + 'POz' + 'PO2' + 'PO4' + 'PO6' + 'PO8' + 'PO10' + 'O1' + 'Oz' + 'O2' + 'I1' + 'Iz' + 'I2' + 'AFp9h' + 'AFp7h' + 'AFp5h' + 'AFp3h' + 'AFp1h' + 'AFp2h' + 'AFp4h' + 'AFp6h' + 'AFp8h' + 'AFp10h' + 'AFF9h' + 'AFF7h' + 'AFF5h' + 'AFF3h' + 'AFF1h' + 'AFF2h' + 'AFF4h' + 'AFF6h' + 'AFF8h' + 'AFF10h' + 'FFT9h' + 'FFT7h' + 'FFC5h' + 'FFC3h' + 'FFC1h' + 'FFC2h' + 'FFC4h' + 'FFC6h' + 'FFT8h' + 'FFT10h' + 'FTT9h' + 'FTT7h' + 'FCC5h' + 'FCC3h' + 'FCC1h' + 'FCC2h' + 'FCC4h' + 'FCC6h' + 'FTT8h' + 'FTT10h' + 'TTP9h' + 'TTP7h' + 'CCP5h' + 'CCP3h' + 'CCP1h' + 'CCP2h' + 'CCP4h' + 'CCP6h' + 'TTP8h' + 'TTP10h' + 'TPP9h' + 'TPP7h' + 'CPP5h' + 'CPP3h' + 'CPP1h' + 'CPP2h' + 'CPP4h' + 'CPP6h' + 'TPP8h' + 'TPP10h' + 'PPO9h' + 'PPO7h' + 'PPO5h' + 'PPO3h' + 'PPO1h' + 'PPO2h' + 'PPO4h' + 'PPO6h' + 'PPO8h' + 'PPO10h' + 'POO9h' + 'POO7h' + 'POO5h' + 'POO3h' + 'POO1h' + 'POO2h' + 'POO4h' + 'POO6h' + 'POO8h' + 'POO10h' + 'OI1h' + 'OI2h' + 'Fp1h' + 'Fp2h' + 'AF9h' + 'AF7h' + 'AF5h' + 'AF3h' + 'AF1h' + 'AF2h' + 'AF4h' + 'AF6h' + 'AF8h' + 'AF10h' + 'F9h' + 'F7h' + 'F5h' + 'F3h' + 'F1h' + 'F2h' + 'F4h' + 'F6h' + 'F8h' + 'F10h' + 'FT9h' + 'FT7h' + 'FC5h' + 'FC3h' + 'FC1h' + 'FC2h' + 'FC4h' + 'FC6h' + 'FT8h' + 'FT10h' + 'T9h' + 'T7h' + 'C5h' + 'C3h' + 'C1h' + 'C2h' + 'C4h' + 'C6h' + 'T8h' + 'T10h' + 'TP9h' + 'TP7h' + 'CP5h' + 'CP3h' + 'CP1h' + 'CP2h' + 'CP4h' + 'CP6h' + 'TP8h' + 'TP10h' + 'P9h' + 'P7h' + 'P5h' + 'P3h' + 'P1h' + 'P2h' + 'P4h' + 'P6h' + 'P8h' + 'P10h' + 'PO9h' + 'PO7h' + 'PO5h' + 'PO3h' + 'PO1h' + 'PO2h' + 'PO4h' + 'PO6h' + 'PO8h' + 'PO10h' + 'O1h' + 'O2h' + 'I1h' + 'I2h' + 'AFp9' + 'AFp7' + 'AFp5' + 'AFp3' + 'AFp1' + 'AFpz' + 'AFp2' + 'AFp4' + 'AFp6' + 'AFp8' + 'AFp10' + 'AFF9' + 'AFF7' + 'AFF5' + 'AFF3' + 'AFF1' + 'AFFz' + 'AFF2' + 'AFF4' + 'AFF6' + 'AFF8' + 'AFF10' + 'FFT9' + 'FFT7' + 'FFC5' + 'FFC3' + 'FFC1' + 'FFCz' + 'FFC2' + 'FFC4' + 'FFC6' + 'FFT8' + 'FFT10' + 'FTT9' + 'FTT7' + 'FCC5' + 'FCC3' + 'FCC1' + 'FCCz' + 'FCC2' + 'FCC4' + 'FCC6' + 'FTT8' + 'FTT10' + 'TTP9' + 'TTP7' + 'CCP5' + 'CCP3' + 'CCP1' + 'CCPz' + 'CCP2' + 'CCP4' + 'CCP6' + 'TTP8' + 'TTP10' + 'TPP9' + 'TPP7' + 'CPP5' + 'CPP3' + 'CPP1' + 'CPPz' + 'CPP2' + 'CPP4' + 'CPP6' + 'TPP8' + 'TPP10' + 'PPO9' + 'PPO7' + 'PPO5' + 'PPO3' + 'PPO1' + 'PPOz' + 'PPO2' + 'PPO4' + 'PPO6' + 'PPO8' + 'PPO10' + 'POO9' + 'POO7' + 'POO5' + 'POO3' + 'POO1' + 'POOz' + 'POO2' + 'POO4' + 'POO6' + 'POO8' + 'POO10' + 'OI1' + 'OIz' + 'OI2' + }; - % Add also alternative labels that are used in some systems - ext1020 = cat(1, eeg1005, {'A1' 'A2' 'M1' 'M2' 'T3' 'T4' 'T5' 'T6'}'); + % Add also alternative labels that are used in some systems + ext1020 = cat(1, eeg1005, {'A1' 'A2' 'M1' 'M2' 'T3' 'T4' 'T5' 'T6'}'); - % This is to account for all variants of case in 1020 systems - ext1020 = unique(cat(1, ext1020, upper(ext1020), lower(ext1020))); + % This is to account for all variants of case in 1020 systems + ext1020 = unique(cat(1, ext1020, upper(ext1020), lower(ext1020))); end % if iseeg || isext if isbiosemi - biosemi128 = { - 'A1' - 'A2' - 'A3' - 'A4' - 'A5' - 'A6' - 'A7' - 'A8' - 'A9' - 'A10' - 'A11' - 'A12' - 'A13' - 'A14' - 'A15' - 'A16' - 'A17' - 'A18' - 'A19' - 'A20' - 'A21' - 'A22' - 'A23' - 'A24' - 'A25' - 'A26' - 'A27' - 'A28' - 'A29' - 'A30' - 'A31' - 'A32' - 'B1' - 'B2' - 'B3' - 'B4' - 'B5' - 'B6' - 'B7' - 'B8' - 'B9' - 'B10' - 'B11' - 'B12' - 'B13' - 'B14' - 'B15' - 'B16' - 'B17' - 'B18' - 'B19' - 'B20' - 'B21' - 'B22' - 'B23' - 'B24' - 'B25' - 'B26' - 'B27' - 'B28' - 'B29' - 'B30' - 'B31' - 'B32' - 'C1' - 'C2' - 'C3' - 'C4' - 'C5' - 'C6' - 'C7' - 'C8' - 'C9' - 'C10' - 'C11' - 'C12' - 'C13' - 'C14' - 'C15' - 'C16' - 'C17' - 'C18' - 'C19' - 'C20' - 'C21' - 'C22' - 'C23' - 'C24' - 'C25' - 'C26' - 'C27' - 'C28' - 'C29' - 'C30' - 'C31' - 'C32' - 'D1' - 'D2' - 'D3' - 'D4' - 'D5' - 'D6' - 'D7' - 'D8' - 'D9' - 'D10' - 'D11' - 'D12' - 'D13' - 'D14' - 'D15' - 'D16' - 'D17' - 'D18' - 'D19' - 'D20' - 'D21' - 'D22' - 'D23' - 'D24' - 'D25' - 'D26' - 'D27' - 'D28' - 'D29' - 'D30' - 'D31' - 'D32' - }; + biosemi64 = { + 'A1' + 'A2' + 'A3' + 'A4' + 'A5' + 'A6' + 'A7' + 'A8' + 'A9' + 'A10' + 'A11' + 'A12' + 'A13' + 'A14' + 'A15' + 'A16' + 'A17' + 'A18' + 'A19' + 'A20' + 'A21' + 'A22' + 'A23' + 'A24' + 'A25' + 'A26' + 'A27' + 'A28' + 'A29' + 'A30' + 'A31' + 'A32' + 'B1' + 'B2' + 'B3' + 'B4' + 'B5' + 'B6' + 'B7' + 'B8' + 'B9' + 'B10' + 'B11' + 'B12' + 'B13' + 'B14' + 'B15' + 'B16' + 'B17' + 'B18' + 'B19' + 'B20' + 'B21' + 'B22' + 'B23' + 'B24' + 'B25' + 'B26' + 'B27' + 'B28' + 'B29' + 'B30' + 'B31' + 'B32' + }; + + biosemi128 = { + 'A1' + 'A2' + 'A3' + 'A4' + 'A5' + 'A6' + 'A7' + 'A8' + 'A9' + 'A10' + 'A11' + 'A12' + 'A13' + 'A14' + 'A15' + 'A16' + 'A17' + 'A18' + 'A19' + 'A20' + 'A21' + 'A22' + 'A23' + 'A24' + 'A25' + 'A26' + 'A27' + 'A28' + 'A29' + 'A30' + 'A31' + 'A32' + 'B1' + 'B2' + 'B3' + 'B4' + 'B5' + 'B6' + 'B7' + 'B8' + 'B9' + 'B10' + 'B11' + 'B12' + 'B13' + 'B14' + 'B15' + 'B16' + 'B17' + 'B18' + 'B19' + 'B20' + 'B21' + 'B22' + 'B23' + 'B24' + 'B25' + 'B26' + 'B27' + 'B28' + 'B29' + 'B30' + 'B31' + 'B32' + 'C1' + 'C2' + 'C3' + 'C4' + 'C5' + 'C6' + 'C7' + 'C8' + 'C9' + 'C10' + 'C11' + 'C12' + 'C13' + 'C14' + 'C15' + 'C16' + 'C17' + 'C18' + 'C19' + 'C20' + 'C21' + 'C22' + 'C23' + 'C24' + 'C25' + 'C26' + 'C27' + 'C28' + 'C29' + 'C30' + 'C31' + 'C32' + 'D1' + 'D2' + 'D3' + 'D4' + 'D5' + 'D6' + 'D7' + 'D8' + 'D9' + 'D10' + 'D11' + 'D12' + 'D13' + 'D14' + 'D15' + 'D16' + 'D17' + 'D18' + 'D19' + 'D20' + 'D21' + 'D22' + 'D23' + 'D24' + 'D25' + 'D26' + 'D27' + 'D28' + 'D29' + 'D30' + 'D31' + 'D32' + }; + + biosemi256 = { + 'A1' + 'A2' + 'A3' + 'A4' + 'A5' + 'A6' + 'A7' + 'A8' + 'A9' + 'A10' + 'A11' + 'A12' + 'A13' + 'A14' + 'A15' + 'A16' + 'A17' + 'A18' + 'A19' + 'A20' + 'A21' + 'A22' + 'A23' + 'A24' + 'A25' + 'A26' + 'A27' + 'A28' + 'A29' + 'A30' + 'A31' + 'A32' + 'B1' + 'B2' + 'B3' + 'B4' + 'B5' + 'B6' + 'B7' + 'B8' + 'B9' + 'B10' + 'B11' + 'B12' + 'B13' + 'B14' + 'B15' + 'B16' + 'B17' + 'B18' + 'B19' + 'B20' + 'B21' + 'B22' + 'B23' + 'B24' + 'B25' + 'B26' + 'B27' + 'B28' + 'B29' + 'B30' + 'B31' + 'B32' + 'C1' + 'C2' + 'C3' + 'C4' + 'C5' + 'C6' + 'C7' + 'C8' + 'C9' + 'C10' + 'C11' + 'C12' + 'C13' + 'C14' + 'C15' + 'C16' + 'C17' + 'C18' + 'C19' + 'C20' + 'C21' + 'C22' + 'C23' + 'C24' + 'C25' + 'C26' + 'C27' + 'C28' + 'C29' + 'C30' + 'C31' + 'C32' + 'D1' + 'D2' + 'D3' + 'D4' + 'D5' + 'D6' + 'D7' + 'D8' + 'D9' + 'D10' + 'D11' + 'D12' + 'D13' + 'D14' + 'D15' + 'D16' + 'D17' + 'D18' + 'D19' + 'D20' + 'D21' + 'D22' + 'D23' + 'D24' + 'D25' + 'D26' + 'D27' + 'D28' + 'D29' + 'D30' + 'D31' + 'D32' + 'E1' + 'E2' + 'E3' + 'E4' + 'E5' + 'E6' + 'E7' + 'E8' + 'E9' + 'E10' + 'E11' + 'E12' + 'E13' + 'E14' + 'E15' + 'E16' + 'E17' + 'E18' + 'E19' + 'E20' + 'E21' + 'E22' + 'E23' + 'E24' + 'E25' + 'E26' + 'E27' + 'E28' + 'E29' + 'E30' + 'E31' + 'E32' + 'F1' + 'F2' + 'F3' + 'F4' + 'F5' + 'F6' + 'F7' + 'F8' + 'F9' + 'F10' + 'F11' + 'F12' + 'F13' + 'F14' + 'F15' + 'F16' + 'F17' + 'F18' + 'F19' + 'F20' + 'F21' + 'F22' + 'F23' + 'F24' + 'F25' + 'F26' + 'F27' + 'F28' + 'F29' + 'F30' + 'F31' + 'F32' + 'G1' + 'G2' + 'G3' + 'G4' + 'G5' + 'G6' + 'G7' + 'G8' + 'G9' + 'G10' + 'G11' + 'G12' + 'G13' + 'G14' + 'G15' + 'G16' + 'G17' + 'G18' + 'G19' + 'G20' + 'G21' + 'G22' + 'G23' + 'G24' + 'G25' + 'G26' + 'G27' + 'G28' + 'G29' + 'G30' + 'G31' + 'G32' + 'H1' + 'H2' + 'H3' + 'H4' + 'H5' + 'H6' + 'H7' + 'H8' + 'H9' + 'H10' + 'H11' + 'H12' + 'H13' + 'H14' + 'H15' + 'H16' + 'H17' + 'H18' + 'H19' + 'H20' + 'H21' + 'H22' + 'H23' + 'H24' + 'H25' + 'H26' + 'H27' + 'H28' + 'H29' + 'H30' + 'H31' + 'H32' + }; - biosemi256 = { - 'A1' - 'A2' - 'A3' - 'A4' - 'A5' - 'A6' - 'A7' - 'A8' - 'A9' - 'A10' - 'A11' - 'A12' - 'A13' - 'A14' - 'A15' - 'A16' - 'A17' - 'A18' - 'A19' - 'A20' - 'A21' - 'A22' - 'A23' - 'A24' - 'A25' - 'A26' - 'A27' - 'A28' - 'A29' - 'A30' - 'A31' - 'A32' - 'B1' - 'B2' - 'B3' - 'B4' - 'B5' - 'B6' - 'B7' - 'B8' - 'B9' - 'B10' - 'B11' - 'B12' - 'B13' - 'B14' - 'B15' - 'B16' - 'B17' - 'B18' - 'B19' - 'B20' - 'B21' - 'B22' - 'B23' - 'B24' - 'B25' - 'B26' - 'B27' - 'B28' - 'B29' - 'B30' - 'B31' - 'B32' - 'C1' - 'C2' - 'C3' - 'C4' - 'C5' - 'C6' - 'C7' - 'C8' - 'C9' - 'C10' - 'C11' - 'C12' - 'C13' - 'C14' - 'C15' - 'C16' - 'C17' - 'C18' - 'C19' - 'C20' - 'C21' - 'C22' - 'C23' - 'C24' - 'C25' - 'C26' - 'C27' - 'C28' - 'C29' - 'C30' - 'C31' - 'C32' - 'D1' - 'D2' - 'D3' - 'D4' - 'D5' - 'D6' - 'D7' - 'D8' - 'D9' - 'D10' - 'D11' - 'D12' - 'D13' - 'D14' - 'D15' - 'D16' - 'D17' - 'D18' - 'D19' - 'D20' - 'D21' - 'D22' - 'D23' - 'D24' - 'D25' - 'D26' - 'D27' - 'D28' - 'D29' - 'D30' - 'D31' - 'D32' - 'E1' - 'E2' - 'E3' - 'E4' - 'E5' - 'E6' - 'E7' - 'E8' - 'E9' - 'E10' - 'E11' - 'E12' - 'E13' - 'E14' - 'E15' - 'E16' - 'E17' - 'E18' - 'E19' - 'E20' - 'E21' - 'E22' - 'E23' - 'E24' - 'E25' - 'E26' - 'E27' - 'E28' - 'E29' - 'E30' - 'E31' - 'E32' - 'F1' - 'F2' - 'F3' - 'F4' - 'F5' - 'F6' - 'F7' - 'F8' - 'F9' - 'F10' - 'F11' - 'F12' - 'F13' - 'F14' - 'F15' - 'F16' - 'F17' - 'F18' - 'F19' - 'F20' - 'F21' - 'F22' - 'F23' - 'F24' - 'F25' - 'F26' - 'F27' - 'F28' - 'F29' - 'F30' - 'F31' - 'F32' - 'G1' - 'G2' - 'G3' - 'G4' - 'G5' - 'G6' - 'G7' - 'G8' - 'G9' - 'G10' - 'G11' - 'G12' - 'G13' - 'G14' - 'G15' - 'G16' - 'G17' - 'G18' - 'G19' - 'G20' - 'G21' - 'G22' - 'G23' - 'G24' - 'G25' - 'G26' - 'G27' - 'G28' - 'G29' - 'G30' - 'G31' - 'G32' - 'H1' - 'H2' - 'H3' - 'H4' - 'H5' - 'H6' - 'H7' - 'H8' - 'H9' - 'H10' - 'H11' - 'H12' - 'H13' - 'H14' - 'H15' - 'H16' - 'H17' - 'H18' - 'H19' - 'H20' - 'H21' - 'H22' - 'H23' - 'H24' - 'H25' - 'H26' - 'H27' - 'H28' - 'H29' - 'H30' - 'H31' - 'H32' - }; end % if isbiosemi if isegi - egi256 = cell(256, 1); - for i = 1:256 - egi256{i} = sprintf('e%d', i); - end + egi256 = cell(256, 1); + for i = 1:256 + egi256{i} = sprintf('e%d', i); + end - % the others are subsets - egi32 = egi256(1:32); - egi64 = egi256(1:64); - egi128 = egi256(1:128); + % the others are subsets + egi32 = egi256(1:32); + egi64 = egi256(1:64); + egi128 = egi256(1:128); end % if isegi % search for the requested definition of channel labels if exist(type, 'var') - label = eval(type); - label = label(:); + label = eval(type); + label = label(:); else - error('the requested sensor type is not supported'); + error('the requested sensor type is not supported'); +end + +% remember the current input and output arguments, so that they can be +% reused on a subsequent call in case the same input argument is given +current_argout = {label}; +if isempty(previous_argin) + previous_argin = current_argin; + previous_argout = current_argout; end diff --git a/external/fileio/private/senstype.m b/external/fileio/private/senstype.m index a00eaba..df3a355 100644 --- a/external/fileio/private/senstype.m +++ b/external/fileio/private/senstype.m @@ -12,6 +12,7 @@ % The output type can be any of the following % 'electrode' % 'magnetometer' +% 'biosemi64' % 'biosemi128' % 'biosemi256' % 'bti148' @@ -58,6 +59,21 @@ % Copyright (C) 2007-2008, Robert Oostenveld % % $Log: senstype.m,v $ +% Revision 1.19 2009/07/29 08:04:38 roboos +% cleaned up the code, no functional change +% +% Revision 1.18 2009/07/28 11:16:23 roboos +% removed keyboard statement, thanks to Jurrian +% +% Revision 1.17 2009/07/28 10:17:41 roboos +% make distinction between only label, label+pnt and label+pnt+ori +% +% Revision 1.16 2009/07/27 16:04:51 roboos +% improved distinction between eeg and meg, fixes problem with biosemi-eeg being detected as "ctf" due to reference channel match +% +% Revision 1.15 2009/06/19 16:51:50 vlalit +% Added biosemi64 system of Diane Whitmer, I don't know how generic it is. +% % Revision 1.14 2009/05/07 13:34:09 roboos % added ctf64 % @@ -138,14 +154,20 @@ % preferably the structure specifies its own type type = sens.type; +elseif issubfield(sens, 'orig.FileHeader') && issubfield(sens, 'orig.VarHeader') + % this is a complete header that was read from a Plexon *.nex file using read_plexon_nex + type = 'plexon'; + else % start with unknown, then try to determine the proper type by looking at the labels type = 'unknown'; - if isfield(sens, 'label') + if isfield(sens, 'label') && isfield(sens, 'pnt') && isfield(sens, 'ori') % probably this is MEG, determine the type of magnetometer/gradiometer system % note that the order here is important: first check whether it matches a 275 channel system, then a 151 channel system, since the 151 channels are a subset of the 275 - if (mean(ismember(senslabel('ctf275'), sens.label)) > 0.8) || (mean(ismember(senslabel('ctfheadloc'), sens.label)) > 0.8) + if (mean(ismember(senslabel('ctf275'), sens.label)) > 0.8) + type = 'ctf275'; + elseif (mean(ismember(senslabel('ctfheadloc'), sens.label)) > 0.8) % look at the head localization channels type = 'ctf275'; elseif (mean(ismember(senslabel('ctf151'), sens.label)) > 0.8) type = 'ctf151'; @@ -177,12 +199,18 @@ type = 'ctf'; % it might be 151 or 275 channels elseif isfield(sens, 'pnt') && isfield(sens, 'ori') && numel(sens.label)==numel(sens.pnt) type = 'magnetometer'; - elseif isfield(sens, 'pnt') && isfield(sens, 'ori') + else type = 'meg'; - elseif (mean(ismember(senslabel('biosemi256'), sens.label)) > 0.8) + end + + elseif isfield(sens, 'label') && isfield(sens, 'pnt') && ~isfield(sens, 'ori') + % probably this is EEG + if (mean(ismember(senslabel('biosemi256'), sens.label)) > 0.8) type = 'biosemi256'; elseif (mean(ismember(senslabel('biosemi128'), sens.label)) > 0.8) type = 'biosemi128'; + elseif (mean(ismember(senslabel('biosemi64'), sens.label)) > 0.8) + type = 'biosemi64'; elseif (mean(ismember(senslabel('egi256'), sens.label)) > 0.8) type = 'egi256'; elseif (mean(ismember(senslabel('egi128'), sens.label)) > 0.8) @@ -193,26 +221,72 @@ type = 'egi32'; elseif (sum(ismember(sens.label, senslabel('eeg1005'))) > 10) % Otherwise it's not even worth recognizing type = 'ext1020'; - elseif isfield(sens, 'label') && isfield(sens, 'pnt') && ~isfield(sens, 'ori') % looks like EEG + else type = 'electrode'; end - end + elseif isfield(sens, 'label') && ~isfield(sens, 'pnt') && ~isfield(sens, 'ori') + % look only at the channel labels + if (mean(ismember(senslabel('ctf275'), sens.label)) > 0.8) + type = 'ctf275'; + elseif (mean(ismember(senslabel('ctfheadloc'), sens.label)) > 0.8) % look at the head localization channels + type = 'ctf275'; + elseif (mean(ismember(senslabel('ctf151'), sens.label)) > 0.8) + type = 'ctf151'; + elseif (mean(ismember(senslabel('ctf64'), sens.label)) > 0.8) + type = 'ctf64'; + elseif (mean(ismember(senslabel('ctf275_planar'), sens.label)) > 0.8) + type = 'ctf275_planar'; + elseif (mean(ismember(senslabel('ctf151_planar'), sens.label)) > 0.8) + type = 'ctf151_planar'; + elseif (mean(ismember(senslabel('bti248'), sens.label)) > 0.8) + type = 'bti248'; + elseif (mean(ismember(senslabel('bti148'), sens.label)) > 0.8) + type = 'bti148'; + elseif (mean(ismember(senslabel('bti248_planar'), sens.label)) > 0.8) + type = 'bti248_planar'; + elseif (mean(ismember(senslabel('bti148_planar'), sens.label)) > 0.8) + type = 'bti148_planar'; + elseif (mean(ismember(senslabel('neuromag306'), sens.label)) > 0.8) + type = 'neuromag306'; + elseif (mean(ismember(senslabel('neuromag306alt'),sens.label)) > 0.8) % an alternative set without spaces in the name + type = 'neuromag306'; + elseif (mean(ismember(senslabel('neuromag122'), sens.label)) > 0.8) + type = 'neuromag122'; + elseif (mean(ismember(senslabel('neuromag122alt'),sens.label)) > 0.8) % an alternative set without spaces in the name + type = 'neuromag122'; + elseif (mean(ismember(senslabel('biosemi256'), sens.label)) > 0.8) + type = 'biosemi256'; + elseif (mean(ismember(senslabel('biosemi128'), sens.label)) > 0.8) + type = 'biosemi128'; + elseif (mean(ismember(senslabel('biosemi64'), sens.label)) > 0.8) + type = 'biosemi64'; + elseif (mean(ismember(senslabel('egi256'), sens.label)) > 0.8) + type = 'egi256'; + elseif (mean(ismember(senslabel('egi128'), sens.label)) > 0.8) + type = 'egi128'; + elseif (mean(ismember(senslabel('egi64'), sens.label)) > 0.8) + type = 'egi64'; + elseif (mean(ismember(senslabel('egi32'), sens.label)) > 0.8) + type = 'egi32'; + elseif (sum(ismember(sens.label, senslabel('eeg1005'))) > 10) % Otherwise it's not even worth recognizing + type = 'ext1020'; + elseif any(ismember(senslabel('btiref'), sens.label)) + type = 'bti'; % it might be 148 or 248 channels + elseif any(ismember(senslabel('ctfref'), sens.label)) + type = 'ctf'; % it might be 151 or 275 channels + end + + end % look at label, ori and/or pnt end % if isfield(sens, 'type') -% use an alternative approach if it is still unknown -if strcmp(type, 'unknown') && issubfield(sens, 'orig.FileHeader') && issubfield(sens, 'orig.VarHeader') - % this is a complete header that was read from a Plexon *.nex file using read_plexon_nex - type = 'plexon'; -end - if ~isempty(desired) % return a boolean flag switch desired case 'eeg' - type = any(strcmp(type, {'eeg' 'electrode' 'biosemi128' 'biosemi256' 'egi32' 'egi64' 'egi128' 'egi256' 'ext1020'})); + type = any(strcmp(type, {'eeg' 'electrode' 'biosemi64' 'biosemi128' 'biosemi256' 'egi32' 'egi64' 'egi128' 'egi256' 'ext1020'})); case 'biosemi' - type = any(strcmp(type, {'biosemi128' 'biosemi256'})); + type = any(strcmp(type, {'biosemi64' 'biosemi128' 'biosemi256'})); case 'egi' type = any(strcmp(type, {'egi64' 'egi128' 'egi256'})); case 'meg' @@ -233,8 +307,8 @@ type = any(strcmp(type, {'neuromag122' 'neuromag306' 'ctf151_planar' 'ctf275_planar' 'bti148_planar' 'bti248_planar' 'yokogawa160_planar'})); otherwise type = any(strcmp(type, desired)); - end -end + end % switch desired +end % detemine the correspondence to the desired type % remember the current input and output arguments, so that they can be % reused on a subsequent call in case the same input argument is given @@ -244,4 +318,4 @@ previous_argout = current_argout; end -return % voltype main() +return % senstype main() diff --git a/external/fileio/private/write_data.m b/external/fileio/private/write_data.m index abbaf7e..7b2f15b 100644 --- a/external/fileio/private/write_data.m +++ b/external/fileio/private/write_data.m @@ -24,12 +24,16 @@ function write_data(filename, dat, varargin) % fcdc_buffer % plexon_nex % neuralynx_ncs +% neuralynx_sdma % % See also READ_HEADER, READ_DATA, READ_EVEN, WRITE_EVENT % Copyright (C) 2007-2008, Robert Oostenveld % % $Log: write_data.m,v $ +% Revision 1.23 2009/06/17 10:15:43 roboos +% added support for neuralynx_sdma, only for 32 bit integer data +% % Revision 1.22 2009/05/19 18:29:48 roboos % give instructive error if the fieldtrip buffer mex file is not on the path % @@ -126,7 +130,7 @@ function write_data(filename, dat, varargin) case 'disp' % display it on screen, this is only for debugging disp('new data arived'); - + case 'fcdc_global' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % store it in a global variable, this is only for debugging @@ -139,21 +143,21 @@ function write_data(filename, dat, varargin) else data_queue = cat(2, data_queue, dat); end - + case 'fcdc_buffer' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % network transparent buffer %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - + if ~isempty(chanindx) % assume that the header corresponds to the original multichannel % file and that the data represents a subset of channels hdr.label = hdr.label(chanindx); hdr.nChans = length(chanindx); end - + [host, port] = filetype_check_uri(filename); - + type = { 'char' 'uint8' @@ -167,7 +171,7 @@ function write_data(filename, dat, varargin) 'single' 'double' }; - + wordsize = { 1 % 'char' 1 % 'uint8' @@ -181,7 +185,7 @@ function write_data(filename, dat, varargin) 4 % 'single' 8 % 'double' }; - + % this should only be done the first time if ~append && ~isempty(hdr) % reformat the header into a buffer-compatible format @@ -190,25 +194,25 @@ function write_data(filename, dat, varargin) packet.nsamples = 0; packet.nevents = 0; packet.data_type = find(strcmp(type, class(dat))) - 1; % zero-offset - + % try to put_hdr and initialize if necessary try % try writing the packet buffer('put_hdr', packet, host, port); - + catch if ~isempty(strfind(lasterr, 'Buffer size N must be an integer-valued scalar double.')) % this happens if the MATLAB75/toolbox/signal/signal/buffer % function is used instead of the fieldtrip buffer error('the FieldTrip buffer mex file was not found on your path, it should be in fieldtrip/fileio/private'); - + elseif ~isempty(strfind(lasterr, 'failed to create socket')) && (strcmp(host, 'localhost') || strcmp(host, '127.0.0.1')) - + % start a local instance of the TCP server warning('starting fieldtrip buffer on %s:%d', host, port); buffer('tcpserver', 'init', host, port); pause(1); - + % rewrite the packet until success success = false; while ~success @@ -222,11 +226,11 @@ function write_data(filename, dat, varargin) end end end % if strfind... - + end % try - + end % writing header - + if ~isempty(dat) max_nsamples = 32556; if size(dat,2)>max_nsamples @@ -257,7 +261,7 @@ function write_data(filename, dat, varargin) buffer('put_dat', packet, host, port); end % if data larger than chuncksize end - + case 'brainvision_eeg' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % combination of *.eeg and *.vhdr file @@ -265,7 +269,7 @@ function write_data(filename, dat, varargin) if append error('appending data is not yet supported for this data format'); end - + if nchans~=hdr.nChans && length(chanindx)==nchans % assume that the header corresponds to the original multichannel % file and that the data represents a subset of channels @@ -277,7 +281,7 @@ function write_data(filename, dat, varargin) % hdr.nChans % hdr.Fs write_brainvision_eeg(filename, hdr, dat); - + case 'fcdc_matbin' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % multiplexed data in a *.bin file (ieee-le, 64 bit floating point values), @@ -286,7 +290,7 @@ function write_data(filename, dat, varargin) if append error('appending data is not yet supported for this data format'); end - + [path, file, ext] = fileparts(filename); headerfile = fullfile(path, [file '.mat']); datafile = fullfile(path, [file '.bin']); @@ -302,7 +306,7 @@ function write_data(filename, dat, varargin) [fid,message] = fopen(datafile,'wb','ieee-le'); fwrite(fid, dat, 'double'); fclose(fid); - + case 'fcdc_mysql' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % write to a MySQL server listening somewhere else on the network @@ -329,7 +333,7 @@ function write_data(filename, dat, varargin) end db_insert('fieldtrip.header', s); end - + elseif isempty(hdr) && ~isempty(dat) dim = size(dat); if numel(dim)==2 @@ -356,11 +360,11 @@ function write_data(filename, dat, varargin) db_insert('fieldtrip.data', s); end end - + else error('you should specify either the header or the data when writing to a MySQL database'); end - + case 'matlab' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % plain matlab file @@ -385,7 +389,104 @@ function write_data(filename, dat, varargin) % file does not yet exist, which is not a problem end save(filename, 'dat', 'hdr'); - + + case 'neuralynx_sdma' + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % The first version of this file format contained in the first 8 bytes the + % channel label as string. Subsequently it contained 32 bit integer values. + % + % The second version of this file format starts with 8 bytes describing (as + % a space-padded string) the data type. The channel label is contained in + % the filename as dataset.chanlabel.bin. + % + % The third version of this file format starts with 7 bytes describing (as + % a zero-padded string) the data type, followed by the 8th byte which + % describes the downscaling for the 8 and 16 bit integer representations. + % The downscaling itself is represented as uint8 and should be interpreted as + % the number of bits to shift. The channel label is contained in the + % filename as dataset.chanlabel.bin. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + statuschannel = { + 'stx' + 'pid' + 'siz' + 'tsh' + 'tsl' + 'cpu' + 'ttl' + 'x01' + 'x02' + 'x03' + 'x04' + 'x05' + 'x06' + 'x07' + 'x08' + 'x09' + 'x10' + 'crc' + }; + + dirname = filename; + clear filename + [path, file] = fileparts(dirname); + for i=1:hdr.nChans + downscale(i) = 0; + if ~isempty(strmatch(hdr.label{i}, statuschannel)) + format{i} = 'uint32'; + else + format{i} = 'int32'; + end + filename{i} = fullfile(dirname, [file '.' hdr.label{i} '.bin']); + end + + if ~isdir(dirname) + mkdir(dirname); + end + + % open and write to the output files, one for each selected channel + fid = zeros(hdr.nChans,1); + for j=1:hdr.nChans + + if append==false + fid(j) = fopen(filename{j}, 'wb', 'ieee-le'); % open the file + magic = format{j}; % this used to be the channel name + magic((end+1):8) = 0; % pad with zeros + magic(8) = downscale(j); % number of bits to shift + fwrite(fid(j), magic(1:8)); % write the 8-byte file header + else + fid(j) = fopen(filename{j}, 'ab', 'ieee-le'); % open the file for appending + end % if append + + % convert the data into the correct class + buf = dat(j,:); + if ~strcmp(class(buf), format{j}) + switch format{j} + case 'int16' + buf = int16(buf); + case 'int32' + buf = int32(buf); + case 'single' + buf = single(buf); + case 'double' + buf = double(buf); + case 'uint32' + buf = uint32(buf); + otherwise + error('unsupported format conversion'); + end + end + + % apply the scaling, this corresponds to bit shifting + buf = buf ./ (2^downscale(j)); + + % write the segment of data to the output file + fwrite(fid(j), buf, format{j}, 'ieee-le'); + + fclose(fid(j)); + end % for each channel + case 'riff_wave' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % This writes data Y to a Windows WAVE file specified by the file name @@ -396,7 +497,7 @@ function write_data(filename, dat, varargin) if append error('appending data is not yet supported for this data format'); end - + if nchans~=hdr.nChans && length(chanindx)==nchans % assume that the header corresponds to the original multichannel % file and that the data represents a subset of channels @@ -407,7 +508,7 @@ function write_data(filename, dat, varargin) error('this format only supports single channel continuous data'); end wavwrite(dat, hdr.Fs, nbits, filename); - + case 'plexon_nex' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % single or mulitple channel Plexon NEX file @@ -415,8 +516,8 @@ function write_data(filename, dat, varargin) if append error('appending data is not yet supported for this data format'); end - - [path, file, ext] = fileparts(filename); + + [path, file] = fileparts(filename); filename = fullfile(path, [file, '.nex']); if nchans~=1 error('only supported for single-channel data'); @@ -435,15 +536,15 @@ function write_data(filename, dat, varargin) end nex.var.indx = 0; nex.var.dat = dat; - + write_plexon_nex(filename, nex); - + if 0 % the following code snippet can be used for testing nex2 = []; [nex2.var, nex2.hdr] = read_plexon_nex(filename, 'channel', 1); end - + case 'neuralynx_ncs' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % single channel Neuralynx NCS file @@ -451,14 +552,14 @@ function write_data(filename, dat, varargin) if append error('appending data is not yet supported for this data format'); end - + if nchans>1 error('only supported for single-channel data'); end - + [path, file, ext] = fileparts(filename); filename = fullfile(path, [file, '.ncs']); - + if nchans~=hdr.nChans && length(chanindx)==nchans % assume that the header corresponds to the original multichannel % file and that the data represents a subset of channels @@ -472,35 +573,35 @@ function write_data(filename, dat, varargin) else error('cannot determine channel label'); end - + FSAMPLE = hdr.Fs; RECORDNSMP = 512; RECORDSIZE = 1044; - + % cut the downsampled LFP data into record-size pieces nrecords = ceil(nsamples/RECORDNSMP); fprintf('construct ncs with %d records\n', nrecords); - + % construct a ncs structure with all header details and the data in it ncs = []; ncs.NumValidSamp = ones(1,nrecords) * RECORDNSMP; % except for the last block ncs.ChanNumber = ones(1,nrecords) * ADCHANNEL; ncs.SampFreq = ones(1,nrecords) * FSAMPLE; ncs.TimeStamp = zeros(1,nrecords,'uint64'); - + if rem(nsamples, RECORDNSMP)>0 % the data length is not an integer number of records, pad the last record with zeros dat = cat(2, dat, zeros(nchans, nrecords*RECORDNSMP-nsamples)); ncs.NumValidSamp(end) = rem(nsamples, RECORDNSMP); end - + ncs.dat = reshape(dat, RECORDNSMP, nrecords); - + for i=1:nrecords % timestamps should be 64 bit unsigned integers ncs.TimeStamp(i) = uint64(hdr.FirstTimeStamp) + uint64((i-1)*RECORDNSMP*hdr.TimeStampPerSample); end - + % add the elements that will go into the ascii header ncs.hdr.CheetahRev = '4.23.0'; ncs.hdr.NLX_Base_Class_Type = 'CscAcqEnt'; @@ -508,16 +609,16 @@ function write_data(filename, dat, varargin) ncs.hdr.RecordSize = RECORDSIZE; ncs.hdr.ADChannel = ADCHANNEL; ncs.hdr.SamplingFrequency = FSAMPLE; - + % write it to a file fprintf('writing to %s\n', filename); write_neuralynx_ncs(filename, ncs); - + if 0 % the following code snippet can be used for testing ncs2 = read_neuralynx_ncs(filename, 1, inf); end - + otherwise error('unsupported data format'); end % switch dataformat diff --git a/external/fileio/private/write_plexon_nex.m b/external/fileio/private/write_plexon_nex.m new file mode 100644 index 0000000..d0bd72b --- /dev/null +++ b/external/fileio/private/write_plexon_nex.m @@ -0,0 +1,225 @@ +function write_plexon_nex(filename, nex) + +% WRITE_PLEXON_NEX writes a Plexon *.nex file, which is a file +% containing action-potential (spike) timestamps and waveforms (spike +% channels), event timestamps (event channels), and continuous variable +% data (continuous A/D channels). +% +% Use as +% write_plexon_nex(filename, nex); +% +% The data structure should contain +% nex.hdr.FileHeader.Frequency = TimeStampFreq +% nex.hdr.VarHeader.Type = type, 5 for continuous +% nex.hdr.VarHeader.Name = label, padded to length 64 +% nex.hdr.VarHeader.WFrequency = sampling rate of continuous channel +% nex.var.dat = data +% nex.var.ts = timestamps +% +% See also READ_PLEXON_NEX, READ_PLEXON_PLX, READ_PLEXON_DDT + +% Copyright (C) 2007, Robert Oostenveld +% +% $Log: write_plexon_nex.m,v $ +% Revision 1.1 2009/01/14 09:24:45 roboos +% moved even more files from fileio to fileio/privtae, see previous log entry +% +% Revision 1.7 2009/01/08 13:03:09 roboos +% fixed bug in scaling of data, causing it to be clipped (thanks to Conrado) +% fixed bug in seperate scaling of data, the old code was not incorrect, but suboptimal +% +% Revision 1.6 2008/12/15 14:48:22 roboos +% read and write the data in uV/microvolt instead of in mV/milivolt +% +% Revision 1.5 2008/02/04 14:59:00 roboos +% do not rescale the input data if it is int16 already +% +% Revision 1.4 2007/10/08 13:00:24 roboos +% fixed small bug in including cvs identifier as comment in file header +% +% Revision 1.3 2007/03/21 13:01:37 roboos +% reimplemented the calibration and conversion into int16 values +% fixed a bug in the padding of the channel name to 64 chars +% +% Revision 1.2 2007/03/19 17:10:38 roboos +% implemented writing spike waveform data +% +% Revision 1.1 2007/02/21 09:50:51 roboos +% initial implementation +% + +% get the optional arguments, these are all required +% FirstTimeStamp = keyval('FirstTimeStamp', varargin); +% TimeStampFreq = keyval('TimeStampFreq', varargin); + +hdr = nex.hdr; + +UVtoMV = 1/1000; + +switch hdr.VarHeader.Type + case 5 + dat = nex.var.dat; % this is in microVolt + buf = zeros(size(dat), 'int16'); + nchans = size(dat,1); + nsamples = size(dat,2); + nwaves = 1; % only one continuous datasegment is supported + if length(hdr.VarHeader)~=nchans + error('incorrect number of channels'); + end + % convert the data from floating point into int16 values + % each channel gets its own optimal calibration factor + for varlop=1:nchans + ADMaxValue = double(intmax('int16')); + ADMaxUV = max(abs(dat(varlop,:))); % this is in microVolt + ADMaxMV = ADMaxUV/1000; % this is in miliVolt + if isa(dat, 'int16') + % do not rescale data that is already 16 bit + MVtoAD = 1; + elseif ADMaxMV==0 + % do not rescale the data if the data is zero + MVtoAD = 1; + elseif ADMaxMV>0 + % rescale the data so that it fits into the 16 bits with as little loss as possible + MVtoAD = ADMaxValue / ADMaxMV; + end + buf(varlop,:) = int16(double(dat) * UVtoMV * MVtoAD); + % remember the calibration value, it should be stored in the variable header + ADtoMV(varlop) = 1/MVtoAD; + end + dat = buf; + clear buf; + + case 3 + dat = nex.var.dat; % this is in microVolt + nchans = 1; % only one channel is supported + nsamples = size(dat,1); + nwaves = size(dat,2); + if length(hdr.VarHeader)~=nchans + error('incorrect number of channels'); + end + % convert the data from floating point into int16 values + ADMaxValue = double(intmax('int16')); + ADMaxUV = max(abs(dat(:))); % this is in microVolt + ADMaxMV = ADMaxUV/1000; % this is in miliVolt + if isa(dat, 'int16') + % do not rescale data that is already 16 bit + MVtoAD = 1; + elseif ADMaxMV==0 + % do not rescale the data if the data is zero + MVtoAD = 1; + elseif ADMaxMV>0 + % rescale the data so that it fits into the 16 bits with as little loss as possible + MVtoAD = ADMaxValue / ADMaxMV; + end + dat = int16(double(dat) * UVtoMV * MVtoAD); + % remember the calibration value, it should be stored in the variable header + ADtoMV = 1/MVtoAD; + + otherwise + error('unsupported data type') +end % switch type + +% determine the first and last timestamp +ts = nex.var.ts; +ts_beg = min(ts); +ts_end = 0; % FIXME + +fid = fopen(filename, 'wb', 'ieee-le'); + +% write the file header +write_NexFileHeader; + +% write the variable headers +for varlop=1:nchans + write_NexVarHeader; +end + +% write the variable data +for varlop=1:nchans + write_NexVarData; +end + +fclose(fid); +return + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% nested function for writing the details +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + function write_NexFileHeader + % prepare the two char buffers + buf1 = padstr('$Id: write_plexon_nex.m,v 1.1 2009/01/14 09:24:45 roboos Exp $', 256); + buf2 = char(zeros(1, 256)); + % write the stuff to the file + fwrite(fid, 'NEX1' , 'char'); % NexFileHeader = string NEX1 + fwrite(fid, 100 , 'int32'); % Version = version + fwrite(fid, buf1 , 'char'); % Comment = comment, 256 bytes + fwrite(fid, hdr.FileHeader.Frequency, 'double'); % Frequency = timestamped freq. - tics per second + fwrite(fid, ts_beg, 'int32'); % Beg = usually 0, minimum of all the timestamps in the file + fwrite(fid, ts_end, 'int32'); % End = maximum timestamp + 1 + fwrite(fid, nchans, 'int32'); % NumVars = number of variables in the first batch + fwrite(fid, 0 , 'int32'); % NextFileHeader = position of the next file header in the file, not implemented yet + fwrite(fid, buf2 , 'char'); % Padding = future expansion + end % of the nested function + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% nested function for writing the details +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + function write_NexVarHeader + filheadersize = 544; + varheadersize = 208; + offset = filheadersize + nchans*varheadersize + (varlop-1)*nsamples; + calib = ADtoMV(varlop); + % prepare the two char buffers + buf1 = padstr(hdr.VarHeader(varlop).Name, 64); + buf2 = char(zeros(1, 68)); + % write the continuous variable to the file + fwrite(fid, hdr.VarHeader.Type, 'int32'); % Type = 0 - neuron, 1 event, 2- interval, 3 - waveform, 4 - pop. vector, 5 - continuously recorded + fwrite(fid, 100, 'int32'); % Version = 100 + fwrite(fid, buf1, 'char'); % Name = variable name, 1x64 char + fwrite(fid, offset, 'int32'); % DataOffset = where the data array for this variable is located in the file + fwrite(fid, nwaves, 'int32'); % Count = number of events, intervals, waveforms or weights + fwrite(fid, 0, 'int32'); % WireNumber = neuron only, not used now + fwrite(fid, 0, 'int32'); % UnitNumber = neuron only, not used now + fwrite(fid, 0, 'int32'); % Gain = neuron only, not used now + fwrite(fid, 0, 'int32'); % Filter = neuron only, not used now + fwrite(fid, 0, 'double'); % XPos = neuron only, electrode position in (0,100) range, used in 3D + fwrite(fid, 0, 'double'); % YPos = neuron only, electrode position in (0,100) range, used in 3D + fwrite(fid, hdr.VarHeader.WFrequency, 'double'); % WFrequency = waveform and continuous vars only, w/f sampling frequency + fwrite(fid, calib, 'double'); % ADtoMV = waveform continuous vars only, coeff. to convert from A/D values to Millivolts + fwrite(fid, nsamples, 'int32'); % NPointsWave = waveform only, number of points in each wave + fwrite(fid, 0, 'int32'); % NMarkers = how many values are associated with each marker + fwrite(fid, 0, 'int32'); % MarkerLength = how many characters are in each marker value + fwrite(fid, buf2, 'char'); % Padding, 1x68 char + end % of the nested function + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% nested function for writing the details +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + function write_NexVarData + switch hdr.VarHeader.Type + case 5 + % this code only supports one continuous segment + index = 0; + fwrite(fid, ts , 'int32'); % timestamps, one for each continuous segment + fwrite(fid, index , 'int32'); % where to cut the segments, zero offset + fwrite(fid, dat(varlop,:) , 'int16'); % data + case 3 + fwrite(fid, ts , 'int32'); % timestamps, one for each spike + fwrite(fid, dat , 'int16'); % waveforms, one for each spike + otherwise + error('unsupported data type'); + end % switch + end % of the nested function + +end % of the primary function + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% subfunction for zero padding a char array to fixed length +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function str = padstr(str, num); +if length(str)>num + str = str(1:num); +else + str((end+1):num) = 0; +end +end % of the padstr subfunction diff --git a/external/forwinv/private/apply_montage.m b/external/forwinv/private/apply_montage.m index e7a0f07..ccf0716 100644 --- a/external/forwinv/private/apply_montage.m +++ b/external/forwinv/private/apply_montage.m @@ -7,7 +7,11 @@ % forward computation and source reconstruction of the data. % % Use as -% [sens] = apply_montage(sens, montage, ...) +% [sens] = apply_montage(sens, montage, ...) +% [data] = apply_montage(data, montage, ...) +% [montage] = apply_montage(montage1, montage2, ...) +% where the input is a FieldTrip sensor definition as obtained from READ_SENS +% or a FieldTrip raw data structure as obtained from PREPROCESSING. % % A montage is specified as a structure with the fields % montage.tra = MxN matrix @@ -18,11 +22,35 @@ % 'keepunused' string, 'yes' or 'no' (default = 'no') % 'inverse' string, 'yes' or 'no' (default = 'no') % +% If the first input is a montage, then the second input montage will be +% applied to the first. In effect the resulting montage will first do +% montage1, then montage2. +% % See also READ_SENS, TRANSFORM_SENS % Copyright (C) 2008, Robert Oostenveld % % $Log: apply_montage.m,v $ +% Revision 1.19 2009/07/02 16:14:15 roboos +% allow to apply one montage on another one +% +% Revision 1.18 2009/07/02 09:18:41 vlalit +% Fixing a bug in building an inverse montage (confusion between 'montage' and 'tra') +% +% Revision 1.17 2009/07/01 09:21:37 roboos +% changed handling of rank-reduced montage for inversion, give warning +% removed the modification by vladimir for the sequential application of montages +% +% Revision 1.16 2009/06/27 13:24:54 vlalit +% Additional fixes to make custom balancing work. +% +% Revision 1.15 2009/06/26 17:39:17 vlalit +% Added the possiblity to handle custom montages applied to MEG sensors (for removing +% spatial confounds). Hopefully there won't be major side effects. +% +% Revision 1.14 2009/06/17 10:13:05 roboos +% improved documentation +% % Revision 1.13 2009/03/26 11:07:40 roboos % start with an explicit check on the channel number % @@ -70,6 +98,12 @@ keepunused = keyval('keepunused', varargin{:}); if isempty(keepunused), keepunused = 'no'; end inverse = keyval('inverse', varargin{:}); if isempty(inverse), inverse = 'no'; end +% check the consistency of the input sensor array or data +if isfield(sens, 'labelorg') && isfield(sens, 'labelnew') + % the input data structure is also a montage, i.e. apply the montages sequentially + sens.label = sens.labelnew; +end + % check the consistency of the montage if size(montage.tra,1)~=length(montage.labelnew) error('the number of channels in the montage is inconsistent'); @@ -79,9 +113,15 @@ if strcmp(inverse, 'yes') % apply the inverse montage, i.e. undo a previously applied montage - tmp.labelnew = montage.labelorg; - tmp.labelorg = montage.labelnew; - tmp.tra = inv(montage.tra); + tmp.labelnew = montage.labelorg; % swap around + tmp.labelorg = montage.labelnew; % swap around + tmp.tra = full(montage.tra); + if rank(tmp.tra) < length(tmp.tra) + warning('the linear projection for the montage is not full-rank, the resulting data will have reduced dimensionality'); + tmp.tra = pinv(tmp.tra); + else + tmp.tra = inv(tmp.tra); + end montage = tmp; end @@ -150,10 +190,17 @@ montage.tra = sparse(montage.tra(:,selmont)); montage.labelorg = montage.labelorg(selmont); -if isfield(sens, 'tra') +if isfield(sens, 'labelorg') && isfield(sens, 'labelnew') + % apply the montage on top of the other montage + sens = rmfield(sens, 'label'); + sens.tra = montage.tra * sens.tra; + sens.labelnew = montage.labelnew; + +elseif isfield(sens, 'tra') % apply the montage to the sensor array sens.tra = montage.tra * sens.tra; sens.label = montage.labelnew; + elseif isfield(sens, 'trial') % apply the montage to the data that was preprocessed using fieldtrip Ntrials = numel(sens.trial); @@ -177,5 +224,3 @@ function y = indx2logical(x, n) y = false(1,n); y(x) = true; - - diff --git a/external/forwinv/private/beamformer_dics.m b/external/forwinv/private/beamformer_dics.m index fe84b22..3cf9393 100644 --- a/external/forwinv/private/beamformer_dics.m +++ b/external/forwinv/private/beamformer_dics.m @@ -47,6 +47,9 @@ % Copyright (C) 2003-2008, Robert Oostenveld % % $Log: beamformer_dics.m,v $ +% Revision 1.15 2009/06/17 13:40:37 roboos +% small change in the order of the code for subspace projection +% % Revision 1.14 2009/05/14 19:25:12 roboos % added a FIXME comment % @@ -263,6 +266,13 @@ % compute the leadfield lf = compute_leadfield(dip.pos(i,:), grad, vol, 'reducerank', reducerank, 'normalize', normalize, 'normalizeparam', normalizeparam); end + if isfield(dip, 'subspace') + % do subspace projection of the forward model + lf = dip.subspace{i} * lf; + % the cross-spectral density becomes voxel dependent due to the projection + Cf = dip.subspace{i} * Cf_pre_subspace * dip.subspace{i}'; + invCf = pinv(dip.subspace{i} * (Cf_pre_subspace + lambda * eye(size(Cf))) * dip.subspace{i}'); + end if fixedori % compute the leadfield for the optimal dipole orientation % subsequently the leadfield for only that dipole orientation will be used for the final filter computation @@ -272,13 +282,6 @@ lf = lf * eta; dipout.ori{i} = eta; end - if isfield(dip, 'subspace') - % do subspace projection of the forward model - lf = dip.subspace{i} * lf; - % the cross-spectral density becomes voxel dependent due to the projection - Cf = dip.subspace{i} * Cf_pre_subspace * dip.subspace{i}'; - invCf = pinv(dip.subspace{i} * (Cf_pre_subspace + lambda * eye(size(Cf))) * dip.subspace{i}'); - end if isfield(dip, 'filter') % use the provided filter filt = dip.filter{i}; @@ -327,6 +330,13 @@ % compute the leadfield lf = compute_leadfield(dip.pos(i,:), grad, vol, 'reducerank', reducerank, 'normalize', normalize); end + if isfield(dip, 'subspace') + % do subspace projection of the forward model + lf = dip.subspace{i} * lf; + % the cross-spectral density becomes voxel dependent due to the projection + Cf = dip.subspace{i} * Cf_pre_subspace * dip.subspace{i}'; + invCf = pinv(dip.subspace{i} * (Cf_pre_subspace + lambda * eye(size(Cf))) * dip.subspace{i}'); + end if fixedori % compute the leadfield for the optimal dipole orientation % subsequently the leadfield for only that dipole orientation will be used for the final filter computation @@ -336,14 +346,6 @@ lf = lf * eta; dipout.ori{i} = eta; end - if isfield(dip, 'subspace') - % do subspace projection of the forward model - lf = dip.subspace{i} * lf; - % the cross-spectral density becomes voxel dependent due to the projection - Cr = dip.subspace{i} * Cr_pre_subspace; - Cf = dip.subspace{i} * Cf_pre_subspace * dip.subspace{i}'; - invCf = pinv(dip.subspace{i} * (Cf_pre_subspace + lambda * eye(size(Cf))) * dip.subspace{i}'); - end if isfield(dip, 'filter') % use the provided filter filt = dip.filter{i}; @@ -358,6 +360,7 @@ end csd = filt*Cr; % Gross eqn. 6 if powlambda1 + % FIXME this should use the dipole orientation with maximum power coh = lambda1(csd)^2 / (pow * Pr); % Gross eqn. 9 elseif powtrace coh = norm(csd)^2 / (pow * Pr); @@ -504,7 +507,7 @@ % standard Matlab function, except that the default tolerance is twice as % high. % Copyright 1984-2004 The MathWorks, Inc. -% $Revision: 1.14 $ $Date: 2009/05/14 19:25:12 $ +% $Revision: 1.15 $ $Date: 2009/06/17 13:40:37 $ % default tolerance increased by factor 2 (Robert Oostenveld, 7 Feb 2004) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function X = pinv(A,varargin) diff --git a/external/forwinv/private/dipole_fit.m b/external/forwinv/private/dipole_fit.m index e68a5a5..04137f1 100644 --- a/external/forwinv/private/dipole_fit.m +++ b/external/forwinv/private/dipole_fit.m @@ -28,6 +28,9 @@ % Copyright (C) 2003-2008, Robert Oostenveld % % $Log: dipole_fit.m,v $ +% Revision 1.15 2009/07/02 15:38:06 roboos +% use fixdipole for consistent dipole structure representation +% % Revision 1.14 2009/03/13 07:24:52 roboos % changed handling of optimization function (fminunc/fminsearch), now uses hastoolbox which is also able to check the availablity of a license for the optimimization toolbox % @@ -169,38 +172,15 @@ dat = avgref(dat); end -% check the input dipole position specification, should be Nx3 -[m, n] = size(dip.pos); -if n==3 - % this is ok -elseif m==3 - % it is possible to translate it into a Nx3 unambiguously - warning('input dipole positions should be specified as Nx3 matrix'); - dip.pos = dip.pos'; -else - error('input dipole positions should be specified as Nx3 matrix'); -end - -if isfield(dip, 'mom') - % check the input dipole moment specification, should be 3xN - [m, n] = size(dip.mom); - if m==3 - % this is ok - elseif n==3 - % it is possible to translate it into a 3xN unambiguously - warning('input dipole moments should be specified as 3xN matrix'); - dip.mom = dip.mom'; - else - error('input dipole moments should be specified as 3xN matrix'); - end -end +% ensure correct dipole position and moment specification +dip = fixdipole(dip); % reformat the position parameters in case of multiple dipoles, this % should result in the matrix changing from [x1 y1 z1; x2 y2 z2] to % [x1 y1 z1 x2 y2 z2] for the constraints to work -param = dip.pos'; -param = param(:)'; numdip = size(dip.pos, 1); +param = dip.pos'; +param = param(:)'; % add the orientation to the nonlinear parameters if isfield(constr, 'fixedori') && constr.fixedori @@ -271,9 +251,12 @@ dipout.mom = ori; % dipole orientation as vector dipout.ampl = mom; % dipole strength else - dipout.mom = mom; % dipole moment, which represents both the orientation and strength as vector + dipout.mom = mom; % dipole moment as vector or matrix, which represents both the orientation and strength as vector end +% ensure correct dipole position and moment specification +dipout = fixdipole(dipout); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % DIPFIT_ERROR computes the error between measured and model data % and can be used for non-linear fitting of dipole position diff --git a/external/forwinv/private/fixdipole.m b/external/forwinv/private/fixdipole.m new file mode 100644 index 0000000..8a9de8c --- /dev/null +++ b/external/forwinv/private/fixdipole.m @@ -0,0 +1,39 @@ +function dip = fixdipole(dip) + +% FIXDIPOLE ensures that the dipole position and moment are +% consistently represented throughout FieldTrip functions. + +% Copyright (C) 2009, Robert Oostenveld +% +% $Log: fixdipole.m,v $ +% Revision 1.1 2009/07/02 15:35:55 roboos +% helper function for consistent dipole representation +% + +[m, n] = size(dip.pos); + +if n==3 + % the input representation is Nx3, which is what we want +elseif m==3 + % it is possible to translate it into a Nx3 unambiguously + warning('input dipole positions should be specified as Nx3 matrix'); + dip.pos = dip.pos'; +elseif m==1 + % it is possible to translate it into a Nx3 unambiguously + warning('input dipole positions should be specified as Nx3 matrix'); + dip.pos = reshape(dip.pos, 3, n/3)'; +else + % it is not clear how to convert to a Nx3 matrix + error('input dipole positions should be specified as Nx3 matrix'); +end + +if isfield(dip, 'mom') + ndip = size(dip.pos,1); + if numel(dip.mom)==ndip*3 + ntime = 1; + else + ntime = numel(dip.mom)/(ndip*3); + end + dip.mom = reshape(dip.mom, ndip*3, ntime); +end + diff --git a/external/forwinv/private/hastoolbox.m b/external/forwinv/private/hastoolbox.m index 8289f0d..79640d3 100644 --- a/external/forwinv/private/hastoolbox.m +++ b/external/forwinv/private/hastoolbox.m @@ -10,6 +10,9 @@ % Copyright (C) 2005-2008, Robert Oostenveld % % $Log: hastoolbox.m,v $ +% Revision 1.36 2009/09/08 14:34:01 roboos +% also detect 64 bit windows version (thanks to arno) +% % Revision 1.35 2009/04/21 09:54:15 roboos % added prtools % @@ -318,7 +321,7 @@ % for windows computers in the F.C. Donders Centre prefix = 'h:\common\matlab'; - if ~status && strcmp(computer, 'PCWIN') + if ~status && (strcmp(computer, 'PCWIN') || strcmp(computer, 'PCWIN64')) status = myaddpath(fullfile(prefix, lower(toolbox)), silent); end diff --git a/external/forwinv/private/issubfield.m b/external/forwinv/private/issubfield.m index f494642..60d94f4 100644 --- a/external/forwinv/private/issubfield.m +++ b/external/forwinv/private/issubfield.m @@ -1,4 +1,4 @@ -function [r] = issubfield(s, f); +function [r] = issubfield(s, f) % ISSUBFIELD tests for the presence of a field in a structure just like the standard % Matlab ISFIELD function, except that you can also specify nested fields @@ -17,6 +17,9 @@ % Copyright (C) 2005, Robert Oostenveld % % $Log: issubfield.m,v $ +% Revision 1.2 2009/07/30 20:11:44 ingnie +% made output boolian +% % Revision 1.1 2008/11/13 09:55:36 roboos % moved from fieldtrip/private, fileio or from roboos/misc to new location at fieldtrip/public % @@ -26,7 +29,7 @@ try getsubfield(s, f); % if this works, then the subfield must be present - r = 1; + r = true; catch - r = 0; % apparently the subfield is not present + r = false; % apparently the subfield is not present end \ No newline at end of file diff --git a/external/forwinv/private/keyval.m b/external/forwinv/private/keyval.m index b9fefe7..106cf02 100644 --- a/external/forwinv/private/keyval.m +++ b/external/forwinv/private/keyval.m @@ -11,6 +11,12 @@ % Copyright (C) 2005-2007, Robert Oostenveld % % $Log: keyval.m,v $ +% Revision 1.5 2009/08/04 11:58:28 roboos +% perform a case-insensitive string comparison for keys +% +% Revision 1.4 2009/07/14 16:11:02 roboos +% speed up the input checks +% % Revision 1.3 2009/05/14 19:24:02 roboos % removed ; at end of function declaration % @@ -35,22 +41,20 @@ error('optional input arguments should come in key-value pairs, i.e. there should be an even number'); end -for i=1:2:length(varargin) - if ~ischar(varargin{i}) - % the 1st, 3rd, etc. contain the keys, the 2nd, 4th, etc. contain the values - error('optional input arguments should come in key-value pairs, the optional input argument %d is invalid (should be a string)', i); - end -end - +% the 1st, 3rd, etc. contain the keys, the 2nd, 4th, etc. contain the values keys = varargin(1:2:end); vals = varargin(2:2:end); -hit = find(strcmp(key, keys)); +if ~all(cellfun(@ischar, keys)) + error('optional input arguments should come in key-value pairs, the optional input argument %d is invalid (should be a string)', i); +end + +hit = find(strcmpi(key, keys)); if isempty(hit) % the requested key was not found val = []; elseif length(hit)==1 - % the requested key was found + % the requested key was found val = vals{hit}; else error('multiple input arguments with the same name'); diff --git a/external/forwinv/private/meg_leadfield1.m b/external/forwinv/private/meg_leadfield1.m index 447fcb0..c3ef0af 100644 --- a/external/forwinv/private/meg_leadfield1.m +++ b/external/forwinv/private/meg_leadfield1.m @@ -5,7 +5,7 @@ % [lf] = meg_leadfield1(R, pos, ori) % % with input arguments -% R position dipole +% R position dipole % pos position magnetometers % ori orientation magnetometers % @@ -21,6 +21,9 @@ % Copyright (C) 2002-2008, Robert Oostenveld % % $Log: meg_leadfield1.m,v $ +% Revision 1.3 2009/06/17 13:38:06 roboos +% cleaned up documentation +% % Revision 1.2 2009/03/12 11:05:04 roboos % implemented auto-compilation of the mex file in case it is missing % diff --git a/external/forwinv/private/prepare_vol_sens.m b/external/forwinv/private/prepare_vol_sens.m index fef176e..6a47ff5 100644 --- a/external/forwinv/private/prepare_vol_sens.m +++ b/external/forwinv/private/prepare_vol_sens.m @@ -40,6 +40,9 @@ % Copyright (C) 2004-2009, Robert Oostenveld % % $Log: prepare_vol_sens.m,v $ +% Revision 1.19 2009/05/29 11:50:34 vlalit +% Fixed a bug with wrong kind of brackets +% % Revision 1.18 2009/05/25 11:50:40 roboos % consistent handling of multiple spheres in case of ctf localspheres.hdm and fieldtrip prepare_localspheres, also in case of synthetic gradients. % @@ -228,7 +231,7 @@ warning('using the global fitted single sphere for %d channels that do not have a local sphere', length(missing)); end for i=1:length(missing) - vol.label(end+1) = missing{i}; + vol.label(end+1) = missing(i); vol.r(end+1,:) = singlesphere.r; vol.o(end+1,:) = singlesphere.o; end diff --git a/external/forwinv/private/senslabel.m b/external/forwinv/private/senslabel.m index 81db081..f75cc93 100644 --- a/external/forwinv/private/senslabel.m +++ b/external/forwinv/private/senslabel.m @@ -6,6 +6,7 @@ % label = senslabel(type) % % The input type can be any of the following +% 'biosemi64' % 'biosemi128' % 'biosemi256' % 'bti148' @@ -40,6 +41,12 @@ % Copyright (C) 2008, Vladimir Litvak % % $Log: senslabel.m,v $ +% Revision 1.4 2009/07/29 07:07:59 roboos +% use caching of input and output arguments to speed up the handling of multiple calls with the same input argument +% +% Revision 1.3 2009/06/19 16:51:50 vlalit +% Added biosemi64 system of Diane Whitmer, I don't know how generic it is. +% % Revision 1.2 2009/05/07 13:34:09 roboos % added ctf64 % @@ -59,6 +66,21 @@ % moved definition of channel label sets to seperate function % +% these are for remembering the type on subsequent calls with the same input arguments +persistent previous_argin previous_argout + +if nargin<1 + % ensure that all input arguments are defined + type = []; +end + +current_argin = {type}; +if isequal(current_argin, previous_argin) + % don't do the type detection again, but return the previous values from cache + label = previous_argout{1}; + return +end + % prevent defining all possible labels if not needed isbiosemi = ~isempty(regexp(type, '^biosemi', 'once')); isbti = ~isempty(regexp(type, '^bti', 'once')); @@ -69,1836 +91,1912 @@ isneuromag = ~isempty(regexp(type, '^neuromag', 'once')); if isbti - btiref = { - 'MRxA' - 'MRyA' - 'MRzA' - 'MLxA' - 'MLyA' - 'MLzA' - 'MCxA' - 'MCyA' - 'MCzA' - 'MRxaA' - 'MRyaA' - 'MRzaA' - 'MLxaA' - 'MLyaA' - 'MLzaA' - 'MCxaA' - 'MCyaA' - 'MCzaA' - 'GxxA' - 'GyxA' - 'GzxA' - 'GyyA' - 'GzyA' - }; + btiref = { + 'MRxA' + 'MRyA' + 'MRzA' + 'MLxA' + 'MLyA' + 'MLzA' + 'MCxA' + 'MCyA' + 'MCzA' + 'MRxaA' + 'MRyaA' + 'MRzaA' + 'MLxaA' + 'MLyaA' + 'MLzaA' + 'MCxaA' + 'MCyaA' + 'MCzaA' + 'GxxA' + 'GyxA' + 'GzxA' + 'GyyA' + 'GzyA' + }; - bti148 = cell(148,1); - for i=1:148 - bti148{i,1} = sprintf('A%d', i); - end + bti148 = cell(148,1); + for i=1:148 + bti148{i,1} = sprintf('A%d', i); + end - bti148_planar = cell(148,1); - for i=1:148 - bti148_planar{i,1} = sprintf('A%d_dH', i); - bti148_planar{i,2} = sprintf('A%d_dV', i); - end + bti148_planar = cell(148,1); + for i=1:148 + bti148_planar{i,1} = sprintf('A%d_dH', i); + bti148_planar{i,2} = sprintf('A%d_dV', i); + end - bti248 = cell(248,1); - for i=1:248 - bti248{i,1} = sprintf('A%d', i); - end + bti248 = cell(248,1); + for i=1:248 + bti248{i,1} = sprintf('A%d', i); + end - bti248_planar = cell(248,2); - for i=1:248 - bti248_planar{i,1} = sprintf('A%d_dH', i); - bti248_planar{i,2} = sprintf('A%d_dV', i); - end + bti248_planar = cell(248,2); + for i=1:248 + bti248_planar{i,1} = sprintf('A%d_dH', i); + bti248_planar{i,2} = sprintf('A%d_dV', i); + end end % if isbti if isctf - ctfref = { - 'BG1' - 'BG2' - 'BG3' - 'BP1' - 'BP2' - 'BP3' - 'BR1' - 'BR2' - 'BR3' - 'G11' - 'G12' - 'G13' - 'G22' - 'G23' - 'P11' - 'P12' - 'P13' - 'P22' - 'P23' - 'Q11' - 'Q12' - 'Q13' - 'Q22' - 'Q23' - 'R11' - 'R12' - 'R13' - 'R22' - 'R23' - }; + ctfref = { + 'BG1' + 'BG2' + 'BG3' + 'BP1' + 'BP2' + 'BP3' + 'BR1' + 'BR2' + 'BR3' + 'G11' + 'G12' + 'G13' + 'G22' + 'G23' + 'P11' + 'P12' + 'P13' + 'P22' + 'P23' + 'Q11' + 'Q12' + 'Q13' + 'Q22' + 'Q23' + 'R11' + 'R12' + 'R13' + 'R22' + 'R23' + }; - ctfheadloc = { - 'HLC0011' - 'HLC0012' - 'HLC0013' - 'HLC0021' - 'HLC0022' - 'HLC0023' - 'HLC0031' - 'HLC0032' - 'HLC0033' - 'HLC0018' - 'HLC0028' - 'HLC0038' - 'HLC0014' - 'HLC0015' - 'HLC0016' - 'HLC0017' - 'HLC0024' - 'HLC0025' - 'HLC0026' - 'HLC0027' - 'HLC0034' - 'HLC0035' - 'HLC0036' - 'HLC0037' - }; + ctfheadloc = { + 'HLC0011' + 'HLC0012' + 'HLC0013' + 'HLC0021' + 'HLC0022' + 'HLC0023' + 'HLC0031' + 'HLC0032' + 'HLC0033' + 'HLC0018' + 'HLC0028' + 'HLC0038' + 'HLC0014' + 'HLC0015' + 'HLC0016' + 'HLC0017' + 'HLC0024' + 'HLC0025' + 'HLC0026' + 'HLC0027' + 'HLC0034' + 'HLC0035' + 'HLC0036' + 'HLC0037' + }; - ctf64 = { - 'SL11' - 'SL12' - 'SL13' - 'SL14' - 'SL15' - 'SL16' - 'SL17' - 'SL18' - 'SL19' - 'SL21' - 'SL22' - 'SL23' - 'SL24' - 'SL25' - 'SL26' - 'SL27' - 'SL28' - 'SL29' - 'SL31' - 'SL32' - 'SL33' - 'SL34' - 'SL35' - 'SL41' - 'SL42' - 'SL43' - 'SL44' - 'SL45' - 'SL46' - 'SL47' - 'SL51' - 'SL52' - 'SR11' - 'SR12' - 'SR13' - 'SR14' - 'SR15' - 'SR16' - 'SR17' - 'SR18' - 'SR19' - 'SR21' - 'SR22' - 'SR23' - 'SR24' - 'SR25' - 'SR26' - 'SR27' - 'SR28' - 'SR29' - 'SR31' - 'SR32' - 'SR33' - 'SR34' - 'SR35' - 'SR41' - 'SR42' - 'SR43' - 'SR44' - 'SR45' - 'SR46' - 'SR47' - 'SR51' - 'SR52' - }; + ctf64 = { + 'SL11' + 'SL12' + 'SL13' + 'SL14' + 'SL15' + 'SL16' + 'SL17' + 'SL18' + 'SL19' + 'SL21' + 'SL22' + 'SL23' + 'SL24' + 'SL25' + 'SL26' + 'SL27' + 'SL28' + 'SL29' + 'SL31' + 'SL32' + 'SL33' + 'SL34' + 'SL35' + 'SL41' + 'SL42' + 'SL43' + 'SL44' + 'SL45' + 'SL46' + 'SL47' + 'SL51' + 'SL52' + 'SR11' + 'SR12' + 'SR13' + 'SR14' + 'SR15' + 'SR16' + 'SR17' + 'SR18' + 'SR19' + 'SR21' + 'SR22' + 'SR23' + 'SR24' + 'SR25' + 'SR26' + 'SR27' + 'SR28' + 'SR29' + 'SR31' + 'SR32' + 'SR33' + 'SR34' + 'SR35' + 'SR41' + 'SR42' + 'SR43' + 'SR44' + 'SR45' + 'SR46' + 'SR47' + 'SR51' + 'SR52' + }; - ctf151 = { - 'MLC11' - 'MLC12' - 'MLC13' - 'MLC14' - 'MLC15' - 'MLC21' - 'MLC22' - 'MLC23' - 'MLC24' - 'MLC31' - 'MLC32' - 'MLC33' - 'MLC41' - 'MLC42' - 'MLC43' - 'MLF11' - 'MLF12' - 'MLF21' - 'MLF22' - 'MLF23' - 'MLF31' - 'MLF32' - 'MLF33' - 'MLF34' - 'MLF41' - 'MLF42' - 'MLF43' - 'MLF44' - 'MLF45' - 'MLF51' - 'MLF52' - 'MLO11' - 'MLO12' - 'MLO21' - 'MLO22' - 'MLO31' - 'MLO32' - 'MLO33' - 'MLO41' - 'MLO42' - 'MLO43' - 'MLP11' - 'MLP12' - 'MLP13' - 'MLP21' - 'MLP22' - 'MLP31' - 'MLP32' - 'MLP33' - 'MLP34' - 'MLT11' - 'MLT12' - 'MLT13' - 'MLT14' - 'MLT15' - 'MLT16' - 'MLT21' - 'MLT22' - 'MLT23' - 'MLT24' - 'MLT25' - 'MLT26' - 'MLT31' - 'MLT32' - 'MLT33' - 'MLT34' - 'MLT35' - 'MLT41' - 'MLT42' - 'MLT43' - 'MLT44' - 'MRC11' - 'MRC12' - 'MRC13' - 'MRC14' - 'MRC15' - 'MRC21' - 'MRC22' - 'MRC23' - 'MRC24' - 'MRC31' - 'MRC32' - 'MRC33' - 'MRC41' - 'MRC42' - 'MRC43' - 'MRF11' - 'MRF12' - 'MRF21' - 'MRF22' - 'MRF23' - 'MRF31' - 'MRF32' - 'MRF33' - 'MRF34' - 'MRF41' - 'MRF42' - 'MRF43' - 'MRF44' - 'MRF45' - 'MRF51' - 'MRF52' - 'MRO11' - 'MRO12' - 'MRO21' - 'MRO22' - 'MRO31' - 'MRO32' - 'MRO33' - 'MRO41' - 'MRO42' - 'MRO43' - 'MRP11' - 'MRP12' - 'MRP13' - 'MRP21' - 'MRP22' - 'MRP31' - 'MRP32' - 'MRP33' - 'MRP34' - 'MRT11' - 'MRT12' - 'MRT13' - 'MRT14' - 'MRT15' - 'MRT16' - 'MRT21' - 'MRT22' - 'MRT23' - 'MRT24' - 'MRT25' - 'MRT26' - 'MRT31' - 'MRT32' - 'MRT33' - 'MRT34' - 'MRT35' - 'MRT41' - 'MRT42' - 'MRT43' - 'MRT44' - 'MZC01' - 'MZC02' - 'MZF01' - 'MZF02' - 'MZF03' - 'MZO01' - 'MZO02' - 'MZP01' - 'MZP02' - }; + ctf151 = { + 'MLC11' + 'MLC12' + 'MLC13' + 'MLC14' + 'MLC15' + 'MLC21' + 'MLC22' + 'MLC23' + 'MLC24' + 'MLC31' + 'MLC32' + 'MLC33' + 'MLC41' + 'MLC42' + 'MLC43' + 'MLF11' + 'MLF12' + 'MLF21' + 'MLF22' + 'MLF23' + 'MLF31' + 'MLF32' + 'MLF33' + 'MLF34' + 'MLF41' + 'MLF42' + 'MLF43' + 'MLF44' + 'MLF45' + 'MLF51' + 'MLF52' + 'MLO11' + 'MLO12' + 'MLO21' + 'MLO22' + 'MLO31' + 'MLO32' + 'MLO33' + 'MLO41' + 'MLO42' + 'MLO43' + 'MLP11' + 'MLP12' + 'MLP13' + 'MLP21' + 'MLP22' + 'MLP31' + 'MLP32' + 'MLP33' + 'MLP34' + 'MLT11' + 'MLT12' + 'MLT13' + 'MLT14' + 'MLT15' + 'MLT16' + 'MLT21' + 'MLT22' + 'MLT23' + 'MLT24' + 'MLT25' + 'MLT26' + 'MLT31' + 'MLT32' + 'MLT33' + 'MLT34' + 'MLT35' + 'MLT41' + 'MLT42' + 'MLT43' + 'MLT44' + 'MRC11' + 'MRC12' + 'MRC13' + 'MRC14' + 'MRC15' + 'MRC21' + 'MRC22' + 'MRC23' + 'MRC24' + 'MRC31' + 'MRC32' + 'MRC33' + 'MRC41' + 'MRC42' + 'MRC43' + 'MRF11' + 'MRF12' + 'MRF21' + 'MRF22' + 'MRF23' + 'MRF31' + 'MRF32' + 'MRF33' + 'MRF34' + 'MRF41' + 'MRF42' + 'MRF43' + 'MRF44' + 'MRF45' + 'MRF51' + 'MRF52' + 'MRO11' + 'MRO12' + 'MRO21' + 'MRO22' + 'MRO31' + 'MRO32' + 'MRO33' + 'MRO41' + 'MRO42' + 'MRO43' + 'MRP11' + 'MRP12' + 'MRP13' + 'MRP21' + 'MRP22' + 'MRP31' + 'MRP32' + 'MRP33' + 'MRP34' + 'MRT11' + 'MRT12' + 'MRT13' + 'MRT14' + 'MRT15' + 'MRT16' + 'MRT21' + 'MRT22' + 'MRT23' + 'MRT24' + 'MRT25' + 'MRT26' + 'MRT31' + 'MRT32' + 'MRT33' + 'MRT34' + 'MRT35' + 'MRT41' + 'MRT42' + 'MRT43' + 'MRT44' + 'MZC01' + 'MZC02' + 'MZF01' + 'MZF02' + 'MZF03' + 'MZO01' + 'MZO02' + 'MZP01' + 'MZP02' + }; - ctf151_planar = cell(151, 2); - for i=1:151 - ctf151_planar{i,1} = sprintf('%s_dH', ctf151{i}); - ctf151_planar{i,2} = sprintf('%s_dV', ctf151{i}); - end + ctf151_planar = cell(151, 2); + for i=1:151 + ctf151_planar{i,1} = sprintf('%s_dH', ctf151{i}); + ctf151_planar{i,2} = sprintf('%s_dV', ctf151{i}); + end - ctf275 = { - 'MLC11' - 'MLC12' - 'MLC13' - 'MLC14' - 'MLC15' - 'MLC16' - 'MLC17' - 'MLC21' - 'MLC22' - 'MLC23' - 'MLC24' - 'MLC25' - 'MLC31' - 'MLC32' - 'MLC41' - 'MLC42' - 'MLC51' - 'MLC52' - 'MLC53' - 'MLC54' - 'MLC55' - 'MLC61' - 'MLC62' - 'MLC63' - 'MLF11' - 'MLF12' - 'MLF13' - 'MLF14' - 'MLF21' - 'MLF22' - 'MLF23' - 'MLF24' - 'MLF25' - 'MLF31' - 'MLF32' - 'MLF33' - 'MLF34' - 'MLF35' - 'MLF41' - 'MLF42' - 'MLF43' - 'MLF44' - 'MLF45' - 'MLF46' - 'MLF51' - 'MLF52' - 'MLF53' - 'MLF54' - 'MLF55' - 'MLF56' - 'MLF61' - 'MLF62' - 'MLF63' - 'MLF64' - 'MLF65' - 'MLF66' - 'MLF67' - 'MLO11' - 'MLO12' - 'MLO13' - 'MLO14' - 'MLO21' - 'MLO22' - 'MLO23' - 'MLO24' - 'MLO31' - 'MLO32' - 'MLO33' - 'MLO34' - 'MLO41' - 'MLO42' - 'MLO43' - 'MLO44' - 'MLO51' - 'MLO52' - 'MLO53' - 'MLP11' - 'MLP12' - 'MLP21' - 'MLP22' - 'MLP23' - 'MLP31' - 'MLP32' - 'MLP33' - 'MLP34' - 'MLP35' - 'MLP41' - 'MLP42' - 'MLP43' - 'MLP44' - 'MLP45' - 'MLP51' - 'MLP52' - 'MLP53' - 'MLP54' - 'MLP55' - 'MLP56' - 'MLP57' - 'MLT11' - 'MLT12' - 'MLT13' - 'MLT14' - 'MLT15' - 'MLT16' - 'MLT21' - 'MLT22' - 'MLT23' - 'MLT24' - 'MLT25' - 'MLT26' - 'MLT27' - 'MLT31' - 'MLT32' - 'MLT33' - 'MLT34' - 'MLT35' - 'MLT36' - 'MLT37' - 'MLT41' - 'MLT42' - 'MLT43' - 'MLT44' - 'MLT45' - 'MLT46' - 'MLT47' - 'MLT51' - 'MLT52' - 'MLT53' - 'MLT54' - 'MLT55' - 'MLT56' - 'MLT57' - 'MRC11' - 'MRC12' - 'MRC13' - 'MRC14' - 'MRC15' - 'MRC16' - 'MRC17' - 'MRC21' - 'MRC22' - 'MRC23' - 'MRC24' - 'MRC25' - 'MRC31' - 'MRC32' - 'MRC41' - 'MRC42' - 'MRC51' - 'MRC52' - 'MRC53' - 'MRC54' - 'MRC55' - 'MRC61' - 'MRC62' - 'MRC63' - 'MRF11' - 'MRF12' - 'MRF13' - 'MRF14' - 'MRF21' - 'MRF22' - 'MRF23' - 'MRF24' - 'MRF25' - 'MRF31' - 'MRF32' - 'MRF33' - 'MRF34' - 'MRF35' - 'MRF41' - 'MRF42' - 'MRF43' - 'MRF44' - 'MRF45' - 'MRF46' - 'MRF51' - 'MRF52' - 'MRF53' - 'MRF54' - 'MRF55' - 'MRF56' - 'MRF61' - 'MRF62' - 'MRF63' - 'MRF64' - 'MRF65' - 'MRF66' - 'MRF67' - 'MRO11' - 'MRO12' - 'MRO13' - 'MRO14' - 'MRO21' - 'MRO22' - 'MRO23' - 'MRO24' - 'MRO31' - 'MRO32' - 'MRO33' - 'MRO34' - 'MRO41' - 'MRO42' - 'MRO43' - 'MRO44' - 'MRO51' - 'MRO52' - 'MRO53' - 'MRP11' - 'MRP12' - 'MRP21' - 'MRP22' - 'MRP23' - 'MRP32' - 'MRP33' - 'MRP34' - 'MRP35' - 'MRP41' - 'MRP42' - 'MRP43' - 'MRP44' - 'MRP45' - 'MRP51' - 'MRP52' - 'MRP53' - 'MRP54' - 'MRP55' - 'MRP56' - 'MRP57' - 'MRT11' - 'MRT12' - 'MRT13' - 'MRT14' - 'MRT15' - 'MRT16' - 'MRT21' - 'MRT22' - 'MRT23' - 'MRT24' - 'MRT25' - 'MRT26' - 'MRT27' - 'MRT31' - 'MRT32' - 'MRT33' - 'MRT34' - 'MRT35' - 'MRT36' - 'MRT37' - 'MRT41' - 'MRT42' - 'MRT43' - 'MRT44' - 'MRT45' - 'MRT46' - 'MRT47' - 'MRT51' - 'MRT52' - 'MRT53' - 'MRT54' - 'MRT55' - 'MRT56' - 'MRT57' - 'MZC01' - 'MZC02' - 'MZC03' - 'MZC04' - 'MZF01' - 'MZF02' - 'MZF03' - 'MZO01' - 'MZO02' - 'MZO03' - 'MZP01' - }; + ctf275 = { + 'MLC11' + 'MLC12' + 'MLC13' + 'MLC14' + 'MLC15' + 'MLC16' + 'MLC17' + 'MLC21' + 'MLC22' + 'MLC23' + 'MLC24' + 'MLC25' + 'MLC31' + 'MLC32' + 'MLC41' + 'MLC42' + 'MLC51' + 'MLC52' + 'MLC53' + 'MLC54' + 'MLC55' + 'MLC61' + 'MLC62' + 'MLC63' + 'MLF11' + 'MLF12' + 'MLF13' + 'MLF14' + 'MLF21' + 'MLF22' + 'MLF23' + 'MLF24' + 'MLF25' + 'MLF31' + 'MLF32' + 'MLF33' + 'MLF34' + 'MLF35' + 'MLF41' + 'MLF42' + 'MLF43' + 'MLF44' + 'MLF45' + 'MLF46' + 'MLF51' + 'MLF52' + 'MLF53' + 'MLF54' + 'MLF55' + 'MLF56' + 'MLF61' + 'MLF62' + 'MLF63' + 'MLF64' + 'MLF65' + 'MLF66' + 'MLF67' + 'MLO11' + 'MLO12' + 'MLO13' + 'MLO14' + 'MLO21' + 'MLO22' + 'MLO23' + 'MLO24' + 'MLO31' + 'MLO32' + 'MLO33' + 'MLO34' + 'MLO41' + 'MLO42' + 'MLO43' + 'MLO44' + 'MLO51' + 'MLO52' + 'MLO53' + 'MLP11' + 'MLP12' + 'MLP21' + 'MLP22' + 'MLP23' + 'MLP31' + 'MLP32' + 'MLP33' + 'MLP34' + 'MLP35' + 'MLP41' + 'MLP42' + 'MLP43' + 'MLP44' + 'MLP45' + 'MLP51' + 'MLP52' + 'MLP53' + 'MLP54' + 'MLP55' + 'MLP56' + 'MLP57' + 'MLT11' + 'MLT12' + 'MLT13' + 'MLT14' + 'MLT15' + 'MLT16' + 'MLT21' + 'MLT22' + 'MLT23' + 'MLT24' + 'MLT25' + 'MLT26' + 'MLT27' + 'MLT31' + 'MLT32' + 'MLT33' + 'MLT34' + 'MLT35' + 'MLT36' + 'MLT37' + 'MLT41' + 'MLT42' + 'MLT43' + 'MLT44' + 'MLT45' + 'MLT46' + 'MLT47' + 'MLT51' + 'MLT52' + 'MLT53' + 'MLT54' + 'MLT55' + 'MLT56' + 'MLT57' + 'MRC11' + 'MRC12' + 'MRC13' + 'MRC14' + 'MRC15' + 'MRC16' + 'MRC17' + 'MRC21' + 'MRC22' + 'MRC23' + 'MRC24' + 'MRC25' + 'MRC31' + 'MRC32' + 'MRC41' + 'MRC42' + 'MRC51' + 'MRC52' + 'MRC53' + 'MRC54' + 'MRC55' + 'MRC61' + 'MRC62' + 'MRC63' + 'MRF11' + 'MRF12' + 'MRF13' + 'MRF14' + 'MRF21' + 'MRF22' + 'MRF23' + 'MRF24' + 'MRF25' + 'MRF31' + 'MRF32' + 'MRF33' + 'MRF34' + 'MRF35' + 'MRF41' + 'MRF42' + 'MRF43' + 'MRF44' + 'MRF45' + 'MRF46' + 'MRF51' + 'MRF52' + 'MRF53' + 'MRF54' + 'MRF55' + 'MRF56' + 'MRF61' + 'MRF62' + 'MRF63' + 'MRF64' + 'MRF65' + 'MRF66' + 'MRF67' + 'MRO11' + 'MRO12' + 'MRO13' + 'MRO14' + 'MRO21' + 'MRO22' + 'MRO23' + 'MRO24' + 'MRO31' + 'MRO32' + 'MRO33' + 'MRO34' + 'MRO41' + 'MRO42' + 'MRO43' + 'MRO44' + 'MRO51' + 'MRO52' + 'MRO53' + 'MRP11' + 'MRP12' + 'MRP21' + 'MRP22' + 'MRP23' + 'MRP32' + 'MRP33' + 'MRP34' + 'MRP35' + 'MRP41' + 'MRP42' + 'MRP43' + 'MRP44' + 'MRP45' + 'MRP51' + 'MRP52' + 'MRP53' + 'MRP54' + 'MRP55' + 'MRP56' + 'MRP57' + 'MRT11' + 'MRT12' + 'MRT13' + 'MRT14' + 'MRT15' + 'MRT16' + 'MRT21' + 'MRT22' + 'MRT23' + 'MRT24' + 'MRT25' + 'MRT26' + 'MRT27' + 'MRT31' + 'MRT32' + 'MRT33' + 'MRT34' + 'MRT35' + 'MRT36' + 'MRT37' + 'MRT41' + 'MRT42' + 'MRT43' + 'MRT44' + 'MRT45' + 'MRT46' + 'MRT47' + 'MRT51' + 'MRT52' + 'MRT53' + 'MRT54' + 'MRT55' + 'MRT56' + 'MRT57' + 'MZC01' + 'MZC02' + 'MZC03' + 'MZC04' + 'MZF01' + 'MZF02' + 'MZF03' + 'MZO01' + 'MZO02' + 'MZO03' + 'MZP01' + }; - % f.ck, apparently one channel is missing - ctf275_planar = cell(274,2); - for i=1:274 - ctf275_planar{i,1} = sprintf('%s_dH', ctf275{i}); - ctf275_planar{i,2} = sprintf('%s_dV', ctf275{i}); - end + % f.ck, apparently one channel is missing + ctf275_planar = cell(274,2); + for i=1:274 + ctf275_planar{i,1} = sprintf('%s_dH', ctf275{i}); + ctf275_planar{i,2} = sprintf('%s_dV', ctf275{i}); + end end % if issctf if isneuromag - neuromag122 = { - 'MEG 001' 'MEG 002' - 'MEG 003' 'MEG 004' - 'MEG 005' 'MEG 006' - 'MEG 007' 'MEG 008' - 'MEG 009' 'MEG 010' - 'MEG 011' 'MEG 012' - 'MEG 013' 'MEG 014' - 'MEG 015' 'MEG 016' - 'MEG 017' 'MEG 018' - 'MEG 019' 'MEG 020' - 'MEG 021' 'MEG 022' - 'MEG 023' 'MEG 024' - 'MEG 025' 'MEG 026' - 'MEG 027' 'MEG 028' - 'MEG 029' 'MEG 030' - 'MEG 031' 'MEG 032' - 'MEG 033' 'MEG 034' - 'MEG 035' 'MEG 036' - 'MEG 037' 'MEG 038' - 'MEG 039' 'MEG 040' - 'MEG 041' 'MEG 042' - 'MEG 043' 'MEG 044' - 'MEG 045' 'MEG 046' - 'MEG 047' 'MEG 048' - 'MEG 049' 'MEG 050' - 'MEG 051' 'MEG 052' - 'MEG 053' 'MEG 054' - 'MEG 055' 'MEG 056' - 'MEG 057' 'MEG 058' - 'MEG 059' 'MEG 060' - 'MEG 061' 'MEG 062' - 'MEG 063' 'MEG 064' - 'MEG 065' 'MEG 066' - 'MEG 067' 'MEG 068' - 'MEG 069' 'MEG 070' - 'MEG 071' 'MEG 072' - 'MEG 073' 'MEG 074' - 'MEG 075' 'MEG 076' - 'MEG 077' 'MEG 078' - 'MEG 079' 'MEG 080' - 'MEG 081' 'MEG 082' - 'MEG 083' 'MEG 084' - 'MEG 085' 'MEG 086' - 'MEG 087' 'MEG 088' - 'MEG 089' 'MEG 090' - 'MEG 091' 'MEG 092' - 'MEG 093' 'MEG 094' - 'MEG 095' 'MEG 096' - 'MEG 097' 'MEG 098' - 'MEG 099' 'MEG 100' - 'MEG 101' 'MEG 102' - 'MEG 103' 'MEG 104' - 'MEG 105' 'MEG 106' - 'MEG 107' 'MEG 108' - 'MEG 109' 'MEG 110' - 'MEG 111' 'MEG 112' - 'MEG 113' 'MEG 114' - 'MEG 115' 'MEG 116' - 'MEG 117' 'MEG 118' - 'MEG 119' 'MEG 120' - 'MEG 121' 'MEG 122' - }; + neuromag122 = { + 'MEG 001' 'MEG 002' + 'MEG 003' 'MEG 004' + 'MEG 005' 'MEG 006' + 'MEG 007' 'MEG 008' + 'MEG 009' 'MEG 010' + 'MEG 011' 'MEG 012' + 'MEG 013' 'MEG 014' + 'MEG 015' 'MEG 016' + 'MEG 017' 'MEG 018' + 'MEG 019' 'MEG 020' + 'MEG 021' 'MEG 022' + 'MEG 023' 'MEG 024' + 'MEG 025' 'MEG 026' + 'MEG 027' 'MEG 028' + 'MEG 029' 'MEG 030' + 'MEG 031' 'MEG 032' + 'MEG 033' 'MEG 034' + 'MEG 035' 'MEG 036' + 'MEG 037' 'MEG 038' + 'MEG 039' 'MEG 040' + 'MEG 041' 'MEG 042' + 'MEG 043' 'MEG 044' + 'MEG 045' 'MEG 046' + 'MEG 047' 'MEG 048' + 'MEG 049' 'MEG 050' + 'MEG 051' 'MEG 052' + 'MEG 053' 'MEG 054' + 'MEG 055' 'MEG 056' + 'MEG 057' 'MEG 058' + 'MEG 059' 'MEG 060' + 'MEG 061' 'MEG 062' + 'MEG 063' 'MEG 064' + 'MEG 065' 'MEG 066' + 'MEG 067' 'MEG 068' + 'MEG 069' 'MEG 070' + 'MEG 071' 'MEG 072' + 'MEG 073' 'MEG 074' + 'MEG 075' 'MEG 076' + 'MEG 077' 'MEG 078' + 'MEG 079' 'MEG 080' + 'MEG 081' 'MEG 082' + 'MEG 083' 'MEG 084' + 'MEG 085' 'MEG 086' + 'MEG 087' 'MEG 088' + 'MEG 089' 'MEG 090' + 'MEG 091' 'MEG 092' + 'MEG 093' 'MEG 094' + 'MEG 095' 'MEG 096' + 'MEG 097' 'MEG 098' + 'MEG 099' 'MEG 100' + 'MEG 101' 'MEG 102' + 'MEG 103' 'MEG 104' + 'MEG 105' 'MEG 106' + 'MEG 107' 'MEG 108' + 'MEG 109' 'MEG 110' + 'MEG 111' 'MEG 112' + 'MEG 113' 'MEG 114' + 'MEG 115' 'MEG 116' + 'MEG 117' 'MEG 118' + 'MEG 119' 'MEG 120' + 'MEG 121' 'MEG 122' + }; - % this is an alternative set of labels without a space in them - neuromag122alt = { - 'MEG001' 'MEG002' - 'MEG003' 'MEG004' - 'MEG005' 'MEG006' - 'MEG007' 'MEG008' - 'MEG009' 'MEG010' - 'MEG011' 'MEG012' - 'MEG013' 'MEG014' - 'MEG015' 'MEG016' - 'MEG017' 'MEG018' - 'MEG019' 'MEG020' - 'MEG021' 'MEG022' - 'MEG023' 'MEG024' - 'MEG025' 'MEG026' - 'MEG027' 'MEG028' - 'MEG029' 'MEG030' - 'MEG031' 'MEG032' - 'MEG033' 'MEG034' - 'MEG035' 'MEG036' - 'MEG037' 'MEG038' - 'MEG039' 'MEG040' - 'MEG041' 'MEG042' - 'MEG043' 'MEG044' - 'MEG045' 'MEG046' - 'MEG047' 'MEG048' - 'MEG049' 'MEG050' - 'MEG051' 'MEG052' - 'MEG053' 'MEG054' - 'MEG055' 'MEG056' - 'MEG057' 'MEG058' - 'MEG059' 'MEG060' - 'MEG061' 'MEG062' - 'MEG063' 'MEG064' - 'MEG065' 'MEG066' - 'MEG067' 'MEG068' - 'MEG069' 'MEG070' - 'MEG071' 'MEG072' - 'MEG073' 'MEG074' - 'MEG075' 'MEG076' - 'MEG077' 'MEG078' - 'MEG079' 'MEG080' - 'MEG081' 'MEG082' - 'MEG083' 'MEG084' - 'MEG085' 'MEG086' - 'MEG087' 'MEG088' - 'MEG089' 'MEG090' - 'MEG091' 'MEG092' - 'MEG093' 'MEG094' - 'MEG095' 'MEG096' - 'MEG097' 'MEG098' - 'MEG099' 'MEG100' - 'MEG101' 'MEG102' - 'MEG103' 'MEG104' - 'MEG105' 'MEG106' - 'MEG107' 'MEG108' - 'MEG109' 'MEG110' - 'MEG111' 'MEG112' - 'MEG113' 'MEG114' - 'MEG115' 'MEG116' - 'MEG117' 'MEG118' - 'MEG119' 'MEG120' - 'MEG121' 'MEG122' - }; + % this is an alternative set of labels without a space in them + neuromag122alt = { + 'MEG001' 'MEG002' + 'MEG003' 'MEG004' + 'MEG005' 'MEG006' + 'MEG007' 'MEG008' + 'MEG009' 'MEG010' + 'MEG011' 'MEG012' + 'MEG013' 'MEG014' + 'MEG015' 'MEG016' + 'MEG017' 'MEG018' + 'MEG019' 'MEG020' + 'MEG021' 'MEG022' + 'MEG023' 'MEG024' + 'MEG025' 'MEG026' + 'MEG027' 'MEG028' + 'MEG029' 'MEG030' + 'MEG031' 'MEG032' + 'MEG033' 'MEG034' + 'MEG035' 'MEG036' + 'MEG037' 'MEG038' + 'MEG039' 'MEG040' + 'MEG041' 'MEG042' + 'MEG043' 'MEG044' + 'MEG045' 'MEG046' + 'MEG047' 'MEG048' + 'MEG049' 'MEG050' + 'MEG051' 'MEG052' + 'MEG053' 'MEG054' + 'MEG055' 'MEG056' + 'MEG057' 'MEG058' + 'MEG059' 'MEG060' + 'MEG061' 'MEG062' + 'MEG063' 'MEG064' + 'MEG065' 'MEG066' + 'MEG067' 'MEG068' + 'MEG069' 'MEG070' + 'MEG071' 'MEG072' + 'MEG073' 'MEG074' + 'MEG075' 'MEG076' + 'MEG077' 'MEG078' + 'MEG079' 'MEG080' + 'MEG081' 'MEG082' + 'MEG083' 'MEG084' + 'MEG085' 'MEG086' + 'MEG087' 'MEG088' + 'MEG089' 'MEG090' + 'MEG091' 'MEG092' + 'MEG093' 'MEG094' + 'MEG095' 'MEG096' + 'MEG097' 'MEG098' + 'MEG099' 'MEG100' + 'MEG101' 'MEG102' + 'MEG103' 'MEG104' + 'MEG105' 'MEG106' + 'MEG107' 'MEG108' + 'MEG109' 'MEG110' + 'MEG111' 'MEG112' + 'MEG113' 'MEG114' + 'MEG115' 'MEG116' + 'MEG117' 'MEG118' + 'MEG119' 'MEG120' + 'MEG121' 'MEG122' + }; - neuromag306 = { - 'MEG 0113' 'MEG 0112' 'MEG 0111' - 'MEG 0122' 'MEG 0123' 'MEG 0121' - 'MEG 0132' 'MEG 0133' 'MEG 0131' - 'MEG 0143' 'MEG 0142' 'MEG 0141' - 'MEG 0213' 'MEG 0212' 'MEG 0211' - 'MEG 0222' 'MEG 0223' 'MEG 0221' - 'MEG 0232' 'MEG 0233' 'MEG 0231' - 'MEG 0243' 'MEG 0242' 'MEG 0241' - 'MEG 0313' 'MEG 0312' 'MEG 0311' - 'MEG 0322' 'MEG 0323' 'MEG 0321' - 'MEG 0333' 'MEG 0332' 'MEG 0331' - 'MEG 0343' 'MEG 0342' 'MEG 0341' - 'MEG 0413' 'MEG 0412' 'MEG 0411' - 'MEG 0422' 'MEG 0423' 'MEG 0421' - 'MEG 0432' 'MEG 0433' 'MEG 0431' - 'MEG 0443' 'MEG 0442' 'MEG 0441' - 'MEG 0513' 'MEG 0512' 'MEG 0511' - 'MEG 0523' 'MEG 0522' 'MEG 0521' - 'MEG 0532' 'MEG 0533' 'MEG 0531' - 'MEG 0542' 'MEG 0543' 'MEG 0541' - 'MEG 0613' 'MEG 0612' 'MEG 0611' - 'MEG 0622' 'MEG 0623' 'MEG 0621' - 'MEG 0633' 'MEG 0632' 'MEG 0631' - 'MEG 0642' 'MEG 0643' 'MEG 0641' - 'MEG 0713' 'MEG 0712' 'MEG 0711' - 'MEG 0723' 'MEG 0722' 'MEG 0721' - 'MEG 0733' 'MEG 0732' 'MEG 0731' - 'MEG 0743' 'MEG 0742' 'MEG 0741' - 'MEG 0813' 'MEG 0812' 'MEG 0811' - 'MEG 0822' 'MEG 0823' 'MEG 0821' - 'MEG 0913' 'MEG 0912' 'MEG 0911' - 'MEG 0923' 'MEG 0922' 'MEG 0921' - 'MEG 0932' 'MEG 0933' 'MEG 0931' - 'MEG 0942' 'MEG 0943' 'MEG 0941' - 'MEG 1013' 'MEG 1012' 'MEG 1011' - 'MEG 1023' 'MEG 1022' 'MEG 1021' - 'MEG 1032' 'MEG 1033' 'MEG 1031' - 'MEG 1043' 'MEG 1042' 'MEG 1041' - 'MEG 1112' 'MEG 1113' 'MEG 1111' - 'MEG 1123' 'MEG 1122' 'MEG 1121' - 'MEG 1133' 'MEG 1132' 'MEG 1131' - 'MEG 1142' 'MEG 1143' 'MEG 1141' - 'MEG 1213' 'MEG 1212' 'MEG 1211' - 'MEG 1223' 'MEG 1222' 'MEG 1221' - 'MEG 1232' 'MEG 1233' 'MEG 1231' - 'MEG 1243' 'MEG 1242' 'MEG 1241' - 'MEG 1312' 'MEG 1313' 'MEG 1311' - 'MEG 1323' 'MEG 1322' 'MEG 1321' - 'MEG 1333' 'MEG 1332' 'MEG 1331' - 'MEG 1342' 'MEG 1343' 'MEG 1341' - 'MEG 1412' 'MEG 1413' 'MEG 1411' - 'MEG 1423' 'MEG 1422' 'MEG 1421' - 'MEG 1433' 'MEG 1432' 'MEG 1431' - 'MEG 1442' 'MEG 1443' 'MEG 1441' - 'MEG 1512' 'MEG 1513' 'MEG 1511' - 'MEG 1522' 'MEG 1523' 'MEG 1521' - 'MEG 1533' 'MEG 1532' 'MEG 1531' - 'MEG 1543' 'MEG 1542' 'MEG 1541' - 'MEG 1613' 'MEG 1612' 'MEG 1611' - 'MEG 1622' 'MEG 1623' 'MEG 1621' - 'MEG 1632' 'MEG 1633' 'MEG 1631' - 'MEG 1643' 'MEG 1642' 'MEG 1641' - 'MEG 1713' 'MEG 1712' 'MEG 1711' - 'MEG 1722' 'MEG 1723' 'MEG 1721' - 'MEG 1732' 'MEG 1733' 'MEG 1731' - 'MEG 1743' 'MEG 1742' 'MEG 1741' - 'MEG 1813' 'MEG 1812' 'MEG 1811' - 'MEG 1822' 'MEG 1823' 'MEG 1821' - 'MEG 1832' 'MEG 1833' 'MEG 1831' - 'MEG 1843' 'MEG 1842' 'MEG 1841' - 'MEG 1912' 'MEG 1913' 'MEG 1911' - 'MEG 1923' 'MEG 1922' 'MEG 1921' - 'MEG 1932' 'MEG 1933' 'MEG 1931' - 'MEG 1943' 'MEG 1942' 'MEG 1941' - 'MEG 2013' 'MEG 2012' 'MEG 2011' - 'MEG 2023' 'MEG 2022' 'MEG 2021' - 'MEG 2032' 'MEG 2033' 'MEG 2031' - 'MEG 2042' 'MEG 2043' 'MEG 2041' - 'MEG 2113' 'MEG 2112' 'MEG 2111' - 'MEG 2122' 'MEG 2123' 'MEG 2121' - 'MEG 2133' 'MEG 2132' 'MEG 2131' - 'MEG 2143' 'MEG 2142' 'MEG 2141' - 'MEG 2212' 'MEG 2213' 'MEG 2211' - 'MEG 2223' 'MEG 2222' 'MEG 2221' - 'MEG 2233' 'MEG 2232' 'MEG 2231' - 'MEG 2242' 'MEG 2243' 'MEG 2241' - 'MEG 2312' 'MEG 2313' 'MEG 2311' - 'MEG 2323' 'MEG 2322' 'MEG 2321' - 'MEG 2332' 'MEG 2333' 'MEG 2331' - 'MEG 2343' 'MEG 2342' 'MEG 2341' - 'MEG 2412' 'MEG 2413' 'MEG 2411' - 'MEG 2423' 'MEG 2422' 'MEG 2421' - 'MEG 2433' 'MEG 2432' 'MEG 2431' - 'MEG 2442' 'MEG 2443' 'MEG 2441' - 'MEG 2512' 'MEG 2513' 'MEG 2511' - 'MEG 2522' 'MEG 2523' 'MEG 2521' - 'MEG 2533' 'MEG 2532' 'MEG 2531' - 'MEG 2543' 'MEG 2542' 'MEG 2541' - 'MEG 2612' 'MEG 2613' 'MEG 2611' - 'MEG 2623' 'MEG 2622' 'MEG 2621' - 'MEG 2633' 'MEG 2632' 'MEG 2631' - 'MEG 2642' 'MEG 2643' 'MEG 2641' - }; + neuromag306 = { + 'MEG 0113' 'MEG 0112' 'MEG 0111' + 'MEG 0122' 'MEG 0123' 'MEG 0121' + 'MEG 0132' 'MEG 0133' 'MEG 0131' + 'MEG 0143' 'MEG 0142' 'MEG 0141' + 'MEG 0213' 'MEG 0212' 'MEG 0211' + 'MEG 0222' 'MEG 0223' 'MEG 0221' + 'MEG 0232' 'MEG 0233' 'MEG 0231' + 'MEG 0243' 'MEG 0242' 'MEG 0241' + 'MEG 0313' 'MEG 0312' 'MEG 0311' + 'MEG 0322' 'MEG 0323' 'MEG 0321' + 'MEG 0333' 'MEG 0332' 'MEG 0331' + 'MEG 0343' 'MEG 0342' 'MEG 0341' + 'MEG 0413' 'MEG 0412' 'MEG 0411' + 'MEG 0422' 'MEG 0423' 'MEG 0421' + 'MEG 0432' 'MEG 0433' 'MEG 0431' + 'MEG 0443' 'MEG 0442' 'MEG 0441' + 'MEG 0513' 'MEG 0512' 'MEG 0511' + 'MEG 0523' 'MEG 0522' 'MEG 0521' + 'MEG 0532' 'MEG 0533' 'MEG 0531' + 'MEG 0542' 'MEG 0543' 'MEG 0541' + 'MEG 0613' 'MEG 0612' 'MEG 0611' + 'MEG 0622' 'MEG 0623' 'MEG 0621' + 'MEG 0633' 'MEG 0632' 'MEG 0631' + 'MEG 0642' 'MEG 0643' 'MEG 0641' + 'MEG 0713' 'MEG 0712' 'MEG 0711' + 'MEG 0723' 'MEG 0722' 'MEG 0721' + 'MEG 0733' 'MEG 0732' 'MEG 0731' + 'MEG 0743' 'MEG 0742' 'MEG 0741' + 'MEG 0813' 'MEG 0812' 'MEG 0811' + 'MEG 0822' 'MEG 0823' 'MEG 0821' + 'MEG 0913' 'MEG 0912' 'MEG 0911' + 'MEG 0923' 'MEG 0922' 'MEG 0921' + 'MEG 0932' 'MEG 0933' 'MEG 0931' + 'MEG 0942' 'MEG 0943' 'MEG 0941' + 'MEG 1013' 'MEG 1012' 'MEG 1011' + 'MEG 1023' 'MEG 1022' 'MEG 1021' + 'MEG 1032' 'MEG 1033' 'MEG 1031' + 'MEG 1043' 'MEG 1042' 'MEG 1041' + 'MEG 1112' 'MEG 1113' 'MEG 1111' + 'MEG 1123' 'MEG 1122' 'MEG 1121' + 'MEG 1133' 'MEG 1132' 'MEG 1131' + 'MEG 1142' 'MEG 1143' 'MEG 1141' + 'MEG 1213' 'MEG 1212' 'MEG 1211' + 'MEG 1223' 'MEG 1222' 'MEG 1221' + 'MEG 1232' 'MEG 1233' 'MEG 1231' + 'MEG 1243' 'MEG 1242' 'MEG 1241' + 'MEG 1312' 'MEG 1313' 'MEG 1311' + 'MEG 1323' 'MEG 1322' 'MEG 1321' + 'MEG 1333' 'MEG 1332' 'MEG 1331' + 'MEG 1342' 'MEG 1343' 'MEG 1341' + 'MEG 1412' 'MEG 1413' 'MEG 1411' + 'MEG 1423' 'MEG 1422' 'MEG 1421' + 'MEG 1433' 'MEG 1432' 'MEG 1431' + 'MEG 1442' 'MEG 1443' 'MEG 1441' + 'MEG 1512' 'MEG 1513' 'MEG 1511' + 'MEG 1522' 'MEG 1523' 'MEG 1521' + 'MEG 1533' 'MEG 1532' 'MEG 1531' + 'MEG 1543' 'MEG 1542' 'MEG 1541' + 'MEG 1613' 'MEG 1612' 'MEG 1611' + 'MEG 1622' 'MEG 1623' 'MEG 1621' + 'MEG 1632' 'MEG 1633' 'MEG 1631' + 'MEG 1643' 'MEG 1642' 'MEG 1641' + 'MEG 1713' 'MEG 1712' 'MEG 1711' + 'MEG 1722' 'MEG 1723' 'MEG 1721' + 'MEG 1732' 'MEG 1733' 'MEG 1731' + 'MEG 1743' 'MEG 1742' 'MEG 1741' + 'MEG 1813' 'MEG 1812' 'MEG 1811' + 'MEG 1822' 'MEG 1823' 'MEG 1821' + 'MEG 1832' 'MEG 1833' 'MEG 1831' + 'MEG 1843' 'MEG 1842' 'MEG 1841' + 'MEG 1912' 'MEG 1913' 'MEG 1911' + 'MEG 1923' 'MEG 1922' 'MEG 1921' + 'MEG 1932' 'MEG 1933' 'MEG 1931' + 'MEG 1943' 'MEG 1942' 'MEG 1941' + 'MEG 2013' 'MEG 2012' 'MEG 2011' + 'MEG 2023' 'MEG 2022' 'MEG 2021' + 'MEG 2032' 'MEG 2033' 'MEG 2031' + 'MEG 2042' 'MEG 2043' 'MEG 2041' + 'MEG 2113' 'MEG 2112' 'MEG 2111' + 'MEG 2122' 'MEG 2123' 'MEG 2121' + 'MEG 2133' 'MEG 2132' 'MEG 2131' + 'MEG 2143' 'MEG 2142' 'MEG 2141' + 'MEG 2212' 'MEG 2213' 'MEG 2211' + 'MEG 2223' 'MEG 2222' 'MEG 2221' + 'MEG 2233' 'MEG 2232' 'MEG 2231' + 'MEG 2242' 'MEG 2243' 'MEG 2241' + 'MEG 2312' 'MEG 2313' 'MEG 2311' + 'MEG 2323' 'MEG 2322' 'MEG 2321' + 'MEG 2332' 'MEG 2333' 'MEG 2331' + 'MEG 2343' 'MEG 2342' 'MEG 2341' + 'MEG 2412' 'MEG 2413' 'MEG 2411' + 'MEG 2423' 'MEG 2422' 'MEG 2421' + 'MEG 2433' 'MEG 2432' 'MEG 2431' + 'MEG 2442' 'MEG 2443' 'MEG 2441' + 'MEG 2512' 'MEG 2513' 'MEG 2511' + 'MEG 2522' 'MEG 2523' 'MEG 2521' + 'MEG 2533' 'MEG 2532' 'MEG 2531' + 'MEG 2543' 'MEG 2542' 'MEG 2541' + 'MEG 2612' 'MEG 2613' 'MEG 2611' + 'MEG 2623' 'MEG 2622' 'MEG 2621' + 'MEG 2633' 'MEG 2632' 'MEG 2631' + 'MEG 2642' 'MEG 2643' 'MEG 2641' + }; - % this is an alternative set of labels without a space in them - neuromag306alt = { - 'MEG0113' 'MEG0112' 'MEG0111' - 'MEG0122' 'MEG0123' 'MEG0121' - 'MEG0132' 'MEG0133' 'MEG0131' - 'MEG0143' 'MEG0142' 'MEG0141' - 'MEG0213' 'MEG0212' 'MEG0211' - 'MEG0222' 'MEG0223' 'MEG0221' - 'MEG0232' 'MEG0233' 'MEG0231' - 'MEG0243' 'MEG0242' 'MEG0241' - 'MEG0313' 'MEG0312' 'MEG0311' - 'MEG0322' 'MEG0323' 'MEG0321' - 'MEG0333' 'MEG0332' 'MEG0331' - 'MEG0343' 'MEG0342' 'MEG0341' - 'MEG0413' 'MEG0412' 'MEG0411' - 'MEG0422' 'MEG0423' 'MEG0421' - 'MEG0432' 'MEG0433' 'MEG0431' - 'MEG0443' 'MEG0442' 'MEG0441' - 'MEG0513' 'MEG0512' 'MEG0511' - 'MEG0523' 'MEG0522' 'MEG0521' - 'MEG0532' 'MEG0533' 'MEG0531' - 'MEG0542' 'MEG0543' 'MEG0541' - 'MEG0613' 'MEG0612' 'MEG0611' - 'MEG0622' 'MEG0623' 'MEG0621' - 'MEG0633' 'MEG0632' 'MEG0631' - 'MEG0642' 'MEG0643' 'MEG0641' - 'MEG0713' 'MEG0712' 'MEG0711' - 'MEG0723' 'MEG0722' 'MEG0721' - 'MEG0733' 'MEG0732' 'MEG0731' - 'MEG0743' 'MEG0742' 'MEG0741' - 'MEG0813' 'MEG0812' 'MEG0811' - 'MEG0822' 'MEG0823' 'MEG0821' - 'MEG0913' 'MEG0912' 'MEG0911' - 'MEG0923' 'MEG0922' 'MEG0921' - 'MEG0932' 'MEG0933' 'MEG0931' - 'MEG0942' 'MEG0943' 'MEG0941' - 'MEG1013' 'MEG1012' 'MEG1011' - 'MEG1023' 'MEG1022' 'MEG1021' - 'MEG1032' 'MEG1033' 'MEG1031' - 'MEG1043' 'MEG1042' 'MEG1041' - 'MEG1112' 'MEG1113' 'MEG1111' - 'MEG1123' 'MEG1122' 'MEG1121' - 'MEG1133' 'MEG1132' 'MEG1131' - 'MEG1142' 'MEG1143' 'MEG1141' - 'MEG1213' 'MEG1212' 'MEG1211' - 'MEG1223' 'MEG1222' 'MEG1221' - 'MEG1232' 'MEG1233' 'MEG1231' - 'MEG1243' 'MEG1242' 'MEG1241' - 'MEG1312' 'MEG1313' 'MEG1311' - 'MEG1323' 'MEG1322' 'MEG1321' - 'MEG1333' 'MEG1332' 'MEG1331' - 'MEG1342' 'MEG1343' 'MEG1341' - 'MEG1412' 'MEG1413' 'MEG1411' - 'MEG1423' 'MEG1422' 'MEG1421' - 'MEG1433' 'MEG1432' 'MEG1431' - 'MEG1442' 'MEG1443' 'MEG1441' - 'MEG1512' 'MEG1513' 'MEG1511' - 'MEG1522' 'MEG1523' 'MEG1521' - 'MEG1533' 'MEG1532' 'MEG1531' - 'MEG1543' 'MEG1542' 'MEG1541' - 'MEG1613' 'MEG1612' 'MEG1611' - 'MEG1622' 'MEG1623' 'MEG1621' - 'MEG1632' 'MEG1633' 'MEG1631' - 'MEG1643' 'MEG1642' 'MEG1641' - 'MEG1713' 'MEG1712' 'MEG1711' - 'MEG1722' 'MEG1723' 'MEG1721' - 'MEG1732' 'MEG1733' 'MEG1731' - 'MEG1743' 'MEG1742' 'MEG1741' - 'MEG1813' 'MEG1812' 'MEG1811' - 'MEG1822' 'MEG1823' 'MEG1821' - 'MEG1832' 'MEG1833' 'MEG1831' - 'MEG1843' 'MEG1842' 'MEG1841' - 'MEG1912' 'MEG1913' 'MEG1911' - 'MEG1923' 'MEG1922' 'MEG1921' - 'MEG1932' 'MEG1933' 'MEG1931' - 'MEG1943' 'MEG1942' 'MEG1941' - 'MEG2013' 'MEG2012' 'MEG2011' - 'MEG2023' 'MEG2022' 'MEG2021' - 'MEG2032' 'MEG2033' 'MEG2031' - 'MEG2042' 'MEG2043' 'MEG2041' - 'MEG2113' 'MEG2112' 'MEG2111' - 'MEG2122' 'MEG2123' 'MEG2121' - 'MEG2133' 'MEG2132' 'MEG2131' - 'MEG2143' 'MEG2142' 'MEG2141' - 'MEG2212' 'MEG2213' 'MEG2211' - 'MEG2223' 'MEG2222' 'MEG2221' - 'MEG2233' 'MEG2232' 'MEG2231' - 'MEG2242' 'MEG2243' 'MEG2241' - 'MEG2312' 'MEG2313' 'MEG2311' - 'MEG2323' 'MEG2322' 'MEG2321' - 'MEG2332' 'MEG2333' 'MEG2331' - 'MEG2343' 'MEG2342' 'MEG2341' - 'MEG2412' 'MEG2413' 'MEG2411' - 'MEG2423' 'MEG2422' 'MEG2421' - 'MEG2433' 'MEG2432' 'MEG2431' - 'MEG2442' 'MEG2443' 'MEG2441' - 'MEG2512' 'MEG2513' 'MEG2511' - 'MEG2522' 'MEG2523' 'MEG2521' - 'MEG2533' 'MEG2532' 'MEG2531' - 'MEG2543' 'MEG2542' 'MEG2541' - 'MEG2612' 'MEG2613' 'MEG2611' - 'MEG2623' 'MEG2622' 'MEG2621' - 'MEG2633' 'MEG2632' 'MEG2631' - 'MEG2642' 'MEG2643' 'MEG2641' - }; + % this is an alternative set of labels without a space in them + neuromag306alt = { + 'MEG0113' 'MEG0112' 'MEG0111' + 'MEG0122' 'MEG0123' 'MEG0121' + 'MEG0132' 'MEG0133' 'MEG0131' + 'MEG0143' 'MEG0142' 'MEG0141' + 'MEG0213' 'MEG0212' 'MEG0211' + 'MEG0222' 'MEG0223' 'MEG0221' + 'MEG0232' 'MEG0233' 'MEG0231' + 'MEG0243' 'MEG0242' 'MEG0241' + 'MEG0313' 'MEG0312' 'MEG0311' + 'MEG0322' 'MEG0323' 'MEG0321' + 'MEG0333' 'MEG0332' 'MEG0331' + 'MEG0343' 'MEG0342' 'MEG0341' + 'MEG0413' 'MEG0412' 'MEG0411' + 'MEG0422' 'MEG0423' 'MEG0421' + 'MEG0432' 'MEG0433' 'MEG0431' + 'MEG0443' 'MEG0442' 'MEG0441' + 'MEG0513' 'MEG0512' 'MEG0511' + 'MEG0523' 'MEG0522' 'MEG0521' + 'MEG0532' 'MEG0533' 'MEG0531' + 'MEG0542' 'MEG0543' 'MEG0541' + 'MEG0613' 'MEG0612' 'MEG0611' + 'MEG0622' 'MEG0623' 'MEG0621' + 'MEG0633' 'MEG0632' 'MEG0631' + 'MEG0642' 'MEG0643' 'MEG0641' + 'MEG0713' 'MEG0712' 'MEG0711' + 'MEG0723' 'MEG0722' 'MEG0721' + 'MEG0733' 'MEG0732' 'MEG0731' + 'MEG0743' 'MEG0742' 'MEG0741' + 'MEG0813' 'MEG0812' 'MEG0811' + 'MEG0822' 'MEG0823' 'MEG0821' + 'MEG0913' 'MEG0912' 'MEG0911' + 'MEG0923' 'MEG0922' 'MEG0921' + 'MEG0932' 'MEG0933' 'MEG0931' + 'MEG0942' 'MEG0943' 'MEG0941' + 'MEG1013' 'MEG1012' 'MEG1011' + 'MEG1023' 'MEG1022' 'MEG1021' + 'MEG1032' 'MEG1033' 'MEG1031' + 'MEG1043' 'MEG1042' 'MEG1041' + 'MEG1112' 'MEG1113' 'MEG1111' + 'MEG1123' 'MEG1122' 'MEG1121' + 'MEG1133' 'MEG1132' 'MEG1131' + 'MEG1142' 'MEG1143' 'MEG1141' + 'MEG1213' 'MEG1212' 'MEG1211' + 'MEG1223' 'MEG1222' 'MEG1221' + 'MEG1232' 'MEG1233' 'MEG1231' + 'MEG1243' 'MEG1242' 'MEG1241' + 'MEG1312' 'MEG1313' 'MEG1311' + 'MEG1323' 'MEG1322' 'MEG1321' + 'MEG1333' 'MEG1332' 'MEG1331' + 'MEG1342' 'MEG1343' 'MEG1341' + 'MEG1412' 'MEG1413' 'MEG1411' + 'MEG1423' 'MEG1422' 'MEG1421' + 'MEG1433' 'MEG1432' 'MEG1431' + 'MEG1442' 'MEG1443' 'MEG1441' + 'MEG1512' 'MEG1513' 'MEG1511' + 'MEG1522' 'MEG1523' 'MEG1521' + 'MEG1533' 'MEG1532' 'MEG1531' + 'MEG1543' 'MEG1542' 'MEG1541' + 'MEG1613' 'MEG1612' 'MEG1611' + 'MEG1622' 'MEG1623' 'MEG1621' + 'MEG1632' 'MEG1633' 'MEG1631' + 'MEG1643' 'MEG1642' 'MEG1641' + 'MEG1713' 'MEG1712' 'MEG1711' + 'MEG1722' 'MEG1723' 'MEG1721' + 'MEG1732' 'MEG1733' 'MEG1731' + 'MEG1743' 'MEG1742' 'MEG1741' + 'MEG1813' 'MEG1812' 'MEG1811' + 'MEG1822' 'MEG1823' 'MEG1821' + 'MEG1832' 'MEG1833' 'MEG1831' + 'MEG1843' 'MEG1842' 'MEG1841' + 'MEG1912' 'MEG1913' 'MEG1911' + 'MEG1923' 'MEG1922' 'MEG1921' + 'MEG1932' 'MEG1933' 'MEG1931' + 'MEG1943' 'MEG1942' 'MEG1941' + 'MEG2013' 'MEG2012' 'MEG2011' + 'MEG2023' 'MEG2022' 'MEG2021' + 'MEG2032' 'MEG2033' 'MEG2031' + 'MEG2042' 'MEG2043' 'MEG2041' + 'MEG2113' 'MEG2112' 'MEG2111' + 'MEG2122' 'MEG2123' 'MEG2121' + 'MEG2133' 'MEG2132' 'MEG2131' + 'MEG2143' 'MEG2142' 'MEG2141' + 'MEG2212' 'MEG2213' 'MEG2211' + 'MEG2223' 'MEG2222' 'MEG2221' + 'MEG2233' 'MEG2232' 'MEG2231' + 'MEG2242' 'MEG2243' 'MEG2241' + 'MEG2312' 'MEG2313' 'MEG2311' + 'MEG2323' 'MEG2322' 'MEG2321' + 'MEG2332' 'MEG2333' 'MEG2331' + 'MEG2343' 'MEG2342' 'MEG2341' + 'MEG2412' 'MEG2413' 'MEG2411' + 'MEG2423' 'MEG2422' 'MEG2421' + 'MEG2433' 'MEG2432' 'MEG2431' + 'MEG2442' 'MEG2443' 'MEG2441' + 'MEG2512' 'MEG2513' 'MEG2511' + 'MEG2522' 'MEG2523' 'MEG2521' + 'MEG2533' 'MEG2532' 'MEG2531' + 'MEG2543' 'MEG2542' 'MEG2541' + 'MEG2612' 'MEG2613' 'MEG2611' + 'MEG2623' 'MEG2622' 'MEG2621' + 'MEG2633' 'MEG2632' 'MEG2631' + 'MEG2642' 'MEG2643' 'MEG2641' + }; end % if isneuromag if iseeg || isext - eeg1020 = { - 'Fp1' - 'Fpz' - 'Fp2' - 'F7' - 'F3' - 'Fz' - 'F4' - 'F8' - 'T7' - 'C3' - 'Cz' - 'C4' - 'T8' - 'P7' - 'P3' - 'Pz' - 'P4' - 'P8' - 'O1' - 'Oz' - 'O2'}; + eeg1020 = { + 'Fp1' + 'Fpz' + 'Fp2' + 'F7' + 'F3' + 'Fz' + 'F4' + 'F8' + 'T7' + 'C3' + 'Cz' + 'C4' + 'T8' + 'P7' + 'P3' + 'Pz' + 'P4' + 'P8' + 'O1' + 'Oz' + 'O2'}; - eeg1010 = { - 'Fp1' - 'Fpz' - 'Fp2' - 'AF9' - 'AF7' - 'AF5' - 'AF3' - 'AF1' - 'AFz' - 'AF2' - 'AF4' - 'AF6' - 'AF8' - 'AF10' - 'F9' - 'F7' - 'F5' - 'F3' - 'F1' - 'Fz' - 'F2' - 'F4' - 'F6' - 'F8' - 'F10' - 'FT9' - 'FT7' - 'FC5' - 'FC3' - 'FC1' - 'FCz' - 'FC2' - 'FC4' - 'FC6' - 'FT8' - 'FT10' - 'T9' - 'T7' - 'C5' - 'C3' - 'C1' - 'Cz' - 'C2' - 'C4' - 'C6' - 'T8' - 'T10' - 'TP9' - 'TP7' - 'CP5' - 'CP3' - 'CP1' - 'CPz' - 'CP2' - 'CP4' - 'CP6' - 'TP8' - 'TP10' - 'P9' - 'P7' - 'P5' - 'P3' - 'P1' - 'Pz' - 'P2' - 'P4' - 'P6' - 'P8' - 'P10' - 'PO9' - 'PO7' - 'PO5' - 'PO3' - 'PO1' - 'POz' - 'PO2' - 'PO4' - 'PO6' - 'PO8' - 'PO10' - 'O1' - 'Oz' - 'O2' - 'I1' - 'Iz' - 'I2' - }; + eeg1010 = { + 'Fp1' + 'Fpz' + 'Fp2' + 'AF9' + 'AF7' + 'AF5' + 'AF3' + 'AF1' + 'AFz' + 'AF2' + 'AF4' + 'AF6' + 'AF8' + 'AF10' + 'F9' + 'F7' + 'F5' + 'F3' + 'F1' + 'Fz' + 'F2' + 'F4' + 'F6' + 'F8' + 'F10' + 'FT9' + 'FT7' + 'FC5' + 'FC3' + 'FC1' + 'FCz' + 'FC2' + 'FC4' + 'FC6' + 'FT8' + 'FT10' + 'T9' + 'T7' + 'C5' + 'C3' + 'C1' + 'Cz' + 'C2' + 'C4' + 'C6' + 'T8' + 'T10' + 'TP9' + 'TP7' + 'CP5' + 'CP3' + 'CP1' + 'CPz' + 'CP2' + 'CP4' + 'CP6' + 'TP8' + 'TP10' + 'P9' + 'P7' + 'P5' + 'P3' + 'P1' + 'Pz' + 'P2' + 'P4' + 'P6' + 'P8' + 'P10' + 'PO9' + 'PO7' + 'PO5' + 'PO3' + 'PO1' + 'POz' + 'PO2' + 'PO4' + 'PO6' + 'PO8' + 'PO10' + 'O1' + 'Oz' + 'O2' + 'I1' + 'Iz' + 'I2' + }; - eeg1005 = { - 'Fp1' - 'Fpz' - 'Fp2' - 'AF9' - 'AF7' - 'AF5' - 'AF3' - 'AF1' - 'AFz' - 'AF2' - 'AF4' - 'AF6' - 'AF8' - 'AF10' - 'F9' - 'F7' - 'F5' - 'F3' - 'F1' - 'Fz' - 'F2' - 'F4' - 'F6' - 'F8' - 'F10' - 'FT9' - 'FT7' - 'FC5' - 'FC3' - 'FC1' - 'FCz' - 'FC2' - 'FC4' - 'FC6' - 'FT8' - 'FT10' - 'T9' - 'T7' - 'C5' - 'C3' - 'C1' - 'Cz' - 'C2' - 'C4' - 'C6' - 'T8' - 'T10' - 'TP9' - 'TP7' - 'CP5' - 'CP3' - 'CP1' - 'CPz' - 'CP2' - 'CP4' - 'CP6' - 'TP8' - 'TP10' - 'P9' - 'P7' - 'P5' - 'P3' - 'P1' - 'Pz' - 'P2' - 'P4' - 'P6' - 'P8' - 'P10' - 'PO9' - 'PO7' - 'PO5' - 'PO3' - 'PO1' - 'POz' - 'PO2' - 'PO4' - 'PO6' - 'PO8' - 'PO10' - 'O1' - 'Oz' - 'O2' - 'I1' - 'Iz' - 'I2' - 'AFp9h' - 'AFp7h' - 'AFp5h' - 'AFp3h' - 'AFp1h' - 'AFp2h' - 'AFp4h' - 'AFp6h' - 'AFp8h' - 'AFp10h' - 'AFF9h' - 'AFF7h' - 'AFF5h' - 'AFF3h' - 'AFF1h' - 'AFF2h' - 'AFF4h' - 'AFF6h' - 'AFF8h' - 'AFF10h' - 'FFT9h' - 'FFT7h' - 'FFC5h' - 'FFC3h' - 'FFC1h' - 'FFC2h' - 'FFC4h' - 'FFC6h' - 'FFT8h' - 'FFT10h' - 'FTT9h' - 'FTT7h' - 'FCC5h' - 'FCC3h' - 'FCC1h' - 'FCC2h' - 'FCC4h' - 'FCC6h' - 'FTT8h' - 'FTT10h' - 'TTP9h' - 'TTP7h' - 'CCP5h' - 'CCP3h' - 'CCP1h' - 'CCP2h' - 'CCP4h' - 'CCP6h' - 'TTP8h' - 'TTP10h' - 'TPP9h' - 'TPP7h' - 'CPP5h' - 'CPP3h' - 'CPP1h' - 'CPP2h' - 'CPP4h' - 'CPP6h' - 'TPP8h' - 'TPP10h' - 'PPO9h' - 'PPO7h' - 'PPO5h' - 'PPO3h' - 'PPO1h' - 'PPO2h' - 'PPO4h' - 'PPO6h' - 'PPO8h' - 'PPO10h' - 'POO9h' - 'POO7h' - 'POO5h' - 'POO3h' - 'POO1h' - 'POO2h' - 'POO4h' - 'POO6h' - 'POO8h' - 'POO10h' - 'OI1h' - 'OI2h' - 'Fp1h' - 'Fp2h' - 'AF9h' - 'AF7h' - 'AF5h' - 'AF3h' - 'AF1h' - 'AF2h' - 'AF4h' - 'AF6h' - 'AF8h' - 'AF10h' - 'F9h' - 'F7h' - 'F5h' - 'F3h' - 'F1h' - 'F2h' - 'F4h' - 'F6h' - 'F8h' - 'F10h' - 'FT9h' - 'FT7h' - 'FC5h' - 'FC3h' - 'FC1h' - 'FC2h' - 'FC4h' - 'FC6h' - 'FT8h' - 'FT10h' - 'T9h' - 'T7h' - 'C5h' - 'C3h' - 'C1h' - 'C2h' - 'C4h' - 'C6h' - 'T8h' - 'T10h' - 'TP9h' - 'TP7h' - 'CP5h' - 'CP3h' - 'CP1h' - 'CP2h' - 'CP4h' - 'CP6h' - 'TP8h' - 'TP10h' - 'P9h' - 'P7h' - 'P5h' - 'P3h' - 'P1h' - 'P2h' - 'P4h' - 'P6h' - 'P8h' - 'P10h' - 'PO9h' - 'PO7h' - 'PO5h' - 'PO3h' - 'PO1h' - 'PO2h' - 'PO4h' - 'PO6h' - 'PO8h' - 'PO10h' - 'O1h' - 'O2h' - 'I1h' - 'I2h' - 'AFp9' - 'AFp7' - 'AFp5' - 'AFp3' - 'AFp1' - 'AFpz' - 'AFp2' - 'AFp4' - 'AFp6' - 'AFp8' - 'AFp10' - 'AFF9' - 'AFF7' - 'AFF5' - 'AFF3' - 'AFF1' - 'AFFz' - 'AFF2' - 'AFF4' - 'AFF6' - 'AFF8' - 'AFF10' - 'FFT9' - 'FFT7' - 'FFC5' - 'FFC3' - 'FFC1' - 'FFCz' - 'FFC2' - 'FFC4' - 'FFC6' - 'FFT8' - 'FFT10' - 'FTT9' - 'FTT7' - 'FCC5' - 'FCC3' - 'FCC1' - 'FCCz' - 'FCC2' - 'FCC4' - 'FCC6' - 'FTT8' - 'FTT10' - 'TTP9' - 'TTP7' - 'CCP5' - 'CCP3' - 'CCP1' - 'CCPz' - 'CCP2' - 'CCP4' - 'CCP6' - 'TTP8' - 'TTP10' - 'TPP9' - 'TPP7' - 'CPP5' - 'CPP3' - 'CPP1' - 'CPPz' - 'CPP2' - 'CPP4' - 'CPP6' - 'TPP8' - 'TPP10' - 'PPO9' - 'PPO7' - 'PPO5' - 'PPO3' - 'PPO1' - 'PPOz' - 'PPO2' - 'PPO4' - 'PPO6' - 'PPO8' - 'PPO10' - 'POO9' - 'POO7' - 'POO5' - 'POO3' - 'POO1' - 'POOz' - 'POO2' - 'POO4' - 'POO6' - 'POO8' - 'POO10' - 'OI1' - 'OIz' - 'OI2' - }; + eeg1005 = { + 'Fp1' + 'Fpz' + 'Fp2' + 'AF9' + 'AF7' + 'AF5' + 'AF3' + 'AF1' + 'AFz' + 'AF2' + 'AF4' + 'AF6' + 'AF8' + 'AF10' + 'F9' + 'F7' + 'F5' + 'F3' + 'F1' + 'Fz' + 'F2' + 'F4' + 'F6' + 'F8' + 'F10' + 'FT9' + 'FT7' + 'FC5' + 'FC3' + 'FC1' + 'FCz' + 'FC2' + 'FC4' + 'FC6' + 'FT8' + 'FT10' + 'T9' + 'T7' + 'C5' + 'C3' + 'C1' + 'Cz' + 'C2' + 'C4' + 'C6' + 'T8' + 'T10' + 'TP9' + 'TP7' + 'CP5' + 'CP3' + 'CP1' + 'CPz' + 'CP2' + 'CP4' + 'CP6' + 'TP8' + 'TP10' + 'P9' + 'P7' + 'P5' + 'P3' + 'P1' + 'Pz' + 'P2' + 'P4' + 'P6' + 'P8' + 'P10' + 'PO9' + 'PO7' + 'PO5' + 'PO3' + 'PO1' + 'POz' + 'PO2' + 'PO4' + 'PO6' + 'PO8' + 'PO10' + 'O1' + 'Oz' + 'O2' + 'I1' + 'Iz' + 'I2' + 'AFp9h' + 'AFp7h' + 'AFp5h' + 'AFp3h' + 'AFp1h' + 'AFp2h' + 'AFp4h' + 'AFp6h' + 'AFp8h' + 'AFp10h' + 'AFF9h' + 'AFF7h' + 'AFF5h' + 'AFF3h' + 'AFF1h' + 'AFF2h' + 'AFF4h' + 'AFF6h' + 'AFF8h' + 'AFF10h' + 'FFT9h' + 'FFT7h' + 'FFC5h' + 'FFC3h' + 'FFC1h' + 'FFC2h' + 'FFC4h' + 'FFC6h' + 'FFT8h' + 'FFT10h' + 'FTT9h' + 'FTT7h' + 'FCC5h' + 'FCC3h' + 'FCC1h' + 'FCC2h' + 'FCC4h' + 'FCC6h' + 'FTT8h' + 'FTT10h' + 'TTP9h' + 'TTP7h' + 'CCP5h' + 'CCP3h' + 'CCP1h' + 'CCP2h' + 'CCP4h' + 'CCP6h' + 'TTP8h' + 'TTP10h' + 'TPP9h' + 'TPP7h' + 'CPP5h' + 'CPP3h' + 'CPP1h' + 'CPP2h' + 'CPP4h' + 'CPP6h' + 'TPP8h' + 'TPP10h' + 'PPO9h' + 'PPO7h' + 'PPO5h' + 'PPO3h' + 'PPO1h' + 'PPO2h' + 'PPO4h' + 'PPO6h' + 'PPO8h' + 'PPO10h' + 'POO9h' + 'POO7h' + 'POO5h' + 'POO3h' + 'POO1h' + 'POO2h' + 'POO4h' + 'POO6h' + 'POO8h' + 'POO10h' + 'OI1h' + 'OI2h' + 'Fp1h' + 'Fp2h' + 'AF9h' + 'AF7h' + 'AF5h' + 'AF3h' + 'AF1h' + 'AF2h' + 'AF4h' + 'AF6h' + 'AF8h' + 'AF10h' + 'F9h' + 'F7h' + 'F5h' + 'F3h' + 'F1h' + 'F2h' + 'F4h' + 'F6h' + 'F8h' + 'F10h' + 'FT9h' + 'FT7h' + 'FC5h' + 'FC3h' + 'FC1h' + 'FC2h' + 'FC4h' + 'FC6h' + 'FT8h' + 'FT10h' + 'T9h' + 'T7h' + 'C5h' + 'C3h' + 'C1h' + 'C2h' + 'C4h' + 'C6h' + 'T8h' + 'T10h' + 'TP9h' + 'TP7h' + 'CP5h' + 'CP3h' + 'CP1h' + 'CP2h' + 'CP4h' + 'CP6h' + 'TP8h' + 'TP10h' + 'P9h' + 'P7h' + 'P5h' + 'P3h' + 'P1h' + 'P2h' + 'P4h' + 'P6h' + 'P8h' + 'P10h' + 'PO9h' + 'PO7h' + 'PO5h' + 'PO3h' + 'PO1h' + 'PO2h' + 'PO4h' + 'PO6h' + 'PO8h' + 'PO10h' + 'O1h' + 'O2h' + 'I1h' + 'I2h' + 'AFp9' + 'AFp7' + 'AFp5' + 'AFp3' + 'AFp1' + 'AFpz' + 'AFp2' + 'AFp4' + 'AFp6' + 'AFp8' + 'AFp10' + 'AFF9' + 'AFF7' + 'AFF5' + 'AFF3' + 'AFF1' + 'AFFz' + 'AFF2' + 'AFF4' + 'AFF6' + 'AFF8' + 'AFF10' + 'FFT9' + 'FFT7' + 'FFC5' + 'FFC3' + 'FFC1' + 'FFCz' + 'FFC2' + 'FFC4' + 'FFC6' + 'FFT8' + 'FFT10' + 'FTT9' + 'FTT7' + 'FCC5' + 'FCC3' + 'FCC1' + 'FCCz' + 'FCC2' + 'FCC4' + 'FCC6' + 'FTT8' + 'FTT10' + 'TTP9' + 'TTP7' + 'CCP5' + 'CCP3' + 'CCP1' + 'CCPz' + 'CCP2' + 'CCP4' + 'CCP6' + 'TTP8' + 'TTP10' + 'TPP9' + 'TPP7' + 'CPP5' + 'CPP3' + 'CPP1' + 'CPPz' + 'CPP2' + 'CPP4' + 'CPP6' + 'TPP8' + 'TPP10' + 'PPO9' + 'PPO7' + 'PPO5' + 'PPO3' + 'PPO1' + 'PPOz' + 'PPO2' + 'PPO4' + 'PPO6' + 'PPO8' + 'PPO10' + 'POO9' + 'POO7' + 'POO5' + 'POO3' + 'POO1' + 'POOz' + 'POO2' + 'POO4' + 'POO6' + 'POO8' + 'POO10' + 'OI1' + 'OIz' + 'OI2' + }; - % Add also alternative labels that are used in some systems - ext1020 = cat(1, eeg1005, {'A1' 'A2' 'M1' 'M2' 'T3' 'T4' 'T5' 'T6'}'); + % Add also alternative labels that are used in some systems + ext1020 = cat(1, eeg1005, {'A1' 'A2' 'M1' 'M2' 'T3' 'T4' 'T5' 'T6'}'); - % This is to account for all variants of case in 1020 systems - ext1020 = unique(cat(1, ext1020, upper(ext1020), lower(ext1020))); + % This is to account for all variants of case in 1020 systems + ext1020 = unique(cat(1, ext1020, upper(ext1020), lower(ext1020))); end % if iseeg || isext if isbiosemi - biosemi128 = { - 'A1' - 'A2' - 'A3' - 'A4' - 'A5' - 'A6' - 'A7' - 'A8' - 'A9' - 'A10' - 'A11' - 'A12' - 'A13' - 'A14' - 'A15' - 'A16' - 'A17' - 'A18' - 'A19' - 'A20' - 'A21' - 'A22' - 'A23' - 'A24' - 'A25' - 'A26' - 'A27' - 'A28' - 'A29' - 'A30' - 'A31' - 'A32' - 'B1' - 'B2' - 'B3' - 'B4' - 'B5' - 'B6' - 'B7' - 'B8' - 'B9' - 'B10' - 'B11' - 'B12' - 'B13' - 'B14' - 'B15' - 'B16' - 'B17' - 'B18' - 'B19' - 'B20' - 'B21' - 'B22' - 'B23' - 'B24' - 'B25' - 'B26' - 'B27' - 'B28' - 'B29' - 'B30' - 'B31' - 'B32' - 'C1' - 'C2' - 'C3' - 'C4' - 'C5' - 'C6' - 'C7' - 'C8' - 'C9' - 'C10' - 'C11' - 'C12' - 'C13' - 'C14' - 'C15' - 'C16' - 'C17' - 'C18' - 'C19' - 'C20' - 'C21' - 'C22' - 'C23' - 'C24' - 'C25' - 'C26' - 'C27' - 'C28' - 'C29' - 'C30' - 'C31' - 'C32' - 'D1' - 'D2' - 'D3' - 'D4' - 'D5' - 'D6' - 'D7' - 'D8' - 'D9' - 'D10' - 'D11' - 'D12' - 'D13' - 'D14' - 'D15' - 'D16' - 'D17' - 'D18' - 'D19' - 'D20' - 'D21' - 'D22' - 'D23' - 'D24' - 'D25' - 'D26' - 'D27' - 'D28' - 'D29' - 'D30' - 'D31' - 'D32' - }; + biosemi64 = { + 'A1' + 'A2' + 'A3' + 'A4' + 'A5' + 'A6' + 'A7' + 'A8' + 'A9' + 'A10' + 'A11' + 'A12' + 'A13' + 'A14' + 'A15' + 'A16' + 'A17' + 'A18' + 'A19' + 'A20' + 'A21' + 'A22' + 'A23' + 'A24' + 'A25' + 'A26' + 'A27' + 'A28' + 'A29' + 'A30' + 'A31' + 'A32' + 'B1' + 'B2' + 'B3' + 'B4' + 'B5' + 'B6' + 'B7' + 'B8' + 'B9' + 'B10' + 'B11' + 'B12' + 'B13' + 'B14' + 'B15' + 'B16' + 'B17' + 'B18' + 'B19' + 'B20' + 'B21' + 'B22' + 'B23' + 'B24' + 'B25' + 'B26' + 'B27' + 'B28' + 'B29' + 'B30' + 'B31' + 'B32' + }; + + biosemi128 = { + 'A1' + 'A2' + 'A3' + 'A4' + 'A5' + 'A6' + 'A7' + 'A8' + 'A9' + 'A10' + 'A11' + 'A12' + 'A13' + 'A14' + 'A15' + 'A16' + 'A17' + 'A18' + 'A19' + 'A20' + 'A21' + 'A22' + 'A23' + 'A24' + 'A25' + 'A26' + 'A27' + 'A28' + 'A29' + 'A30' + 'A31' + 'A32' + 'B1' + 'B2' + 'B3' + 'B4' + 'B5' + 'B6' + 'B7' + 'B8' + 'B9' + 'B10' + 'B11' + 'B12' + 'B13' + 'B14' + 'B15' + 'B16' + 'B17' + 'B18' + 'B19' + 'B20' + 'B21' + 'B22' + 'B23' + 'B24' + 'B25' + 'B26' + 'B27' + 'B28' + 'B29' + 'B30' + 'B31' + 'B32' + 'C1' + 'C2' + 'C3' + 'C4' + 'C5' + 'C6' + 'C7' + 'C8' + 'C9' + 'C10' + 'C11' + 'C12' + 'C13' + 'C14' + 'C15' + 'C16' + 'C17' + 'C18' + 'C19' + 'C20' + 'C21' + 'C22' + 'C23' + 'C24' + 'C25' + 'C26' + 'C27' + 'C28' + 'C29' + 'C30' + 'C31' + 'C32' + 'D1' + 'D2' + 'D3' + 'D4' + 'D5' + 'D6' + 'D7' + 'D8' + 'D9' + 'D10' + 'D11' + 'D12' + 'D13' + 'D14' + 'D15' + 'D16' + 'D17' + 'D18' + 'D19' + 'D20' + 'D21' + 'D22' + 'D23' + 'D24' + 'D25' + 'D26' + 'D27' + 'D28' + 'D29' + 'D30' + 'D31' + 'D32' + }; + + biosemi256 = { + 'A1' + 'A2' + 'A3' + 'A4' + 'A5' + 'A6' + 'A7' + 'A8' + 'A9' + 'A10' + 'A11' + 'A12' + 'A13' + 'A14' + 'A15' + 'A16' + 'A17' + 'A18' + 'A19' + 'A20' + 'A21' + 'A22' + 'A23' + 'A24' + 'A25' + 'A26' + 'A27' + 'A28' + 'A29' + 'A30' + 'A31' + 'A32' + 'B1' + 'B2' + 'B3' + 'B4' + 'B5' + 'B6' + 'B7' + 'B8' + 'B9' + 'B10' + 'B11' + 'B12' + 'B13' + 'B14' + 'B15' + 'B16' + 'B17' + 'B18' + 'B19' + 'B20' + 'B21' + 'B22' + 'B23' + 'B24' + 'B25' + 'B26' + 'B27' + 'B28' + 'B29' + 'B30' + 'B31' + 'B32' + 'C1' + 'C2' + 'C3' + 'C4' + 'C5' + 'C6' + 'C7' + 'C8' + 'C9' + 'C10' + 'C11' + 'C12' + 'C13' + 'C14' + 'C15' + 'C16' + 'C17' + 'C18' + 'C19' + 'C20' + 'C21' + 'C22' + 'C23' + 'C24' + 'C25' + 'C26' + 'C27' + 'C28' + 'C29' + 'C30' + 'C31' + 'C32' + 'D1' + 'D2' + 'D3' + 'D4' + 'D5' + 'D6' + 'D7' + 'D8' + 'D9' + 'D10' + 'D11' + 'D12' + 'D13' + 'D14' + 'D15' + 'D16' + 'D17' + 'D18' + 'D19' + 'D20' + 'D21' + 'D22' + 'D23' + 'D24' + 'D25' + 'D26' + 'D27' + 'D28' + 'D29' + 'D30' + 'D31' + 'D32' + 'E1' + 'E2' + 'E3' + 'E4' + 'E5' + 'E6' + 'E7' + 'E8' + 'E9' + 'E10' + 'E11' + 'E12' + 'E13' + 'E14' + 'E15' + 'E16' + 'E17' + 'E18' + 'E19' + 'E20' + 'E21' + 'E22' + 'E23' + 'E24' + 'E25' + 'E26' + 'E27' + 'E28' + 'E29' + 'E30' + 'E31' + 'E32' + 'F1' + 'F2' + 'F3' + 'F4' + 'F5' + 'F6' + 'F7' + 'F8' + 'F9' + 'F10' + 'F11' + 'F12' + 'F13' + 'F14' + 'F15' + 'F16' + 'F17' + 'F18' + 'F19' + 'F20' + 'F21' + 'F22' + 'F23' + 'F24' + 'F25' + 'F26' + 'F27' + 'F28' + 'F29' + 'F30' + 'F31' + 'F32' + 'G1' + 'G2' + 'G3' + 'G4' + 'G5' + 'G6' + 'G7' + 'G8' + 'G9' + 'G10' + 'G11' + 'G12' + 'G13' + 'G14' + 'G15' + 'G16' + 'G17' + 'G18' + 'G19' + 'G20' + 'G21' + 'G22' + 'G23' + 'G24' + 'G25' + 'G26' + 'G27' + 'G28' + 'G29' + 'G30' + 'G31' + 'G32' + 'H1' + 'H2' + 'H3' + 'H4' + 'H5' + 'H6' + 'H7' + 'H8' + 'H9' + 'H10' + 'H11' + 'H12' + 'H13' + 'H14' + 'H15' + 'H16' + 'H17' + 'H18' + 'H19' + 'H20' + 'H21' + 'H22' + 'H23' + 'H24' + 'H25' + 'H26' + 'H27' + 'H28' + 'H29' + 'H30' + 'H31' + 'H32' + }; - biosemi256 = { - 'A1' - 'A2' - 'A3' - 'A4' - 'A5' - 'A6' - 'A7' - 'A8' - 'A9' - 'A10' - 'A11' - 'A12' - 'A13' - 'A14' - 'A15' - 'A16' - 'A17' - 'A18' - 'A19' - 'A20' - 'A21' - 'A22' - 'A23' - 'A24' - 'A25' - 'A26' - 'A27' - 'A28' - 'A29' - 'A30' - 'A31' - 'A32' - 'B1' - 'B2' - 'B3' - 'B4' - 'B5' - 'B6' - 'B7' - 'B8' - 'B9' - 'B10' - 'B11' - 'B12' - 'B13' - 'B14' - 'B15' - 'B16' - 'B17' - 'B18' - 'B19' - 'B20' - 'B21' - 'B22' - 'B23' - 'B24' - 'B25' - 'B26' - 'B27' - 'B28' - 'B29' - 'B30' - 'B31' - 'B32' - 'C1' - 'C2' - 'C3' - 'C4' - 'C5' - 'C6' - 'C7' - 'C8' - 'C9' - 'C10' - 'C11' - 'C12' - 'C13' - 'C14' - 'C15' - 'C16' - 'C17' - 'C18' - 'C19' - 'C20' - 'C21' - 'C22' - 'C23' - 'C24' - 'C25' - 'C26' - 'C27' - 'C28' - 'C29' - 'C30' - 'C31' - 'C32' - 'D1' - 'D2' - 'D3' - 'D4' - 'D5' - 'D6' - 'D7' - 'D8' - 'D9' - 'D10' - 'D11' - 'D12' - 'D13' - 'D14' - 'D15' - 'D16' - 'D17' - 'D18' - 'D19' - 'D20' - 'D21' - 'D22' - 'D23' - 'D24' - 'D25' - 'D26' - 'D27' - 'D28' - 'D29' - 'D30' - 'D31' - 'D32' - 'E1' - 'E2' - 'E3' - 'E4' - 'E5' - 'E6' - 'E7' - 'E8' - 'E9' - 'E10' - 'E11' - 'E12' - 'E13' - 'E14' - 'E15' - 'E16' - 'E17' - 'E18' - 'E19' - 'E20' - 'E21' - 'E22' - 'E23' - 'E24' - 'E25' - 'E26' - 'E27' - 'E28' - 'E29' - 'E30' - 'E31' - 'E32' - 'F1' - 'F2' - 'F3' - 'F4' - 'F5' - 'F6' - 'F7' - 'F8' - 'F9' - 'F10' - 'F11' - 'F12' - 'F13' - 'F14' - 'F15' - 'F16' - 'F17' - 'F18' - 'F19' - 'F20' - 'F21' - 'F22' - 'F23' - 'F24' - 'F25' - 'F26' - 'F27' - 'F28' - 'F29' - 'F30' - 'F31' - 'F32' - 'G1' - 'G2' - 'G3' - 'G4' - 'G5' - 'G6' - 'G7' - 'G8' - 'G9' - 'G10' - 'G11' - 'G12' - 'G13' - 'G14' - 'G15' - 'G16' - 'G17' - 'G18' - 'G19' - 'G20' - 'G21' - 'G22' - 'G23' - 'G24' - 'G25' - 'G26' - 'G27' - 'G28' - 'G29' - 'G30' - 'G31' - 'G32' - 'H1' - 'H2' - 'H3' - 'H4' - 'H5' - 'H6' - 'H7' - 'H8' - 'H9' - 'H10' - 'H11' - 'H12' - 'H13' - 'H14' - 'H15' - 'H16' - 'H17' - 'H18' - 'H19' - 'H20' - 'H21' - 'H22' - 'H23' - 'H24' - 'H25' - 'H26' - 'H27' - 'H28' - 'H29' - 'H30' - 'H31' - 'H32' - }; end % if isbiosemi if isegi - egi256 = cell(256, 1); - for i = 1:256 - egi256{i} = sprintf('e%d', i); - end + egi256 = cell(256, 1); + for i = 1:256 + egi256{i} = sprintf('e%d', i); + end - % the others are subsets - egi32 = egi256(1:32); - egi64 = egi256(1:64); - egi128 = egi256(1:128); + % the others are subsets + egi32 = egi256(1:32); + egi64 = egi256(1:64); + egi128 = egi256(1:128); end % if isegi % search for the requested definition of channel labels if exist(type, 'var') - label = eval(type); - label = label(:); + label = eval(type); + label = label(:); else - error('the requested sensor type is not supported'); + error('the requested sensor type is not supported'); +end + +% remember the current input and output arguments, so that they can be +% reused on a subsequent call in case the same input argument is given +current_argout = {label}; +if isempty(previous_argin) + previous_argin = current_argin; + previous_argout = current_argout; end diff --git a/external/forwinv/private/senstype.m b/external/forwinv/private/senstype.m index a00eaba..df3a355 100644 --- a/external/forwinv/private/senstype.m +++ b/external/forwinv/private/senstype.m @@ -12,6 +12,7 @@ % The output type can be any of the following % 'electrode' % 'magnetometer' +% 'biosemi64' % 'biosemi128' % 'biosemi256' % 'bti148' @@ -58,6 +59,21 @@ % Copyright (C) 2007-2008, Robert Oostenveld % % $Log: senstype.m,v $ +% Revision 1.19 2009/07/29 08:04:38 roboos +% cleaned up the code, no functional change +% +% Revision 1.18 2009/07/28 11:16:23 roboos +% removed keyboard statement, thanks to Jurrian +% +% Revision 1.17 2009/07/28 10:17:41 roboos +% make distinction between only label, label+pnt and label+pnt+ori +% +% Revision 1.16 2009/07/27 16:04:51 roboos +% improved distinction between eeg and meg, fixes problem with biosemi-eeg being detected as "ctf" due to reference channel match +% +% Revision 1.15 2009/06/19 16:51:50 vlalit +% Added biosemi64 system of Diane Whitmer, I don't know how generic it is. +% % Revision 1.14 2009/05/07 13:34:09 roboos % added ctf64 % @@ -138,14 +154,20 @@ % preferably the structure specifies its own type type = sens.type; +elseif issubfield(sens, 'orig.FileHeader') && issubfield(sens, 'orig.VarHeader') + % this is a complete header that was read from a Plexon *.nex file using read_plexon_nex + type = 'plexon'; + else % start with unknown, then try to determine the proper type by looking at the labels type = 'unknown'; - if isfield(sens, 'label') + if isfield(sens, 'label') && isfield(sens, 'pnt') && isfield(sens, 'ori') % probably this is MEG, determine the type of magnetometer/gradiometer system % note that the order here is important: first check whether it matches a 275 channel system, then a 151 channel system, since the 151 channels are a subset of the 275 - if (mean(ismember(senslabel('ctf275'), sens.label)) > 0.8) || (mean(ismember(senslabel('ctfheadloc'), sens.label)) > 0.8) + if (mean(ismember(senslabel('ctf275'), sens.label)) > 0.8) + type = 'ctf275'; + elseif (mean(ismember(senslabel('ctfheadloc'), sens.label)) > 0.8) % look at the head localization channels type = 'ctf275'; elseif (mean(ismember(senslabel('ctf151'), sens.label)) > 0.8) type = 'ctf151'; @@ -177,12 +199,18 @@ type = 'ctf'; % it might be 151 or 275 channels elseif isfield(sens, 'pnt') && isfield(sens, 'ori') && numel(sens.label)==numel(sens.pnt) type = 'magnetometer'; - elseif isfield(sens, 'pnt') && isfield(sens, 'ori') + else type = 'meg'; - elseif (mean(ismember(senslabel('biosemi256'), sens.label)) > 0.8) + end + + elseif isfield(sens, 'label') && isfield(sens, 'pnt') && ~isfield(sens, 'ori') + % probably this is EEG + if (mean(ismember(senslabel('biosemi256'), sens.label)) > 0.8) type = 'biosemi256'; elseif (mean(ismember(senslabel('biosemi128'), sens.label)) > 0.8) type = 'biosemi128'; + elseif (mean(ismember(senslabel('biosemi64'), sens.label)) > 0.8) + type = 'biosemi64'; elseif (mean(ismember(senslabel('egi256'), sens.label)) > 0.8) type = 'egi256'; elseif (mean(ismember(senslabel('egi128'), sens.label)) > 0.8) @@ -193,26 +221,72 @@ type = 'egi32'; elseif (sum(ismember(sens.label, senslabel('eeg1005'))) > 10) % Otherwise it's not even worth recognizing type = 'ext1020'; - elseif isfield(sens, 'label') && isfield(sens, 'pnt') && ~isfield(sens, 'ori') % looks like EEG + else type = 'electrode'; end - end + elseif isfield(sens, 'label') && ~isfield(sens, 'pnt') && ~isfield(sens, 'ori') + % look only at the channel labels + if (mean(ismember(senslabel('ctf275'), sens.label)) > 0.8) + type = 'ctf275'; + elseif (mean(ismember(senslabel('ctfheadloc'), sens.label)) > 0.8) % look at the head localization channels + type = 'ctf275'; + elseif (mean(ismember(senslabel('ctf151'), sens.label)) > 0.8) + type = 'ctf151'; + elseif (mean(ismember(senslabel('ctf64'), sens.label)) > 0.8) + type = 'ctf64'; + elseif (mean(ismember(senslabel('ctf275_planar'), sens.label)) > 0.8) + type = 'ctf275_planar'; + elseif (mean(ismember(senslabel('ctf151_planar'), sens.label)) > 0.8) + type = 'ctf151_planar'; + elseif (mean(ismember(senslabel('bti248'), sens.label)) > 0.8) + type = 'bti248'; + elseif (mean(ismember(senslabel('bti148'), sens.label)) > 0.8) + type = 'bti148'; + elseif (mean(ismember(senslabel('bti248_planar'), sens.label)) > 0.8) + type = 'bti248_planar'; + elseif (mean(ismember(senslabel('bti148_planar'), sens.label)) > 0.8) + type = 'bti148_planar'; + elseif (mean(ismember(senslabel('neuromag306'), sens.label)) > 0.8) + type = 'neuromag306'; + elseif (mean(ismember(senslabel('neuromag306alt'),sens.label)) > 0.8) % an alternative set without spaces in the name + type = 'neuromag306'; + elseif (mean(ismember(senslabel('neuromag122'), sens.label)) > 0.8) + type = 'neuromag122'; + elseif (mean(ismember(senslabel('neuromag122alt'),sens.label)) > 0.8) % an alternative set without spaces in the name + type = 'neuromag122'; + elseif (mean(ismember(senslabel('biosemi256'), sens.label)) > 0.8) + type = 'biosemi256'; + elseif (mean(ismember(senslabel('biosemi128'), sens.label)) > 0.8) + type = 'biosemi128'; + elseif (mean(ismember(senslabel('biosemi64'), sens.label)) > 0.8) + type = 'biosemi64'; + elseif (mean(ismember(senslabel('egi256'), sens.label)) > 0.8) + type = 'egi256'; + elseif (mean(ismember(senslabel('egi128'), sens.label)) > 0.8) + type = 'egi128'; + elseif (mean(ismember(senslabel('egi64'), sens.label)) > 0.8) + type = 'egi64'; + elseif (mean(ismember(senslabel('egi32'), sens.label)) > 0.8) + type = 'egi32'; + elseif (sum(ismember(sens.label, senslabel('eeg1005'))) > 10) % Otherwise it's not even worth recognizing + type = 'ext1020'; + elseif any(ismember(senslabel('btiref'), sens.label)) + type = 'bti'; % it might be 148 or 248 channels + elseif any(ismember(senslabel('ctfref'), sens.label)) + type = 'ctf'; % it might be 151 or 275 channels + end + + end % look at label, ori and/or pnt end % if isfield(sens, 'type') -% use an alternative approach if it is still unknown -if strcmp(type, 'unknown') && issubfield(sens, 'orig.FileHeader') && issubfield(sens, 'orig.VarHeader') - % this is a complete header that was read from a Plexon *.nex file using read_plexon_nex - type = 'plexon'; -end - if ~isempty(desired) % return a boolean flag switch desired case 'eeg' - type = any(strcmp(type, {'eeg' 'electrode' 'biosemi128' 'biosemi256' 'egi32' 'egi64' 'egi128' 'egi256' 'ext1020'})); + type = any(strcmp(type, {'eeg' 'electrode' 'biosemi64' 'biosemi128' 'biosemi256' 'egi32' 'egi64' 'egi128' 'egi256' 'ext1020'})); case 'biosemi' - type = any(strcmp(type, {'biosemi128' 'biosemi256'})); + type = any(strcmp(type, {'biosemi64' 'biosemi128' 'biosemi256'})); case 'egi' type = any(strcmp(type, {'egi64' 'egi128' 'egi256'})); case 'meg' @@ -233,8 +307,8 @@ type = any(strcmp(type, {'neuromag122' 'neuromag306' 'ctf151_planar' 'ctf275_planar' 'bti148_planar' 'bti248_planar' 'yokogawa160_planar'})); otherwise type = any(strcmp(type, desired)); - end -end + end % switch desired +end % detemine the correspondence to the desired type % remember the current input and output arguments, so that they can be % reused on a subsequent call in case the same input argument is given @@ -244,4 +318,4 @@ previous_argout = current_argout; end -return % voltype main() +return % senstype main() diff --git a/man/batch/batch.tex b/man/batch/batch.tex index c2f059e..8a00f68 100644 --- a/man/batch/batch.tex +++ b/man/batch/batch.tex @@ -450,6 +450,32 @@ \subsection{Command line interface} interface using \verb|spm_jobman|, which combines ``low-level'' callbacks to \verb|cfg_util|. +\subsubsection{SPM startup in command line mode} +\label{sec:batch_interface_spm_startup} + +During normal startup, SPM performs important initialisation +steps. Without initialisation, SPM and its batch system will not +function properly. Consequently, an initialisation sequence needs to +be run before any batch job can be submitted. + +MATLAB has several command line options to start without its GUI +(\verb|-nodesktop|) or even without any graphics output to a screen +(\verb|-nodisplay|). See MATLAB documentation for details. + +To run SPM in \verb|-nodisplay| mode, the file \verb|spm_defaults.m| +has to be modified. The line \verb|defaults.cmdline = 0;| must be +changed to \verb|defaults.cmdline = true;|. In command line mode, SPM +will not open any figure window except the ``Graphics'' window. + +Within MATLAB, the following commands are sufficient to set up SPM +\begin{enumerate} +\item \verb|spm('defaults', MODALITY)| where \verb|MODALITY| has to be + replaced by the desired modality (e.g. \verb|'fmri'|) +\item \verb|spm_jobman('initcfg')| +\end{enumerate} +After executing these commands, any SPM functions and batch jobs +can be run in the same MATLAB session. + \subsubsection{Complete and run a pre-specified job} \label{sec:batch_interface_cmd_cfg_serial} diff --git a/man/biblio/methods_group.bib b/man/biblio/methods_group.bib index 77d67be..dd96259 100644 --- a/man/biblio/methods_group.bib +++ b/man/biblio/methods_group.bib @@ -305,7 +305,7 @@ @Article{kiebel_spm_eeg2 @Article{kiebel_eeg3, author = sjk # and # {C. Tallon-Baudry} # and # karl, - title = {Parametric analysis of osciallatory activity as measured with {EEG/MEG}}, + title = {Parametric analysis of oscillatory activity as measured with {EEG/MEG}}, journal = hbm, year = {2005}, volume = {26}, @@ -1566,7 +1566,7 @@ @Article{multivarEEG year = {1996}, volume = {3}, pages = {167--174}, - keyword = {connectivity, EEG, MEG}, + keyword = {connectivity, multivariate, EEG, MEG}, pdf = {/spm/doc/papers/karl_multivariate.pdf} } @@ -1577,7 +1577,8 @@ @Article{friston96b year = {1996}, volume = {4}, pages = {140--151}, - keyword = {connectivity, PET} + keyword = {connectivity, multivariate, PET}, + pdf = {/spm/doc/papers/karl_multivariate_pet.pdf} } @Article{cog1, @@ -1655,14 +1656,14 @@ @Article{friston95c url = {/spm/doc/papers/SPM_3/welcome.html} } -@Article{friston95d, +@Article{friston95a, author = karl # and # cf # and # rsjf # and # rt, title = {Characterizing Dynamic Brain Responses with f{MRI}: A multivariate Approach}, journal = ni, year = {1995}, volume = {2}, pages = {166--172}, - keyword = {connectivity,fMRI}, + keyword = {connectivity,dynamical,multivariate,fMRI}, pdf = {/spm/doc/papers/karl_dyn_multivariate.pdf} } @@ -1689,16 +1690,6 @@ @Article{worsley95 pdf = {/spm/doc/papers/kjw_revisited_again.pdf} } -@Article{friston95a, - author = karl # and # cf # and # rsjf # and # rt, - title = {Characterizing dynamic brain responses with f{MRI}}, - journal = ni, - year = {1995}, - volume = {2}, - pages = {166--172}, - keyword = {fMRI,dynamical} -} - @Article{effcon_v1v2, author = karl # and # {L.G. Ungerleider} # and # pj # and # rt, title = {Characterizing modulatory interactions between {V1} and {V2} in human cortex with f{MRI}}, @@ -1900,7 +1891,7 @@ @InProceedings{ rnah_multivar pages = {468}, year = {2000}, volume = {11}, - keyword = {fMRI, event-related}, + keyword = {fMRI, event-related, multivariate}, pdf = {/spm/doc/papers/rnah_multivar.pdf} } @@ -2570,9 +2561,9 @@ @Article{vl_group title = {Electromagnetic source reconstruction for group studies}, journal = ni, year = 2008, - volume = {}, - number = {}, - pages = {}, + volume = {42}, + number = {4}, + pages = {1490--1498}, doi = {10.1016/j.neuroimage.2008.06.022}, keyword = {EEG,MEG} } @@ -2946,12 +2937,38 @@ @Article{klein_evaluation @Article{john_computanat, author = john, - title = {Computational anatomy with the SPM software }, + title = {Computational anatomy with the SPM software}, journal = {Magnetic Resonance Imaging}, year = 2009, volume = {}, number = {}, pages = {}, - doi = {10.1016/j.mri.2009.01.006 }, + doi = {10.1016/j.mri.2009.01.006}, keyword = {}, } + +@Article{rnah_fusion, + author = rnah # and # {E. Mouchlianitis} # and # karl, + title = {MEG and EEG data fusion: Simultaneous localisation of face-evoked responses}, + journal = ni, + year = 2009, + volume = {}, + number = {}, + pages = {}, + doi = {10.1016/j.neuroimage.2009.04.063}, + keyword = {EEG, MEG}, + pdf = {/spm/doc/papers/HensonEtAl_Neuroimage_09_MEEG_fusion.pdf} +} + +@Article{rnah_forward, + author = rnah # and # jm # and # cp # and # karl, + title = {Selecting forward models for MEG source-reconstruction using model-evidence }, + journal = ni, + year = 2009, + volume = {46}, + number = {1}, + pages = {168--176}, + doi = {10.1016/j.neuroimage.2009.01.062}, + keyword = {MEG}, + pdf = {/spm/doc/papers/HensonEtAl_Neuroimage_09_MEG_Forward_Models.pdf} +} diff --git a/man/example_scripts/faces_eeg_montage.m b/man/example_scripts/faces_eeg_montage.m new file mode 100644 index 0000000..232db18 --- /dev/null +++ b/man/example_scripts/faces_eeg_montage.m @@ -0,0 +1,19 @@ +D = spm_eeg_load('dspm8_faces_run1.mat'); + +montage.labelorg = D.chanlabels; + +montage.labelnew = [montage.labelorg(1:128), 'HEOG', 'VEOG']; + +tra = eye(D.nchannels); +tra(129:end, :) = []; +tra = detrend(tra, 'constant'); + +% VEOG +tra(129, [131 132]) = [1 -1]; + +% VEOG +tra(130, [135 136]) = [1 -1]; + +montage.tra = tra; + +save faces_eeg_montage.mat montage diff --git a/man/example_scripts/montage_subject1.m b/man/example_scripts/montage_subject1.m index e858613..7d8276f 100644 --- a/man/example_scripts/montage_subject1.m +++ b/man/example_scripts/montage_subject1.m @@ -2,10 +2,13 @@ montage.labelorg = D.chanlabels; -montage.labelnew = [montage.labelorg(1:128), 'HEOG', 'VEOG']; +montage.labelnew = [montage.labelorg(1:128), 'HEOG', 'VEOG']'; tra = eye(D.nchannels); -tra(131:end, :) = []; +tra(129:end, :) = []; + +% Create the average reference montage +tra = detrend(tra, 'constant'); % HEOG tra(129, 129) = 0; diff --git a/man/manual.pdf b/man/manual.pdf index 9684aac..839629f 100644 Binary files a/man/manual.pdf and b/man/manual.pdf differ diff --git a/man/manual.tex b/man/manual.tex index 80c7718..9fda1c4 100644 --- a/man/manual.tex +++ b/man/manual.tex @@ -157,7 +157,7 @@ \part{Data sets and examples} \include{pet/pet} \include{dcm/dcm} \include{mmn/mmn} -%\include{multimodal/multimodal} +\include{multimodal/multimodal} \include{dartelguide/dartelguide} %%%%%% Appendix %%%%%%% diff --git a/man/meeg/eeg_sensoranalysis.tex b/man/meeg/eeg_sensoranalysis.tex index c5a02ee..1393a3a 100644 --- a/man/meeg/eeg_sensoranalysis.tex +++ b/man/meeg/eeg_sensoranalysis.tex @@ -2,24 +2,19 @@ \chapter{Analysis in sensor space \label{Chap:eeg:sensoranalysis}} Second-level analyses for M/EEG are used to make inferences about the population from which subjects are drawn. This chapter describes how to perform second-level analyses of EEG/MEG or time-frequency (TF) data. This simply requires transforming data from \texttt{filename.mat} and \texttt{filename.dat} format to image files (NIfTI format). Once the data are in image format second-level analyses for M/EEG are procedurally identical to 2nd level analyses for fMRI. -We therefor refer the reader to the fMRI section for further details of this last step. +We therefore refer the reader to the fMRI section for further details of this last step. \\ \\ -To display the MEG/EEG/TF data the images are normalised to a standard size. Space (X,Y) dimensions are stretched to be displayed gracefully in SPM glass brain (note that it does not correspond to real \texttt{mm} coordinates). Time dimension is renormalised to be expressed as percent, from 0 to 100. - -This means that the units are in a normalised space. Hopefully we aim at improving the visualisation in a near future. -\\ -\\ -In the GUI ``other'' pull down menu, select the function \texttt{Convert to images}. This will ask you to select the \texttt{filename.mat} of the data you would like to convert to images. +In the drop down ``Other'' menu, select the function \texttt{Convert to images}. This will ask you to select the \texttt{filename.mat} of the data you would like to convert to images. \\ \\ If you supply a result of a time-frequency decomposition you will first be asked to reduce your data from a 4D data (space(X,Y), time, frequency) to either a 3D image (space(X,Y), time) or a 2D time-frequency image (time, frequency). The prompt asks you over which dimension you wish to average your data. \\ \\ -If you select ``electrodes'' you will be asked to enter a vector of channels you wish to average over. Next you will be prompted for a ``region number''. This allows you to average over different electrodes/sensors to create different analyses for more than one region. The image will be created in a new directory with the name \texttt{ROI\_TF\_trialtype\_}. The image that will be created will be a 2D image where the dimensions are time and frequency. Before proceeding to the second-level analysis these images should be smoothed. This is performed using the \textsc{smooth} function in the drop-down ``Other'' menu. Once the images have been smoothed one can proceed to second level analyses. +If you select ``channels'' you will be asked to enter a vector of channels you wish to average over. Next you will be prompted for a ``region number''. This allows you to average over different channels to create different analyses for more than one region. The image will be created in a new directory with the name \texttt{ROI\_TF\_trialtype\_}. The image that will be created will be a 2D image where the dimensions are time and frequency. Before proceeding to the second-level analysis these images should be smoothed. This is performed using the \textsc{smooth} function in the drop-down ``Other'' menu. Once the images have been smoothed one can proceed to second level analyses. \\ \\ -If you select ``frequencies'' you will be asked to specify the frequency range over which you wish to average. You will then be prompted to enter the output dimensions of the (interpolated scalp) image that will be produced. Typically we suggest using 64. You will then be asked whether you want to interpolate or remove bad channels from your images. If you chose interpolate then the image will interpolate missing channels. This is the preferred option. You will then be asked for the pixel dimensions. The default value is 3 and relates voxel-size to a mm-coordinate system used later on when displaying images in SPM. This should be changed to 1. This will create an image file with the file name appended by \texttt{F}. The image will have 3 dimensions (space(X,Y) and time). As with the TF data these images should be smoothed to accomodate spatial/temporal variability between subjects. +If you select ``frequency'' you will be asked to specify the frequency range over which you wish to average. You will then be prompted to enter the output dimensions of the (interpolated scalp) image that will be produced. Typically we suggest using 64. You will then be asked whether you want to interpolate or remove bad channels from your images. If you chose interpolate then the image will interpolate missing channels. This is the preferred option. This will create an image file with the file name appended by \texttt{F}. The image will have 3 dimensions (space(X,Y) and time). As with the TF data these images should be smoothed to accommodate spatial/temporal variability between subjects. \\ \\ -If when calling \texttt{Convert to images} you choose a non time-frequency data file, you will then be prompted to enter the output dimensions of the (interpolated scalp) image that will be produced. Typically we suggest using 64. You will then be asked whether you want to interpolate or remove bad channels from your images. If you choose interpolate then the image will interpolate missing channels. This is the preferred option. This will then create an image file in 3D where the dimensions are space (X,Y) and time. As before a separate image is created for each trial type. These images must be smoothed prior to second level analysis using the \textsc{smooth} function in the drop down ``Other'' menu. Once the images have been smoothed one can proceed to the second level analysis. +If when calling \texttt{Convert to images} you choose a non time-frequency data file, you will then be prompted to enter the output dimensions of the (interpolated scalp) image that will be produced. Typically we suggest using 64. You will then be asked whether you want to interpolate or remove bad channels from your images. If you choose interpolate then the image will interpolate missing channels. This is the preferred option. This will then create an image file in 3D where the dimensions are space (X,Y) and time. As before a separate image is created for each trial type. These images must be smoothed prior to second level analysis using the \textsc{smooth} function in the drop down ``Other'' menu. Once the images have been smoothed one can proceed to the second level analysis. diff --git a/man/mmn/3DSPM.png b/man/mmn/3DSPM.png index 09af5e1..5fdeb8d 100644 Binary files a/man/mmn/3DSPM.png and b/man/mmn/3DSPM.png differ diff --git a/man/mmn/coreg.png b/man/mmn/coreg.png index b4cc436..d120e2d 100644 Binary files a/man/mmn/coreg.png and b/man/mmn/coreg.png differ diff --git a/man/mmn/erp_c23.png b/man/mmn/erp_c23.png index 2bc41c6..be35554 100644 Binary files a/man/mmn/erp_c23.png and b/man/mmn/erp_c23.png differ diff --git a/man/mmn/forward.png b/man/mmn/forward.png index bef0047..43fc0e6 100644 Binary files a/man/mmn/forward.png and b/man/mmn/forward.png differ diff --git a/man/mmn/invert.png b/man/mmn/invert.png index 09ad288..2e10ea8 100644 Binary files a/man/mmn/invert.png and b/man/mmn/invert.png differ diff --git a/man/mmn/mesh.png b/man/mmn/mesh.png index a5bcd0c..8a29393 100644 Binary files a/man/mmn/mesh.png and b/man/mmn/mesh.png differ diff --git a/man/mmn/mmn.tex b/man/mmn/mmn.tex index 5e3a89c..3b4f4c1 100644 --- a/man/mmn/mmn.tex +++ b/man/mmn/mmn.tex @@ -19,52 +19,53 @@ \subsection{Convert} At the \matlab\ prompt type \texttt{spm eeg}, press the \textsc{Convert} button and select the \texttt{subject1.bdf} file. At the prompt ``Define settings ?'' select ``just read''. -SPM will now read the original Biosemi format file and create an SPM combatible data file, called \texttt{spm8\_subject1.mat} and \texttt{spm8\_subject1.dat} in the directory containing the original data file (\texttt{DATA\_DIR}). +SPM will now read the original Biosemi format file and create an SPM compatible data file, called \texttt{spm8\_subject1.mat} and \texttt{spm8\_subject1.dat} in the directory containing the original data file (\texttt{DATA\_DIR}). + +\subsection{Downsample} +Here, we will downsample the data in time. This is useful when the data were acquired like ours with a high sampling rate of 512 Hz. This is an unnecessarily high sampling rate for a simple evoked response analysis, and we will now decrease the sampling rate to 200 Hz, thereby reducing the file size by more than half. Select \textsc{Downsample} from the ``Other'' drop-down menu and select the \texttt{spm8\_subject1.mat} file. Choose a new sampling rate of 200 (Hz). The progress bar will appear and the resulting data will be saved to files \texttt{dspm8\_subject1.mat} and \texttt{dspm8\_subject1.dat}. This step requires the Signal Processing Toolbox. + + +\subsection{Filter} +Filtering the data in time removes unwanted frequency bands from the data. Usually, for evoked response analysis, the low frequencies are kept, while the high frequencies are assumed to carry noise. Here, we will use a bandpass filter to remove ultra-low frequencies close to DC, and also remove high frequencies at the same time +\begin{itemize} +\item{Click on \textsc{Filter} and select the \texttt{dspm8\_subject1.mat} file.} +\item{Select a ``bandpass'' filter with band 0.5 30 (Hz).} +\end{itemize} +The progress bar will appear and the resulting filtered data will be saved in files \texttt{fdspm8\_subject1.mat} and \texttt{fdspm8\_subject1.dat}. This step requires the Signal Processing Toolbox. \subsection{Montage} -In this step, we will identify the VEOG and HEOG channels, and also remove several channels that don't carry EEG data and are of no importance to the following. We generally recommend to remove all data channels that are no longer needed because it will reduce the total file size. To do so, we use the \textsc{montage} tool in SPM, which is a general approach for pre-multiplying the data matrix (channels $\times$ time) by another matrix that linearly weights all channel data. This provides a very general method for data transformation in M/EEG analysis. +In this step, we will identify the VEOG and HEOG channels, and also remove several channels that don't carry EEG data and are of no importance to the following. We generally recommend to remove all data channels that are no longer needed because it will reduce the total file size. We will also convert the data to average reference montage by subtracting from each channel the mean of all EEG channels. To do so, we use the \textsc{montage} tool in SPM, which is a general approach for pre-multiplying the data matrix (channels $\times$ time) by another matrix that linearly weights all channel data. This provides a very general method for data transformation in M/EEG analysis. The appropriate montage-matrix can be derived as follows. -In our case, we would like to only keep channels 1 to 128. In addition, there were three EOG channels (129, 130, 131), where the HEOG is computed as the difference between channels 131 and 130, and the VEOG by the difference between channels 130 and 129. This matrix can be specified in SPM by either using a graphical interface, or by supplying the matrix saved in a file. We will do the latter. The script to generate this file can be found in the \texttt{example\_scripts} folder: \texttt{montage\_subject1.m}. Copy this script into \texttt{DATA\_DIR} and run it. This will generate a file named \texttt{MONT\_EXP.mat}. +In our case, we would like to only keep EEG channels 1 to 128 and subtract from each channel the average of all EEG channels. In addition, there were three EOG channels (129, 130, 131), where the HEOG is computed as the difference between channels 131 and 130, and the VEOG by the difference between channels 130 and 129. This matrix can be specified in SPM by either using a graphical interface, or by supplying the matrix saved in a file. We will do the latter. The script to generate this file can be found in the \texttt{example\_scripts} folder: \texttt{montage\_subject1.m}. Copy this script into \texttt{DATA\_DIR} and run it. This will generate a file named \texttt{MONT\_EXP.mat}. You now call the montage function by choosing \textsc{Montage} in the ``Other'' drop-down menu and: \begin{itemize} -\item{Select the M/EEG-file \texttt{spm8\_subject1.mat}} +\item{Select the M/EEG-file \texttt{fdspm8\_subject1.mat}} \item{`How to specify the montage ?' Answer ``file''.} \item{Then select the generated \texttt{MONT\_EXP.mat} file} \item{``Keep the other channels?'' : ``No''} \end{itemize} -This will remove the uninteresting channels from the data. The progress bar appears and SPM will generate two new files \texttt{Mspm8\_subject1.mat} and \texttt{Mspm8\_subject1.dat}. +This will remove the uninteresting channels from the data. The progress bar appears and SPM will generate two new files \texttt{Mfdspm8\_subject1.mat} and \texttt{Mfdspm8\_subject1.dat}. This step will also assign default locations to the sensors, as this information is unfortunately not contained in the original Biosemi \texttt{*.bdf} file. It is usually the responsibility of the user to link the data to sensors which are located in a coordinate system. In our experience this is a critical step. SPM provide tools (\textsc{Prepare}) for linking data and location information, leaving it the responsibility of the user to verify the success of this process. Chapter \ref{Chap:eeg:preprocessing} describes in detail how you can use the \textsc{Prepare} tool from the ``Other'' drop-down menu to use digitized sensor location data. \subsection{Epoch} -To epoch the data click on \textsc{Epoching}. Select the \texttt{Mspm8\_subject1.mat} file. Choose the peri-stimulus time window, first the start \texttt{-100}, then the end \texttt{400} ms. Choose 2 conditions. You can call the first condition ``standard''. A GUI pops up which gives you a complete list of all events in the EEG file. The standard condition had 480 trials, so select the type with value 1 and press OK. The second condition can be called ``rare''. The rare stimulus was given 120 times and has value 3 in the list. Select this trial type and press OK. Answer two times ``no'' to the questions ``review individual trials'', and ``save trial definitions''. The progress bar will appear and the epoched data will be saved to files \texttt{eMspm8\_subject1.mat} and \texttt{eMspm8\_subject1.dat}. +To epoch the data click on \textsc{Epoching}. Select the \texttt{Mfdspm8\_subject1.mat} file. Choose the peri-stimulus time window, first the start \texttt{-100}, then the end \texttt{400} ms. Choose 2 conditions. You can call the first condition ``standard''. A GUI pops up which gives you a complete list of all events in the EEG file. The standard condition had 480 trials, so select the type with value 1 and press OK. The second condition can be called ``rare''. The rare stimulus was given 120 times and has value 3 in the list. Select this trial type and press OK. Answer two times ``no'' to the questions ``review individual trials'', and ``save trial definitions''. The progress bar will appear and the epoched data will be saved to files \texttt{eMfdspm8\_subject1.mat} and \texttt{eMfdspm8\_subject1.dat}. \begin{figure} \begin{center} -\includegraphics[width=120mm]{mmn/topo1} +\includegraphics[width=150mm]{mmn/topo1} \caption{\em Scalp topography of single trial MMN EEG data. Channel 14, second-row from bottom, left hemisphere contains (slightly) higher variability data than the others. This channel is to be marked as artefactual (ie. 'bad'). \label{topo1}} \end{center} \end{figure} -\subsection{Downsample} -Here, we will downsample the data in time. This is useful when the data were acquired like ours with a high sampling rate of 512 Hz. This is an unnecessarily high sampling rate for a simple evoked response analysis, and we will now decrease the sampling rate to 200 Hz, thereby reducing the file size by more than half. Select \textsc{Downsample} from the ``Other'' drop-down menu and select the \texttt{eMspm8\_subject1.mat} file. Choose a new sampling rate of 200 (Hz). The progress bar will appear and the resulting data will be saved to files \texttt{deMspm8\_subject1.mat} and \texttt{deMspm8\_subject1.dat}. This step is computationally demanding. - -\subsection{Filter} -Filtering the data in time removes unwanted frequency bands from the data. Usually, for evoked response analysis, the low frequencies are kept, while the high frequencies are assumed to carry noise. Here, we will use a bandpass filter to remove ultra-low frequencies close to DC, and also remove high frequencies at the same time -\begin{itemize} -\item{Click on \textsc{Filter} and select the \texttt{deMspm8\_subject1.mat} file.} -\item{Select a ``bandpass'' filter with band 0.5 30 (Hz).} -\end{itemize} -The progress bar will appear and the resulting filtered data will be saved in files \texttt{fdeMspm8\_subject1.mat} and \texttt{fdeMspm8\_subject1.dat}. - \subsection{Artefacts} A number of different methods of artefact removal are implemented in SPM8. Here, we will demonstrate a simple thresholding method. However, before doing so, we will look at the data in the display: \begin{itemize} \item{Choose ``M/EEG'' from the ``Display'' dropdown menu.} -\item{Select the \texttt{fdeMspm8\_subject1.mat} file.} +\item{Select the \texttt{eMfdspm8\_subject1.mat} file.} \item{Click on the ``EEG'' tab.} \item{Press the ``scalp'' radio button.} \end{itemize} @@ -74,21 +75,25 @@ \subsection{Artefacts} Right-click on the channel; this tells you that this channel is ``A14''. You will also see as an entry in this menu ``bad: 0''. Select this entry, and click the left button. This will make the menu disappear, but the channel now has a grey background. You have marked this channel as bad. Click on ``save''in the top-right corner. This channel will then be ignored in subsequent processing. In fact this channel probably doesn't need removing, but we do so for teaching purposes only. \\ \\ -Now, click on \textsc{Artefacts} and: -\begin{itemize} -\item{Select the \texttt{fdeMspm8\_subject1.mat} file.} -\item{Answer ``no'' to ``Read own artefact list ?''.} -\item{Answer ``no'' to ``Robust average ?''.} -\item{Answer ``yes'' to ``Threshold channels ?''} -\item{Press return to choose the default for which channels you want to threshold, i.e., all 130 channels (128 EEG, 2 EOG). } -\item{Choose as threshold 80 ($\mu V$).} -\end{itemize} -The progress bar will appear and the resulting data will be saved to files \texttt{afdeMspm8\_subject1.mat} and \texttt{afdeMspm8\_subject1.dat}. Sixty five trials will have been rejected. There are also interactive artefact removal routines available from \texttt{Toolbox} $\rightarrow$ \texttt{MEEG tools} $\rightarrow$ \texttt{Fieldtrip visual artifact rejection}. +Now, click on \textsc{Artefacts}. A window of SPM8 batch interface will open. You might be already familiar with this interface from other SPM8 functions. It is also possible to use the batch interface to run the preprocessing steps that we have performed until now, but for artefact detection this is the only graphical interface. Click on ``File name'' and select the \texttt{eMfdspm8\_subject1.mat} file. Double click ``How to look for artefacts'' and a new branch will appear. It is possible to define several sets of channels to scan and several different methods for artefact detection. We will use simple thresholding applied to all channels. Click on ``Detection algorithm'' and select ``Threshold channels'' in the small window below. Double click on ``Threshold'' and enter 80 (in this case $\mu V$). The batch is now fully configured. Run it by pressing the green button at the top of the batch window. + +This will detect trials in which the signal recorded at any of the channels exceeds 80 microvolts (relative to pre-stimulus baseline). These trials will be marked as artefacts. Most of these artefacts occur on the VEOG channel, and reflect blinks during the critical time window. The procedure will also detect channels in which there are a large number of artefacts (which may reflect problems specific to those electrodes, allowing them to be removed from subsequent analyses). + +In this case, the Matlab window will show: +\begin{verbatim} + 1 bad channels: A14 +72 rejected trials: 1 3 4 5 27 28 29 88 98 110 125 [...] +\end{verbatim} +A new file will also be created, \texttt{aeMfdspm8\_subject1.mat}. + +There are also interactive artefact removal routines available from \texttt{Toolbox} $\rightarrow$ \texttt{MEEG tools} $\rightarrow$ \texttt{Fieldtrip visual artifact rejection}. \subsection{Averaging} -To produce an ERP click on \textsc{Averaging} and select the \texttt{afdeMspm8\_subject1.mat} file. The progress bar will appear and the resulting data will be saved to files \texttt{mafdeMspm8\_subject1.mat} and \texttt{mafdeMspm8\_subject1.dat}. The Graphics window will pop up and allow you to look at the averaged data. To look at the ERP, click on the EEG tab, and press the ``scalp'' radio button. Now hold the Shift button down on the keyboard whilst selecting trial 2 with the left mouse button in the upper right corner of the graphics window. This will overlay responses to standard and rare trials on the same figure axes. +To produce an ERP click on \textsc{Averaging} and select the \texttt{aeMfdspm8\_subject1.mat} file. At this point you can perform either orinary averaging or ``robust averaging''. Robust averaging makes it possible to supress artefacts automatically without rejecting trials or channels compltely, but just the contaminated parts. For robust averaging answer ``yes'' to `Use robust averaging?''. Answer ``yes'' to ``Save weights'', and ``yes'' to ``Compute weights by condition'' \footnote{In this case we do not want to pool both conditions together because the number of standard and rare trials are quite different.} and press ``Enter'' to accept the default ``Offset of the weighting function''. A new dataset will be generated \texttt{maeMfdspm8\_subject1} and automatically opened in the reviewing tool so that you can examine the ERP. There will also be an additional dataset named \texttt{WaeMfdspm8\_subject1} this dataset will contain instead of EEG data the weights used by robust averaging. This is useful to see what was suppressed and whether there might be some condition-specific bias that could affect the results. + +The Graphics window will pop up and allow you to look at the averaged data. To look at the ERP, click on the EEG tab, and press the ``scalp'' radio button. Now hold the Shift button down on the keyboard whilst selecting trial 2 with the left mouse button in the upper right corner of the graphics window. This will overlay responses to standard and rare trials on the same figure axes. -Now press the 'plus' icon at the top of this graphics window and select channel C23 (seventh central channel down from the top) with a left mouse click. This will plot the ERPs shown in Figure~\ref{c23}. This completes the preprocessing step. +Now press the ``plus'' icon at the top of this graphics window and select channel C23 (seventh central channel down from the top) with a left mouse click. This will plot the ERPs shown in Figure~\ref{c23}. This completes the preprocessing step. \begin{figure} \begin{center} \includegraphics[width=120mm]{mmn/erp_c23} @@ -105,11 +110,11 @@ \section{Sensor space analysis} Here, we will consider a 3D example, where the third dimension is time, and test across trials within this single subject. We first create a 3D image for each trial of the two types, with dimensions M$\times$M$\times$S, where S=101 is the number of samples (time points). We then take these images into an unpaired t-test across trials (in a 2nd-level model) to compare ``standard'' and ``rare'' events. We can then use classical SPM to identify locations in space and time in which a reliable difference occurs, correcting across the multiple comparisons entailed. This would be appropriate if, for example, we had no a priori knowledge where or when the difference between standard and rare trials would emerge. The appropriate images are created as follows \begin{itemize} \item{Select the ``Convert to image'' option from the ``Other'' pulldown menu.} -\item{Select the \texttt{afdeMspm8\_subject1.mat} file.} +\item{Select the \texttt{aeMfdspm8\_subject1.mat} file.} \item{For ``output image dimensions'' accept the default of 32 (leading to a 32x32 pixel space).} \item{For ``interpolate' or ``mask out'' bad channels, select ``interpolate''.} \end{itemize} -SPM will take some time as it writes out a NIfTI image for each trial (except rejected trials), in a new directory called \texttt{afdeMspm8\_subject1}, which will itself contain two subdirectories, one for each trialtype, called \texttt{type\_rare} and \texttt{type\_standard}. In each trialtype subdirectory there will be image and header files for each non-rejected trial of that type, e.g, trial0001.img/hdr. You can press ``Display: images'' to view one of these images - it will have dimensions 32$\times$32$\times$101. +SPM will take some time as it writes out a NIfTI image for each trial (except rejected trials), in a new directory called \texttt{aeMfdspm8\_subject1}, which will itself contain two subdirectories, one for each trialtype, called \texttt{type\_rare} and \texttt{type\_standard}. In each trialtype subdirectory there will be image and header files for each non-rejected trial of that type, e.g, trial0001.img/hdr. You can press ``Display: images'' to view one of these images - it will have dimensions 32$\times$32$\times$101. To perform statistics on these images: \begin{itemize} @@ -122,21 +127,21 @@ \section{Sensor space analysis} \item{Press the green ``Run'' button to execute the job\footnote{Note that we can use the default ``nonsphericity'' selections, i.e, that the two trial-types may have different variances, but are uncorrelated.} This will produce the design matrix for a two-sample t-test.} \item{Now press ``Estimate'' in SPMs main window, and select the \texttt{SPM.mat} file from the \texttt{XYTstats} directory.} \end{itemize} -Now press ``Results'' and define a new F-contrast as [1 -1] (for help with these basic SPM functions, see eg. chapter~\ref{Chap:data:auditory}). Keep the default contrast options, but threshold at $p<.05$ FWE corrected for the whole ``image''. Then press ``volume'', and the Graphics window should now look like that in Figure~\ref{3DSPM}. This reveals a large fronto-central region within the 2D sensor space and within the time epoch in which standard and rare trials differ reliably, having corrected for multiple F-tests across pixels/time. An F-test is used because the sign of the difference reflects the polarity of the ERP difference, which is not of primary interest. +Now press ``Results'' and define a new F-contrast as [1 -1] (for help with these basic SPM functions, see eg. chapter~\ref{Chap:data:auditory}). Keep the default contrast options, but threshold at $p<.05$ FWE corrected for the whole search volume and select ``Scalp-Time'' for the ``Data Type''. Then press ``whole brain'', and the Graphics window should now look like that in Figure~\ref{3DSPM}. This reveals a large fronto-central region within the 2D sensor space and within the time epoch in which standard and rare trials differ reliably, having corrected for multiple F-tests across pixels/time. An F-test is used because the sign of the difference reflects the polarity of the ERP difference, which is not of primary interest. -In the SPM results window, the time epoch (-100ms to 400ms) is referred in percentage units. The cursor in Figure~\ref{3DSPM} has been positioned by selecting the second cluster in the results table. This occurs at a time point 51\% through the time epoch, which corresponds to 155ms post stimulus. +The cursor in Figure~\ref{3DSPM} has been positioned by selecting the third cluster in the results table. This occurs at time point 155ms post stimulus. \begin{figure} \begin{center} \includegraphics[width=120mm]{mmn/3DSPM} -\caption{\em In this SPM the time axis is reflected in the two MIP windows in the top row, with time proceeding from the bottom to the top of the page. The cursor has been positioned by selecting the second cluster in the results table. This occurs at a time point 51\% through the time epoch, which corresponds to 155ms post stimulus. The design matrix on the right hand side comprises two columns, the first for standard trials and the second for rare ones. \label{3DSPM}} +\caption{\em In this SPM the time axis is reflected in the two MIP windows in the top row, with time proceeding from the bottom to the top of the page. The cursor has been positioned by selecting the third cluster in the results table. This occurs at time point 155ms post stimulus. The design matrix on the right hand side comprises two columns, the first for standard trials and the second for rare ones. \label{3DSPM}} \end{center} \end{figure} Now: \begin{itemize} \item{Press the right mouse button in the MIP} -\item{Select ``display/hide'' channels} -\item{Select the \texttt{afdeMspm8\_subject1.mat} file.} +\item{Select ``display/hide channels''} +\item{Select the \texttt{maeMfdspm8\_subject1.mat} file.} \end{itemize} This links the \texttt{SPM.mat} file with the M/EEG file from which the EEG images were created. It is now possible to superimpose the channel labels onto the spatial SPM, and also to ``goto the nearest channel'' (using options provided after a right mouse click, when navigating the MIP). @@ -161,7 +166,7 @@ \section{Source reconstruction} \subsection{Mesh} The first step is to load the data and create a cortical mesh upon which M/EEG data will be projected: \begin{itemize} -\item{Press the ``Load'' button in the souce localisation GUI and select the file \texttt{mafdeMspm8\_subject1.mat}.} +\item{Press the ``Load'' button in the souce localisation GUI and select the file \texttt{maeMfdspm8\_subject1.mat}.} \item{Enter ``Standard'' under ``Comment/Label for this analysis'' and press OK.} \item{Now press the ``template'' button.} \item{For ``Cortical mesh'', select ``normal''.} @@ -217,7 +222,7 @@ \subsection{Invert} Now enter ``150'' for ``ms or mm'' and press the MIP button, to see a MIP of activity in source space at 150ms post-stimulus, and the time series of activities (top panel) at the position with largest magnitude signal. The corresponding graphic is shown in Figure~\ref{invert}. By toggling the ``Condition'' button, and pressing MIP each time, you can view the spatial distribution of activity for the different conditions (at the selected time point). \begin{figure} \begin{center} -\includegraphics[width=80mm]{mmn/invert} +\includegraphics[width=120mm]{mmn/invert} \caption{\em Source reconstructed activity at 150ms post-stimulus. The upper trace shows responses to Condition 1 (Standards) with the red curve, and to Condition 2 (Rare) in gray. \label{invert}} @@ -251,7 +256,7 @@ \section{Dynamic Causal Modeling} \end{figure} We will now complete the three model specification entries shown in Figure~\ref{specify}: \begin{itemize} -\item{Press the ``new data'' button and select the \texttt{mafdeMspm8\_subject1.mat} file.} +\item{Press the ``new data'' button and select the \texttt{maeMfdspm8\_subject1.mat} file.} \item{Enter the ``between-trial effects'' and design matrix information shown in Figure~\ref{specify}(a).} \item{Press the ``Display'' button.} \end{itemize} diff --git a/man/mmn/topo1.png b/man/mmn/topo1.png index cc06203..17cec84 100644 Binary files a/man/mmn/topo1.png and b/man/mmn/topo1.png differ diff --git a/man/multimodal/figures/eeg_check_datareg.jpg b/man/multimodal/figures/eeg_check_datareg.jpg deleted file mode 100644 index 96e5c82..0000000 Binary files a/man/multimodal/figures/eeg_check_datareg.jpg and /dev/null differ diff --git a/man/multimodal/figures/eeg_coreg.png b/man/multimodal/figures/eeg_coreg.png new file mode 100644 index 0000000..87528cc Binary files /dev/null and b/man/multimodal/figures/eeg_coreg.png differ diff --git a/man/multimodal/figures/eeg_ecd.jpg b/man/multimodal/figures/eeg_ecd.jpg deleted file mode 100644 index c5cddad..0000000 Binary files a/man/multimodal/figures/eeg_ecd.jpg and /dev/null differ diff --git a/man/multimodal/figures/eeg_erp.png b/man/multimodal/figures/eeg_erp.png new file mode 100644 index 0000000..01d9606 Binary files /dev/null and b/man/multimodal/figures/eeg_erp.png differ diff --git a/man/multimodal/figures/eeg_forward.png b/man/multimodal/figures/eeg_forward.png new file mode 100644 index 0000000..beadf60 Binary files /dev/null and b/man/multimodal/figures/eeg_forward.png differ diff --git a/man/multimodal/figures/eeg_meshing.png b/man/multimodal/figures/eeg_meshing.png new file mode 100644 index 0000000..f03d727 Binary files /dev/null and b/man/multimodal/figures/eeg_meshing.png differ diff --git a/man/multimodal/figures/eeg_msp.png b/man/multimodal/figures/eeg_msp.png new file mode 100644 index 0000000..00d43fe Binary files /dev/null and b/man/multimodal/figures/eeg_msp.png differ diff --git a/man/multimodal/figures/eeg_recon.png b/man/multimodal/figures/eeg_recon.png new file mode 100644 index 0000000..8e5ee6e Binary files /dev/null and b/man/multimodal/figures/eeg_recon.png differ diff --git a/man/multimodal/figures/eeg_scalptime.png b/man/multimodal/figures/eeg_scalptime.png new file mode 100644 index 0000000..8ed700f Binary files /dev/null and b/man/multimodal/figures/eeg_scalptime.png differ diff --git a/man/multimodal/figures/eeg_scalptime_results.png b/man/multimodal/figures/eeg_scalptime_results.png new file mode 100644 index 0000000..3b803e1 Binary files /dev/null and b/man/multimodal/figures/eeg_scalptime_results.png differ diff --git a/man/multimodal/figures/eeg_topo.png b/man/multimodal/figures/eeg_topo.png new file mode 100644 index 0000000..69ed795 Binary files /dev/null and b/man/multimodal/figures/eeg_topo.png differ diff --git a/man/multimodal/figures/figure_32_1.jpg b/man/multimodal/figures/figure_32_1.jpg deleted file mode 100644 index 4379136..0000000 Binary files a/man/multimodal/figures/figure_32_1.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_10.jpg b/man/multimodal/figures/figure_32_10.jpg deleted file mode 100644 index ed3e9a2..0000000 Binary files a/man/multimodal/figures/figure_32_10.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_11.jpg b/man/multimodal/figures/figure_32_11.jpg deleted file mode 100644 index 8f80118..0000000 Binary files a/man/multimodal/figures/figure_32_11.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_12.jpg b/man/multimodal/figures/figure_32_12.jpg deleted file mode 100644 index 4dc7cfb..0000000 Binary files a/man/multimodal/figures/figure_32_12.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_13_L.jpg b/man/multimodal/figures/figure_32_13_L.jpg deleted file mode 100644 index e192ebe..0000000 Binary files a/man/multimodal/figures/figure_32_13_L.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_13_R.jpg b/man/multimodal/figures/figure_32_13_R.jpg deleted file mode 100644 index e28c97f..0000000 Binary files a/man/multimodal/figures/figure_32_13_R.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_14_L.jpg b/man/multimodal/figures/figure_32_14_L.jpg deleted file mode 100644 index 54040a4..0000000 Binary files a/man/multimodal/figures/figure_32_14_L.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_14_R.jpg b/man/multimodal/figures/figure_32_14_R.jpg deleted file mode 100644 index 5eb4e59..0000000 Binary files a/man/multimodal/figures/figure_32_14_R.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_15.jpg b/man/multimodal/figures/figure_32_15.jpg deleted file mode 100644 index 75c7875..0000000 Binary files a/man/multimodal/figures/figure_32_15.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_16.jpg b/man/multimodal/figures/figure_32_16.jpg deleted file mode 100644 index a933bf0..0000000 Binary files a/man/multimodal/figures/figure_32_16.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_17.jpg b/man/multimodal/figures/figure_32_17.jpg deleted file mode 100644 index 94d66d4..0000000 Binary files a/man/multimodal/figures/figure_32_17.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_18.jpg b/man/multimodal/figures/figure_32_18.jpg deleted file mode 100644 index 6d019ec..0000000 Binary files a/man/multimodal/figures/figure_32_18.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_19.jpg b/man/multimodal/figures/figure_32_19.jpg deleted file mode 100644 index 977698d..0000000 Binary files a/man/multimodal/figures/figure_32_19.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_2.jpg b/man/multimodal/figures/figure_32_2.jpg deleted file mode 100644 index adf7f6e..0000000 Binary files a/man/multimodal/figures/figure_32_2.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_20.jpg b/man/multimodal/figures/figure_32_20.jpg deleted file mode 100644 index cd99127..0000000 Binary files a/man/multimodal/figures/figure_32_20.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_21.jpg b/man/multimodal/figures/figure_32_21.jpg deleted file mode 100644 index 8784ce9..0000000 Binary files a/man/multimodal/figures/figure_32_21.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_22.jpg b/man/multimodal/figures/figure_32_22.jpg deleted file mode 100644 index 08c2a26..0000000 Binary files a/man/multimodal/figures/figure_32_22.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_3.jpg b/man/multimodal/figures/figure_32_3.jpg deleted file mode 100644 index 57f1716..0000000 Binary files a/man/multimodal/figures/figure_32_3.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_4.jpg b/man/multimodal/figures/figure_32_4.jpg deleted file mode 100644 index 1c2c780..0000000 Binary files a/man/multimodal/figures/figure_32_4.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_5.jpg b/man/multimodal/figures/figure_32_5.jpg deleted file mode 100644 index 6097a93..0000000 Binary files a/man/multimodal/figures/figure_32_5.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_6.jpg b/man/multimodal/figures/figure_32_6.jpg deleted file mode 100644 index 777a207..0000000 Binary files a/man/multimodal/figures/figure_32_6.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_7.jpg b/man/multimodal/figures/figure_32_7.jpg deleted file mode 100644 index 1285059..0000000 Binary files a/man/multimodal/figures/figure_32_7.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_8.jpg b/man/multimodal/figures/figure_32_8.jpg deleted file mode 100644 index 07a1296..0000000 Binary files a/man/multimodal/figures/figure_32_8.jpg and /dev/null differ diff --git a/man/multimodal/figures/figure_32_9.jpg b/man/multimodal/figures/figure_32_9.jpg deleted file mode 100644 index 88cc3db..0000000 Binary files a/man/multimodal/figures/figure_32_9.jpg and /dev/null differ diff --git a/man/multimodal/figures/fmri_faces_vs_scrambled.png b/man/multimodal/figures/fmri_faces_vs_scrambled.png new file mode 100644 index 0000000..fc976ae Binary files /dev/null and b/man/multimodal/figures/fmri_faces_vs_scrambled.png differ diff --git a/man/multimodal/figures/fmri_spmf_001_10.jpg b/man/multimodal/figures/fmri_spmf_001_10.jpg deleted file mode 100644 index 540c5f2..0000000 Binary files a/man/multimodal/figures/fmri_spmf_001_10.jpg and /dev/null differ diff --git a/man/multimodal/figures/meg_TF_results.png b/man/multimodal/figures/meg_TF_results.png new file mode 100644 index 0000000..debb387 Binary files /dev/null and b/man/multimodal/figures/meg_TF_results.png differ diff --git a/man/multimodal/figures/meg_TFimage.png b/man/multimodal/figures/meg_TFimage.png new file mode 100644 index 0000000..cff9933 Binary files /dev/null and b/man/multimodal/figures/meg_TFimage.png differ diff --git a/man/multimodal/figures/meg_coreg.png b/man/multimodal/figures/meg_coreg.png new file mode 100644 index 0000000..2a088a2 Binary files /dev/null and b/man/multimodal/figures/meg_coreg.png differ diff --git a/man/multimodal/figures/meg_mesh.jpg b/man/multimodal/figures/meg_mesh.jpg deleted file mode 100644 index 7398fbd..0000000 Binary files a/man/multimodal/figures/meg_mesh.jpg and /dev/null differ diff --git a/man/multimodal/figures/meg_mesh_sens.jpg b/man/multimodal/figures/meg_mesh_sens.jpg deleted file mode 100644 index 4759b06..0000000 Binary files a/man/multimodal/figures/meg_mesh_sens.jpg and /dev/null differ diff --git a/man/multimodal/figures/meg_msp.png b/man/multimodal/figures/meg_msp.png new file mode 100644 index 0000000..599efd7 Binary files /dev/null and b/man/multimodal/figures/meg_msp.png differ diff --git a/man/multimodal/figures/meg_plv_faces.png b/man/multimodal/figures/meg_plv_faces.png new file mode 100644 index 0000000..7a7ed90 Binary files /dev/null and b/man/multimodal/figures/meg_plv_faces.png differ diff --git a/man/multimodal/figures/meg_plv_scrambled.png b/man/multimodal/figures/meg_plv_scrambled.png new file mode 100644 index 0000000..1491681 Binary files /dev/null and b/man/multimodal/figures/meg_plv_scrambled.png differ diff --git a/man/multimodal/figures/meg_pow_faces.png b/man/multimodal/figures/meg_pow_faces.png new file mode 100644 index 0000000..7dea006 Binary files /dev/null and b/man/multimodal/figures/meg_pow_faces.png differ diff --git a/man/multimodal/figures/meg_pow_scrambled.png b/man/multimodal/figures/meg_pow_scrambled.png new file mode 100644 index 0000000..49fc8e9 Binary files /dev/null and b/man/multimodal/figures/meg_pow_scrambled.png differ diff --git a/man/multimodal/figures/meg_recon_window.jpg b/man/multimodal/figures/meg_recon_window.jpg deleted file mode 100644 index 23646d2..0000000 Binary files a/man/multimodal/figures/meg_recon_window.jpg and /dev/null differ diff --git a/man/multimodal/figures/meg_scalp_erf.png b/man/multimodal/figures/meg_scalp_erf.png new file mode 100644 index 0000000..4e74fb9 Binary files /dev/null and b/man/multimodal/figures/meg_scalp_erf.png differ diff --git a/man/multimodal/figures/meg_sol_lef.jpg b/man/multimodal/figures/meg_sol_lef.jpg deleted file mode 100644 index e39471b..0000000 Binary files a/man/multimodal/figures/meg_sol_lef.jpg and /dev/null differ diff --git a/man/multimodal/figures/meg_sol_rig.jpg b/man/multimodal/figures/meg_sol_rig.jpg deleted file mode 100644 index 91a126f..0000000 Binary files a/man/multimodal/figures/meg_sol_rig.jpg and /dev/null differ diff --git a/man/multimodal/figures/meg_sol_ven.jpg b/man/multimodal/figures/meg_sol_ven.jpg deleted file mode 100644 index b5e9433..0000000 Binary files a/man/multimodal/figures/meg_sol_ven.jpg and /dev/null differ diff --git a/man/multimodal/figures/meg_topo180.png b/man/multimodal/figures/meg_topo180.png new file mode 100644 index 0000000..44f866b Binary files /dev/null and b/man/multimodal/figures/meg_topo180.png differ diff --git a/man/multimodal/figures/paradigm.png b/man/multimodal/figures/paradigm.png new file mode 100644 index 0000000..4faf452 Binary files /dev/null and b/man/multimodal/figures/paradigm.png differ diff --git a/man/multimodal/figures/unwarp.jpeg b/man/multimodal/figures/unwarp.jpeg deleted file mode 100644 index 58318cc..0000000 Binary files a/man/multimodal/figures/unwarp.jpeg and /dev/null differ diff --git a/man/multimodal/figures/unwarp2.jpeg b/man/multimodal/figures/unwarp2.jpeg deleted file mode 100644 index 833f8c2..0000000 Binary files a/man/multimodal/figures/unwarp2.jpeg and /dev/null differ diff --git a/man/multimodal/multimodal.tex b/man/multimodal/multimodal.tex index 72c5783..cece33f 100644 --- a/man/multimodal/multimodal.tex +++ b/man/multimodal/multimodal.tex @@ -1,388 +1,483 @@ \chapter{Multimodal face-evoked responses \label{Chap:data:multimodal}} - \section{Overview} This dataset contains EEG, MEG, functional MRI and structural MRI data on the same subject within the same paradigm, which allows a basic comparison of faces versus scrambled faces. -It can be used to demonstrate, for example, 3D source reconstruction of various electrophysiological measures of face perception, such as the "N170" evoked response (ERP) recorded with EEG, or the analogous "M170" evoked field (ERF) recorded with MEG. These localisations are informed by the anatomy of the brain (from the structural MRI) and possibly by functional activation in the same paradigm (from the functional MRI). +It can be used to demonstrate, for example, 3D source reconstruction of various electrophysiological measures of face perception, such as the ``N170'' evoked response (ERP) recorded with EEG, or the analogous ``M170'' evoked field (ERF) recorded with MEG. These localisations are informed by the anatomy of the brain (from the structural MRI) and possibly by functional activation in the same paradigm (from the functional MRI). -The demonstration below involves localising the N170 using a distributed source method (called an "imaging" solution in SPM) analogous to "weighted minimum L2-norm". The data can also be used to explore further effects, e.g. induced effects (Friston et al, 2006), effects at different latencies, or the effects of adding fMRI constraints on the localisation. +The demonstration below involves localising the N170 using a distributed source method (called an ``imaging'' solution in SPM). The data can also be used to explore further effects, e.g. induced effects (Friston et al, 2006), effects at different latencies, or the effects of adding fMRI constraints on the localisation. -The EEG data were acquired on a 128 channel ActiveTwo system; the MEG data were acquired on a 151 channel CTF Omega system; the sMRI data were acquired using a phased-array headcoil on a Siemens Sonata 1.5T; the fMRI data were acquired using a gradient-echo EPI sequence on the Sonata. The dataset also includes data from a Polhemus digitizer, which are used to coregister the EEG and the MEG data with the structural MRI. +The EEG data were acquired on a 128 channel ActiveTwo system; the MEG data were acquired on a 275 channel CTF/VSM system; the sMRI data were acquired using a phased-array headcoil on a Siemens Sonata 1.5T; the fMRI data were acquired using a gradient-echo EPI sequence on the Sonata. The dataset also includes data from a Polhemus digitizer, which are used to coregister the EEG and the MEG data with the structural MRI. -Some related analyses of these data are reported in Henson et al (2005a, 2005b, 2007), Kiebel and Friston (2004) and Friston et al (2006; in press-a). +Some related analyses of these data are reported in Henson et al (2005a, 2005b, 2007, 2009a, 2009b, under revision), Kiebel and Friston (2004) and Friston et al (2006). -The analysis below is best done in Matlab7, but all mat files should be in a format readable by Matlab6.5. +To proceed with the data analysis, first download the data set from the SPM website\footnote{Multimodal face-evoked dataset: \url{http://www.fil.ion.ucl.ac.uk/spm/data/mmfaces/}}. -\section{Paradigm and Data} +Most of the analysis below can be done in \matlab\ 7.1 (R14SP3) and above. However, recoding condition labels using GUI requires features of SPM8 only available in \matlab\ 7.4 (R2007a) and above. The Signal Processing toolbox is required for the filtering and downsampling steps. -The basic paradigm involves randomised presentation of at least 86 faces and 86 scrambled faces (Figure~\ref{fig_32_1}), based on Phase 1 of a previous study by Henson et al (2003). The scrambled faces were created by 2D Fourier transformation, random phase permutation, inverse transformation and outline-masking of each face. Thus faces and scrambled faces are closely matched for low-level visual properties such as spatial frequency power density. Half the faces were famous, but this factor is collapsed in the current analyses. Each face required a four-way, left-right symmetry judgment (mean RTs over a second; judgments roughly orthogonal to conditions; reasons for this task are explained in Henson et al, 2003). The subject was instructed not to blink while the fixation cross was present on the screen. +\section{Paradigm and Data} +The basic paradigm involves randomised presentation of at least 86 faces and 86 scrambled faces (Figure~\ref{multimodal:fig:1}), based on Phase 1 of a previous study by Henson et al (2003). The scrambled faces were created by 2D Fourier transformation, random phase permutation, inverse transformation and outline-masking of each face. Thus faces and scrambled faces are closely matched for low-level visual properties such as spatial frequency power density. Half the faces were famous, but this factor is collapsed in the current analyses. Each face required a four-way, left-right symmetry judgment (mean RTs over a second; judgments roughly orthogonal to conditions; reasons for this task are explained in Henson et al, 2003). The subject was instructed not to blink while the fixation cross was present on the screen. \begin{figure} \begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_1} -\caption{\em One trial in the experimental paradigm: Trials involved either a Face (F) or Scrambled face (S). \label{fig_32_1}} +\includegraphics[width=100mm]{multimodal/figures/paradigm} +\caption{\em One trial in the experimental paradigm: Trials involved either a Face (F) or Scrambled face (S). \label{multimodal:fig:1}} \end{center} \end{figure} \subsection{Structural MRI} -The T1-weighted structural MRI of a young male was acquired on a 1.5T Siemens Sonata via an MDEFT sequence with resolution $1 x 1 x 1 mm^3$ voxels, using a whole-body coil for RF transmission and an 8-element phased array head coil for signal reception. +The T1-weighted structural MRI of a young male was acquired on a 1.5T Siemens Sonata via an MDEFT sequence with resolution $1 \times 1 \times 1 mm^3$ voxels, using a whole-body coil for RF transmission and an 8-element phased array head coil for signal reception. The images are in Analyze format in the sMRI sub-directory, consisting of two files: \begin{verbatim} sMRI/sMRI.img sMRI/sMRI.hdr \end{verbatim} -The structural was manually-positioned to match roughly Talairach space, with the origin close to the Anterior Commissure, which produced the associated SPM Matlab file: +The structural was manually-positioned to match roughly Talairach space, with the origin close to the Anterior Commissure, which produced the associated SPM \matlab\ file: \begin{verbatim} sMRI/sMRI.mat \end{verbatim} The approximate position of 3 fiducials within this MRI space - the nasion, and the left and right peri-aricular points - are stored in the file: \begin{verbatim} - sMRI/smri_fids.mat + sMRI/smri_fid.txt \end{verbatim} -These were identified manually (based on anatomy) and are used to define the MRI space relative to the EEG and MEG spaces, which need to be coregistered (see below). It doesn't matter that the positions are approximate, because more precise coregistration is done via digitised surfaces of the scalp ("head shape functions") that were created using the Polhemus 3D digitizer. +These were identified manually (based on anatomy) and are used to define the MRI space relative to the EEG and MEG spaces, which need to be coregistered (see below). It doesn't matter that the positions are approximate, because more precise coregistration is done via digitised surfaces of the scalp (``head shape functions'') that were created using the Polhemus 3D digitizer. \subsection{EEG data} -The EEG data were acquired on a 128-channel ActiveTwo system, sampled at 2048 Hz (subsequently downsampled to 200Hz to reduce filesize), plus electrodes on left earlobe, right earlobe, and two bipolar channels to measure HEOG and VEOG. The data were referenced to the average of the left and right earlobes (for consistency with Henson et al, 2003). The 128 scalp channels are named: 32 A (Back), 32 B (Right), 32 C (Front) and 32 D (Left). +The EEG data were acquired on a 128-channel ActiveTwo system, sampled at 2048 Hz, plus electrodes on left earlobe, right earlobe, and two bipolar channels to measure HEOG and VEOG. The 128 scalp channels are named: 32 A (Back), 32 B (Right), 32 C (Front) and 32 D (Left). The data acquired in two runs of the protocol is contained in two Biosemi raw data files: +\begin{verbatim} + EEG/faces_run1.bdf + EEG/faces_run2.bdf +\end{verbatim} -The original data were converted into SPM M/EEG format and epoched from -200ms to +600ms post-stimulus (baseline-corrected from -200 to 0ms), ie 161 samples: +The EEG directory also contains the following files: \begin{verbatim} - EEG/e_eeg.mat - EEG/e_eeg.dat + EEG/condition_labels.txt \end{verbatim} -(using the \verb!bdf_setup.mat! channel template provided with SPM5 in the EEGtemplates sub-directory). -Other details about the data can be examined by typing: +This text file contains a list of condition labels in the same order as the trials appear in the two files - ``faces'' for presentation of faces and ``scrambled'' for presentation of scrambled faces. +The EEG directory also contains the following files: \begin{verbatim} - D = spm_eeg_ldata + EEG/electrode_locations_and_headshape.sfp \end{verbatim} -and selecting the \verb!e_meg.mat! file. This will show the contents of the structure "D" that is loaded into the Matlab workspace, the various fields of which can be explored. Note that the data values themselves are memory-mapped from the \verb!e_eeg.dat! file to the field D.data (e.g, D.data(1,2,3) returns the field strength in the first sensor at the second sample point during the third trial). +This ASCII file contains electrode locations, fiducials and headshape points measured with Polhemus digitizer. -You will see that there are 344 events (D.Nevents), consisting of 172 faces (event code 1) and 172 scrambled faces (event code 2), which are randomly intermixed (see D.events.code)\footnote{These data were actually concatenated from two separate runs on the same subject (using spm-eeg-merge), which is why there are twice as many events as with the MEG and fMRI data.}. If you type D.channels.name, you will see the order and the names of the channels. +The 3 fiducial markers were placed approximately on the nasion and pre-aricular points and digitised by the Polhemus digitizer. The digitizer was also used to locate the position of each electrode (in the \texttt{electrode\_locations.sfp} file), and to record multiple points on the surface of the subject's scalp and nose (the ``head shape function'' in the \texttt{headshape.mat} file). Later, we will coregister the fiducial points and the head shape to map the electrode positions in the ``Polhemus space'' to the ``MRI space''. + +Also included as reference are some SPM batch files and SPM scripts (though these are recreated as part of the demo): -The EEG directory also contains a Polhemus sub-directory with the following files: \begin{verbatim} - EEG/Polhemus/eeg_fids.mat - EEG/Polhemus/eeg_sens_loc.mat - EEG/Polhemus/eeg_hsf.mat + EEG/batch_eeg_XYTstats.mat + EEG/batch_eeg_artefact.mat + EEG/eeg_preprocess.m + EEG/faces_eeg_preprocess.m \end{verbatim} -All files contain matrices, the three columns of which code location in a right-handed 3D space, the axes of which conform to the Talairach space, i.e, the first column is x (left-right), the second is y (anterior-posterior) and the third is z (inferior-superior). The units are mm. -The \verb!eeg_fids.mat! file contains the position of 3 fiducial markers that were placed approximately on the nasion and peri-aricular points and digitised by the Polhemus digitizer. The digitizer was also used to locate the position of each electrode (in the \verb!eeg_sens_loc.mat! file), and to trace out many points along the surface of the subject's scalp and nose (the "head shape function" in the \verb!eeg_hsf.mat! file). Later, we will coregister the fiducial points and the head shape to map the electrode positions in the "Polhemus space" to the "MRI space". -\subsection{MEG data \label{meg}} +\subsection{MEG data \label{multimodal:data:meg}} -The MEG data were acquired on a 151 channel CTF Omega system, using second-order axial gradiometers sampled at 625 Hz (subsequently downsampled to 200Hz to reduce filesize). The original data were converted into SPM MEEG format and epoched from -200ms to +600ms post-stimulus (i.e, baseline-corrected from -200ms to 0ms), ie 161 samples: -\begin{verbatim} - MEG/e_meg.mat - MEG/e_meg.dat -\end{verbatim} -The channel template for these data is also provided: -\begin{verbatim} - MEG/CTF151_setup.mat -\end{verbatim} -(which may need to be copied to the EEGtemplates sub-directory of your local SPM5 installation directory, if not already there). -The MEG data also contains a Polhemus sub-directory with the following files: +The MEG data were acquired on a 275 channel CTF/VSM system, using second-order axial gradiometers and synthetic third gradient for denoising and sampled at 480 Hz. Two runs (sessions) of the protocol have been saved in two CTF datasets (each one is a directory with multiple files) \begin{verbatim} - MEG/Polhemus/meg_fids.mat - MEG/Polhemus/meg_sens_loc.mat - MEG/Polhemus/meg_sens_or.mat - MEG/Polhemus/meg_hsf.mat + MEG/SPM_CTF_MEG_example_faces1_3D.ds + MEG/SPM_CTF_MEG_example_faces2_3D.ds \end{verbatim} -which are analogous to the corresponding MEG files described in the previous section\footnote{These matrices are transformations from the original CTF / Polhemus files - in which x codes anterior-posterior and y codes left-right - i.e, a 90 degree clockwise rotation about the z-axis.}. More specifically, the \verb!meg_fids.mat! contains the position of 3 "locator coils", positioned close to the fiducials\footnote{Unlike the MRI and EEG data, these fiducials were not precisely the nasion and peri-aricular points. However, given that the coregistration of the MEG and MRI data is based mainly on the headshape (see later), this inaccuracy in the MEG fiducials does not matter.}, the locations of which are measured by the CTF machine, and used to define the coordinates (in "CTF space") for the location of the 151 sensors (in the \verb!meg_sens_loc.mat! file) and their (axial gradiometer) orientations (\verb!meg_sens_or.mat!). The same three locator coils were digitised by a Polhemus digitizer outside the MEG machine, together with the head shape, to define the "Polhemus space". Subsequently, the fiducials in the Polhemus space were coregistered with the fiducials in the CTF space, and the resulting rigid-body transformation applied to the Polhemus head shape. Thus the coordinates in all four files above are in alignment in the CTF space, which will subsequently be transformed into the "MRI space". +The MEG data also contains a \texttt{headshape.mat} file, containing the headshape recorded during the MEG experiment with a Polhemus digitizer. +The locations of the 3 fiducials in the \texttt{headshape.mat} file are the same as the positions of 3 ``locator coils'' the locations of which are measured by the CTF machine, and used to define the coordinates (in ``CTF space'') for the location of the 275 sensors. +Also included as reference are two SPM batch files and two trial definition files (though these are recreated as part of the demo): +\begin{verbatim} + MEG/batch_meg_preproc.mat + MEG/batch_meg_TFstats.mat + MEG/trials_run1.mat + MEG/trials_run2.mat +\end{verbatim} \subsection{fMRI data} -The fMRI data were acquired using a Trajectory-Based Reconstruction (TBR) gradient-echo EPI sequence (Josephs et al, 2000) on a 1.5T Sonata. There were 32, 3mm slices of 3x3 mm2 pixels, acquired in a sequential descending order with a TR of 2.88s. There are 215 images in the 'Scans' sub-directory (5 initial dummy scans have been removed), each consisting of an Analyze image and header file: +The fMRI data were acquired using a gradient-echo EPI sequence on a 3T Siemens TIM Trio, with 32, 3mm slices (skip 0.75mm) of $3\times 3 mm^2$ pixels, acquired in a sequential descending order with a TR of 2s. There are 390 images in each of the two ``Session'' sub-directories (5 initial dummy scans have been removed), each consisting of an Analyze image and header file: \begin{verbatim} - fMRI/Scans/fM*.img - fMRI/Scans/fM*.hdr + fMRI/Session1/fM*.{hdr,img} + fMRI/Session2/fM*.{hdr,img} \end{verbatim} -Also provided are the onsets of faces and scrambled faces (in units of scans) in the Matlab file: +Also provided are the onsets of faces and scrambled faces (in units of scans) in the \matlab\ file: \begin{verbatim} - fMRI/onsets.mat + fMRI/trials_ses1.mat + fMRI/trials_ses2.mat \end{verbatim} -and the SPM "Job" files (see Section~\ref{fMRI}): +and two example SPM batch files (see Section~\ref{multimodal:data:fMRI}): \begin{verbatim} - fMRI/realign_job.mat - fMRI/slicetime_job.mat - fMRI/smooth_job.mat - fMRI/stats_job.mat + fMRI/batch_fmri_preproc.mat + fMRI/batch_fmri_stats.mat \end{verbatim} -\section{Getting Started} -You need to start SPM5 and toggle "EEG" as the modality (bottom-right of SPM main window), or start SPM5 with \verb!spm eeg!. -You will also need to 1) copy the MEG template file (\verb!CTF151_setup.mat!) to the EEGtemplates sub-directory within your SPM5 installation directory, if it is not already there (see ~section \ref{meg} above), and 2) ensure this EEGtemplates directory is on your Matlab path. +\section{Getting Started} +You need to start SPM8 and toggle ``EEG'' as the modality (bottom-right of SPM main window), or start SPM8 with \texttt{spm eeg}. In order for this to work you need to ensure that the main SPM directory is on your \matlab\ path. \section{EEG analysis} -\subsection{Preprocessing the EEG data} +First change directory to the EEG subdirectory (either in \matlab\, or via the ``CD'' option in the SPM ``Utils'' menu). + +\subsection{Convert} + +Press the \textsc{Convert} button and select the \texttt{faces\_run1.bdf} file. At the prompt ``Define settings?'' select ``just read''. +SPM will now read the original Biosemi format file and create an SPM compatible data file, called \texttt{spm8\_faces\_run1.mat} and \texttt{spm8\_faces\_run1.dat} in the current \matlab\ directory. After the conversion is complete the data file will be automatically opened in SPM8 reviewing tool. By default you will see the ``info'' tab. At the top of the window there is some basic information about the file. Below it you will see several clickable tabs with additional information. The ``history'' tab lists the processing steps that have been applied to the file. At this stage there is only one such step - conversion. The ``channels'' tab lists the channels in the file and their properties, the ``trial'' tab lists the trials or in the case of a continuous file all the triggers (events) that have been recorded. The ``inv'' tab is used for reviewing the inverse solutions and is not relevant for the time being. Note that the detailed information in the tabs will not be available for \matlab\ versions older than 7.4. At the top of the window there is another set of tabs. If you click on the ``EEG'' tab you will see the raw EEG traces. They all look unusually flat because the continuous data we have just converted contains very low frequencies and baseline shifts. Therefore, if we try to view all the channels together, this can only be done with very low gain. +If you press the ``intensity rescaling'' button (with arrows pointing up and down) several times you will start seeing EEG activity in a few channels but the other channels will not be visible as they will go out of range. You can also use the controls at the bottom of the window to scroll through the recording. If you press the icon to the right of the mini-topography icon, with the rightwards pointing arrow, the display will move to the next trigger, shown as a vertical line through the display. (New triggers/events can be added by the rightmost icon). At the bottom of the display is a plot of the global field power across the session, with the black line indicating the current timewindow displayed (the width of this timewindow can be controlled by the two leftmost top icons). + +\subsection{Downsample} + +Here, we will downsample the data in time. This is useful when the data were acquired like ours with a high sampling rate of 2048 Hz. This is an unnecessarily high sampling rate for a simple evoked response analysis, and we will now decrease the sampling rate to 200 Hz, thereby reducing the file size by more than ten fold and greatly speeding up the subsequent processing steps. This step requires the Signal Processing toolbox. Select \textsc{Downsample} from the ``Other'' drop-down menu and select the \texttt{spm8\_faces\_run1.mat} file. Choose a new sampling rate of 200 (Hz). The progress bar will appear and the resulting data will be saved to files \texttt{dspm8\_faces\_run1.mat} and \texttt{dspm8\_faces\_run1.dat}. Note that this dataset and other intermediate datasets created during preprocessing will not be automatically opened in the reviewing tool, but you can always review them by selecting \texttt{M/EEG} from the ``Display'' drop down menu and choosing the corresponding \texttt{.mat} file. -* Change directory to the EEG subdirectory (either in Matlab, or via the "CD" option in the SPM "Utils" menu) +\subsection{Montage} -* Press 'Artefacts', select the \verb!e_eeg.mat! file, press 'no' to the 'read own artefact list?' question, 'no' to 'robust average?', 'yes' to 'threshold channels?', and enter 200 for the threshold +In this step, we will identify the VEOG and HEOG channels, remove several channels that don't carry EEG data and are of no importance to the following and convert the 128 EEG channels to ``average reference'' by subtracting the mean of all the channels from each channel\footnote{Re-referencing EEG to the mean over EEG channels is important for source localisation. Note also that if some channels are subsequently marked ``bad'' (see later), one should re-reference again, because bad channels are ignored in any localisation.}. We generally recommend removal of data channels that are no longer needed because this will reduce the total file size and conversion to average reference is necessary at present for source modelling to work correctly. To do so, we use the \textsc{montage} tool in SPM, which is a general approach for pre-multiplying the data matrix (channels $\times$ time) by another matrix that linearly weights all channel data. This provides a very general method for data transformation in M/EEG analysis. -This will detect trials in which the signal recorded at any of the channels exceeds 200 microvolts (relative to pre-stimulus baseline). These trials will be marked as artefacts. Most of these artefacts occur on the VEOG channel, and reflect blinks during the critical time window\footnote{Channel-specific thresholds can be used by entering 130 thresholds, one per EEG/EOG channel, with a value of Inf for those channels that you do not want to threshold.}. The procedure will also detect channels in which there are a large number of artefacts (which may reflect problems specific to those electrodes, allowing them to be removed from subsequent analyses). +The appropriate montage-matrix can be specified in SPM by either using a graphical interface, or by supplying the matrix saved in a file. We will do the latter. The script to generate this file can be found in the \texttt{example\_scripts} folder: \texttt{faces\_eeg\_montage.m}. Copy this script into the directory with \texttt{dspm8\_faces\_run1.mat} file and run it. This will generate a file named \texttt{faces\_eeg\_montage.mat}. In our case, we would like to only keep channels 1 to 128. To re-reference each of these to their average, the script uses \matlab\ ``detrend'' to remove the mean of each column (of an identity matrix). In addition, there were four EOG channels (131, 132, 135, 136), where the VEOG is computed as the difference between channels 131 and 132, and the VEOG by the difference between channels 135 and 136. -In this case, the Matlab window will show: +You now call the montage function by choosing \textsc{Montage} in the ``Other'' drop-down menu and: +\begin{itemize} +\item Select the M/EEG-file \texttt{dspm8\_faces\_run1.mat}. +\item ``How to specify the montage ?'' Answer ``file''. +\item Then select the generated \texttt{faces\_eeg\_montage.mat} file. +\item ``Keep the other channels?'' : ``No''. +\end{itemize} +This will remove the uninteresting channels from the data. The progress bar appears and SPM will generate two new files \texttt{Mdspm8\_faces\_run1.mat} and \texttt{Mdspm8\_faces\_run1.dat}. + +\subsection{Epoch} +To epoch the data click on \textsc{Epoching}. Select the \texttt{Mdspm8\_faces\_run1.mat} file. Choose the peri-stimulus time window, first the start \texttt{-200}, then the end \texttt{600} ms. Choose 1 condition. There is no information in the file at this stage to distinguish between faces and scrambled faces. We will add this information at a later stage. You can give this condition any label, for instance ``stim''. A GUI pops up which gives you a complete list of all events in the EEG file. Each event has type and value which might mean different things for different EEG and MEG systems. So you should be familiar with your particular system to find the right trigger for epoching. In our case it is not very difficult as all the events but one appear only once in the recording, whereas the event with type ``STATUS'' and value 1 appears 172 times which is exactly the number of times a visual stimulus was presented. Select this event and press OK. Answer two times ``no'' to the questions ``review individual trials'', and ``save trial definitions''. The progress bar will appear and the epoched data will be saved to files \texttt{eMdspm8\_faces\_run1.mat} and \texttt{eMdspm8\_faces\_run1.dat}. The epoching function also performs baseline correction by default (with baseline -200 to 0ms). Therefore, in the epoched data the large channel-specific baseline shifts are removed and it is finally possible to see the EEG data clearly in the reviewing tool. + +\subsection{Reassignment of trial labels} + +Open the file \texttt{eMdspm8\_faces\_run1.mat} in the reviewing tool (under ``Display'' button). The first thing you will see is that in the history tab there are now 4 processing steps. Now switch to the ``trials'' tab. You will see a table with 172 rows - exactly the number of events we selected before. In the first column the label ``stim'' appears in every row. What we would like to do now is change this label to ``faces'' or ``scrambled'' where appropriate. We should first open the file \texttt{condition\_labels.txt} (in the EEG directory) with any text editor, such as \matlab\ editor or Windows notepad. In this file there are exactly 172 rows with either ``faces'' or ``scrambled'' in each row. Select and copy all the rows (Ctrl-A, Ctrl-C on Windows). Then go back to SPM and the trials tab. Place the cursor in the first row and first column cell with the ``stim'' label and paste the copied labels (Ctrl-V). The new labels should now appear for all rows. Press the ``update'' button above the table and then the ``SAVE'' button at the top right corner of the window. The new labels are now saved in the dataset. + +\subsection{Using the history and object methods to preprocess the second file} + +At this stage we need to repeat the preprocessing steps for the second file \texttt{faces\_run2.bdf}. You can do it by going back to the ``Convert'' section and repeating all the steps for this file, but there is a more efficient way. If you have been following the instructions until now the file \texttt{eMdspm8\_faces\_run1.mat} should be open in the reviewing tool. If it is not the case, open it. Go to the ``history'' tab and press the ``Save as script'' button. A dialog will appear asking for the name of the \matlab\ script to save. Let's call it \texttt{eeg\_preprocess.m}. Then there will be another dialogue suggesting to select the steps to save in the script. Just press ``OK'' to save all the steps. Now open the script in the \matlab\ editor. You will now need to make some changes to make it work for the second file. Here we suggest the simplest way to do it that does not require familiaty with \matlab\ programming. But if you are more familar with \matlab\ you'll definitely be able to do much better job. First, replace all the occurences of ``run1'' in the file with ``run2''. You can use the ``Find \& Replace'' functionalty (Ctrl-F) to do it. Secondly, erase the line starting with \texttt{S.timewindow} (line 5). This line defines the time window to read, in this case from the first to the last sample of the first file. The second file is slghtly longer than the first so we should let SPM determine the right time window automatically. Save the changes and run the script by pressing the ``Run'' button or writing \texttt{eeg\_preprocess} in the command line. SPM will now automatically perform all the steps we have done before using the GUI. This is a very easy way for you to start processing your data automatically once you come up with the right sequence of steps for one file. After the script finishes running there will be a new set of files in the current directory including \texttt{eMdspm8\_faces\_run2.mat} and \texttt{eMdspm8\_faces\_run2.dat}. If you open these files in the reviewing tool and go to the ``trials'' tab you will see that the trial labels are still ``stim''. The reason for this is that updates done using the reviewing tool are not presently recorded in the history (with the exception of the ``Prepare'' interface, see below). You can still do this update automatically and add it to your script. If you write \texttt{D} in the command line just after running the script and press ``Enter'' you will see some information about the dataset \texttt{eMdspm8\_faces\_run2}. \texttt{D} is an object, this is a special kind of data structure that makes it possible to keep different kinds of related information (in our case all the properties of our dataset) and define generic ways of manipulating these properties. For instance we can use the command: +\begin{verbatim} +D = conditions(D, [], importdata('condition_labels.txt')); D.save; +\end{verbatim} +to update the trial labels using information imported from the \texttt{condition\_labels.txt}\footnote{You might need to change the full path to this text file inside the single quotes, depending on your current directory and the directory of the original data.} (the two runs had identical trials). Now, \texttt{conditions}' is a ``method'', special function that knows where to store the labels in the object. All the methods take the M/EEG object (usually called \texttt{D} in SPM by convention) as the first argument. The second argument is a list of indices of trials for which we want to change the label. We specify an empty matrix which is interpreted as ``all''. The third argument is the new labels which are imported from the text file using \matlab\ built-in function. We then save the updated dataset on disk using the \texttt{save} method. If you now write \texttt{D.conditions} or \texttt{conditions(D)} (which are two equivalent ways of calling the \texttt{conditions} method with just D as an argument), you should see a list of 172 labels, either ``faces'' or ``scrambled''. If you add the commands above at the end of your automatically generated script, you can run it again and this time the output will have the right labels. + +\subsection{Merge} + +We will now merge the two epoched files we have generated until now and continue working on the merged file. Select the ``Merge'' command from the ``Other'' drop-down menu. In the selection window that comes up click on \texttt{eMdspm8\_faces\_run1.mat} and \texttt{eMdspm8\_faces\_run2.mat}. Press ``done''. Answer ``Leave as they are'' to ``What to do with condition labels?''. This means that the trial labels we have just specified will be copied as they are to the merged file. A new dataset will be generated called \texttt{ceMdspm8\_faces\_run1.\{mat,dat\}}. + +\subsection{Prepare} + +In this section we will add the separately measured electrode locations and headshape points to our merged dataset. In principle, this step is not essential for further analysis because SPM8 automatically assigns electrode locations for commonly used EEG caps and the Biosemi 128 cap is one of these. Thus, default electrode locations are present in the dataset already after conversion. But since these locations are based on channel labels they may not be precise enough and in some cases may be completely wrong because sometimes electrodes are not placed in the correct locations for the corresponding channel labels. This can be corrected by importing individually measured electrode locations. Select \textsc{Prepare} from the ``Other'' menu and in the file selection window select \texttt{ceMdspm8\_faces\_run1.mat}. A menu will appear at the top of SPM interactive window (bottom left window). In the ``Sensors'' submenu choose ``Load EEG sensors''/``Convert locations file''. In the file selection window choose the \texttt{electrode\_locations\_and\_headshape.sfp} file (in the original EEG directory). Then from the ``2D projection'' submenu select ``Project 3D (EEG)''. 2D channel layout will appear in the Graphics window. Select ``Apply'' from ``2D Projection'' and ``Save'' from ``File'' submenu. Note that the same functionality can also be accessed from the reviewing tool by pressing the ``Prepare SPM file'' button. + +\subsection{Artefact rejection} + +Here we will use SPM8 artefact detection functionality to exclude from analysis trials contaminated with large artefacts. Press the \textsc{Artefacts} button. A window of SPM8 batch interface will open. You might be already familiar with this interface from other SPM8 functions. It is also possible to use the batch interface to run the preprocessing steps that we have performed until now, but for artefact detection this is the only graphical interface. Click on ``File name'' and select the \texttt{ceMdspm8\_faces\_run1.mat} file. Double click ``How to look for artefacts'' and a new branch will appear. It is possible to define several sets of channels to scan and several different methods for artefact detection. We will use simple thresholding applied to all channels. Click on ``Detection algorithm'' and select ``Threshold channels'' in the small window below. Double click on ``Threshold'' and enter 200 (in this case $\mu V$). The batch is now fully configured. Run it by pressing the green button at the top of the batch window. + +This will detect trials in which the signal recorded at any of the channels exceeds 200 microvolts (relative to pre-stimulus baseline). These trials will be marked as artefacts. Most of these artefacts occur on the VEOG channel, and reflect blinks during the critical time window. The procedure will also detect channels in which there is a large number of artefacts (which may reflect problems specific to those electrodes, allowing them to be removed from subsequent analyses). + +In this case, the \matlab\ window will show: \begin{verbatim} There isn't a bad channel. - 45 rejected trials: [5 38 76 82 83 86 87 88 89 90 92 93 94 96 98 99 - 100 101 104 105 106 107 108 112 117 118 119 120 122 124 126 130 137 - 139 159 173 221 266 268 279 281 292 293 298 326] + 39 rejected trials: 38 76 82 83 86 88 89 90 92 [...] \end{verbatim} -(leaving 299 valid trials). A new file will also be created, \verb!ae_eeg.mat!(in which these artefacts are coded in the fields D.events.reject and D.channels.thresholded). +(leaving 305 valid trials). A new file will also be created, \texttt{aceMdspm8\_faces\_run1.\{mat,dat\}}. -At this point, you may want to look at the data. Press "Display: M/EEG", and select the \verb!ae_eeg.mat! file. After a short delay, the Graphics window should show the mean ERP (for trial 1) at each of the 130 channels (as in Figure~\ref{fig_32_2}). You can click on one of the channels (e.g, VEOG, on the top right of the display) to get a new window with the data for that channel expanded. You can alter the scaling or trial number using the sliders on the bottom of the Graphics window, or select a subset of channels to display by pressing the 'Channels' button. +\subsection{Exploring the M/EEG object} -\begin{figure} -\begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_2} -\caption{\em SPM Display window for trial 5 for all 128 EEG plus 2 EOG channels (top left and top right) in ae-eeg.mat. Trial 5 is marked as an artefact because the VEOG channel (top right) exceeds the user-specified threshold of 200uV, most likely reflecting the onset of a blink towards the end of the epoch. \label{fig_32_2}} -\end{center} -\end{figure} +We can now review the preprocessed dataset from the \matlab\ command line by typing: +\begin{verbatim} + D = spm_eeg_load +\end{verbatim} +and selecting the \texttt{aceMdspm8\_faces\_run1.mat} file. This will print out some basic information about the M/EEG object \texttt{D} that has been loaded into \matlab\ workspace. +\begin{verbatim} + SPM M/EEG data object + Type: single + Transform: time + 2 conditions + 130 channels + 161 samples/trial + 344 trials + Sampling frequency: 200 Hz + Loaded from file ...\EEG\aceMdspm8_faces_run1.mat + Use the syntax D(channels, samples, trials) to access the data. +\end{verbatim} +Note that the data values themselves are memory-mapped from \verb!aceMdspm8_faces_run1.dat! and can be accessed by indexing the \texttt{D} object (e.g, \texttt{D(1,2,3)} returns the field strength in the first sensor at the second sample point during the third trial). You will see that there are 344 trials (\texttt{D.ntrials}). Typing \texttt{D.conditions} will show the list of condition labels consisting of 172 faces (``faces'') and 172 scrambled faces (``scrambled''). \texttt{D.reject} will return a $1\times 344$ vector of ones (for rejected trials) and zeros (for retained trials). \texttt{D.condlist} will display a list of unique condition labels. The order of this list is important because every time SPM needs to process the conditions in some order, this will be the order. If you type \texttt{D.chanlabels}, you will see the order and the names of the channels. \texttt{D.chantype} will display the type for each channel (in this case either ``EEG'' or ``EOG''). \texttt{D.size} will show the size of the data matrix, [130 161 344] (for channels, samples and trials respectively). The size of each dimension separately can be accessed by \texttt{D.nchannels}, \texttt{D.nsamples} and \texttt{D.ntrials}. Note that although the syntax of these commands is similar to the commands used for accessing the fields of struct data type in \matlab\ what's actually happening is that these commands evoke special functions called ``methods'' and these methods actually collect and return the requested information from the internal data structure of the \texttt{D} object. The internal structure is not accessible directly when working with the object. This mechanism greatly enhances the robustness of SPM code. For instance you don't need to check whether some field is present in the internal structure. The methods will always do it automatically or return some default result if the information is missing without causing an error. +Type \texttt{methods('meeg')} for the full list of methods performing operations with the object. Type \texttt{help meeg/method\_name} to get help about a method. -\subsection{Basic ERPs} -* Press the 'Averaging' button and select the \verb!ae_eeg.mat! file. After a few moments, the matlab window will echo: +\subsection{Basic ERPs} - \verb!ae_eeg.mat!: Number of replications per contrast: - average 1: 151 trials, average 2: 148 trials +Press the \textsc{Averaging} button and select the \texttt{aceMdspm8\_faces\_run1.mat} file. At this point you can perform either orinary averaging or ``robust averaging'' (Wager et al., 2005). Robust averaging makes it possible to suppress artefacts automatically without rejecting trials or channels completely, but just the contaminated parts. Thus, in principle we could do robust averaging without rejecting trials with eye blinks and this is something you can do as an exercise and see how much difference the artefact rejection makes with ordinary averaging vs. robust averaging. For robust averaging answer ``yes'' to ``Use robust averaging?''. Answer ``yes'' to ``Save weights'', and ``no'' to ``Compute weights by condition''\footnote{When there are approximately equal numbers of trials in each condition, as here, it is probably safer to compute weights across all conditions, so as not to introduce artifactual differences between conditions. However, if one condition has fewer trials than the others, it is likely to be safer to estimate the weights separately for each condition, otherwise evoked responses in the rarer condition will be downweighted so as to become more similar to the more common condition(s).}. - (artefact trials are excluded from averaging) and a new file will be created in the MEG directory called \verb!mae_eeg.mat! ("m" for "mean"). +Finally, press ``Enter'' to accept the default ``Offset of the weighting function''. A new dataset will be generated \texttt{maceMdspm8\_faces\_run1.\{mat,dat\}} (``m'' for ``mean'') and automatically opened in the reviewing tool so that you can examine the ERP. There will also be an additional dataset named \texttt{WaceMdspm8\_faces\_run1.\{mat,dat\}}. This dataset will contain the weights used by robust averaging. This is useful to see what was suppressed and whether there might be some condition-specific bias that could affect the results. -* Press the 'Filtering' button, select the \verb!mae_eeg.mat! file, select 'lowpass', and enter 40 (Hz) as the cutoff. This smooths the data to 40Hz, producing the file \verb!fmae_eeg.mat! (using zero-phase-shift forward and reverse digital filtering with a 5th-order Butterworth filter)\footnote{Note that (lowpass) filtering short epochs like this is not necessarily a good idea, since ringing or "end-effects" can result at the start and end of the epoch. Filtering is normally better performed on continuous data (or longer epochs). The filtering performed here is simply to demonstrate the option and for display purposes (though the averaging process also tends to act like a lowpass filter anyway).}. +Select ``Contrast'' from the ``Other'' pulldown menu on the SPM window. This function creates linear contrasts of ERPs/ERFs. Select the \texttt{maceMdspm8\_\-faces\_\-run1.mat} file, enter $[1\: -1]$ as the first contrast and label it ``Difference'', answer ``yes'' to ``Add another'', enter $[1/2\: 1/2]$ as the second contrast and label it ``Mean''. Press ``no'' to the question ``Add another'' and not to ``weight by num replications''. This will create new file \texttt{wmaceMdspm8\_faces\_run1.\{mat,dat\}}, in which the first trial-type is now the differential ERP between faces and scrambled faces, and the second trial-type is the average ERP for faces and scrambled faces. -You can display the mean ERPs using the "Display: M/EEG" menu option again. Once you have done so, press the "channels" button in the Graphics window, then "Deselect all", and then click only, eg channels 'a1', 'd7', 'a12', 'b9' and 'c7'. (You can save these selections as a file, and use this file to display only a subset of channels in future). After pressing "ok", you will now only see these 5 channels (which will also make the display much faster!). Once you hold SHIFT and select trial-type 2, you should see something like Figure~\ref{fig_32_3}. +To look at the differential ERP, again press ``Display: M/EEG'', and select the \texttt{wmaceMdspm8\_faces\_run1.mat} file. Switch to the ``EEG'' tab and to ``scalp'' display by toggling a radio button at the top of the tab. The Graphics window should then show the ERP for each channel (for Trial 1 the ``Difference'' condition). Hold SHIFT and select Trial 2 to see both conditions superimposed. Then click on the zoom button and then on one of the channels (e.g, ``B9'' on the bottom right of the display) to get a new window with the data for that channel expanded, as in Figure~\ref{multimodal:fig:4}. \begin{figure} \begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_3} -\caption{\em SPM Display window for smoothed, average ERPs for faces (blue) and scrambled faces (red) for 5 selected channels in fmae-eeg.mat. \label{fig_32_3}} +\includegraphics[width=100mm]{multimodal/figures/eeg_erp} +\caption{\em Average (green) and differential (blue) ERPs for faces and scrambled faces at channel B9 in \texttt{wmaceMdspm8\_faces\_run1.mat}. \label{multimodal:fig:4}} \end{center} \end{figure} -* Select "Contrast" from the "Other..." pulldown menu on the SPM window (or type \verb!spm_eeg_weight_epochs! in the Matlab window). This function creates linear contrasts of ERPs/ERFs. Select the \verb!fmae_eeg.mat! file, and enter $[1 -1; 1/2 1/2]$ as the contrast matrix. Press "no" to the question "weight by num replications". This will create new file \verb!mfmae_eeg.mat!, in which the first trial-type is now the differential ERP between faces and scrambled faces, and the second trial-type is the average ERP for faces and scambled faces. +The green line shows the average ERP evoked by faces and scrambled faces (at this occipitotemporal channel). A P1 and N1 are clearly seen. The blue line shows the differential ERP between faces and scrambled faces. The difference is small around the P1 latency, but large and negative around the N1 latency. The latter likely corresponds to the ``N170'' (Henson et al, 2003). We will try to localise the cortical sources of the P1 and N170 in Section~\ref{multimodal:eeg:3D}. -To look at the differential ERP, again press 'Display: M/EEG', and select the \verb!mfmae_eeg.mat! file. After a short delay, the Graphics window should show the ERP for each channel (for trial-type 1). Hold SHIFT and select trial-type 2 to see both conditions superimposed. Then click on one of the channels (e.g, 'B9' on the bottom right of the display) to get a new window with the data for that channel expanded, as in Figure~\ref{fig_32_4}. +To see the topography of the differential ERP, click on Trial 1 again, press the ``topography'' button at the top of the window and scroll the latency from baseline to the end of the epoch, to see a maximal difference around 180ms (possibly including a small delay of about 8ms for the CRT display to scan to the centre of the screen) Figure~\ref{multimodal:fig:5}. \begin{figure} \begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_4} -\caption{\em Average (red) and differential (blue) ERPs for faces and scrambled faces at channel B9 in mfmae-eeg.mat. \label{fig_32_4}} +\includegraphics[width=100mm]{multimodal/figures/eeg_topo} +\caption{\em 2D topography for faces minus scrambled faces at 180ms. \label{multimodal:fig:5}} \end{center} \end{figure} -The red line shows the average ERP evoked by faces and scrambled faces (at this occipitotemporal channel). A P1 and N1 are clearly seen. The blue line shows the differential ERP between faces and scrambled faces. This is approx zero around the P1 latency, but negative around the N1 latency. The latter likely corresponds to the "N170" (Henson et al, 2003). We will try to localise the cortical sources of the P1 and N170 in Section~\ref{3D}. +\subsection{3D SPMs (Sensor Maps over Time) \label{multimodal:eeg:3DSPM}} + +A feature of SPM is the ability to use Random Field Theory to correct for multiple statistical comparisons across N-dimensional spaces. For example, a 2D space representing the scalp data can be constructed by flattening the sensor locations (using the 2D layout we created earlier) and interpolating between them to create an image of $M\times M$ pixels (when $M$ is user-specified, eg $M=32$). This would allow one to identify locations where, for example, the ERP amplitude in two conditions at a given timepoint differed reliably across subjects, having corrected for the multiple t-tests performed across pixels. That correction uses Random Field Theory, which takes into account the spatial correlation across pixels (i.e, that the tests are not independent). This kind of analysis is described earlier in the SPM manual, where a 1st-level design is used to create the images for a given weighting across timepoints of an ERP/ERF, and a 2nd-level design can then be used to test these images across subjects. + +Here, we will consider a 3D example, where the third dimension is time, and test across trials within the single subject. We first create a 3D image for each trial of the two types, with dimensions $M\times M\times S$, where S=161 is the number of samples. We then take these images into an unpaired t-test across trials (in a 2nd-level model) to compare faces versus scrambled faces. We can then use classical SPM to identify locations in space and time in which a reliable difference occurs, correcting across the multiple comparisons entailed. This would be appropriate if, for example, we had no a priori knowledge where or when the difference between faces and scrambled faces would emerge\footnote{Note that the 2D location in sensor space for EEG will depend on the choice of montage.}. -To see the topography of the differential ERP, press the "topography" button in the main graphics window, enter 165ms for the latency, and select "3D", to produce Figure~\ref{fig_32_5}. Choose the rotate3D cursor to surf. +Select the ``Convert to images'' option in the ``Other'' menu in the SPM main window, and select the \texttt{aceMdspm8\_faces\_run1.mat} file. You will then be prompted for ``output image dimensions'', for which you can accept the default of 32 (leading to a $32\times 32$ pixel space). It will then ask whether you want to interpolate or mask out bad channels, for which you select ``interpolate'' (though it will make no difference here because there are no bad channels). + +This will take some time as it writes out an image for each trial (except rejected trials), in a new directory called \texttt{aceMdspm8\_faces\_run1}, which will itself contain two subdirectories, one for each trialtype. In each trialtype subdirectory there will be image and header files for each non-rejected trial of that type, e.g, \texttt{trial0002.\{hdr,img\}}. You can press ``Display: images'' to view one of these images - it will have dimensions $32\times 32\times 161$, with the origin set at [16 18.6 41] (where 41 samples is 0ms), as in Figure~\ref{multimodal:fig:6}. \begin{figure} \begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_5} -\caption{\em 3D topography for faces minus scrambled faces at 165ms. \label{fig_32_5}} +\includegraphics[width=120mm]{multimodal/figures/eeg_scalptime} +\caption{\em 3D image for trial 2 of \texttt{aceMdspm8\_faces\_run1.mat}. The bottom image is a 2D x-y space interpolated from the flattened electrode locations (at one point in time). The two top images are sections through x and y respectively, now expressed over time (vertical (z) dimension).\label{multimodal:fig:6}} \end{center} \end{figure} +To perform statistics on these images, first create a new directory, eg. \texttt{mkdir XYTstats}. -\subsection{3D SPMs (Sensor Maps over Time) \label{3DSPM}} +Then press the ``Specify 2nd level'' button, to produce the batch editor window again. Select the new \texttt{XYTstats} as the ``Directory'', and ``two-sample t-test'' (unpaired t-test) as the ``Design''. Then select the images for ``group 1 scans'' as all those in the subdirectory ``type\_faces'' (using right click, and ``select all'') and the images for ``group 2 scans'' as all those in the subdirectory ``type\_scrambled''. You might want to save this batch specification, but then press ``run''\footnote{Note that we can use the default ``nonsphericity'' selections, i.e, that the two trial-types may have different variances, but are uncorrelated.}. -One novel feature of SPM is the ability to use Random Field Theory to correct for multiple statistical comparisons across N-dimensional spaces. For example, a 2D space representing the scalp data can be constructed by flattening the sensor locations and interpolating between them to create an image of MxM pixels (when M is user-specified, eg M=32). This would allow one to identify locations where, for example, the ERP amplitude in two conditions at a given timepoint differed reliably across subjects, having corrected for the multiple t-tests performed across pixels. That correction uses Random Field Theory, which takes into account the spatial correlation across pixels (i.e, that the tests are not independent). This kind of analysis is described earlier in the SPM manual, where a 1st-level design is used to create the images for a given weighting across timepoints of an ERP/ERF, and a 2nd-level design can then be used to test these images across subjects. +This will produce the design matrix for a two-sample t-test. -Here, we will consider a 3D example, where the third dimension is time, and test across trials within the single subject. We first create a 3D image for each trial of the two types, with dimensions MxMxS, where S=161 is the number of samples. We then take these images into an unpaired t-test across trials (in a 2nd-level model) to compare faces versus scrambled faces. We can then use classical SPM to identify locations in space and time in which a reliable difference occurs, correcting across the multiple comparisons entailed. This would be appropriate if, for example, we had no a priori knowledge where or when the difference between faces and scrambled faces would emerge\footnote{Note that the 2D location in sensor space for EEG will depend on the choice of reference channel.}. +Then press ``Estimate'', and when it has finished, press ``Results'' and define a new F-contrast as [1 -1]. Keep the default contrast options, but threshold at $p<.05$ FWE corrected for the whole search volume and select ``Scalp-Time'' for the ``Data Type''. Then press ``whole brain'', and the Graphics window should now look like that in Figure~\ref{multimodal:fig:7}. -* Select the "mat-2-3Dimage" option in the "Other..." menu in the Matlab window, and select the \verb!ae_eeg.mat! file. You will then be prompted for "output image dimensions", for which you can accept the default of 32 (leading to a 32x32 pixel space), and a pixel dimension, which you can change to 5 (this is rather arbitrary, but will make the images easier to view). It will then ask whether you want to interpolate or mask out bad channels, for which you can select interpolate (though it will make no difference here because there are no bad channels). +This will reveal ``regions'' within the 2D sensor space and within the -200ms to 600ms epoch in which faces and scrambled faces differ reliably, having corrected for multiple F-tests across pixels/time. There are a number of such regions, but the largest has maxima at [-13 -78 180] and [21 -68 180], corresponding to left and right posterior sites at 180ms. The second largest has a maximum at [0 8 180], which is close to Cz. An F-test was used because the sign of the difference reflects the polarity of the ERP difference, which is not of primary interest (and depends on the choice of reference). Indeed, if you plot the contrast of interest from the cluster maxima, you will see that the difference is negative for the first posterior, cluster but positive for the second, central cluster. This is consistent with the polarity of the differences in Figure~\ref{multimodal:fig:5}\footnote{The former likely corresponds to the ``N170'', while the latter likely corresponds to the ``VPP'', which may be two signs of the same effect, though of course these effects depend on the choice of reference.}. -This will take some time as it writes out an image for each trial (except rejected trials), in a new directory called \verb!ae_eeg!, which will itself contain two subdirectories, one for each trialtype. In each trialtype subdirectory there will be image and header files for each non-rejected trial of that type, e.g, trial02.img/hdr. You can press "Display: images" to view one of these images - it will have dimensions 32x32x161(x1), with the origin set at [16 16 40] (where 40 samples is 0ms), as in Figure~\ref{fig_32_6}. +If one had more constrained a priori knowledge about where and when the N170 would appear, one could perform an SVC based on, for example, a box around posterior channels and between 150 and 200ms poststimulus. See \url{http://imaging.mrc-cbu.cam.ac.uk/meg/SensorSpm} for more details. +If you go to the global maximum, then press ``overlays'', ``sections'' and select the ``mask.img'' in the stats directory, you will get sections through the space-time image, and by moving the cursor around, you can see that the N170/VPP effects start to be significant (after whole-image correction) around 150ms (and may also notice a smaller but earlier effect around 100ms). \begin{figure} \begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_6} -\caption{\em 3D image for trial 2 of ae-eeg.mat. The bottom image is a square 2D x-y space interpolated from the flattened electrode locations (at one point in time). The two top images are sections through x and y respectively, now expressed over time (vertical (z) dimension). (Colormap changed to 'jet').\label{fig_32_6}} +\includegraphics[width=120mm]{multimodal/figures/eeg_scalptime_results} +\caption{\em 3D sensor-time SPM{F} at $p<.05$ FWE corrected for the amplitude difference between face and scrambled face trials. The x, y coordinates refer to position in the 32x32 electrode plane (with units of mm); the z coordinate refers to peristimulus time in ms (to the nearest sampling of 5ms). \label{multimodal:fig:7}} \end{center} \end{figure} +\subsection{3D ``imaging'' reconstruction \label{multimodal:eeg:3D}} -To perform statistics on these images, first create a new directory, eg. mkdir XYTstats. +Here we will demonstrate a distributed source reconstruction of the N170 differential evoked response between faces and scrambled faces, using a grey-matter mesh extracted from the subject's MRI, and the Multiple Sparse Priors (MSP) method in which multiple constraints on the solution can be imposed (Friston et al, 2008, Henson et al, 2009a). -* Then press the "specify 2nd level" button, select "two-sample t-test" (unpaired t-test), and define the images for "group 1" as all those in the subdirectory "trialtype1" (using right mouse, and "select all") and the images for "group 2" as all those in the subdirectory "trialtype2". Finally, specify the new XYTstats directory as the output directory, and press "run"\footnote{Note that we can use the default "nonsphericity" selections, i.e, that the two trial-types may have different variances, but are uncorrelated.}. +Press the ``3D source reconstruction'' button, and press the ``load'' button at the top of the new window. Select the \verb!wmaceMdspm8_faces_run1.mat! file and type a label (eg "N170 MSP") for this analysis\footnote{Note that no new M/EEG files are created during each stage of the 3D reconstruction; rather, each step involves updating of the cell-array field \texttt{D.inv}, which will have one entry per analysis performed on that dataset (e.g, \texttt{D.inv\{1\}} in this case).}. +Press the ``MRI'' button, select the \texttt{smri.img} file within the \texttt{sMRI} sub-directory, and select ``normal'' for the cortical mesh. +The ``imaging'' option corresponds to a distributed source localisation, where current sources are estimated at a large number of fixed points (8196 for a ``normal'' mesh here) within a cortical mesh, rather than approximated by a small number of equivalent dipoles (the ECD option). The imaging or distributed approach is better suited for group analyses and probably for later components; the ECD approach may be better suited for very early sensory components (when only small parts of the brain are active), or for DCM models of a small number of regions (Kiebel et al, 2006). -This will produce the design matrix for a two-sample t-test. +The first time you use a particular structural image for 3D source reconstruction, it will take some time while the MRI is segmented (and normalisation parameters determined). This will create in the \texttt{sMRI} directory the files \texttt{y\_smri.nii} and \texttt{smri\_seg8.mat} for normalisation parameters and 4 GIfTI (\texttt{.gii}) files defining the cortical mesh, inner skull, outer skull and scalp surface. + +When meshing has finished, the cortex (blue), inner skull (red), outer skull (orange) and scalp (pink) meshes will also be shown in the Graphics window with slices from the sMRI image, see Figure~\ref{multimodal:fig:3}. This makes it possible to verify that the meshes indeed fit the original image well. The field \texttt{D.inv\{1\}}.mesh field will be updated in \matlab\ . Press ``save'' in top right of window to update the corresponding \texttt{mat} file on disk. + +\begin{figure} +\begin{center} +\includegraphics[width=90mm]{multimodal/figures/eeg_meshing} +\caption{\em Cortex (blue), inner skull (red), outer skull (orange) and scalp (pink) meshes with transverse slices of the subject's MRI. \label{multimodal:fig:3}} +\end{center} +\end{figure} -* Then press "Estimate", and when it has finished, press "Results" and define a new F-contrast as [1 -1] (for help with these basic SPM functions, see eg. chapter 26). Keep the default contrast options, but threshold at $p<.05$ FWE corrected for the whole "image". Then press "volume", and the Graphics window should now look like that in Figure~\ref{fig_32_7} (ignore the outline of the brain in the MIP!). +Both the cortical mesh and the skull and scalp meshes are not created directly from the segmented MRI, but rather are determined from template meshes in MNI space via inverse spatial normalisation (Mattout et al, 2007). -This will reveal "regions" within the 2D sensor space and within the -200ms to 600ms epoch in which faces and scrambled faces differ reliably, having corrected for multiple F-tests across pixels/time. There are a number of such regions, but we will concentrate on the first two (largest ones), with cluster maxima of [25 -55 200] and [10 5 160]. An F-test was used because the sign of the difference reflects the polarity of the ERP difference, which is not of primary interest (and depends on the choice of reference; see footnote 6). Indeed, if you plot the contrast of interest from the cluster maxima, you will see that the difference is negative for the first cluster (which is located posteriorly) but positive for the second cluster (which is more central, close to Cz). This is consistent with the polarity of the differences in Figure~\ref{fig_32_3}\footnote{With a reference similar to the current earlobes, the former likely corresponds to the "N170", while the latter likely corresponds to the "VPP" (though we have no evidence here for a dissociation between them).}. +Press the ``Co-register'' button. You will first asked to select at least 3 fiducials from a list of points in the EEG dataset (from Polhemus file): by default, SPM has already highlighted what it thinks are the fiducials, i.e, points labelled ``nas'' (nasion), ``lpa'' (left pre-aricular) and ``rpa'' (right pre-aricular). So just press ``ok''. -If one had more constrained a priori knowledge about where and when the N170 would appear, one could perform an SVC based on, for example, a box around posterior channels and between 150 and 200ms poststimulus. +You will then be asked for each of the 3 fiducial points to specify its location on the MRI images. This can be done by selecting a corresponding point from a hard-coded list (``select''). These points are inverse transformed for each individual image using the same deformation field that is used to create the meshes. The other two options are typing the MNI coordinates for each point (``type'') or clicking on the corresponding point in the image (``click''). Here, we will type coordinates based on where the experimenter defined the fiducials on the \texttt{smri.img}. These coordinates can be found in the \texttt{smri\_fid.txt} file also provided. So press ``type'' and for ``nas'', enter [0 91 -28]; for ``lpa'' press ``type'' and enter [-72 4 -59]; for ``rpa'' press ``type'' and enter [71 -6 -62]. Finally, answer ``yes'' to ``Use headshape points?''. + +This stage coregisters the EEG sensor positions with the structural MRI and cortical mesh, via an approximate matching of the fiducials in the two spaces, followed by a more accurate surface-matching routine that fits the head-shape function (measured by Polhemus) to the scalp that was created in the previous meshing stage via segmentation of the MRI. When coregistration has finished, the field D.inv\{1\}.datareg will be updated in \matlab\ . Press ``save'' in top right of window to update the corresponding mat file on disk. With the \matlab\ Rotation tool on (from the ``Tools'' tab in the SPM Graphics window, if not already on), right click near the top image and select ``Go to Y-Z'' view. Finally, a figure like that in Figure~\ref{multimodal:fig:8} will also be produced, which you can rotate with the mouse (using the Rotate3D \matlab\ Menu option) to check all sensors. + +Note that for these data, the coregistration is not optimal, with several EEG electrodes appearing inside the scalp. This is not actually a problem for the BEM calculated below, because the electrodes are re-projected to the scalp surface (as a precaution). This may be inaccurate Polhemus recording of the headshape or inaccurate surface matching for the scalp mesh, or ``slippage'' of headpoints across the top of the scalp (which might be reduced in future by digitising features like the nose and ears, and including them in the scalp mesh). In the meantime, if this happens in your data, you can stick with fiducials if you are confident that you can localise them reliably and accurately. \begin{figure} \begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_7} -\caption{\em 3D sensor-time SPM{F} at $p<.05$ FWE corrected for the amplitude difference between face and scrambled face trials. Note that the brain outline in the MIP should be ignored. The x, y coordinates refer to arbitrary units in the 32x32 electrode plane (origin = [16 16]); the z coordinate refers to peristimulus time in ms (to the nearest sampling of 5ms). \label{fig_32_7}} +\includegraphics[width=70mm]{multimodal/figures/eeg_coreg.png} +\caption{\em Graphical output of Co-registration of EEG data, showing (upper panel) cortex (blue), inner skull (red) and scalp (black) meshes, electrode locations (green), MRI/Polhemus fiducials (cyan/magneta), and headshape (red dots).\label{multimodal:fig:8}} \end{center} \end{figure} -\subsection{3D "imaging" reconstruction \label{3D}} - -Here we will demonstrate a distributed source reconstruction of the N170 differential evoked response between faces and scrambled faces, using a grey-matter mesh extracted from the subject's MRI, and an L2-norm method in which multiple constraints on the solution can be imposed (Phillips et al, 2002; Mattout et al, 2005; Henson et al, 2007; Friston et al, in press-a). +Press ``Forward Model'', and select ``EEG BEM''. The first time you do this, there will be a lengthy computation and a large file \texttt{smri\_EEG\_BEM.mat} will be saved in the \texttt{sMRI} directory containing the parameters of the boundary element model (BEM). In the Graphics window the BEM meshes will be displayed with the EEG sensors marked as asterisks see Figure~\ref{multimodal:fig:2}. This display is the final quality control before the model is used for lead field computation. -* Press the '3D source reconstruction' button, and press the "load" button at the top of the new window. Select the \verb!mfmae_eeg.mat! file and type a label (eg "N170") for this analysis\footnote{Note that no new M/EEG files are created during each stage of the 3D reconstruction; rather, each step involves updating of the cell-array field D.inv, which will have one entry per analysis performed on that dataset (e.g, D.inv\{1\} in this case).}. +\begin{figure} +\begin{center} +\includegraphics[width=70mm]{multimodal/figures/eeg_forward.png} +\caption{\em BEM meshes with the EEG sensors marked as asterisks.\label{multimodal:fig:2}} +\end{center} +\end{figure} -* Press the 'MRI' button, select the smri.img file within the sMRI sub-directory, press the "Imaging" button, and select 3000 for the number of vertices in the mesh... +Press ``Invert'', select ``Imaging'' (i.e, a distributed solution rather than DCM; Kiebel et al (2006)), select ``yes'' to include all conditions (i.e, both the differential and common effects of faces and scrambled faces) and then ``Standard'' to use the default settings. -The "imaging" option corresponds to a distributed source localisation, where current sources are estimated at a large number of fixed points (3000 here) within a cortical mesh, rather than approximated by a small number of equivalent dipoles (the ECD option). The imaging or distributed approach is better suited for group analyses and probably for later components; the ECD approach may be better suited for very early sensory components (when only small parts of the brain are active), or for DCM models of a small number of regions (Kiebel et al, 2006). +By default the MSP method will be used. MSP stands for ``Multiple Sparse Priors'' Friston et al (2008a), and has been shown to be superior to standard minimum norm (the alternative IID option) or a maximal smoothness solution (like LORETA; the COH option) - see Henson et al (2009a). Note that by default, MSP uses a ``Greedy Search'' (GS) (Friston et al, 2008b), though the standard ReML (as used in Henson et al, 2007) can also be selected (ARD). -This will take some time while the MRI is segmented (and normalisation parameters determined). This will create the usual files, i.e, c1/c2/c3smri.img/hdr, for grey/white/CSF respectively, msmri.img/hdr for the attentuation-corrected image, and the normalisation and inverse normalisation parameters (\verb!mri_vbm_sn_1.mat! and \verb!smri_vbm_inv_sn_1.mat! respectively) in the sMRI directory (see Chapter 5 for further details). +The ``Standard'' option uses default values for the MSP approach (to customise some of these parameters, press ``Custom'' instead). -This process will also create binary images of the cortex, inner skull surface and scalp, which are then used to create meshes (of 2002 vertices) for these surfaces, stored in the following files: -\begin{verbatim} - sMRI/smri_cortex.img - sMRI/smri_iskull.img - sMRI/smri_scalp.img -\end{verbatim} -When meshing has finished, the cortex (blue), inner skull (red) and scalp (orange) meshes will also be shown in the Graphics window. The field D.inv\{1\}.mesh field will be updated in matlab. Press "save" in top right of window to update the corresponding mat file on disk. +At the first stage of the inversion lead fields will be computed for all the mesh verticed and saved in the file \texttt{SPMgainmatrix\_wmaceMdspm8\_faces\_run1\_1.mat}. Then the actual MSP algorithm will run and the summary of the solution will be displayed in the Graphics window. -Note that the cortical mesh (and the distances within the mesh) are not created directly from the segmented MRI (like the skull and scalp meshes), but rather are determined from a template cortical mesh in MNI space via inverse spatial normalisation (Mattout et al, in press). +Press ``save'' to save the results. You can now explore the results via the 3D reconstruction window. If you type 180 into the box in the bottom right (corresponding to the time in ms) and press ``mip'', you should see an output like in~\ref{multimodal:fig:9}. This fit explains approx 98\% of the data. -* Press the 'Co-register' button, respond "no" to the 'Read Polhemus?' question (which is if you want to read in a Polhemus file directly), and then select the following files in response to each prompt (pressing "yes" to the 'Use headshape file' prompt): -\begin{verbatim} - EEG/Polhemus/eeg_sens_loc.mat - EEG/Polhemus/eeg_fids.mat - EEG/Polhemus/eeg_hsf.mat - sMRI/smri_fids.mat -\end{verbatim} -This stage coregisters the EEG sensor positions with the structural MRI and cortical mesh, via an approximate matching of the fiducials in the two spaces, followed by a more accurate surface-matching routine that fits the head-shape function (measured by Polhemus) to the scalp that was created in the previous meshing stage via segmentation of the MRI. +Note the hot-spots in the posterior and mid-lateral ventral temporal lobe. The timecourses come from the peak voxel. The red line shows the condition currently being shown (corresponding to the ``Condition 1'' toggle bar in the reconstruction window); the grey line(s) will show all other conditions. ``Condition 1'' is the differential evoked responses for faces vs scrambled; if you press the ``condition 1'' toggle, it will change to ``Condition 2'' (average evoked response for faces and scrambled faces), then press ``mip'' again and the display will update (note the colours of the lines have now reversed from before, with red now corresponding to average ERP). For the average effect, change the time to 100ms, and note mainly posterior occipital activation (though also some strong medial sources, whose truth is unclear). -When coregistration has finished, the field D.inv\{1\}.datareg will be updated in matlab. Press "save" in top right of window to update the corresponding mat file on disk. Finally, a figure like that in Figure~\ref{fig_32_8} will also be produced, which you can rotate with the mouse (using the Rotate3D Matlab Menu option) to check all sensors. +If you toggle back to ``Condition 1'' and press ``movie'', you will see the changes in the source strengths for the differential response over peristimulus time (from the limits 0 to 300ms currently chosen by default). +If you press ``render'' you can get a very neat graphical interface to explore the data (the buttons are fairly self-explanatory). However, we will concentrate on how one might perform statistics (eg with more subjects in a group analysis). \begin{figure} \begin{center} -\includegraphics[width=70mm]{multimodal/figures/figure_32_8} -\caption{\em Graphical output of Co-registration of EEG data, showing (upper panel) cortex (blue), inner skull (red) and scalp (black) meshes, electrode locations (green), MRI/Polhemus fiducials (cyan/magneta), and headshape (red dots).\label{fig_32_8}} +\includegraphics[width=90mm]{multimodal/figures/eeg_msp.png} +\caption{\em Graphical output of an MSP estimation of the differential ERP between faces and scrambled faces at 180ms. \label{multimodal:fig:9}} \end{center} \end{figure} -\noindent * Press 'Forward Model', and select "3 {Berg}". - -This will create a forward model (lead field matrix) based on a three sphere model (using a subset of BrainStorm functions, packaged with SPM~\footnote{Brainstorm is available from http://neuroimage.usc.edu/ResearchMEGEEGBrainStorm.html}). The Matlab window will output: -\begin{verbatim} - Scalp best fitting sphere computed (in 11 iterations) - Centre = [0.0001 -0.0218 0.0027] (Radius = 0.0774) - Computing EEG "BERG" Parameters. . . - Computing EEG "BERG" Parameters -> DONE - - Computing the Image Gain Matrices. . . - Foward model complete - thank you - \end{verbatim} +Press the ``Window'' button in the reconstruction window, enter ``150 200'' as the timewindow of interest and keep ``0'' as the frequency band of interest (0 means all frequencies). The Graphics window will then show the mean activity for this time/frequency contrast (and the contrast itself; note additional use of a Hanning window). -and a picture of the best-fitting sphere to the inner skull surface will be shown in the Graphics window (this defines the centre of the concentric spheres). The leadfield matrix (with source orientations fixed as normal to the cortical surface) is stored in the file: +Then press ``Image'', ``12'' for the smoothing kernel, and SPM will write 3D NIfTI images corresponding to the above contrast for each condition: \begin{verbatim} - smri_SPMgainmatrix_1.mat + w_wmaceMdspm8_faces_run1_1_1.nii + w_wmaceMdspm8_faces_run1_1_2.nii + sw_wmaceMdspm8_faces_run1_1_1.nii + sw_wmaceMdspm8_faces_run1_1_2.nii \end{verbatim} -(The file \verb!smri_SPMgainmatxyz_1.mat! stores a version with three orthogonal orientations per source location). +Note that the first two images are unsmoothed (but normalised); the latter two are smoothed by a 12mm isotropic Gaussian kernel. The last number in the file name refers to the condition number; the penultimate number refers to the reconstruction number (i.e. the number in red in the reconstruction window, i.e, \texttt{D.val}, here 1). -* Press 'Invert', select "Classical" (i.e, a distributed solution rather than DCM; Kiebel et al, 2006), select "yes" to include all conditions (i.e, both the differential and common effects of faces and scrambled faces), press "MSP" for the type of inversion, and then "Standard". +The smoothed results for Condition 1 (i.e, the differential evoked response for faces vs scrambled faces) will also be displayed in the Graphics window, see Figure~\ref{multimodal:fig:eegrecon}, together with the normalised structural. Note that the solution image is in MNI (normalised) space, because the use of a canonical mesh provides us with a mapping between the cortex mesh in native space and the corresponding MNI space. -MSP stands for "Multiple Sparse Priors", and has been shown to be superior to standard minimum norm (the alternative MNM option) or a maximal smoothness solution (like LORETA; the COH option) - see Friston et al (in press-a). Note that by default, MSP uses a "Greedy Search" (Friston et al, in press-b), though the standard ReML (as used in Friston et al, in press-a) can be selected as a hidden option. +You can also of course view the image with the normal SPM ``Display:image'' option, and locate the coordinates of the ``hotspots'' in MNI space. Note that these images contain RMS (unsigned) source estimates (see Henson et al, 2007). -The "Standard" option uses default values for the MSP approach (to customise some of these parameters, press "Customise" instead). +You could also explore the other inversion options, like COH and IID, which you will note give more superficial solutions (a known problem with standard minimum norm). To do this quickly (without repeating the MRI segmentation, coregistration and forward modelling), press the ``new'' button in the reconstruction window, which by default will copy these parts from the previous reconstruction. + +\begin{figure}[h!t] +\begin{center} +\includegraphics[width=90mm]{multimodal/figures/eeg_recon.png} +\caption{\em 3D reconstruction saved as a smoothed NIfTI image of the differential evoked response for faces vs scrambled faces around the N170. \label{multimodal:fig:eegrecon}} +\end{center} +\end{figure} -* Press "save" to save the results. You can now explore the results via the 3D reconstruction window. If you type 165 into the box in the bottom right (corresponding to the time in ms) and press "mip", you should see an output like in~\ref{fig_32_9}. This fit explains approx 97\% of the data. +\section{MEG analysis} -Note the hot-spots in the fusiform. The timecourses come from the peak voxel. The red line shows the condition currently being shown (corresponding to the "Condition 1" toggle bar in the reconstruction window); the grey line(s) will show all other conditions. Condition 1 is the differential evoked responses for faces vs scrambled; if you press the "condition 1" toggle, it will change to Condition 2 (average evoked response for faces and scrambled faces), then press "mip" again and the display will update (note the colours of the lines have now reversed from before, with red now corresponding to average ERP). +\subsection{Preprocessing the MEG data} -If you toggle back to condition 1 and press "movie", you will see the changes in the source strengths for the differential response over peristimulus time (from the limits 0 to 300ms currently chosen by default). +First change directory to the MEG subdirectory (either in \matlab\, or via the ``CD'' option in the SPM ``Utils'' menu) -If you press "render" you can get a very neat graphical interface to explore the data (the buttons are fairly self-explanatory). However, we will concentrate on how one might perform statistics (eg with more subjects in a group analysis). +\subsection{Adjust trigger latency} +For the EEG data, the faces were displayed directly via a CRT monitor. For the MEG data on the other hand, the faces were displayed inside the MSR via a projector. This projector produces a delay of 1.5 screen refreshes, which at 60Hz, is 25ms. This means that the subject actually saw the stimuli 25ms after the trigger was sent to the MEG acquisition machine. To correct for this visual delay, we will illustrate how to manipulate ``trial'' structures\footnote{Alternatively, you could correct by 1 refresh, to match the delay in the EEG data.}. First, we need to read in the triggers from the MEG data (unlike the EEG dataset, the MEG dataset contains information about trial type so we can define the correct condition labels already at this stage). To do this, type the following in the \matlab\ window: -\begin{figure} -\begin{center} -\includegraphics[width=90mm]{multimodal/figures/figure_32_9} -\caption{\em Graphical output of an MSP estimation of the differential ERP between faces and scrambled faces at 165ms. \label{fig_32_9}} -\end{center} -\end{figure} +\begin{verbatim} + [trl, conditionlabels, S] = spm_eeg_definetrial; +\end{verbatim} -\noindent * Press the "Window" button in the reconstruction window, enter "150 200" as the timewindow of interest and keep "0" as the frequency band of interest (0 means all frequencies). The Graphics window will then show the mean activity for this time/frequency contrast (and the contrast itself; note additional use of a Hanning window). +and follow these steps: +\begin{itemize} + \item Select the \texttt{SPM\_CTF\_MEG\_example\_faces1\_3D.ds/SPM\_CTF\_MEG\_example\_faces1\_3D.meg4} file. + \item Enter \texttt{-200} for ``Start of trial in PST [ms]'' and \texttt{600} to ``End of trial in PST [ms]''. + \item Enter 2 for ``How many conditions?''. + \begin{itemize} + \item Enter ``\texttt{faces}'' for ``Label of condition 1''. A dialog with a list of events will come up and Select the even with type \texttt{UPPT001\_up} and Value 1. + \item Enter ``\texttt{scrambled}'' for ``Label of condition 2''. Select the even with type \texttt{UPPT001\_up} and Value 2. + \end{itemize} + \item Answer ``no'' to the question about reviewing trials. + \item Answer ``yes'' to the prompt to save the trial definition. + \item Enter a filename like \texttt{trials\_run1.mat} and save in the MEG directory. +\end{itemize} + +Then type \texttt{load trials\_run1.mat} in \matlab\, to see the contents of the file you just saved. It contains two variables, \texttt{trl} and \texttt{conditionlabels}. The \texttt{trl} variable contains as many rows as triggers were found (across all conditions) and three columns: the initial sample of the epoch, the final sample of the epoch and the offset in samples corresponding to a peristimulus time of 0. The sampling rate for the MEG data was 480Hz (as can be found in the \texttt{S.fsample} field of the structure \texttt{S} returned by the \texttt{spm\_eeg\_definetrial} call above). Thus the figure of -96 samples in the third column corresponds to the 200ms baseline period that you specified. Now we need to shift the initial and final samples of the epochs by 25ms. You can do this by typing: -\noindent * If you then press "Image", press "12" for the smoothing kernel, and SPM will write 3D Nifti images corresponding to the above contrast for each condition: \begin{verbatim} - w_mfmae_eeg_1_1.nii - w_mfmae_eeg_1_2.nii - sw_mfmae_eeg_1_1.nii - sw_mfmae_eeg_1_2.nii + trl(:,1:2) = trl(:,1:2) + round(25*S.fsample/1000); + save trials_run1 trl conditionlabels \end{verbatim} -Note that the first two images are unsmoothed (but normalised); the latter two are smoothed by a 12mm isotropic Gaussian kernel. The last number in the file name refers to the condition number; the penultimate number refers to the reconstruction number (ie the number in red in the reconstruction window, i.e, D.val, here 1). -The smoothed results for Condition 1 (i.e, the differential evoked response for faces vs scrambled faces) will also be displayed in the Graphics window, together with the normalised structural. Note that the solution image is in MNI (normalised) space, because the use of a canonical mesh provides us with a mapping between the cortex mesh in native space and the corresponding MNI space. +The new trial definition is thus resaved, and we can use this file when next converting the data. -You can also of course view the image with the normal SPM "Display:image" option, and locate the coordinates of the "hotspots" in MNI space. Note that these images contain RMS (unsigned) source estimates (see Henson et al, 2007). +\subsection{Convert} -You could also explore the other inversion options, like COH and MNM, which you will note give more superficial solutions (a known problem with standard minimum norm). To do this quickly (without repeating the MRI segmentation, coregistration and forward modelling), press the "new" button in the reconstruction window, which by default will copy these parts from the previous reconstruction. +Press the \textsc{Convert} button, and in the file selection window again select the \texttt{SPM\_CTF\_MEG\_example\_\-faces1\_3D.ds} subdirectory and the \texttt{\hyphenchar\font45\relax SPM\_CTF\_MEG\_example\_faces1\_3D.meg4} file. At the prompt ``Define settings?'' select ``yes''. Here we will use the option to define more precisely the part of data that should be read during conversion. Answer ``trials'' to ``How to read?'', ``file'' to ``Where to look for trials?''. Then in the file selector window, select the new \texttt{trials\_run1.mat} file. Press ``no'' to ``Read across trials?'' and select ``meg'' for ``What channels?''. Press ``no'' to avoid saving the channel selection. Press ``Enter'' to accept the default suggestion for the name of the output dataset. Two files will be generated \texttt{espm8\_SPM\_CTF\_MEG\_example\_faces1\_3D.mat} and \texttt{espm8\_SPM\_CTF\_MEG\_example\_faces1\_3D.dat}. After the conversion is complete the data file will be automatically opened in SPM8 reviewing tool. If you click on the ``MEG'' tab you will see the MEG data which is already epoched. By pressing press the ``intensity rescaling'' button (with arrows pointing up and down) several times you will start seeing MEG activity. +\subsection{Baseline correction} -\section{MEG analysis} +We need to perform baseline correction which is not done automatically during conversion. This will prevent excessive edge artefacts from appearing after subsequent filtering and downsampling. Select \textsc{Baseline correction} from the ``Other'' drop-down menu and select the \texttt{espm8\_SPM\_CTF\_MEG\_example\_faces1\_3D.mat} file. Enter $[-200\: 0]$ for ``Start and stop of baseline [ms]''. The progress bar will appear and the resulting data will be saved to dataset \texttt{bespm8\_SPM\_\-CTF\_\-MEG\_\-example\_faces1\_3D.\{mat,dat\}}. -\subsection{Preprocessing the MEG data} +\subsection{Downsample} -* Change directory to the MEG subdirectory (either in Matlab, or via the "CD" option in the SPM "Utils" menu) +Select \textsc{Downsample} from the ``Other'' drop-down menu and select the \texttt{bespm8\_SPM\_CTF\_\-MEG\_\-example\_\-faces1\_3D.mat} file. Choose a new sampling rate of 200 (Hz). The progress bar will appear and the resulting data will be saved to dataset \texttt{dbespm8\_SPM\_CTF\_MEG\_example\_faces1\_3D.\{mat,dat\}}. -* Press 'Artefacts', select the \verb!e_meg.mat! file, press 'no' to the 'read own artefact list?' question, but 'yes' to 'robust average?' and select the default 'offset weighting function' (3) and default FWHM residual smoothing (20), and 'no' to 'threshold channels?' +\subsection{Batch preprocessing} -This will take a while. The new file produced, \verb!ae_meg.mat!, will contain the same data, but a new field of "D.weights" will be created. These weights will be applied during the next step of averaging (see Kilner et al, in prep.): +Here we will preprocess the second half of the MEG data using using SPM8 batch to demonstrate this third (after interactive GUI and Matlab script) possibility. First though, we have to correct the visual onset latency for the second run, repeating the above steps that you did for the first run: -* Press the 'Averaging' button and select the \verb!ae_meg.mat! file. After a few moments, the matlab window will echo: \begin{verbatim} - e_meg.mat: Number of replications per contrast: - average 1: 86 trials, average 2: 86 trials + [trl, conditionlabels, S] = spm_eeg_definetrial; \end{verbatim} - and a new file will be created in the MEG directory called \verb!mae_meg.mat! ("m" for "mean") -* Press the 'Filtering' button, select the \verb!mae_eeg.mat! file, select 'lowpass', and enter 40 (Hz) as the cutoff. This smooths the data to 40Hz, producing the file \verb!fmae_eeg.mat! (see again footnote 5 about filtering). +and select the \texttt{SPM\_CTF\_MEG\_example\_faces2\_3D.ds} subdirectory and the \texttt{SPM\_\-CTF\_\-MEG\_\-example\_\-faces2\_3D.meg4} file. Then enter \texttt{-200} for ``Start of trial in PST [ms]'' and \texttt{600} to ``End of trial in PST [ms]''. Enter \texttt{2} for ``How many conditions?''. Enter ``\texttt{faces}'' for ``Label of condition 1''. A dialog with a list of events will come up. Select the even with type ``\texttt{UPPT001\_up}'' and Value 1. Enter \texttt{scrambled} for ``Label of condition 2''. Select the even with type ``\texttt{UPPT001\_up}'' and Value 2. Answer ``no'' to the question about reviewing trials, but ``yes'' to the prompt to save the trial definition. Enter a filename like \texttt{trials\_run2.mat} and save in the MEG directory. Then type: -As before, you can display these data by "Display: M/EEG" and selecting the \verb!fmae_eeg.mat!. Hold SHIFT and select trial-type 2 with the mouse in the bottom right of the window to see both conditions superimposed (as Figure~\ref{fig_32_10}). +\begin{verbatim} + load trials_run2 + trl(:,1:2) = trl(:,1:2)+round(25*S.fsample/1000); + save trials_run2 trl conditionlabels +\end{verbatim} -\begin{figure} -\begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_10} -\caption{\em SPM Display window for mean, smoothed ERF (fmae-meg.mat) for all 151 MEG channels. \label{fig_32_10}} -\end{center} -\end{figure} +Now press the \textsc{Batch} button (lower right corner of the SPM8 menu window). Batch tool window will appear. We will define exactly the same settings as we have just done using the interactive GUI. From the ``SPM'' menu, ``M/EEG'' submenu select ``M/EEG Conversion''. Click on ``File name'' and select the \texttt{SPM\_CTF\_MEG\_example\_faces2\_3D.meg4} file from \texttt{SPM\_CTF\_\-MEG\_\-example\_\-faces2\_3D.ds} subdirectory. Click on ``Reading mode'' and switch to ``Epoched''. Click on ``Epoched'' and choose ``Trial file'', double-click on the new ``Trial file'' branch and then select the \texttt{trials\_run2.mat} file. Then click on ``Channel selection'' and select MEG from the menu below. Finally enter \texttt{espm8\_SPM\_CTF\_MEG\_example\_faces2\_3D} for ``Output filename'' to be consistent with the file preprocessed interactively. + +Now select ``M/EEG Baseline correction'' from the ``SPM'' menu, ``M/EEG'' submenu. Another line will appear in the Module list on the left. Click on it. Baseline correction configuration branch will appear. Select ``File name'' with a single click. The file that we need to downsample has not been generated yet but we can use the ``Dependency'' button. A dialog will appear with a list of previous steps (in this case just the conversion) and we can set the output of one of these steps as the input to the present step. Now just enter enter $-200\: 0$ for ``Baseline''. Similarly we can now add ``M/EEG Downsampling'' to the module list, define the output of baseline correction step for ``File name'' and 200 for the ``New sampling rate''. This completes our batch. We can now save it for future use (e.g, as \texttt{batch\_meg\_preprocess} and run it by pressing the green ``Run'' button. This will generate all the intermediate datasets and finally \texttt{dbespm8\_SPM\_CTF\_MEG\_example\_faces2\_3D.\{mat,dat\}}. + +\subsection{Merge} -You can also press this 'Channels' button and in the new window, "deselect" all the channels, then select MRT24 and MLT24 (e.g, from the channel names on the right), and press 'ok'. (It will help if you save these selections as a file, which can be used later to display only a subset of channels). You will now only see these two channels in the SPM Graphics Window, which clearly show a difference between faces (trial-type 1, in blue) and scrambled faces (trial-type 2, in red) around approximately 170ms (the "M170"; Figure~\ref{fig_32_11}). The sign of this difference is reversed across left and right hemispheres, as is common for the axial components of the magnetic fields from tangential current sources. +We will now merge the two epoched files we have generated until now and continue working on the merged file. Select the \textsc{Merge} command from the ``Other'' drop-down menu. In the selection window that comes up click on \texttt{dbespm8\_SPM\_CTF\_MEG\_example\_faces1\_3D.mat} and \texttt{dbespm8\_SPM\_CTF\_MEG\_example\_faces2\_3D.mat}. Press ``done''. Answer ``Leave as they are'' to ``What to do with condition labels?''. A new dataset will be generated called \texttt{cdbespm8\_SPM\_CTF\_\-MEG\_\-example\_\-faces1\_\-3D.\{mat,dat\}}. +\subsection{Prepare} + +In this section we will add the separately measured headshape points to our merged dataset. This is useful when one wants to improve the coregistration using head shape measured outside the MEG. Also in some cases the anatomical landmarks detectable on the MRI scan and actual locations of MEG locator coils do not coincide and need to be measured in one common coordinate system by an external digitizer (though this is not the case here). First lets examine the contents of the headshape file. If you load it to \matlab\ workspace (type \texttt{load headshape.mat}), you will see that it contains one \matlab\ structure called \texttt{shape} with the following fields: +\begin{itemize} +\item \texttt{.unit} - units of the measurement (optional) +\item \texttt{.pnt} - Nx3 matrix of headshape points +\item \texttt{.fid} - substruct with the fields .pnt - Kx3 matrix of points and .label -Kx1 cell array of point labels. +\end{itemize} + +The difference between \texttt{shape.pnt} and \texttt{shape.fid.pnt} is that the former contains unnamed points (such as continuous headshape measurement) whereas the latter contains labeled points (such as fiducials). Note that this Polhemus space (which will define the ``head space'') has the X and Y axes switched relative to MNI space. + +Now select \textsc{Prepare} from the ``Other'' menu and in the file selection window select \texttt{cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_3D.mat}. A menu will appear at the top of SPM interactive window (bottom left window). In the ``Sensors'' submenu choose ``Load MEG Fiducials/Headshape''. In the file selection window choose the \texttt{headshape.mat} file and save the dataset with \texttt{File/Save}. + +\subsection{Basic ERFs} + +Press the \textsc{Averaging} button and select the \texttt{cdbespm8\_SPM\_CTF\_MEG\_example\_faces1\_3D.mat} file. Answer ``yes'' to ``Use robust averaging?''. You can either save the weights if you want to examine them or not save if you want the averaging to work faster since the weights dataset that needs to be written is quite large. Answer ``no'' to ``weight by condition'' and accept the default ``Offset of the weighting function''. New dataset will be created in the MEG directory called \texttt{mcdbespm8\_SPM\_CTF\_MEG\_example\_faces1\_3D.\{mat,dat\}} (``m'' for ``mean''). + +As before, you can display these data by ``Display: M/EEG'' and selecting the \texttt{mcdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_3D.mat}. In the MEG tag with the scalp radio button selected, hold the Shift key and select trial-type 2 with the mouse in the bottom right of the window to see both conditions superimposed (as Figure~\ref{multimodal:fig:10}). \begin{figure} \begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_11} -\caption{\em Two selected MEG channels (MLT24 and MRT24). \label{fig_32_11}} +\includegraphics[width=100mm]{multimodal/figures/meg_scalp_erf} +\caption{\em SPM Display window for mean, smoothed ERF (\texttt{mcdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D.mat}) for all 275 MEG channels. \label{multimodal:fig:10}} \end{center} \end{figure} -* Select "Contrast" from the "Other..." pulldown menu on the SPM window (or type \verb!spm_eeg_weight_epochs! in the Matlab window). This function creates linear contrasts of ERPs/ERFs. Select the \verb!fmae_meg.mat! file, and enter [1 -1; 1/2 1/2]as the contrasts. This will create new file \verb!mfmae_meg.mat!, in which the first trial-type is now the differential ERF between faces and scrambled faces, and the second trial-type is the average ERF. +Select ``Contrast'' from the ``Other'' pulldown menu on the SPM window. This function creates linear contrasts of ERPs/ERFs. Select the \texttt{mcdbespm8\_SPM\_CTF\_MEG\_example\_faces1\_3D.mat} file, enter $[1\: -1]$ as the first contrast and label it ``\texttt{Difference}'', answer ``yes'' to ``Add another'', enter $[1/2\: 1/2]$ as the second contrast and label it ``\texttt{Mean}''. Press ``no'' to the question ``Add another'' and not to ``weight by num replications''. This will create new file \texttt{wmcdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D.mat}, in which the first trial-type is now the differential ERF between faces and scrambled faces, and the second trial-type is the average ERF for faces and scambled faces. -If you want to see the 2D topography of the differential ERF between faces and scrambled faces, you can Display the new file \verb!mfmae_eeg.mat!, select trial-type 1, press "Topography" and in the new window, select "2D" and 165ms as the timepoint (Figure~\ref{fig_32_12}). This will show a bilinear interpolation of the difference across the 151 channels. +To see the topography of the differential ERF, select ``Display: M/EEG'', MEG tab and click on Trial 1, press the ``topography'' button at the top of the window and scroll to 180ms for the latency to produce Figure~\ref{multimodal:fig:12}. You can move the slider left and right to see the development of the M170 over time. \begin{figure} \begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_12} -\caption{\em 2D topography of the ERF of faces minus scrambled faces at 165ms\label{fig_32_12}} +\includegraphics[width=100mm]{multimodal/figures/meg_topo180} +\caption{\em 2D topography of the ERF of faces minus scrambled faces at 180ms. \label{multimodal:fig:12}} \end{center} \end{figure} @@ -390,264 +485,224 @@ \subsection{Time-Frequency Analysis} SPM uses Morlet wavelets to perform time-frequency analyses. -* Select the 'time-frequency' option under the 'Other' pull-down menu, and select the \verb!ae_meg.mat! file. SPM will then prompt you for the frequencies you wish to analyse, for which you can type [5:40] (Hz). To the question "remove baseline", press "no" (because for frequencies as low as 5Hz, one would need a longer pre-stimulus baseline, to avoid edge-effects\footnote{For example, for 5Hz, one would need at least N/2 x 1000ms/5, where N is the order of the Morlet wavelets (i.e, number of cycles per Gaussian window), e.g, 600ms for a 6th-order wavelet.}). Later, we will compare two trial-types directly, and hence any pre-stimulus differences will become apparent. Change the default Morlet wavelet order (N) from 7 to 5. This factor effectively trades off frequency vs time resolution, with a lower order giving higher temporal resolution. You will then be prompted to select channels, for which you can highlight and delete the default option of all channels, and type just 66 (which corresponds to channel 'MLT34', as can be confirmed by typing D.channels.names in the Matlab window)\footnote{You can of course obtain time-frequency plots for every channel, but it will take much longer (and result in a large file).}. +Select the \textsc{time-frequency} option under the ``Other'' pull-down menu, and select the \texttt{cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D.mat} file. SPM will then prompt you for the frequencies you wish to analyse, for which you can type [5:40] (Hz). To the question ``remove baseline'', press ``no'' (because for frequencies as low as 5Hz, one would need a longer pre-stimulus baseline, to avoid edge-effects\footnote{For example, for 5Hz, one would need at least N/2 x 1000ms/5, where N is the order of the Morlet wavelets (i.e, number of cycles per Gaussian window), e.g, 600ms for a 6th-order wavelet.}). Later, we will compare two trial-types directly, and hence any pre-stimulus differences will become apparent. Change the default Morlet wavelet order (N) from 7 to 5. This factor effectively trades off frequency vs time resolution, with a lower order giving higher temporal resolution. You will then be prompted to select channels, for which you can highlight and delete the default option of all channels, and type just 114 (which corresponds to channel ``MLT34'', as can be confirmed by typing D.indchannel(`MLT34') in the \matlab\ window)\footnote{You can of course obtain time-frequency plots for every channel, but it will take much longer (and result in a large file).}. Answer ``yes'' to ``Compute phase?''. -This will produce two new files, \verb!t1_e_eeg.mat! and \verb!t2_e_eeg.mat!. The former contains the power at each frequency, time and channel; the latter contains the corresponding phase angles. +This will produce two new datasets, \texttt{tf1\_\-cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_3D.\{mat,dat\}} and \texttt{tf2\_\-cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D.\{mat,dat\}}. The former contains the power at each frequency, time and channel; the latter contains the corresponding phase angles. -* Press the 'Averaging' button and select the \verb!t1_e_meg.mat! file. After a few moments, the matlab window will echo: -\begin{verbatim} - e_meg.mat: Number of replications per contrast: - average 1: 86 trials, average 2: 86 trials -\end{verbatim} - and a new file will be created in the MEG directory called \verb!mt1_e_meg.mat!. +Press the \textsc{Averaging} button and select the \texttt{tf1\_\-cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D.mat} file. You can use straight (or robust if you prefer) averaging to compute the average time-frequency representation. A new file will be created in the MEG directory called \texttt{mtf1\_cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D.\{mat,dat\}}. Note that you can use the reviewing tool to review the time-frequency datasets. -This contains the power spectrum averaged over all trials, and will include both "evoked" and "induced" power. Induced power is (high-frequency) power that is not phase-locked to the stimulus onset, which is therefore removed when averaging the amplitude of responses across trials (i.e, would be absent from a time-frequency analysis of the \verb!mae_eeg.mat! file). +This contains the power spectrum averaged over all trials, and will include both ``evoked'' and ``induced'' power. Induced power is (high-frequency) power that is not phase-locked to the stimulus onset, which is therefore removed when averaging the amplitude of responses across trials (i.e, would be absent from a time-frequency analysis of the \texttt{mcdbespm8\_SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D.mat} file). -The power spectra for each trial-type can be displayed using the usual Display button and selecting the \verb!mt1_e_eeg.mat! file. This will produce a plot of power as a function of frequency (y-axis) and time (x-axis) for Channel MLT34. If you use the "trial" slider to switch between trial(types) 1 and 2, you will see the greater power around 150ms and 10Hz for faces than scrambled faces (click on one channel to get scales for the axes, as in Figure~\ref{fig_32_13}). This corresponds to the M170 again. +The power spectra for each trial-type can be displayed using the usual Display button and selecting the \texttt{mtf1\_\-cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_3D.mat} file. This will produce a plot of power as a function of frequency (y-axis) and time (x-axis) for Channel MLT34. If you use the ``trial'' slider to switch between trial(types) 1 and 2, you will see the greater power around 150ms and 10Hz for faces than scrambled faces (click on the magnifying glass icon and on the single channel to get scales for the axes, as in Figure~\ref{multimodal:fig:13}). This corresponds to the M170 again. \begin{figure} \begin{center} -\includegraphics[width=60mm]{multimodal/figures/figure_32_13_L} -\includegraphics[width=60mm]{multimodal/figures/figure_32_13_R} -\caption{\em Total power spectra for faces (left) and scrambled faces (right) for channel MLT34\label{fig_32_13}} +\includegraphics[width=60mm]{multimodal/figures/meg_pow_faces} +\includegraphics[width=60mm]{multimodal/figures/meg_pow_scrambled} +\caption{\em Total power spectra for faces (left) and scrambled faces (right) for channel MLT34\label{multimodal:fig:13}} \end{center} \end{figure} -We can also look at evidence of phase-locking of ongoing oscillatory activity by averaging the phase angle information. This time, we do not take the straight (arithmetric) mean, since the data are phase angles, and this average is not particularly meaningful. Instead we calculate their vector mean (when converting the angles to -vectors in Argand space), which corresponds to a "Phase-Locking Value" (PLV) which lies between 0 (no phase-locking across trials) to 1 (perfect phase-locking). +We can also look at evidence of phase-locking of ongoing oscillatory activity by averaging the phase angle information. This time, we do not take the straight (arithmetric) mean, since the data are phase angles, and this average is not particularly meaningful. Instead we calculate their vector mean (when converting the angles to vectors in Argand space), which corresponds to a ``Phase-Locking Value'' (PLV) which lies between 0 (no phase-locking across trials) to 1 (perfect phase-locking). + +Press the \textsc{Averaging} button and select the \texttt{tf2\_\-cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D.mat} file. This time you will be prompted for either a straight or a vector average, for which you should select ``Vector (PLV)''. The \matlab\ window will echo: -* Press the 'Averaging' button and select the \verb!t2_e_meg.mat! file. This time you will be prompted for either a straight or a vector average, for which you should select "vector". The matlab window will echo: \begin{verbatim} - e_meg.mat: Number of replications per contrast: - average 1: 86 trials, average 2: 86 trials + mtf2_cdbespm8_SPM_CTF_MEG_example_faces1_3D.mat: Number of replications per contrast: + average faces: 168 trials, average scrambled: 168 trials \end{verbatim} - and a new file will be created in the MEG directory called \verb!mt2_e_meg.mat!. -If you now display the file \verb!mt2_e_eeg.mat! file, you will see PLV as a function of frequency (y-axis) and time (x-axis) for Channel MLT34. Again, if you use the "trial" slider to switch between trial(types) 1 and 2, you will see greater phase-locking around 10Hz and 100ms for faces than scrambled faces, as in Figure~\ref{fig_32_14}. Together with the above power analysis, these data suggest that the M170 includes an increase both in power and in phase-locking of ongoing oscillatory activity in the alpha range (Henson et al, 2005b). +and a new file will be created in the MEG directory called \texttt{mtf2\_\-cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D.mat}. +If you now display the file \texttt{mtf2\_\-cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D.mat} file, you will see PLV as a function of frequency (y-axis) and time (x-axis) for Channel MLT34. Again, if you use the ``trial'' slider to switch between trial(types) 1 and 2, you will see greater phase-locking around for faces than scrambled faces at lower frequencies, as in Figure~\ref{multimodal:fig:14}. Together with the above power analysis, these data suggest that the M170 includes an increase both in power and in phase-locking of ongoing oscillatory activity in the alpha range (Henson et al, 2005b). \begin{figure} \begin{center} -\includegraphics[width=60mm]{multimodal/figures/figure_32_14_L} -\includegraphics[width=60mm]{multimodal/figures/figure_32_14_R} -\caption{\em Phase-Locking Values for faces (left) and scrambled faces (right) for channel MLT34 \label{fig_32_14}} +\includegraphics[width=60mm]{multimodal/figures/meg_plv_faces} +\includegraphics[width=60mm]{multimodal/figures/meg_plv_faces} +\caption{\em Phase-Locking Values for faces (left) and scrambled faces (right) for channel MLT34 \label{multimodal:fig:14}} \end{center} \end{figure} \subsection{2D Time-Frequency SPMs} -Analogous to Section~\ref{3DSPM}, we can also use Random Field Theory to correct for multiple statistical comparisons across the 2-dimensional time-frequency space. +Analogous to Section~\ref{multimodal:eeg:3DSPM}, we can also use Random Field Theory to correct for multiple statistical comparisons across the 2-dimensional time-frequency space. -* Type \verb!spm_eeg_convertmat2ana3Dtf! in the Matlab window, and select the \verb!t1_e_eeg.mat! file. +Select \textsc{Convert to images} in the ``Other'' pulldown menu, and select the \texttt{tf1\_cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D.mat} file. You will be asked whether you want to average over channels or frequencies. Even though there is actually only one channel in this dataset, select ``channels''. -This will create time-frequency images for each trial of the two types, with dimensions 161x36x1, as for the example shown in Figure~\ref{fig_32_15} from pressing "Display: images" on the main SPM window. +This will create 3D time-frequency images for each trial of the two types with dimensions $36\times 161\times 1$, as for the example shown in Figure~\ref{multimodal:fig:15}. These images can be found in the subdirectories \texttt{1ROI\_TF\_\-trialtype\_\-faces} and \texttt{1ROI\_TF\_\-trialtype\_\-scrambled} of the new directory created \texttt{tf1\_\-cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_3D}, and examined by pressing ``Display: images'' on the main SPM window. \begin{figure} \begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_15} -\caption{\em 3D image for trial 2 of t1-e-eeg.mat. The left section is through time (x) and frequency (y) (the right image is an y-z section, though there is only one value in z, i.e, it is really a 2D image).\label{fig_32_15}} +\includegraphics[width=100mm]{multimodal/figures/meg_TFimage} +\caption{\em 3D image for trial 2 within \texttt{1ROI\_TF\_trialtype\_faces} subdirectory. The bottom left section is through frequency (x) and time (y) (the other images are strips because there is only one value in the z dimension, i.e, this is really a 2D image).\label{multimodal:fig:15}} \end{center} \end{figure} -As in Section~\ref{3DSPM}, we then take these images into an unpaired t-test across trials to compare faces versus scrambled faces. We can then use classical SPM to identify times and frequencies in which a reliable difference occurs, correcting across the multiple comparisons entailed (Kilner et al, 2005). +As in Section~\ref{multimodal:eeg:3DSPM}, we then take these images into an unpaired t-test across trials to compare faces versus scrambled faces. We can then use classical SPM to identify times and frequencies in which a reliable difference occurs, correcting across the multiple comparisons entailed (Kilner et al, 2005). -* First create a new directory, eg. mkdir TFstatsPow. +First create a new directory, eg. \texttt{mkdir TFstatsPow}. -* Then press the "specify 2nd level" button, select "two-sample t-test" (unpaired t-test), and define the images for "group 1" as all those in the subdirectory "trialtype1" (using right mouse, and "select all") and the images for "group 2" as all those in the subdirectory "trialtype2". Finally, specify the new TFstatsPow directory as the output directory, and press "run". (Note that this will be faster if you saved and could load an SPM job file from Section~\ref{3DSPM}). - -This will produce the design matrix for a two-sample t-test. - -* The press "Estimate", and when it has finished, press "Results" and define a new T-contrast as [1 -1]. Keep the default contrast options, but threshold at $p<.05$ FWE corrected for the whole "image". Then press "whole brain", and the Graphics window should now look like that in Figure~\ref{fig_32_16} (ignore the glass brain MIP). +Then press the \textsc{specify 2nd level} button, select ``two-sample t-test'' (unpaired t-test), and define the images for ``group 1'' as all those in the subdirectory \texttt{trialtype\_faces} (using right click, and ``select all'') and the images for ``group 2'' as all those in the subdirectory \texttt{trialtype\_scrambled}. Finally, specify the new \texttt{TFstatsPow} directory as the output directory. (Note that this will be faster if you saved and could load an SPM job file from Section~\ref{multimodal:eeg:3DSPM} in order to just change the input files and output directory.) Then add an ``Estimate'' module from the ``SPM'' tab, and select the output from the previous factorial design specification stage as the dependency input. Press ``Run'' (green arrow button). +Press \textsc{Results} and define a new T-contrast as $[1\: -1]$. Keep the default contrast options, but threshold at $p<.05$ FWE corrected for the whole search volume, and then select ``Time-Frequency'' for the ``Data Type''. Then press ``whole brain'', and the Graphics window should now look like that in Figure~\ref{multimodal:fig:16}. \begin{figure} \begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_16} -\caption{\em 2D time-frequency SPM{T} at $p<.001$ uncorrected for the power difference between face and scrambled faces at Channel MLT34. Note that the brain outline in the MIP should be ignored. The x coordinates refer to time in ms; the y coordinates refer to frequency in Hz (the z-coordinate is always 1).\label{fig_32_16}} +\includegraphics[width=100mm]{multimodal/figures/meg_TF_results} +\caption{\em 2D time-frequency SPM{T} at $p<.05$ FWE-corrected for the power difference between face and scrambled faces at Channel MLT34.\label{multimodal:fig:16}} \end{center} \end{figure} -This will list one "region" within the 2D time-frequency space in which faces produce greater power than scrambled faces, having corrected for multiple T-tests across pixels. This has a maximum of [180 12 1], ie 12 Hz and 180ms post-stimulus. +This will list two ``regions'' within the 2D time-frequency space in which faces produce greater power than scrambled faces, having corrected for multiple T-tests across pixels. The larger one has a maximum at 5 Hz and 185 ms post-stimulus, corresponding to the M170 seen earlier in the averaged files (but now with a statistical test of its significance, in terms of evoked and induced power). The second, smaller region has a maximum at 12 Hz and 100 ms, possibly corresponding to a smaller but earlier effect on the M100 (also sometimes reported, depending on what faces are contrasted with). -If you repeat the above time-frequency analysis on the \verb!e_meg.mat! file, but this time keep every channel and answer 'yes' to the "average over channels?" question, and then repeat the above statistical analysis of power, you will notice that there is also a reliable decrease in induced high-frequency power (around 400ms and 35 Hz) for faces vs scrambled faces, which could also be source-localised. +\subsection{``Imaging'' reconstruction of differential power} +In Section~\ref{multimodal:eeg:3D} we localised the differential evoked potential difference in EEG data corresponding to the N170. Here we will localise the total power of faces vs scrambled faces in a timewindow corresponding to that of the M170, ie including potential induced components (see Friston et al, 2006). -\subsection{"Imaging" reconstruction of differential power} +Press the ``3D source reconstruction'' button, and press the ``load'' button at the top of the new window. Select the \texttt{cdbespm8\_SPM\_CTF\_MEG\_example\_faces1\_3D.mat} file and type a label (eg \texttt{M170}) for this analysis. -In Section~\ref{3D} we localised the differential evoked potential difference in EEG data corresponding to the N170. Here we will localise the total power of faces vs scrambled faces in a timewindow corresponding to that of the M170, ie including potential induced components (see Friston et al, 2000). +Press the ``MRI'' button, select the \texttt{smri.img} file within the \texttt{sMRI} sub-directory and select the ``normal'' mesh. -* Press the '3D source reconstruction' button, and press the "load" button at the top of the new window. Select the \verb!e_meg.mat! file and type a label (eg "M170") for this analysis. +If you have not used this MRI image for source reconstruction before, this step will take some time while the MRI is segmented and the deformation parameters computed (see Section~\ref{multimodal:eeg:3D} for more details on these files). When meshing has finished, the cortex (blue), inner skull (red) and scalp (orange) meshes will also be shown in the Graphics window with slices from the sMRI image. This makes it possible to verify that the meshes indeed fit the original image well. The field \texttt{D.inv\{1\}.mesh} will be updated. Press ``save'' in top right of window to update the corresponding mat file on disk. -* Press the 'MRI' button, select 3000 for the number of vertices in the mesh, and select the smri.img file within the sMRI sub-directory... +Both the cortical mesh and the skull and scalp meshes are not created directly from the segmented MRI, but rather are determined from template meshes in MNI space via inverse spatial normalisation (Mattout et al, 2007; Henson et al, 2009a). -This will take some time while the MRI is segmented and binary images of the skull created (see Section~\ref{3D} for more details on these files)\footnote{Note that this procedure can be shortened in the batch script included here, by loading the normalisation parameters and binary masks from previous segmentations of the structural MRI.}. +Press the ``Co-register'' button. You will be asked for each of the 3 fiducial points to specify its location on the MRI images. This can be done by selecting a corresponding point from a hard-coded list (``select''). These points are inverse transformed for each individual image using the same deformation field that is used to create the meshes. The other two options are typing the MNI coordinates for each point (``type'') or clicking on the corresponding point in the image (``click''). Here, we will type coordinates based on where the experimenter defined the fiducials on the \texttt{smri.img}. These coordinates can be found in the \texttt{smri\_fid.txt} file also provided. So press ``type'' and for ``nas'', enter [0 91 -28]; for ``lpa'' press ``type'' and enter [-72 4 -59]; for ``rpa'' press ``type'' and enter [71 -6 -62]. Finally, answer ``yes'' to ``Use headshape points?''. -The choice of the minimum of 3000 vertices in the cortical mesh is simply to reduce computation time (the actual number of vertices resulting will be 3004). +This stage coregisters the MEG sensor positions with the structural MRI and cortical mesh, via an approximate matching of the fiducials in the two spaces, followed by a more accurate surface-matching routine that fits the head-shape function (measured by Polhemus) to the scalp that was created in the previous meshing stage via segmentation of the MRI. When coregistration has finished, the field \texttt{D.inv\{1\}}.datareg will be updated. Press ``save'' in top right of window to update the corresponding mat file on disk. With the \matlab\ Rotation tool on (from the ``Tools'' tab in the SPM Graphics window, if not already on), right click near the top image and select ``Go to X-Z'' view. This should produce a figure like that in Figure~\ref{multimodal:fig:17} will also be produced, which you can rotate with the mouse to check all sensors. Note that the data are in head space (not MNI space), in this case corresponding to the Polhemus space in which the X and Y dimensions are swapped relative to MNI space. -Note that the cortical mesh (and the distances within the mesh) are not created directly from the segmented MRI (like the skull and scalp meshes), but rather are determined from a template cortical mesh in MNI space via inverse spatial normalisation (Mattout et al, in press). +\begin{figure} +\begin{center} +\includegraphics[width=70mm]{multimodal/figures/meg_coreg.png} +\caption{\em Graphical output of registration of MEG and sMRI data, showing (upper panel) cortex (blue) and scalp (black) meshes, sensor locations (green), MRI and Polhemus fiducials (cyan/magneta), and headshape (red dots).\label{multimodal:fig:17}} +\end{center} +\end{figure} -When meshing has finished, the field D.inv\{1\}.mesh field will be updated in matlab. Press "save" in top right of window to update the corresponding mat file on disk. The cortex (blue), inner skull (red) and scalp (orange) meshes will also be shown in the Graphics window. +Press the ``Forward Model'' button. Choose ``Local Spheres'' (you may also try the other options; see Henson et al, 2009a). A figure will be displaying showing the local (overlapping) spheres fit to each sensor, and final the set of all spheres. -* Press the 'Co-register' button, respond "no" to the 'Read Polhemus?' question, and then select the following files in response to each prompt (pressing "yes" to the 'Use headshape file' prompt): -\begin{verbatim} - MEG/Polhemus/meg_sens_loc.mat - MEG/Polhemus/meg_fids.mat - MEG/Polhemus/meg_hsf.mat - MEG/Polhemus/meg_sens_or.mat - sMRI/smri_fids.mat -\end{verbatim} - (like in Section~\ref{3DSPM}, except now we also need to provide information about the orientation of each MEG sensor, as in the penultimate file here). +Press ``Invert'', select ``Imaging'', select ``yes'' to ``All conditions or trials?'', and ``Standard'' for the model (i.e, to use defaults; you can customise a number of options if you press Custom instead) (see Friston et al, 2008, for more details about these parameters). There will be lead field computation followed by the actual inversion and a plot of summary of the results will be displayed at the end, as in Figure~\ref{multimodal:fig:18}. -This stage coregisters the MEG sensor positions and orientations (in "MEG" space) with the structural MRI and solution mesh (in "MRI" space). This is done via an approximate matching of the fiducials in the two spaces, followed by a more accurate surface-matching routine that fits the head-shape function (in "MEG" space) to the scalp that was created in the previous meshing stage via segmentation of the MRI. The match will be shown in a window like that in Figure~\ref{fig_32_17}. (Note that the match of the MEG and MRI fiducials is poor because the MEG fiducials did not correspond exactly to the nasion and peri-aricular points (see footnote 3); this does not matter because the coregistration is dominated by the close fit of the digitized headshape to the scalp mesh). +You can now explore the results via the 3D reconstruction window. If you type 159 into the box in the bottom right (corresponding to the time in ms) and press ``mip'', you should see bilateral ventral lateral temporal activity. Note that this localisation is different from the previous EEG localisation because 1) condition 1 now refers to faces, not the difference between faces and scrambled faces, and 2) the results reflect total power (across trials), induced and evoked, rather than purely evoked\footnote{Though in reality, most of the power is low-frequency and evoked}. The timecourses come from the peak voxel. The red line shows the condition currently being shown (corresponding to the ``Condition 1'' toggle bar in the reconstruction window); the grey line(s) will show all other conditions. If you press the "condition 1" toggle, it will change to Condition 2, which is the total power for scrambled faces, then press ``mip'' again and the display will update (note the colours of the lines have now reversed from before, with red now corresponding to scrambled faces). + +If press ``movie'', you will see the changes in the source strengths over peristimulus time (from the limits 0 to 300ms currently chosen by default). + +If you press ``render'' you can get a very neat graphical interface to explore the data (the buttons are fairly self-explanatory). However, we will concentrate on how one might perform statistics. \begin{figure} \begin{center} -\includegraphics[width=70mm]{multimodal/figures/figure_32_17} -\caption{\em Graphical output of registration of MEG and sMRI data, showing (upper panel) cortex (blue) and scalp (black) meshes, sensor locations (green), MRI and Polhemus fiducials (cyan/magneta), and headshape (red dots).\label{fig_32_17}} +\includegraphics[width=90mm]{multimodal/figures/meg_msp.png} +\caption{\em Graphic output for MSP-estimated activity at 159ms for faces.\label{multimodal:fig:18}} \end{center} \end{figure} -When coregistration has finished, the field D.inv\{1\}.datareg will be updated in matlab. Press "save" in top right of window to update the corresponding mat file on disk. Finally, a figure like that in Figure~\ref{fig_32_17} will also be produced, which you can rotate with the mouse (using the Rotate3D Matlab Menu option) to check all sensors. +Press the ``Window'' button in the reconstruction window, enter ``150 200'' as the timewindow of interest and ``5 15'' as the frequency band of interest (from the SPM time-frequency analysis, at least from one channel). Then choose the ``induced'' option. After a delay (as SPM calculates the power across all trials) the Graphics window will show the mean activity for this time/frequency contrast (for faces alone, assuming the condition toggle is showing ``condition 1''). + +If you then press ``Image'', press ``12'' for the smoothing kernel, and SPM will write 3D NIfTI images corresponding to the above contrast for each condition: -* Press the 'Forward Model' button. This assumes the sensors lie on a single best-fitting sphere, which allows analytical computation of the forward model (lead field) that maps each "dipole" in the cortical mesh to each sensor, assuming that the orientation of the dipole at each vertex is normal to the surface of the mesh at that point. This stage uses BrainStorm functions~\footnote{Brainstorm is available from http://neuroimage.usc.edu/ResearchMEGEEGBrainStorm.html}. The Matlab window will output: \begin{verbatim} - Scalp best fitting sphere computed (in 11 iterations) - Centre = [0.0001 -0.0218 0.0027] (Radius = 0.0774) + w_cdbespm8_SPM_CTF_MEG_example_faces1_3D_1_1.nii + w_cdbespm8_SPM_CTF_MEG_example_faces1_3D_1_2.nii + sw_cdbespm8_SPM_CTF_MEG_example_faces1_3D_1_1.nii + sw_ecdbespm8_SPM_CTF_MEG_example_faces1_3D_1_2.nii \end{verbatim} -* Press 'Invert.', select 'Classical', select 'yes' to 'All conditions or trials?', select 'MSP' (for Multiple Sparse Priors) for the type of inversion, "Standard" for the model (i.e, to use defaults; you can customise a number of options if you press Custom instead) (see Friston et al, in press-a, for more details about these parameters). -Press "save" to save the results. You can now explore the results via the 3D reconstruction window. If you type 165 into the box in the bottom right (corresponding to the time in ms) and press "mip", you should see an output like in Figure~\ref{fig_32_18}. This fit explains approx 87\% of the data. +Note that the first two images are unsmoothed (but normalised); the latter two are smoothed by a 12mm isotropic Gaussian kernel. The last number in the file name refers to the condition number; the penultimate number refers to the reconstruction number (i.e. the number in red in the reconstruction window, i.e, \texttt{D.val}, here 1). -Note the hot-spots in the fusiform. The timecourses come from the peak voxel. The red line shows the condition currently being shown (corresponding to the "Condition 1" toggle bar in the reconstruction window); the grey line(s) will show all other conditions. Condition 1 is faces; if you press the "condition 1" toggle, it will change to Condition 2 (scrambled faces), then press "mip" again and the display will update (note the colours of the lines have now reversed from before, with red now corresponding to scrambled faces). +The smoothed results for Condition 1 will also be displayed in the Graphics window, together with the normalised structural. Note that the solution image is in MNI (normalised) space, because the use of a canonical mesh provides us with a mapping between the cortex mesh in native space and the corresponding MNI space. -If you toggle back to condition 1 and press "movie", you will see the changes in the source strengths over peristimulus time (from the limits 0 to 300ms currently chosen by default). +You can also of course view the image with the normal SPM ``Display:image'' option, and locate the coordinates of the ``hotspots'' in MNI space. Note that these images contain RMS (unsigned) source estimates (see Henson et al, 2007). -If you press "render" you can get a very neat graphical interface to explore the data (the buttons are fairly self-explanatory). However, we will concentrate on how one might perform statistics. +If you want to see where activity (in this time/freq contrast) is greater for faces and scrambled faces, you can use SPM \texttt{ImCalc} facility to create a difference image of \texttt{sw\_\-cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D\_\-1\_\-1.nii} - \texttt{sw\_\-cdbespm8\_\-SPM\_\-CTF\_\-MEG\_\-example\_\-faces1\_\-3D\_\-1\_\-2.nii}: you should see bilateral fusiform. For further discussion of localising a differential effect (as in Section~\ref{multimodal:eeg:3D} with ERPs), vs taking the difference of two localisations, as here, see Henson et al (2007). +You could also explore the other inversion options, like COH and IID, which you will note give more superficial solutions (a known problem with standard minimum norm; see also Friston et al, 2008; Henson et al, 2009a). To do this quickly (without repeating the MRI segmentation, coregistration and forward modelling), press the ``new'' button in the reconstruction window, which by default will copy these parts from the previous reconstruction. -\begin{figure} -\begin{center} -\includegraphics[width=90mm]{multimodal/figures/figure_32_18} -\caption{\em Graphic output for MSP-estimated activity at 165ms for faces.\label{fig_32_18}} -\end{center} -\end{figure} +\section{fMRI analysis \label{multimodal:data:fMRI}} -* Press the "Window" button in the reconstruction window, enter "150 200" as the timewindow of interest and "5 40" as the frequency band of interest. The Graphics window will show the mean activity for this time/frequency contrast (for faces alone, assuming the condition toggle is showing "condition 1"). - -* If you then press "Image", press "12" for the smoothing kernel, and SPM will write 3D Nifti images corresponding to the above contrast for each condition: -\begin{verbatim} - w_e_meg_1_1.nii - w_e_meg_1_2.nii - sw_e_meg_1_1.nii - sw_e_meg_1_2.nii -\end{verbatim} -Note that the first two images are unsmoothed (but normalised); the latter two are smoothed by a 12mm isotropic Gaussian kernel. The last number in the file name refers to the condition number; the penultimate number refers to the reconstruction number (ie the number in red in the reconstruction window, i.e, D.val, here 1). +Only the main characteristics of the fMRI analysis are described below; for a more detailed demonstration of fMRI analysis, read previous tutorial chapters describing fMRI analyses. -The smoothed results for Condition 1 will also be displayed in the Graphics window, together with the normalised structural, as in Figure~\ref{fig_32_19}. Note that the solution image is in MNI (normalised) space, because the use of a canonical mesh provides us with a mapping between the cortex mesh in native space and the corresponding MNI space. +Toggle the modality from EEG to fMRI, and change directory to the fMRI subdirectory (either in \matlab\, or via the ``CD'' option in the SPM ``Utils'' menu) -You can also of course view the image with the normal SPM "Display:image" option, and locate the coordinates of the "hotspots" in MNI space. Note that these images contain RMS (unsigned) source estimates (see Henson et al, 2007). +\subsection{Preprocessing the fMRI data} -If you want to see where activity (in this time/freq contrast) is greater for faces and scrambled faces, you can use SPM's ImCalc to create a difference image of \verb!sw_e_meg_1_1.nii - sw_e_meg_1_2.nii! - you should see bilateral fusiform. For further discussion of localising a differential effect (as in Section~\ref{3D} with ERPs), vs taking the difference of two localisations, as here, see Henson et al (2007). +Press the \textsc{Batch} button and then: - +\begin{itemize} +\item Select \textsc{Spatial: Realign: Estimate \& Reslice} from the SPM menu, create two sessions, and select the 390 \texttt{fM*.img} EPI images within the corresponding Session1 / Session 2 subdirectories. -\begin{figure} -\begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_19} -\caption{\em Display of the smoothed 3D image of the MSP-estimated activity between 150-200ms in the frequency band 5-40Hz for faces, together with the normalised structural. Note large hotspots in bilateral fusiform.\label{fig_32_19}} -\end{center} -\end{figure} +\item Add a \textsc{Spatial: Coreg: Estimate} module, and select the \texttt{smri.img} image in the \texttt{sMRI} directory for the ``Reference Image'' and select a ``Dependency'' for the ``Source Image'', which is the Mean image produced by the previous Realign module. -You could also explore the other inversion options, like COH and MNM, which you will note give more superficial solutions (a known problem with standard minimum norm; see also Friston et al, in press-a). To do this quickly (without repeating the MRI segmentation, coregistration and forward modelling), press the "new" button in the reconstruction window, which by default will copy these parts from the previous reconstruction. +\item Add a \textsc{Spatial: Segment} module, and select the \texttt{smri.img} image as the ``Data''. +\item Add a \textsc{Spatial: Normalise: Write} module, make a ``New: Subject'', and for the ``Parameter File'', select a ``Dependency'' of the ``Norm Params Subj-$>$MNI'' (from the prior segmentation module). For the ``Images to Write'', select a ``Dependency'' of the ``Coreg: Estimate: Coregistered Images'' (which will be all the coregistered EPI images) and ``Segment: Bias Corr Images'' (which will be the bias-corrected structural image). Also, change the ``Voxel sizes'' to [3 3 3], to save diskspace. -\section{fMRI analysis \label{fMRI}} +\item Add a \textsc{Spatial: Smooth} module, and for ``Images to Smooth'', select a ``Dependency'' of ``Normalise: Write: Normalised Images (Subj 1)''. -Only the main characteristics of the fMRI analysis are described below; for a more detailed demonstration of fMRI analysis, see Chapter 29. +\item Save the batch file (e.g, as \texttt{batch\_fmri\_preproc.mat}, and then press the ``Run'' button. +\end{itemize} -Note that all the job files for each stage of preprocessing and analysis are also provided: -\begin{verbatim} - fMRI/realign_job.mat - fMRI/slicetime_job.mat - fMRI/smooth_job.mat - fMRI/stats_job.mat -\end{verbatim} -These can be loaded and run, though of course the location of the files and the output directory will need to be changed. +These steps will take a while, and SPM will output some postscript files with the movement parameters and the coregistration results (see earlier chapters for further explanation). The result will be a series of 2 lots of 390 \texttt{swf*.img} files that will be the data for the following 1st-level fMRI timeseries analysis. -\subsection{Preprocessing the fMRI data} +\subsection{Statistical analysis of fMRI data} -* Toggle the modality from EEG to fMRI, and change directory to the fMRI subdirectory (either in Matlab, or via the "CD" option in the SPM "Utils" menu) +First make a new directory for the stats output, e.g, a \texttt{Stats} subdirectory within the fMRI directory. -* Select 'Realign' from the 'Realign and Unwarp' menu, click on 'Data', and select 'New Session'. Double-click on the new Session branch, and click on the 'Images' button, click on the 'specify files' and select all 215 \verb!fM*.img! files in the Scan directory (using the right mouse to 'select all', assuming the filter is set to \verb!^f.*img!). +Press the \textsc{batch} button and then: -Realignment will produce a \verb!spm*.ps! postscript file in the current directory, which shows the estimated movement (like in Figure~\ref{fig_32_20}). Importantly, the resliced images will be output as rfM*.img files. A mean image will also be written: -\begin{verbatim} - meanfMS02554-0003-000006.img -\end{verbatim} -as will the movement parameters in the text file: -\begin{verbatim} - rp_fMS02554-0003-000006.txt -\end{verbatim} +\begin{itemize} -. -\begin{figure} -\begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_20} -\caption{\em Movement parameters from Realignment of the fMRI data. \label{fig_32_20}} -\end{center} -\end{figure} +\item Select ``Stats: fMRI model specification'' from the SPM module menu, and select the new \texttt{Stats} subdirectory as the ``Directory''. -* Press the 'slice-timing' button, select the functional images (filter set to \verb!^rf.*! to avoid the mean image), enter 2.88 as the TR, 2.88*31/32 as the TA, the slice-order as [1:32] (since the first slice in the file is the top slice, and this was the first slice acquired in the descending sequence), and the reference slice to 16. This will write out 215 images arfM*.img, in which the data have been interpolated to match the acquisition time of the middle slice (in space and time, given the sequential acquisition). +\item Select ``Scans'' for ``Units of design''. -* Press the 'smooth' button and keep the default 10x10x10mm smoothing. This will produce 215 spatially-smoothed images sarfM*.img. +\item Enter \texttt{2} for the ``Interscan interval'' (i.e, a 2s TR). -Note that we will not normalise these images, in order to compare them with the MEG and EEG source reconstructions, which are in the native MRI space. +\item Create a new session from the ``Data \& Design'' menu. For ``Scans'', select all the \texttt{swf*.img} files from the \texttt{Session1} subdirectory. Under ``Conditions'', click ``Select File'' for ``Multiple conditions'', and select the \texttt{trials\_ses1.mat} file that is provided with these data. (This file just contains the onsets, durations and names of every event within each session.). For ``Multiple regressors'', click ``Select File'', and select the \texttt{rp*.txt} file that is also in the \texttt{Session1} subdirectory (created during realignment). -\subsection{Statistical analysis of fMRI data} +\item Repeat the above steps for the second session. -* Load the onsets.mat file provided into the Matlab workspace +\item Under ``Basis Functions'', add the ``Time and Dispersion'' derivatives to the Canonical HRF. -* Press the 'specify 1st-level' button, change the microtime onset from 1 to 8, select the 215 'sarfM*img' images, define two new conditions - condition 1 called "faces" with onsets set to onsets{1} and condition 2 called "scrambled faces" with onsets set to onsets{2} (all duration 0) - select the \verb!rp_fMS02554-0003-000006.txt! file as 'Multiple Regressors' (to covary out some of the residual movement-related effects), and select the fMRI/Stats as the output directory (loading and editing the \verb!stats_job.mat! file provided will save a lot of time here!). Keep the rest of the parameters (e.g, use of a canonical HRF basis function) at their default settings. +\item Then add a ``Stats: Model estimation'' module, and for the ``Select SPM.mat'', choose the ``Dependency'' of the \texttt{SPM.mat} file from the previous ``fMRI model specification'' module. -This will produce a design matrix like that in Figure~\ref{fig_32_21}, which is stored in the file: -\begin{verbatim} - fMRI/Stats/SPM.mat -\end{verbatim} +\item Add a ``Stats: Contrast Manager'' module, and for the ``Select SPM.mat'', choose the ``Dependency'' of the \texttt{SPM.mat} file from the previous ``Model Estimation module''. -\begin{figure} -\begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_21} -\caption{\em Design matrix for the fMRI analysis. \label{fig_32_21}} -\end{center} -\end{figure} +\item Under ``Contrast Sessions'', create a new F-contrast with a ``Name'' like \texttt{faces vs scrambled (all BFs)} and then enter the \matlab \texttt{[eye(3) eye(3) zeros(3,6)]}. This will produce a 3x12 matrix that picks out the three basis functions per condition (each as a separate row), summing across the two conditions (with zeros for the movement parameter regressors, which are of no interest). Then select ``Replicate (average over sessions)''. -* Then estimate the parameters of the design matrix by pressing the 'Estimate' button and selecting the SPM.mat file +\item Under ``Contrast Sessions'', create a new F-contrast with a ``Name'' like \texttt{faces + scrambled vs Baseline (all BFs)} and then enter the \matlab \texttt{[eye(3) eye(3) zeros(3,6)]}. Again, select ``Replicate (average over sessions)''. -* Finally, to see the regions that respond differentially between faces and scrambled faces, press 'Results' and define a new F-contrast (called, e.g, 'Faces - Scrambled') by typing the contrast weights [1 -1]. +\item Save the batch file (e.g, as \texttt{batch\_fmri\_stats.mat}, and then press the ``Run'' button. -This will identify regions in which the parameter estimate for the canonical HRF differs reliably between faces and scrambled faces. This could include regions that show both a "greater" relative response for faces, and regions that show a "greater" relative response for scrambled faces (such a two-tailed test is used because we do not know the precise relationship between haemodynamic changes measured by fMRI and the synchronous current changes measured by EEG/MEG). +\end{itemize} -If the resulting SPM{F} is thresholded at $p<.05$ FWE corrected, the resulting MIP and table of values should be like that in Figure ~\ref{fig_32_22}. Only two regions survive correction: right fusiform and orbitofrontal cortex (note coordinates refer to native MRI space; not MNI space). These can be displayed on the (attentuation-corrected) structural msmri.nii. They are a subset of the regions identified by the same contrast in a group of 18 subjects in Henson et al (2003). At a lower threshold (e.g, $p<.01$ uncorrected), one can see additional activation in left fusiform, as well as other regions. +When this has finished, press \textsc{Results} and select the \texttt{SPM.mat} file that should have been created in the new \texttt{Stats} directory. The Contrast Manager window will appear, and you can select the ``faces vs scrambled (all BFs)'' contrast. Do not mask, keep the title, threshold at $p<.05$ FWE corrected, use an extent threshold of 60 voxels, and you should get the MIP and table of values (once you have pressed ``whole brain'') like that in Figure ~\ref{multimodal:fig:22}. This shows clusters in bilateral midfusiform (FFA), right occipital (OFA), right superior temporal gyrus/sulcus (STS), in addition to posterior cingulate and anterior medial prefrontal cortex. These clusters show a reliable difference in the evoked BOLD response to faces compared with scrambled faces that can be captured within the ``signal space'' spanned by the canonical HRF and its temporal and dispersion derivatives. Note that this F-contrast can include regions that show both increased and decreased amplitude of the fitted HRF for faces relative to scrambled faces (though if you plot the ``faces vs scrambled'' contrast estimates, you will see that the leftmost bar (canonical HRF) is positive for all the clusters, suggesting greater neural activity for faces than scrambled faces (also apparent if you plot the event-related responses)). +There is some agreement between these fMRI effects and the localisation of the differential ERP for faces vs scrambled faces in the EEG data (see earlier section). Note however that the fMRI BOLD response reflects the integrated neural activity over several seconds, so some of the BOLD differences could arise from neural differences outside the 0-600ms epoch localised in the EEG data (there are of course other reasons too for differences in the two localisations; see, eg, Henson et al, under revision). \begin{figure} \begin{center} -\includegraphics[width=100mm]{multimodal/figures/figure_32_22} -\caption{\em SPM{F} for faces vs scrambled faces. Note that the coordinates are in the MRI native space (no normalisation performed) so bear a close, but not exact, relationship with MNI coordinates (affecting brain outline in MIP too).\label{fig_32_22}} +\includegraphics[width=100mm]{multimodal/figures/fmri_faces_vs_scrambled} +\caption{\em SPM{F} for faces vs scrambled faces. Note that the coordinates are in the MRI native space (no normalisation performed) so bear a close, but not exact, relationship with MNI coordinates (affecting brain outline in MIP too).\label{multimodal:fig:22}} \end{center} \end{figure} -There is some agreement between these fMRI effects and the localised EEG/MEG effects around the 170ms latency - eg in orbitofrontal and right fusiform - though of course the EEG dipoles were bilateral, and there were more extensive posterior occipitotemporal effects in the source-localised MEG data. Note of course that the fMRI data may include differences between faces and scrambled faces that occur at latencies other than the M170 (e.g, later), or differences in "induced" high-frequency M/EEG power that is not phase-locked to stimulus onset (Henson et al, 2005b). +You can also press \textsc{Results} and select the ``faces + scrambled vs Baseline (all BFs)'' contrast. Using the same threshold of $p<.05$ FWE corrected, you should see a large swathe of activity over most of the occipital, parietal and motor cortices, reflecting the general visuomotor requirements of the task (relative to interstimulus fixation). The more posterior vental occipital/temporal BOLD response are consistent with the MEG localisation of faces (or scrambled faces) versus baseline, though note that the more anterior vental temporal activity in the MEG localisation is not present in the fMRI data, which suggests (but does not imply) that the MEG localisation may be erroneous. -One could use the unthresholded F-image as an additional continuous prior within the PEB L2-norm method offered by SPM5, or probably better, one could take a number of regions after thresholding the SPM{F}, and enter each as a separate prior on the PEB L2-norm method (this way, different regions can be up-weighted or down-weighted as a function of whether they are likely to be active during the critical timewindow being localised). +Anyway, these contrasts of fMRI data can now be used as spatial priors to aid the localisation of the EEG and/or MEG data (Henson et al, under revision), as in the next section. +\section{Multimodal integration \label{multimodal:integration}} +In a future version of this chapter, we will illustrate here two types of multimodal integration: +\begin{itemize} + \item ``fusion'' of the EEG and MEG data (Henson et al, 2009b), + \item use of the fMRI data as spatial priors on the MEG/EEG data (Henson et al, under revision). +\end{itemize} \section{References} -\noindent 1. Friston, K, Daunizeau, J, Kiebel, S, Phillips, C, Trujillo-Barreto, N, Henson, R, Flandin, G, Mattout, J (in press-a). Multiple sparse priors for the M/EEG inverse problem. Neuroimage. \\ +\noindent 1. Friston, K, Daunizeau, J, Kiebel, S, Phillips, C, Trujillo-Barreto, N, Henson, R, Flandin, G, Mattout, J (2008). Multiple sparse priors for the M/EEG inverse problem. Neuroimage, 39(3):1104-20.\\ -\noindent 2. Friston, K, Carlton Chu, Janaina Mouro-Miranda, Oliver Hulme, Geraint Rees, Will Penny and John Ashburner (in press-b). Bayesian decoding of brain images. NeuroImage.\\ +\noindent 2. Friston, K, Carlton Chu, Janaina Mouro-Miranda, Oliver Hulme, Geraint Rees, Will Penny and John Ashburner (2008). Bayesian decoding of brain images. NeuroImage, 39(1):181-205.\\ \noindent 3. Friston K, Henson R, Phillips C, and Mattout J. (2006). Bayesian estimation of evoked and induced responses. Human Brain Mapping, 27, 722-735.\\ @@ -657,25 +712,30 @@ \section{References} \noindent 6. Henson R, Kiebel S, Kilner J, Friston K, Hillebrand A, Barnes G and Singh K. (2005b) Time-frequency SPMs for MEG data on face perception: Power changes and phase-locking. HBM05 Abstract.\\ -\noindent 7. Henson, R.N. Mattout, J, Singh, K, Barnes, G, Hillebrand, A. and Friston, K.J. (2007). Population-level inferences for distributed MEG source localisation under multiple constraints: Application to face-evoked fields. Neuroimage, 38, 422-438.\\ +\noindent 7. Henson, R, Mattout, J, Singh, K, Barnes, G, Hillebrand, A and Friston, K.J. (2007). Population-level inferences for distributed MEG source localisation under multiple constraints: Application to face-evoked fields. Neuroimage, 38, 422-438.\\ + +\noindent 8. Henson, R, Mattout, J, Phillips, C and Friston, K.J. (2009a). Selecting forward models for MEG source-reconstruction using model-evidence. Neuroimage, 46, 168-176.\\ + +\noindent 9. Henson, R, Mouchlianitis, E and Friston, K.J. (2009b). MEG and EEG data fusion: Simultaneous localisation of face-evoked responses. Neuroimage, 47, 581-589.\\ -\noindent 8. Josephs, O., Deichmann, R., Turner, R. (2000). Trajectory measurement andgeneralised reconstruction in rectilinear EPI. NeuroImage 11, S543.\\ +\noindent 10. Henson, R, Flandin, G, Friston, K.J. and Mattout, J. (under revision). A Parametric Empirical Bayesian framework for fMRI-constrained MEG/EEG source reconstruction.\\ -\noindent 9. Kilner, J., Kiebel, S and Friston, K. J. (2005). Applications of random field theory to electrophysiology. Neuroscience Letters, 374:174-178.\\ +\noindent 11. Kilner, J., Kiebel, S and Friston, K. J. (2005). Applications of random field theory to electrophysiology. Neuroscience Letters, 374:174-178.\\ -\noindent 10. Kilner, J. and Penny. W. (2006). Robust Averaging for EEG/MEG. HBM06 Abstract.\\ +\noindent 12. Kilner, J. and Penny. W. (2006). Robust Averaging for EEG/MEG. HBM06 Abstract.\\ -\noindent 11. Kiebel S and Friston K (2004). Statistical Parametric Mapping for Event-Related Potentials II: A Hierarchical Temporal Model. NeuroImage, 22, 503-520.\\ +\noindent 13. Kiebel S and Friston K (2004). Statistical Parametric Mapping for Event-Related Potentials II: A Hierarchical Temporal Model. NeuroImage, 22, 503-520.\\ -\noindent 12. Kiebel, S.J., David, O. and Friston, K. J. (2006). Dynamic Causal Modelling of Evoked Responses in EEG/MEG with lead-field parameterization. NeuroImage, 30:1273-1284.\\ +\noindent 14. Kiebel, S.J., David, O. and Friston, K. J. (2006). Dynamic Causal Modelling of Evoked Responses in EEG/MEG with lead-field parameterization. NeuroImage, 30:1273-1284.\\ -\noindent 13. Mattout J, Pelegrini-Issac M, Garnero L and Benali H. (2005a). Multivariate source prelocalization (MSP): use of functionally informed basis functions for better conditioning the MEG inverse problem. Neuroimage, 26, 356-73.\\ +\noindent 15. Mattout J, Pelegrini-Issac M, Garnero L and Benali H. (2005a). Multivariate source prelocalization (MSP): use of functionally informed basis functions for better conditioning the MEG inverse problem. Neuroimage, 26, 356-73.\\ -\noindent 14. Mattout, J, Phillips, C, Penny, W, Rugg, M and Friston, KJ (2005b). MEG source localisation under multiple constraints: an extended Bayesian framework. NeuroImage.\\ +\noindent 16. Mattout, J, Phillips, C, Penny, W, Rugg, M and Friston, KJ (2005b). MEG source localisation under multiple constraints: an extended Bayesian framework. NeuroImage.\\ -\noindent 15. Mattout, J., Henson, R N. and Friston, K.J. (in press). Canonical Source Reconstruction for MEG. Computational Intelligence and Neuroscience.\\ +\noindent 17. Mattout, J., Henson, R N. and Friston, K.J. (in press). Canonical Source Reconstruction for MEG. Computational Intelligence and Neuroscience.\\ -\noindent 16. Phillips, C. M.D. Rugg, M and Friston, K (2002). Systematic Regularization of Linear Inverse Solutions of the EEG Source Localisation Problem. NeuroImage, 17, 287-301.\\ +\noindent 18. Phillips, C, M.D. Rugg, M and Friston, K.J. (2002). Systematic Regularization of Linear Inverse Solutions of the EEG Source Localisation Problem. NeuroImage, 17, 287-301.\\ -\noindent 17. Spinelli, L, Gonzalez S, Lantz, G, Seeck, M and Michel, C. (2000). Electromagnetic inverse solutions in anatomically constrained spherical head models. Brain Topography, 13, 2.\\ +\noindent 19. Spinelli, L, Gonzalez S, Lantz, G, Seeck, M and Michel, C. (2000). Electromagnetic inverse solutions in anatomically constrained spherical head models. Brain Topography, 13, 2.\\ +\noindent 20. Wager, TD, Keller, MC, Lacey SC and Jonides J. (2005). Increased sensitivity in neuroimaging analyses using robust regression. NeuroImage, 26(1), 99-113.\\ \ No newline at end of file diff --git a/matlabbatch/@cfg_choice/gencode.m b/matlabbatch/@cfg_choice/gencode_item.m similarity index 81% rename from matlabbatch/@cfg_choice/gencode.m rename to matlabbatch/@cfg_choice/gencode_item.m index 5f1a9a7..7537219 100644 --- a/matlabbatch/@cfg_choice/gencode.m +++ b/matlabbatch/@cfg_choice/gencode_item.m @@ -1,6 +1,6 @@ -function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) -% function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +% function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) % Generate code to recreate a cfg_choice item. This code does not deal with % arrays of cfg_items, such a configuration should not exist with the % current definition of a configuration tree. @@ -23,9 +23,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode.m 1716 2008-05-23 08:18:45Z volkmar $ +% $Id: gencode_item.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 1716 $'; %#ok +rev = '$Rev: 3355 $'; %#ok %% Parent object % Generate generic object @@ -37,9 +37,9 @@ itropts.mlvl = 1; istoptag = ''; end; -[str tag cind ccnt] = gencode(item.cfg_item, tag, tagctx, istoptag, ... +[str tag cind ccnt] = gencode_item(item.cfg_item, tag, tagctx, istoptag, ... itropts); -tagctx = {tagctx{:} tag}; +tagctx = [tagctx {tag}]; % Check whether to generate code - ccnt == 0 means that generic object did % not return code if (tropts.clvl > tropts.mlvl || (~isempty(tropts.stopspec) && match(item, tropts.stopspec))) || ccnt == 0 @@ -53,7 +53,7 @@ %% Values % Generate values field if numel(item.values) > 0 - % Traverse values{:} tree, if items are cfg_items + % Traverse values{:} tree cstr = {}; % Update clvl ctropts = tropts; @@ -66,12 +66,12 @@ % and the tags of its immediate children. ctag{k} = genvarname(subsref(item.values{k}, substruct('.','tag')), ... tagctx); - [ccstr ctag{k} ccind cccnt] = gencode(item.values{k}, ctag{k}, ... + [ccstr ctag{k} ccind cccnt] = gencode_item(item.values{k}, ctag{k}, ... tagctx, stoptag, ctropts); - tagctx = {tagctx{:} ctag{k}}; + tagctx = [tagctx ctag(k)]; if ~isempty(ccstr) % Child has returned code - cstr = {cstr{:} ccstr{:}}; + cstr = [cstr(:)' ccstr(:)']; ctropts.cnt = ctropts.cnt + cccnt; ccnt = ccnt + cccnt; end; @@ -79,6 +79,6 @@ % Update position of class definition cind = cind+numel(cstr); % Prepend code of children - str = {cstr{:} str{:}}; + str = [cstr(:)' str(:)']; str{end+1} = sprintf('%s.values = {%s};', tag, sprintf('%s ', ctag{:})); end; diff --git a/matlabbatch/@cfg_dep/gencode.m b/matlabbatch/@cfg_dep/gencode.m index ee06b43..483f474 100644 --- a/matlabbatch/@cfg_dep/gencode.m +++ b/matlabbatch/@cfg_dep/gencode.m @@ -1,19 +1,8 @@ -function [str, tag, cind, ccnt] = gencode(item, tag, stoptag, tropts) +function [str, tag, cind] = gencode(item, tag, tagctx) -% function [str, tag, cind, ccnt] = gencode(item, tag, stoptag, tropts) +% function [str, tag, cind] = gencode(item, tag, tagctx) % Generate code to recreate an cfg_dep object. % -% Traversal options -% struct with fields -% stopspec - match spec to stop code generation (not used here) -% dflag - (not used here) -% clvl - current level in tree - level is increased if fields of -% structures or cell items are traversed -% mlvl - maximum level to generate - range 1 (top level only) to -% Inf (all levels) -% cnt - item count - not used outside cfg_item objects -% mcnt - (not evaluated here) -% % This code is part of a batch job configuration system for MATLAB. See % help matlabbatch % for a general overview. @@ -21,37 +10,20 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode.m 1716 2008-05-23 08:18:45Z volkmar $ +% $Id: gencode.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 1716 $'; %#ok +rev = '$Rev: 3355 $'; %#ok if nargin < 2 tag = inputname(1); end; if nargin < 3 - stoptag = tag; -end; -if nargin < 4 - tropts = cfg_tropts({{}},1,inf,1,inf,true); + tagctx = {}; +end +if isempty(tag) + tag = genvarname('val', tagctx); end; -%% Get variable name -% Check whether to generate code -if tropts.clvl > tropts.mlvl - % Stopping - tag based on stoptag, tag of item and expected new item count - tag = genvarname(sprintf('%s_%s_0001', stoptag, tag)); - str = {}; - cind = []; - ccnt = 0; - return; -else - % Tag based on item count - if isempty(tag) - tag = genvarname(sprintf('val_%04d', tropts.cnt)); - end; -end; -% Item count -ccnt = 1; cind = 1; str = {}; @@ -68,14 +40,14 @@ if numel(item(k).(fn{l})) >= 1 % force index (1) if there is exactly one entry tag1 = sprintf('%s(%d).%s', tag, k, fn{l}); - [str1 tag1] = gencode_substructcode(item(k).(fn{l}), tag1); - str = {str{:} str1{:}}; + str1 = gencode_substructcode(item(k).(fn{l}), tag1); + str = [str(:)' str1(:)']; end; otherwise % other field should not be indexed tag1 = sprintf('%s(%d).%s', tag, k, fn{l}); - [str1 tag1 ccnt1 cind1] = gencode(item(k).(fn{l}), tag1, tag1, tropts); - str = {str{:} str1{:}}; + str1 = gencode(item(k).(fn{l}), tag1, [{tag1} tagctx]); + str = [str(:)' str1(:)']; end; end; end; \ No newline at end of file diff --git a/matlabbatch/@cfg_entry/gencode.m b/matlabbatch/@cfg_entry/gencode_item.m similarity index 71% rename from matlabbatch/@cfg_entry/gencode.m rename to matlabbatch/@cfg_entry/gencode_item.m index 5a1b00f..a6d263f 100644 --- a/matlabbatch/@cfg_entry/gencode.m +++ b/matlabbatch/@cfg_entry/gencode_item.m @@ -1,6 +1,6 @@ -function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) -% function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +% function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) % Generate code to recreate a generic item. This code does not deal with % arrays of cfg_items, such a configuration should not exist with the % current definition of a configuration tree. @@ -22,13 +22,13 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode.m 1716 2008-05-23 08:18:45Z volkmar $ +% $Id: gencode_item.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 1716 $'; %#ok +rev = '$Rev: 3355 $'; %#ok %% Parent object % Generate generic object -[str tag cind ccnt] = gencode(item.cfg_item, tag, tagctx, stoptag, tropts); +[str tag cind ccnt] = gencode_item(item.cfg_item, tag, tagctx, stoptag, tropts); % Check whether to generate code - ccnt == 0 means that generic object did % not return code if (tropts.clvl > tropts.mlvl || (~isempty(tropts.stopspec) && match(item, tropts.stopspec))) || ccnt == 0 @@ -42,17 +42,15 @@ %% Strtype % Generate strtype field if ~isempty(item.strtype) - str1 = gencode(item.strtype, sprintf('%s.strtype', tag), stoptag, tropts); - str = {str{:} str1{:}}; + str1 = gencode(item.strtype, sprintf('%s.strtype', tag), tagctx); + str = [str(:)' str1(:)']; end; %% Num % Generate num field str{end+1} = sprintf('%s.num = [%s];', tag, num2str(item.num)); -%% Def -% Do not create deprecated def field %% Extras % Generate extras field if ~isempty(item.extras) - str1 = gencode(item.extras, sprintf('%s.extras', tag), stoptag, tropts); - str = {str{:} str1{:}}; + str1 = gencode(item.extras, sprintf('%s.extras', tag), tagctx); + str = [str(:)' str1(:)']; end; \ No newline at end of file diff --git a/matlabbatch/@cfg_exbranch/gencode.m b/matlabbatch/@cfg_exbranch/gencode_item.m similarity index 79% rename from matlabbatch/@cfg_exbranch/gencode.m rename to matlabbatch/@cfg_exbranch/gencode_item.m index 0888e34..068e265 100644 --- a/matlabbatch/@cfg_exbranch/gencode.m +++ b/matlabbatch/@cfg_exbranch/gencode_item.m @@ -1,6 +1,6 @@ -function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) -% function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +% function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) % Generate code to recreate a cfg_exbranch item. This code first generates % code for the parent cfg_branch item and adds code for its own fields. % Note that function references will be broken if they refer to a local @@ -25,9 +25,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode.m 1775 2008-06-02 09:18:18Z volkmar $ +% $Id: gencode_item.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 1775 $'; %#ok +rev = '$Rev: 3355 $'; %#ok %% Parent object % if there are function handles in .prog, .vout, .vfiles add their names to @@ -43,9 +43,9 @@ if ~isempty(item.vfiles) && isa(item.vfiles, 'function_handle') functx{end+1} = func2str(item.vfiles); end -tagctx = {tagctx{:} functx{:}}; +tagctx = [tagctx functx]; % Generate branch object -[str tag cind ccnt] = gencode(item.cfg_branch, tag, tagctx, stoptag, tropts); +[str tag cind ccnt] = gencode_item(item.cfg_branch, tag, tagctx, stoptag, tropts); % Check whether to generate code - ccnt == 0 means that generic object did % not return code if (tropts.clvl > tropts.mlvl || (~isempty(tropts.stopspec) && match(item, tropts.stopspec))) || ccnt == 0 @@ -60,13 +60,14 @@ funs = {'prog', 'vfiles', 'vout'}; for k = 1:numel(funs) if ~isempty(item.(funs{k})) - str1 = gencode(item.(funs{k}), sprintf('%s.%s', tag, funs{k}), stoptag, tropts); - str = {str{:} str1{:}}; + str1 = gencode(item.(funs{k}), sprintf('%s.%s', tag, funs{k}), ... + tagctx); + str = [str(:)' str1(:)']; end; end; %% Modality % Generate modality field if numel(item.modality) > 0 - str1 = gencode(item.modality, sprintf('%s.modality', tag), stoptag, tropts); - str = {str{:} str1{:}}; + str1 = gencode(item.modality, sprintf('%s.modality', tag), tagctx); + str = [str(:)' str1(:)']; end; diff --git a/matlabbatch/@cfg_files/gencode.m b/matlabbatch/@cfg_files/gencode_item.m similarity index 77% rename from matlabbatch/@cfg_files/gencode.m rename to matlabbatch/@cfg_files/gencode_item.m index 03d549e..8e79f8c 100644 --- a/matlabbatch/@cfg_files/gencode.m +++ b/matlabbatch/@cfg_files/gencode_item.m @@ -1,6 +1,6 @@ -function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) -% function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +% function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) % Generate code to recreate a generic item. This code does not deal with % arrays of cfg_items, such a configuration should not exist with the % current definition of a configuration tree. @@ -22,13 +22,13 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode.m 1716 2008-05-23 08:18:45Z volkmar $ +% $Id: gencode_item.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 1716 $'; %#ok +rev = '$Rev: 3355 $'; %#ok %% Parent object % Generate generic object -[str tag cind ccnt] = gencode(item.cfg_item, tag, tagctx, stoptag, tropts); +[str tag cind ccnt] = gencode_item(item.cfg_item, tag, tagctx, stoptag, tropts); % Check whether to generate code - ccnt == 0 means that generic object did % not return code if (tropts.clvl > tropts.mlvl || (~isempty(tropts.stopspec) && match(item, tropts.stopspec))) || ccnt == 0 @@ -45,12 +45,10 @@ for k = 1:numel(fn) if ~isempty(item.(fn{k})) str1 = gencode(item.(fn{k}), sprintf('%s.%s', tag, fn{k}), ... - stoptag, tropts); - str = {str{:} str1{:}}; + tagctx); + str = [str(:)' str1(:)']; end; end; %% Num % Generate num field str{end+1} = sprintf('%s.num = [%d %d];', tag, item.num); -%% Def -% Do not create deprecated def field \ No newline at end of file diff --git a/matlabbatch/@cfg_item/gencode.m b/matlabbatch/@cfg_item/gencode.m index 03789ce..5733c5a 100644 --- a/matlabbatch/@cfg_item/gencode.m +++ b/matlabbatch/@cfg_item/gencode.m @@ -1,26 +1,25 @@ -function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +function [str, tag, cind] = gencode(item, tag, tagctx) -% function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) -% Generate code to recreate a generic item. This code should be suitable -% for all derived classes. Derived classes that add their own fields should -% first call this code and then add code to recreate their additional -% fields. This code does not deal with arrays of cfg_items, such a -% configuration should not exist with the current definition of a -% configuration tree. +% function [str, tag, cind] = gencode(item, tag, tagctx) +% Generate code to recreate a cfg_item object. This function calls the +% cfg_item specific gencode_item method. Instead of creating one large, +% deeply nested struct/cell array, it creates separate variables for each +% cfg_item. % -% Traversal options -% struct with fields -% stopspec - match spec to stop code generation -% dflag - (not used here) -% clvl - current level in tree -% mlvl - maximum level to generate - range 1 (top level only) to -% Inf (all levels) -% cnt - item count - used for unique tags -% mcnt - (not evaluated here) -% Code generation stops at this item, if item matches tropts.stopspec or -% tropts.clvl > tropts.mlvl. In this case, the tag of the item is -% generated from genvarname(sprintf('%s%s', stoptag, tag), tagctx), but -% no code is generated. +% Input arguments: +% item - MATLAB variable to generate code for (the variable itself, not its +% name) +% tag - optional: name of the variable, i.e. what will be displayed left +% of the '=' sign. This can also be a valid struct/cell array +% reference, like 'x(2).y'. If not provided, inputname(1) will be +% used. +% tagctx - optional: variable names not to be used (e.g. keywords, +% reserved variables). A cell array of strings. +% +% Output arguments: +% str - cellstr containing code lines to reproduce +% tag - name of the generated variable +% cind - index into str to the line where the variable assignment is coded % % This code is part of a batch job configuration system for MATLAB. See % help matlabbatch @@ -29,119 +28,16 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode.m 2512 2008-12-01 13:21:29Z volkmar $ +% $Id: gencode.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 2512 $'; %#ok +rev = '$Rev: 3355 $'; %#ok -%% Class of item -% if there are function handles in .check or .def, add their names to -% tagctx -if ~isempty(item.check) && isa(item.check, 'function_handle') - functx = {func2str(item.check)}; -else - functx = {}; -end -if ~isempty(item.def) && isa(item.def, 'function_handle') - functx{end+1} = func2str(item.def); -end -tagctx = {tagctx{:} functx{:}}; -% Check whether to generate code -if (tropts.clvl > tropts.mlvl || (~isempty(tropts.stopspec) && match(item, tropts.stopspec))) - % Stopping - tag based on stoptag, and tag of item - if isempty(tag) - tag = genvarname(sprintf('%s%s', stoptag, item.tag), tagctx); - else - tag = genvarname(sprintf('%s%s', stoptag, tag), tagctx); - end; - str = {}; - cind = []; - ccnt = 0; - return; -else - if isempty(tag) - tag = genvarname(item.tag, tagctx); - else - tag = genvarname(tag, tagctx); - end; -end; -tagctx = {tagctx{:} tag}; -% Item count -ccnt = 1; -% Generate generic object -str{1} = '% ---------------------------------------------------------------------'; -str{2} = sprintf('%% %s %s', item.tag, item.name); -str{3} = '% ---------------------------------------------------------------------'; -str{4} = sprintf('%s = %s;', tag, class(item)); -% save position of class definition (needs to be overwritten if derived -% items call this generic function on their cfg_item field) -cind = 4; -%% Tag and Name -% Set basic fields -str{end+1} = sprintf('%s.tag = ''%s'';', tag, item.tag); -% Add three spaces to name tag - this will align equal signs -% Works only because gencode does not produce subscripts for strings -str1 = gencode(item.name, sprintf('%s.name ', tag), stoptag, tropts); -str = {str{:} str1{:}}; -%% Val -% Generate val field -if numel(item.val) > 0 && isa(item.val{1}, 'cfg_item') - % Traverse val{:} tree, if items are cfg_items - cstr = {}; - % Update clvl - ctropts = tropts; - ctropts.clvl = ctropts.clvl + 1; - ctropts.cnt = ctropts.cnt + ccnt; - ctag = cell(size(item.val)); - for k = 1:numel(item.val) - % tags are used as variable names and need to be unique in the - % context of this .val tag. This includes the item's tag itself - % and the tags of its children. - ctag{k} = genvarname(subsref(item.val{k}, substruct('.','tag')), ... - tagctx); - [ccstr ctag{k} ccind cccnt] = gencode(item.val{k}, ctag{k}, tagctx, ... - stoptag, ctropts); - if ~isempty(ccstr) - % Child has returned code - cstr = {cstr{:} ccstr{:}}; - ccnt = ccnt + cccnt; - ctropts.cnt = ctropts.cnt + cccnt; - tagctx = {tagctx{:} ctag{k}}; - end; - end; - % Update position of class definition - cind = cind+numel(cstr); - % Prepend code of children - str = {cstr{:} str{:}}; - str{end+1} = sprintf('%s.val = {%s};' ,tag, sprintf('%s ', ctag{:})); -elseif numel(item.val) > 0 && ~isa(item.val{1}, 'cfg_item') - % Check .def field. Generate code for .val only, if no defaults - % defined or value is different from defaults. - if isempty(item.def) || ~isequalwithequalnans(feval(item.def), item.val{1}) - str1 = gencode(item.val, sprintf('%s.val', tag), stoptag, tropts); - str = {str{:} str1{:}}; - end; +if nargin < 2 + tag = inputname(1); end; -%% Check -% Generate check field -if ~isempty(item.check) - % Add two spaces to check tag - this will align equal signs - % Works only because gencode does not produce subscripts for function - % strings - str1 = gencode(item.check, sprintf('%s.check ', tag), stoptag, tropts); - str = {str{:} str1{:}}; -end -%% Help -% Generate help field -if numel(item.help) > 0 - % Add three spaces to help tag - this will align equal signs - % Works only because gencode does not produce subscripts for cellstrings - str1 = gencode(item.help, sprintf('%s.help ', tag), stoptag, tropts); - str = {str{:} str1{:}}; -end; -%% Def -% Generate def field -if ~isempty(item.def) - str1 = gencode(item.def, sprintf('%s.def', tag), stoptag, tropts); - str = {str{:} str1{:}}; -end; - +if nargin < 3 + tagctx = {}; +end +stoptag = tag; +tropts = cfg_tropts({{}},1,inf,1,inf,true); +[str, tag, cind] = gencode_item(item, tag, tagctx, stoptag, tropts); diff --git a/matlabbatch/@cfg_item/gencode_item.m b/matlabbatch/@cfg_item/gencode_item.m new file mode 100644 index 0000000..a766109 --- /dev/null +++ b/matlabbatch/@cfg_item/gencode_item.m @@ -0,0 +1,151 @@ +function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) + +% function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) +% Generate code to recreate a generic item. This code should be suitable +% for all derived classes. Derived classes that add their own fields should +% first call this code and then add code to recreate their additional +% fields. This code does not deal with arrays of cfg_items, such a +% configuration should not exist with the current definition of a +% configuration tree. +% +% Traversal options +% struct with fields +% stopspec - match spec to stop code generation +% dflag - (not used here) +% clvl - current level in tree +% mlvl - maximum level to generate - range 1 (top level only) to +% Inf (all levels) +% cnt - item count - used for unique tags +% mcnt - (not evaluated here) +% Code generation stops at this item, if item matches tropts.stopspec or +% tropts.clvl > tropts.mlvl. In this case, the tag of the item is +% generated from genvarname(sprintf('%s%s', stoptag, tag), tagctx), but +% no code is generated. +% +% This code is part of a batch job configuration system for MATLAB. See +% help matlabbatch +% for a general overview. +%_______________________________________________________________________ +% Copyright (C) 2007 Freiburg Brain Imaging + +% Volkmar Glauche +% $Id: gencode_item.m 3355 2009-09-04 09:37:35Z volkmar $ + +rev = '$Rev: 3355 $'; %#ok + +%% Class of item +% if there are function handles in .check or .def, add their names to +% tagctx +if ~isempty(item.check) && isa(item.check, 'function_handle') + functx = {func2str(item.check)}; +else + functx = {}; +end +if ~isempty(item.def) && isa(item.def, 'function_handle') + functx{end+1} = func2str(item.def); +end +tagctx = [tagctx functx]; +% Check whether to generate code +if (tropts.clvl > tropts.mlvl || (~isempty(tropts.stopspec) && match(item, tropts.stopspec))) + % Stopping - tag based on stoptag, and tag of item + if isempty(tag) + tag = genvarname(sprintf('%s%s', stoptag, item.tag), tagctx); + else + tag = genvarname(sprintf('%s%s', stoptag, tag), tagctx); + end; + str = {}; + cind = []; + ccnt = 0; + return; +else + if isempty(tag) + tag = genvarname(item.tag, tagctx); + elseif ~isletter(tag(1)) + % only modify tag if there seems to be something wrong + % could be more specific (parse struct,cell,array subscripts) + tag = genvarname(tag, tagctx); + end; +end; +tagctx = [tagctx {tag}]; +% Item count +ccnt = 1; +% Generate generic object +str{1} = '% ---------------------------------------------------------------------'; +str{2} = sprintf('%% %s %s', item.tag, item.name); +str{3} = '% ---------------------------------------------------------------------'; +str{4} = sprintf('%s = %s;', tag, class(item)); +% save position of class definition (needs to be overwritten if derived +% items call this generic function on their cfg_item field) +cind = 4; +%% Tag and Name +% Set basic fields +str{end+1} = sprintf('%s.tag = ''%s'';', tag, item.tag); +% Add three spaces to name tag - this will align equal signs +% Works only because gencode does not produce subscripts for strings +str1 = gencode(item.name, sprintf('%s.name ', tag), tagctx); +str = [str(:)' str1(:)']; +%% Val +% Generate val field +if numel(item.val) > 0 && isa(item.val{1}, 'cfg_item') + % Traverse val{:} tree, if items are cfg_items + cstr = {}; + % Update clvl + ctropts = tropts; + ctropts.clvl = ctropts.clvl + 1; + ctropts.cnt = ctropts.cnt + ccnt; + ctag = cell(size(item.val)); + for k = 1:numel(item.val) + % tags are used as variable names and need to be unique in the + % context of this .val tag. This includes the item's tag itself + % and the tags of its children. + ctag{k} = genvarname(subsref(item.val{k}, substruct('.','tag')), ... + tagctx); + [ccstr ctag{k} ccind cccnt] = gencode_item(item.val{k}, ctag{k}, tagctx, ... + stoptag, ctropts); + if ~isempty(ccstr) + % Child has returned code + cstr = [cstr(:)' ccstr(:)']; + ccnt = ccnt + cccnt; + ctropts.cnt = ctropts.cnt + cccnt; + tagctx = [tagctx ctag(k)]; + end; + end; + % Update position of class definition + cind = cind+numel(cstr); + % Prepend code of children + str = [cstr(:)' str(:)']; + str{end+1} = sprintf('%s.val = {%s};' ,tag, sprintf('%s ', ctag{:})); +elseif numel(item.val) > 0 && ~isa(item.val{1}, 'cfg_item') + % Check .def field. Generate code for .val only, if no defaults + % defined or value is different from defaults. + if isempty(item.def) || ~isequalwithequalnans(feval(item.def), item.val{1}) + str1 = gencode(item.val, sprintf('%s.val', tag), tagctx); + str = [str(:)' str1(:)']; + end; +end; +%% Check +% Generate check field +if ~isempty(item.check) + % Add two spaces to check tag - this will align equal signs + % Works only because gencode does not produce subscripts for function + % strings + str1 = gencode(item.check, sprintf('%s.check ', tag), tagctx); + str = [str(:)' str1(:)']; +end +%% Help +% Generate help field +if numel(item.help) > 0 + % Add three spaces to help tag - this will align equal signs + % Works only because gencode does not produce subscripts for cellstrings + str1 = gencode(item.help, sprintf('%s.help ', tag), tagctx); + str = [str(:)' str1(:)']; +end; +%% Def +% Generate def field +if ~isempty(item.def) + % Add four spaces to def tag - this will align equal signs + % Works only because gencode does not produce subscripts for function + % strings + str1 = gencode(item.def, sprintf('%s.def ', tag), tagctx); + str = [str(:)' str1(:)']; +end; diff --git a/matlabbatch/@cfg_menu/gencode.m b/matlabbatch/@cfg_menu/gencode_item.m similarity index 69% rename from matlabbatch/@cfg_menu/gencode.m rename to matlabbatch/@cfg_menu/gencode_item.m index 185a9bc..ab6c492 100644 --- a/matlabbatch/@cfg_menu/gencode.m +++ b/matlabbatch/@cfg_menu/gencode_item.m @@ -1,6 +1,6 @@ -function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) -% function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +% function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) % Generate code to recreate a generic item. This code does not deal with % arrays of cfg_items, such a configuration should not exist with the % current definition of a configuration tree. @@ -22,13 +22,13 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode.m 1716 2008-05-23 08:18:45Z volkmar $ +% $Id: gencode_item.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 1716 $'; %#ok +rev = '$Rev: 3355 $'; %#ok %% Parent object % Generate generic object -[str tag cind ccnt] = gencode(item.cfg_item, tag, tagctx, stoptag, tropts); +[str tag cind ccnt] = gencode_item(item.cfg_item, tag, tagctx, stoptag, tropts); % Check whether to generate code - ccnt == 0 means that generic object did % not return code if (tropts.clvl > tropts.mlvl || (~isempty(tropts.stopspec) && match(item, tropts.stopspec))) || ccnt == 0 @@ -41,11 +41,9 @@ str{cind} = sprintf('%s = %s;', tag, class(item)); %% Labels % Generate labels field -str1 = gencode(item.labels, sprintf('%s.labels', tag), stoptag, tropts); -str = {str{:} str1{:}}; +str1 = gencode(item.labels, sprintf('%s.labels', tag), tagctx); +str = [str(:)' str1(:)']; %% Values % Generate values field -str1 = gencode(item.values, sprintf('%s.values', tag), stoptag, tropts); -str = {str{:} str1{:}}; -%% Def -% Do not create deprecated def field \ No newline at end of file +str1 = gencode(item.values, sprintf('%s.values', tag), tagctx); +str = [str(:)' str1(:)']; diff --git a/matlabbatch/@cfg_repeat/gencode.m b/matlabbatch/@cfg_repeat/gencode_item.m similarity index 83% rename from matlabbatch/@cfg_repeat/gencode.m rename to matlabbatch/@cfg_repeat/gencode_item.m index 0056a55..039efdb 100644 --- a/matlabbatch/@cfg_repeat/gencode.m +++ b/matlabbatch/@cfg_repeat/gencode_item.m @@ -1,6 +1,6 @@ -function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) -% function [str, tag, cind, ccnt] = gencode(item, tag, tagctx, stoptag, tropts) +% function [str, tag, cind, ccnt] = gencode_item(item, tag, tagctx, stoptag, tropts) % Generate code to recreate a cfg_repeat item. This code does not deal with % arrays of cfg_items, such a configuration should not exist with the % current definition of a configuration tree. @@ -23,9 +23,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode.m 1716 2008-05-23 08:18:45Z volkmar $ +% $Id: gencode_item.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 1716 $'; %#ok +rev = '$Rev: 3355 $'; %#ok %% Parent object % Generate generic object @@ -37,8 +37,8 @@ itropts.mlvl = 1; istoptag = ''; end; -[str tag cind ccnt] = gencode(item.cfg_item, tag, tagctx, istoptag, itropts); -tagctx = {tagctx{:} tag}; +[str tag cind ccnt] = gencode_item(item.cfg_item, tag, tagctx, istoptag, itropts); +tagctx = [tagctx {tag}]; % Check whether to generate code - ccnt == 0 means that generic object did % not return code if (tropts.clvl > tropts.mlvl || (~isempty(tropts.stopspec) && match(item, tropts.stopspec))) || ccnt == 0 @@ -65,12 +65,12 @@ % and the tags of its children. ctag{k} = genvarname(subsref(item.values{k}, substruct('.','tag')), ... tagctx); - [ccstr ctag{k} ccind cccnt] = gencode(item.values{k}, ctag{k}, ... + [ccstr ctag{k} ccind cccnt] = gencode_item(item.values{k}, ctag{k}, ... tagctx, stoptag, ctropts); - tagctx = {tagctx{:} ctag{k}}; + tagctx = [tagctx ctag(k)]; if ~isempty(ccstr) % Child has returned code - cstr = {cstr{:} ccstr{:}}; + cstr = [cstr(:)' ccstr(:)']; ctropts.cnt = ctropts.cnt + cccnt; ccnt = ccnt + cccnt; end; @@ -78,7 +78,7 @@ % Update position of class definition cind = cind+numel(cstr); % Prepend code of children - str = {cstr{:} str{:}}; + str = [cstr(:)' str(:)']; str{end+1} = sprintf('%s.values = {%s};', tag, sprintf('%s ', ctag{:})); end; %% Num diff --git a/matlabbatch/cfg_basicio/cfg_cfg_basicio.m b/matlabbatch/cfg_basicio/cfg_cfg_basicio.m index cf921e2..6398c9b 100644 --- a/matlabbatch/cfg_basicio/cfg_cfg_basicio.m +++ b/matlabbatch/cfg_basicio/cfg_cfg_basicio.m @@ -647,7 +647,7 @@ cfg_assignin.val = {name output }; cfg_assignin.help = { 'Assign a computation result to a workspace variable.' - 'The value entered into "Output Item" will be assigned to a MATLAB workspace variable whose name is specified in "Output Variable Name". This can be useful to assess the results of computations with other MATLAB routines or for debugging.' + 'The value entered into "Output Item" will be assigned to a MATLAB workspace variable whose name is specified in "Output Variable Name". If this variable already exists, a new variable name will be generated. This can be useful to assess the results of computations with other MATLAB routines or for debugging.' }'; cfg_assignin.prog = @cfg_run_assignin; % --------------------------------------------------------------------- diff --git a/matlabbatch/cfg_basicio/cfg_run_assignin.m b/matlabbatch/cfg_basicio/cfg_run_assignin.m index 10f2850..a14057e 100644 --- a/matlabbatch/cfg_basicio/cfg_run_assignin.m +++ b/matlabbatch/cfg_basicio/cfg_run_assignin.m @@ -9,8 +9,12 @@ function cfg_run_assignin(job) % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_run_assignin.m 1716 2008-05-23 08:18:45Z volkmar $ +% $Id: cfg_run_assignin.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 1716 $'; %#ok +rev = '$Rev: 3355 $'; %#ok -assignin('base', job.name, job.output); +% check for existence of variable +vars = evalin('base','feval(@who);'); +% generate new name +name = genvarname(job.name, vars); +assignin('base', name, job.output); diff --git a/matlabbatch/cfg_basicio/cfg_run_runjobs.m b/matlabbatch/cfg_basicio/cfg_run_runjobs.m index 11b4e9f..b96f10c 100644 --- a/matlabbatch/cfg_basicio/cfg_run_runjobs.m +++ b/matlabbatch/cfg_basicio/cfg_run_runjobs.m @@ -12,9 +12,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_run_runjobs.m 1973 2008-08-01 11:52:41Z volkmar $ +% $Id: cfg_run_runjobs.m 3402 2009-09-15 12:08:33Z volkmar $ -rev = '$Rev: 1973 $'; %#ok +rev = '$Rev: 3402 $'; %#ok if isfield(job.save, 'savejobs') [p n e v] = fileparts(job.save.savejobs.outstub); @@ -51,10 +51,10 @@ cjob = cfg_util('initjob', hjobs); cfg_util('run', cjob); out.jout = cfg_util('getalloutputs', cjob); - if isfield(job.save, 'savejobs') - [p n e v] = fileparts(job.save.savejobs.outstub); - out.jobrun{1} = fullfile(p, [n '_run.m']); - cfg_util('saverun', cjob, out.jobrun{1}); - end; +% if isfield(job.save, 'savejobs') +% [p n e v] = fileparts(job.save.savejobs.outstub); +% out.jobrun{1} = fullfile(p, [n '_run.m']); +% cfg_util('saverun', cjob, out.jobrun{1}); +% end; cfg_util('deljob', cjob); end; \ No newline at end of file diff --git a/matlabbatch/cfg_basicio/src/batch_basicio_12_assignin.m b/matlabbatch/cfg_basicio/src/batch_basicio_12_assignin.m index 7003677..65b718e 100644 --- a/matlabbatch/cfg_basicio/src/batch_basicio_12_assignin.m +++ b/matlabbatch/cfg_basicio/src/batch_basicio_12_assignin.m @@ -1,5 +1,5 @@ %----------------------------------------------------------------------- -% Job configuration created by cfg_util (rev $Rev: 2787 $) +% Job configuration created by cfg_util (rev $Rev: 3355 $) %----------------------------------------------------------------------- matlabbatch{1}.menu_cfg{1}.menu_entry{1}.conf_entry.type = 'cfg_entry'; matlabbatch{1}.menu_cfg{1}.menu_entry{1}.conf_entry.name = 'Output Variable Name'; @@ -39,5 +39,5 @@ matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.check = []; matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.help = { 'Assign a computation result to a workspace variable.' - 'The value entered into "Output Item" will be assigned to a MATLAB workspace variable whose name is specified in "Output Variable Name". This can be useful to assess the results of computations with other MATLAB routines or for debugging.' + 'The value entered into "Output Item" will be assigned to a MATLAB workspace variable whose name is specified in "Output Variable Name". If this variable already exists, a new variable name will be generated. This can be useful to assess the results of computations with other MATLAB routines or for debugging.' }'; diff --git a/matlabbatch/cfg_confgui/cfg_confgui.m b/matlabbatch/cfg_confgui/cfg_confgui.m index 03817bc..70d31c4 100644 --- a/matlabbatch/cfg_confgui/cfg_confgui.m +++ b/matlabbatch/cfg_confgui/cfg_confgui.m @@ -12,9 +12,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_confgui.m 2787 2009-02-25 08:02:53Z volkmar $ +% $Id: cfg_confgui.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 2787 $'; %#ok +rev = '$Rev: 3355 $'; %#ok %% Declaration of fields @@ -571,7 +571,7 @@ out.c0 = cfg_struct2cfg(varargin{1}.gencode_var); end % Generate code -[str tag] = gencode(out.c0,'',{},'',cfg_tropts({{}},1,inf,1,inf,true)); +[str tag] = gencode(out.c0,'',{}); [p n e v] = fileparts(varargin{1}.gencode_fname); %#ok out.cfg_file{1} = fullfile(varargin{1}.gencode_dir{1}, [n '.m']); fid = fopen(out.cfg_file{1}, 'w'); diff --git a/matlabbatch/cfg_confgui/cfg_run_template.m b/matlabbatch/cfg_confgui/cfg_run_template.m new file mode 100644 index 0000000..8e39b07 --- /dev/null +++ b/matlabbatch/cfg_confgui/cfg_run_template.m @@ -0,0 +1,122 @@ +function varargout = cfg_run_template(cmd, varargin) +% Template function to implement callbacks for an cfg_exbranch. The calling +% syntax is +% varargout = cfg_run_template(cmd, varargin) +% where cmd is one of +% 'run' - out = cfg_run_template('run', job) +% Run a job, and return its output argument +% 'vout' - dep = cfg_run_template('vout', job) +% Examine a job structure with all leafs present and return an +% array of cfg_dep objects. +% 'check' - str = cfg_run_template('check', subcmd, subjob) +% Examine a part of a fully filled job structure. Return an empty +% string if everything is ok, or a string describing the check +% error. subcmd should be a string that identifies the part of +% the configuration to be checked. +% 'defaults' - defval = cfg_run_template('defaults', key) +% Retrieve defaults value. key must be a sequence of dot +% delimited field names into the internal def struct which is +% kept in function local_def. An error is returned if no +% matching field is found. +% cfg_run_template('defaults', key, newval) +% Set the specified field in the internal def struct to a new +% value. +% Application specific code needs to be inserted at the following places: +% 'run' - main switch statement: code to compute the results, based on +% a filled job +% 'vout' - main switch statement: code to compute cfg_dep array, based +% on a job structure that has all leafs, but not necessarily +% any values filled in +% 'check' - create and populate switch subcmd switchyard +% 'defaults' - modify initialisation of defaults in subfunction local_defs +% Callbacks can be constructed using anonymous function handles like this: +% 'run' - @(job)cfg_run_template('run', job) +% 'vout' - @(job)cfg_run_template('vout', job) +% 'check' - @(job)cfg_run_template('check', 'subcmd', job) +% 'defaults' - @(val)cfg_run_template('defaults', 'defstr', val{:}) +% Note the list expansion val{:} - this is used to emulate a +% varargin call in this function handle. +% +% This code is part of a batch job configuration system for MATLAB. See +% help matlabbatch +% for a general overview. +%_______________________________________________________________________ +% Copyright (C) 2007 Freiburg Brain Imaging + +% Volkmar Glauche +% $Id: cfg_run_template.m 3355 2009-09-04 09:37:35Z volkmar $ + +rev = '$Rev: 3355 $'; %#ok + +if ischar(cmd) + switch lower(cmd) + case 'run' + job = local_getjob(varargin{1}); + % do computation, return results in variable out + if nargout > 0 + varargout{1} = out; + end + case 'vout' + job = local_getjob(varargin{1}); + % initialise empty cfg_dep array + dep = cfg_dep; + dep = dep(false); + % determine outputs, return cfg_dep array in variable dep + varargout{1} = dep; + case 'check' + if ischar(varargin{1}) + subcmd = lower(varargin{1}); + subjob = varargin{2}; + str = ''; + switch subcmd + % implement checks, return status string in variable str + otherwise + cfg_message('unknown:check', ... + 'Unknown check subcmd ''%s''.', subcmd); + end + varargout{1} = str; + else + cfg_message('ischar:check', 'Subcmd must be a string.'); + end + case 'defaults' + if nargin == 2 + varargout{1} = local_defs(varargin{1}); + else + local_defs(varargin{1:2}); + end + otherwise + cfg_message('unknown:cmd', 'Unknown command ''%s''.', cmd); + end +else + cfg_message('ischar:cmd', 'Cmd must be a string.'); +end + +function varargout = local_defs(defstr, defval) +persistent defs; +if isempty(defs) + % initialise defaults +end +if ischar(defstr) + % construct subscript reference struct from dot delimited tag string + tags = textscan(defstr,'%s', 'delimiter','.'); + subs = struct('type','.','subs',tags{1}'); + try + cdefval = subsref(local_def, subs); + catch + cdefval = []; + cfg_message('defaults:noval', ... + 'No matching defaults value ''%s'' found.', defstr); + end + if nargin == 1 + varargout{1} = cdefval; + else + defs = subsasgn(defs, subs, defval); + end +else + cfg_message('ischar:defstr', 'Defaults key must be a string.'); +end + +function job = local_getjob(job) +if ~isstruct(job) + cfg_message('isstruct:job', 'Job must be a struct.'); +end \ No newline at end of file diff --git a/matlabbatch/cfg_getfile.m b/matlabbatch/cfg_getfile.m index c46a4f8..3032154 100644 --- a/matlabbatch/cfg_getfile.m +++ b/matlabbatch/cfg_getfile.m @@ -78,7 +78,7 @@ % Copyright (C) 2007 Freiburg Brain Imaging % John Ashburner and Volkmar Glauche -% $Id: cfg_getfile.m 2934 2009-03-24 12:15:28Z volkmar $ +% $Id: cfg_getfile.m 3223 2009-06-25 17:27:12Z volkmar $ t = {}; sts = false; @@ -138,16 +138,17 @@ end filt = mk_filter(typ, varargin{3}, frames); [t sts] = listfiles(varargin{2}, filt); % (sts is subdirs here) - if ~isempty(t) - if regexpi(varargin{1}, 'fplist') % return full pathnames - direc = cfg_getfile('cpath', varargin{2}); - % remove trailing path separator if present - direc = regexprep(direc, [filesep '$'], ''); + sts = sts(~(strcmp(sts,'.')|strcmp(sts,'..'))); % remove '.' and '..' entries + if regexpi(varargin{1}, 'fplist') % return full pathnames + direc = cfg_getfile('cpath', varargin{2}); + % remove trailing path separator if present + direc = regexprep(direc, [filesep '$'], ''); + if ~isempty(t) t = strcat(direc, filesep, t); - if nargout > 1 - % subdirs too - sts = cellfun(@(sts1)cpath(sts1, direc), sts, 'UniformOutput',false); - end + end + if nargout > 1 + % subdirs too + sts = cellfun(@(sts1)cpath(sts1, direc), sts, 'UniformOutput',false); end end case 'prevdirs', @@ -181,7 +182,7 @@ if nargin<3 || ~(ischar(mesg) || iscellstr(mesg)) mesg = 'Select files...'; elseif iscellstr(mesg) - mesg = strvcat(mesg); + mesg = char(mesg); end if nargin<4 || isempty(already) || (iscell(already) && isempty(already{1})) @@ -232,16 +233,20 @@ if ~isempty(fg) delete(fg); end -fg = figure('IntegerHandle','off',... +% create temporary figure to work out position etc. +% seems to be necessary +fgtmp = figure('IntegerHandle','off',... 'Tag',mfilename,... 'Name',mesg,... 'NumberTitle','off',... 'Units','Pixels',... 'MenuBar','none',... 'DefaultTextInterpreter','none',... - 'DefaultUicontrolInterruptible','on'); + 'DefaultUicontrolInterruptible','on',... + 'Visible','off'); -Rect = get(fg,'Position'); +Rect = get(fgtmp,'Position'); +delete(fgtmp); %S0 = spm('WinSize','0',1); S0 = get(0,'MonitorPosition'); if size(S0,1) > 1 % Multiple Monitors @@ -254,7 +259,16 @@ end Rect(1) = S0(1) + (S0(3) - Rect(3))/2; Rect(2) = S0(2) + (S0(4) - Rect(4))/2; -set(fg,'Position',Rect); +% create final figure +fg = figure('IntegerHandle','off',... + 'Tag',mfilename,... + 'Name',mesg,... + 'NumberTitle','off',... + 'Units','Pixels',... + 'MenuBar','none',... + 'DefaultTextInterpreter','none',... + 'DefaultUicontrolInterruptible','on',... + 'Position',Rect); sellines = min([max([n(2) numel(already)]), 4]); [pselp pcntp pfdp pdirp] = panelpositions(fg, sellines+1); @@ -921,7 +935,7 @@ function unselect_all(ob,varargin) if ~isempty(de), d = {de([de.isdir]).name}; if ~any(strcmp(d, '.')) - d = {'.', d{:}}; + d = [{'.'}, d(:)']; end; if filt.code~=-1, f = {de(~[de.isdir]).name}; @@ -1322,7 +1336,7 @@ function select_rec(ob, varargin) if nargin<1, typ = 'any'; end; switch lower(typ), case {'any','*'}, code = 0; ext = {'.*'}; -case {'image'}, code = 1; ext = {'.*\.nii$','.*\.img$','.*\.NII$','.*\.IMG$'}; +case {'image'}, code = 1; ext = {'.*\.nii(,\d+)?$','.*\.img(,\d+)?$','.*\.NII(,\d+)?$','.*\.IMG(,\d+)?$'}; case {'mesh'}, code = 0; ext = {'.*\.gii$','.*\.GII$','.*\.mat$','.*\.MAT$'}; case {'nifti'}, code = 0; ext = {'.*\.nii$','.*\.img$','.*\.NII$','.*\.IMG$'}; case {'gifti'}, code = 0; ext = {'.*\.gii$','.*\.GII$'}; @@ -1332,7 +1346,12 @@ function select_rec(ob, varargin) '.*\.IMG(,[0-9]*){0,1}$'}; case {'xml'}, code = 0; ext = {'.*\.xml$','.*\.XML$'}; case {'mat'}, code = 0; ext = {'.*\.mat$','.*\.MAT$','.*\.txt','.*\.TXT'}; -case {'batch'}, code = 0; ext = {'.*\.mat$','.*\.MAT$','.*\.m$','.*\.M$','.*\.xml$','.*\.XML$'}; +case {'batch'}, code = 0; + if isdeployed, + ext = {'.*\.mat$','.*\.MAT$','.*\.xml$','.*\.XML$'}; + else + ext = {'.*\.mat$','.*\.MAT$','.*\.m$','.*\.M$','.*\.xml$','.*\.XML$'}; + end case {'dir'}, code =-1; ext = {'.*'}; case {'extdir'}, code =-1; ext = {['.*' filesep '$']}; otherwise, code = 0; ext = {typ}; diff --git a/matlabbatch/cfg_mlbatch_appcfg.m b/matlabbatch/cfg_mlbatch_appcfg.m index 8298615..5ba55b8 100644 --- a/matlabbatch/cfg_mlbatch_appcfg.m +++ b/matlabbatch/cfg_mlbatch_appcfg.m @@ -14,16 +14,18 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_mlbatch_appcfg.m 1716 2008-05-23 08:18:45Z volkmar $ +% $Id: cfg_mlbatch_appcfg.m 3179 2009-06-03 12:41:21Z volkmar $ -rev = '$Rev: 1716 $'; %#ok +rev = '$Rev: 3179 $'; %#ok -% get path to this file -p = fileparts(mfilename('fullpath')); -% in this case, the cfg_mlbatch_appcfg file is not in the application -% folder. Therefore, the path to the application needs to be added -% explicitly -addpath(fullfile(p, 'cfg_basicio')); +if ~isdeployed + % get path to this file + p = fileparts(mfilename('fullpath')); + % in this case, the cfg_mlbatch_appcfg file is not in the application + % folder. Therefore, the path to the application needs to be added + % explicitly + addpath(fullfile(p, 'cfg_basicio')); +end % these two files are now on MATLABs path cfg = cfg_cfg_basicio; def = cfg_cfg_basicio_def; diff --git a/matlabbatch/cfg_ui.m b/matlabbatch/cfg_ui.m index bb673f9..55b4cbf 100644 --- a/matlabbatch/cfg_ui.m +++ b/matlabbatch/cfg_ui.m @@ -27,9 +27,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_ui.m 2816 2009-03-03 08:49:21Z volkmar $ +% $Id: cfg_ui.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 2816 $'; %#ok +rev = '$Rev: 3355 $'; %#ok % edit the above text to modify the response to help cfg_ui @@ -366,6 +366,7 @@ function local_showmod(obj) else switch contents{5}{k} case 'cfg_menu', + datastr{k} = 'Unknown selection'; for l = 1:numel(contents{4}{k}) if isequal(contents{2}{k}{1}, contents{4}{k}{l}) datastr{k} = contents{3}{k}{l}; @@ -754,7 +755,8 @@ function local_valedit_edit(hObject) if ishandle(val) % delete accidentally created objects delete(val); end - str = strcat({encl(1)}, cstr, {encl(2)}, {char(10)}); + % escape single quotes and place the whole string in single quotes + str = strcat({encl(1)}, strrep(cstr,'''',''''''), {encl(2)}, {char(10)}); else cestr = {encl(1) cstr{:} encl(2)}; str = strcat(cestr, {char(10)}); @@ -1107,7 +1109,8 @@ function MenuFileSave_Callback(hObject, eventdata, handles) if ~isempty(udmodlist.wd) cd(udmodlist.wd); end; -[file path idx] = uiputfile({'*.mat','Matlab .mat File';'*.m','Matlab Script File'}, 'Save Job'); +[file pth idx] = uiputfile({'*.mat','Matlab .mat File';... + '*.m','Matlab .m Script File'}, 'Save Job'); cd(opwd); if isnumeric(file) && file == 0 return; @@ -1116,10 +1119,24 @@ function MenuFileSave_Callback(hObject, eventdata, handles) [p n e v] = fileparts(file); if isempty(e) || ~any(strcmp(e,{'.mat','.m'})) e1 = {'.mat','.m'}; - file = sprintf('%s%s%s', n, e, e1{idx}); + e2 = e1{idx}; + file = sprintf('%s%s', n, e); +else + file = n; + e2 = e; +end +% Warn if saving as .m in a compiled version +if isdeployed && strcmp(e2,'.m') && ... + strcmp(questdlg({'This batch system will not load ".m" batch jobs.' ... + ['If you want to use your batch with this batch ' ... + 'system, you should save it as a ".mat" file.']}, ... + 'Format for Saved Job',... + 'Save as .mat','Save as .m', 'Save as .mat'), ... + 'Save as .mat') + e2 = '.mat'; end try - cfg_util('savejob', udmodlist.cjob, fullfile(path, file)); + cfg_util('savejob', udmodlist.cjob, fullfile(pth, [file e2])); udmodlist.modified = false; set(handles.modlist,'userdata',udmodlist); catch diff --git a/matlabbatch/cfg_util.m b/matlabbatch/cfg_util.m index bd62c3f..45c4947 100644 --- a/matlabbatch/cfg_util.m +++ b/matlabbatch/cfg_util.m @@ -373,9 +373,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_util.m 3130 2009-05-18 14:41:31Z volkmar $ +% $Id: cfg_util.m 3357 2009-09-04 09:42:17Z volkmar $ -rev = '$Rev: 3130 $'; +rev = '$Rev: 3357 $'; %% Initialisation of cfg variables % load persistent configuration data, initialise if necessary @@ -993,7 +993,7 @@ function local_gencode(c0, fname, tropts, preamble) tropts(1).mlvl = Inf; tropts(1).cnt = 1; [p funcname e v] = fileparts(fname); - [cstr tag] = gencode(c0, '', {}, [funcname '_'], tropts); + [cstr tag] = gencode_item(c0, '', {}, [funcname '_'], tropts); funcname = [funcname '_' tag]; fname = fullfile(p, [funcname '.m']); unpostfix = ''; @@ -1014,7 +1014,7 @@ function local_gencode(c0, fname, tropts, preamble) else % generate root level code [p funcname e v] = fileparts(fname); - [cstr tag] = gencode(c0, 'jobs', {}, [funcname '_'], tropts); + [cstr tag] = gencode_item(c0, 'jobs', {}, [funcname '_'], tropts); fname = fullfile(p, [funcname '.m']); if nargin < 4 || isempty(preamble) || ~iscellstr(preamble) try diff --git a/matlabbatch/examples/batch_example_add1_div.m b/matlabbatch/examples/batch_example_add1_div.m new file mode 100644 index 0000000..a5d345b --- /dev/null +++ b/matlabbatch/examples/batch_example_add1_div.m @@ -0,0 +1,19 @@ +%----------------------------------------------------------------------- +% Job configuration created by cfg_util (rev $Rev: 3355 $) +%----------------------------------------------------------------------- +matlabbatch{1}.cfg_toy{1}.add2{1}.cfg_example_add1.a = ''; +matlabbatch{1}.cfg_toy{1}.add2{1}.cfg_example_add1.b = ''; +matlabbatch{2}.cfg_toy{1}.cfg_example_div.a = ''; +matlabbatch{2}.cfg_toy{1}.cfg_example_div.b(1) = cfg_dep; +matlabbatch{2}.cfg_toy{1}.cfg_example_div.b(1).tname = 'Input b'; +matlabbatch{2}.cfg_toy{1}.cfg_example_div.b(1).tgt_spec = {}; +matlabbatch{2}.cfg_toy{1}.cfg_example_div.b(1).sname = 'Add1: Add1: a + b'; +matlabbatch{2}.cfg_toy{1}.cfg_example_div.b(1).src_exbranch = substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1}); +matlabbatch{2}.cfg_toy{1}.cfg_example_div.b(1).src_output = substruct('()',{1}); +matlabbatch{3}.cfg_basicio.cfg_assignin.name = 'div_sum_ab_c'; +matlabbatch{3}.cfg_basicio.cfg_assignin.output(1) = cfg_dep; +matlabbatch{3}.cfg_basicio.cfg_assignin.output(1).tname = 'Output Item'; +matlabbatch{3}.cfg_basicio.cfg_assignin.output(1).tgt_spec = {}; +matlabbatch{3}.cfg_basicio.cfg_assignin.output(1).sname = 'div: a div b: mod'; +matlabbatch{3}.cfg_basicio.cfg_assignin.output(1).src_exbranch = substruct('.','val', '{}',{2}, '.','val', '{}',{1}); +matlabbatch{3}.cfg_basicio.cfg_assignin.output(1).src_output = substruct('.','mod'); diff --git a/matlabbatch/examples/batch_example_add1_div_reuse_b.m b/matlabbatch/examples/batch_example_add1_div_reuse_b.m new file mode 100644 index 0000000..b6d8750 --- /dev/null +++ b/matlabbatch/examples/batch_example_add1_div_reuse_b.m @@ -0,0 +1,41 @@ +%----------------------------------------------------------------------- +% Job configuration created by cfg_util (rev $Rev: 3355 $) +%----------------------------------------------------------------------- +matlabbatch{1}.cfg_basicio.cfg_named_input.name = 'a'; +matlabbatch{1}.cfg_basicio.cfg_named_input.input = ''; +matlabbatch{2}.cfg_basicio.cfg_named_input.name = 'b'; +matlabbatch{2}.cfg_basicio.cfg_named_input.input = ''; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1) = cfg_dep; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1).tname = 'Input a'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1).tgt_spec{1}.name = 'class'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1).tgt_spec{1}.value = 'cfg_entry'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1).sname = 'Named Input: a'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1).src_exbranch = substruct('.','val', '{}',{1}, '.','val', '{}',{1}); +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1).src_output = substruct('.','input'); +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1) = cfg_dep; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1).tname = 'Input b'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1).tgt_spec{1}.name = 'class'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1).tgt_spec{1}.value = 'cfg_entry'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1).sname = 'Named Input: b'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1).src_exbranch = substruct('.','val', '{}',{2}, '.','val', '{}',{1}); +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1).src_output = substruct('.','input'); +matlabbatch{4}.cfg_toy{1}.cfg_example_div.a(1) = cfg_dep; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.a(1).tname = 'Input a'; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.a(1).tgt_spec = {}; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.a(1).sname = 'Add1: Add1: a + b'; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.a(1).src_exbranch = substruct('.','val', '{}',{3}, '.','val', '{}',{1}, '.','val', '{}',{1}); +matlabbatch{4}.cfg_toy{1}.cfg_example_div.a(1).src_output = substruct('()',{1}); +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1) = cfg_dep; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1).tname = 'Input b'; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1).tgt_spec{1}.name = 'class'; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1).tgt_spec{1}.value = 'cfg_entry'; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1).sname = 'Named Input: b'; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1).src_exbranch = substruct('.','val', '{}',{2}, '.','val', '{}',{1}); +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1).src_output = substruct('.','input'); +matlabbatch{5}.cfg_basicio.cfg_assignin.name = 'result'; +matlabbatch{5}.cfg_basicio.cfg_assignin.output(1) = cfg_dep; +matlabbatch{5}.cfg_basicio.cfg_assignin.output(1).tname = 'Output Item'; +matlabbatch{5}.cfg_basicio.cfg_assignin.output(1).tgt_spec = {}; +matlabbatch{5}.cfg_basicio.cfg_assignin.output(1).sname = 'div: a div b: mod'; +matlabbatch{5}.cfg_basicio.cfg_assignin.output(1).src_exbranch = substruct('.','val', '{}',{4}, '.','val', '{}',{1}); +matlabbatch{5}.cfg_basicio.cfg_assignin.output(1).src_output = substruct('.','mod'); diff --git a/matlabbatch/examples/batch_example_add1_div_reuse_b_outname.m b/matlabbatch/examples/batch_example_add1_div_reuse_b_outname.m new file mode 100644 index 0000000..88c8887 --- /dev/null +++ b/matlabbatch/examples/batch_example_add1_div_reuse_b_outname.m @@ -0,0 +1,41 @@ +%----------------------------------------------------------------------- +% Job configuration created by cfg_util (rev $Rev: 3355 $) +%----------------------------------------------------------------------- +matlabbatch{1}.cfg_basicio.cfg_named_input.name = 'a'; +matlabbatch{1}.cfg_basicio.cfg_named_input.input = ''; +matlabbatch{2}.cfg_basicio.cfg_named_input.name = 'b'; +matlabbatch{2}.cfg_basicio.cfg_named_input.input = ''; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1) = cfg_dep; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1).tname = 'Input a'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1).tgt_spec{1}.name = 'class'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1).tgt_spec{1}.value = 'cfg_entry'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1).sname = 'Named Input: a'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1).src_exbranch = substruct('.','val', '{}',{1}, '.','val', '{}',{1}); +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.a(1).src_output = substruct('.','input'); +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1) = cfg_dep; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1).tname = 'Input b'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1).tgt_spec{1}.name = 'class'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1).tgt_spec{1}.value = 'cfg_entry'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1).sname = 'Named Input: b'; +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1).src_exbranch = substruct('.','val', '{}',{2}, '.','val', '{}',{1}); +matlabbatch{3}.cfg_toy{1}.add2{1}.cfg_example_add1.b(1).src_output = substruct('.','input'); +matlabbatch{4}.cfg_toy{1}.cfg_example_div.a(1) = cfg_dep; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.a(1).tname = 'Input a'; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.a(1).tgt_spec = {}; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.a(1).sname = 'Add1: Add1: a + b'; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.a(1).src_exbranch = substruct('.','val', '{}',{3}, '.','val', '{}',{1}, '.','val', '{}',{1}); +matlabbatch{4}.cfg_toy{1}.cfg_example_div.a(1).src_output = substruct('()',{1}); +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1) = cfg_dep; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1).tname = 'Input b'; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1).tgt_spec{1}.name = 'class'; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1).tgt_spec{1}.value = 'cfg_entry'; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1).sname = 'Named Input: b'; +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1).src_exbranch = substruct('.','val', '{}',{2}, '.','val', '{}',{1}); +matlabbatch{4}.cfg_toy{1}.cfg_example_div.b(1).src_output = substruct('.','input'); +matlabbatch{5}.cfg_basicio.cfg_assignin.name = ''; +matlabbatch{5}.cfg_basicio.cfg_assignin.output(1) = cfg_dep; +matlabbatch{5}.cfg_basicio.cfg_assignin.output(1).tname = 'Output Item'; +matlabbatch{5}.cfg_basicio.cfg_assignin.output(1).tgt_spec = {}; +matlabbatch{5}.cfg_basicio.cfg_assignin.output(1).sname = 'div: a div b: mod'; +matlabbatch{5}.cfg_basicio.cfg_assignin.output(1).src_exbranch = substruct('.','val', '{}',{4}, '.','val', '{}',{1}); +matlabbatch{5}.cfg_basicio.cfg_assignin.output(1).src_output = substruct('.','mod'); diff --git a/matlabbatch/examples/cfg_example_run_div.m b/matlabbatch/examples/cfg_example_run_div.m index bf13dea..15793b8 100644 --- a/matlabbatch/examples/cfg_example_run_div.m +++ b/matlabbatch/examples/cfg_example_run_div.m @@ -9,9 +9,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_example_run_div.m 1716 2008-05-23 08:18:45Z volkmar $ +% $Id: cfg_example_run_div.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 1716 $'; %#ok +rev = '$Rev: 3355 $'; %#ok out.mod = mod(job.a, job.b); -out.rem = mod(job.a, job.b); \ No newline at end of file +out.rem = rem(job.a, job.b); \ No newline at end of file diff --git a/matlabbatch/examples/cfg_mlbatch_appcfg.m b/matlabbatch/examples/cfg_mlbatch_appcfg.m.example similarity index 88% rename from matlabbatch/examples/cfg_mlbatch_appcfg.m rename to matlabbatch/examples/cfg_mlbatch_appcfg.m.example index 85b2b1c..9341576 100644 --- a/matlabbatch/examples/cfg_mlbatch_appcfg.m +++ b/matlabbatch/examples/cfg_mlbatch_appcfg.m.example @@ -14,9 +14,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_mlbatch_appcfg.m 1716 2008-05-23 08:18:45Z volkmar $ +% $Id: cfg_mlbatch_appcfg.m.example 3184 2009-06-05 15:54:40Z volkmar $ -rev = '$Rev: 1716 $'; %#ok +rev = '$Rev: 3184 $'; %#ok % toy example configuration cfg = cfg_example_master; diff --git a/matlabbatch/gencode.m b/matlabbatch/gencode.m index be840d4..e5fdb34 100644 --- a/matlabbatch/gencode.m +++ b/matlabbatch/gencode.m @@ -1,79 +1,51 @@ -function [str, tag, cind, ccnt] = gencode(item, tag, stoptag, tropts) +function [str, tag, cind] = gencode(item, tag, tagctx) -% function [str, tag, cind, ccnt] = gencode(item, tag, stoptag, tropts) -% Generate code to recreate any MATLAB struct/cell variable. Classes need -% to implement their class specific equivalent of gencode with the same -% calling syntax. +% GENCODE Generate code to recreate any MATLAB struct/cell variable. +% For any MATLAB variable, this function generates a .m file that +% can be run to recreate it. Classes can implement their class specific +% equivalent of gencode with the same calling syntax. By default, classes +% are treated similar to struct variables. % +% [str, tag, cind] = gencode(item, tag, tagctx) % Input arguments: % item - MATLAB variable to generate code for (the variable itself, not its % name) -% tag - optional: name of the variable, i.e. what will be displayed left -% of the '=' sign. This can also be a valid struct/cell array -% reference, like 'x(2).y'. If not provided, inputname(1) will be -% used. -% stoptag - optional: tag which is used if struct/cell traversal stopped -% due to tropts limitations. If not provided, tag will be used. -% tropts - optional: traversal options - struct with fields -% .stopspec - (only used for matlabbatch objects) -% .dflag - (only used for matlabbatch objects) -% .clvl - current level in variable - level is increased if fields of -% structures or cell items are traversed -% .mlvl - maximum level to generate code for - range 1 (top level only) -% to Inf (all levels) -% .cnt - (only used for matlabbatch objects) -% .mcnt - (only used for matlabbatch objects) -% +% tag - optional: name of the variable, i.e. what will be displayed left +% of the '=' sign. This can also be a valid struct/cell array +% reference, like 'x(2).y'. If not provided, inputname(1) will be +% used. +% tagctx - optional: variable names not to be used (e.g. keywords, +% reserved variables). A cell array of strings. % Output arguments: -% str - cellstr containing code lines to reproduce +% str - cellstr containing code lines to reproduce the input variable % tag - name of the generated variable (equal to input tag) % cind - index into str to the line where the variable assignment is coded % (usually 1st line for non-object variables) -% ccnt - item count (outside matlabbatch objects always 1) % -% For scalar, 1D or 2D char, numeric or cell arrays whose contents can be -% written as a MATLAB array, the helper function GENCODE_RVALUE will be -% called. This function can also be used on its own. It will produce code -% to generate the array, but without a left hand side assignment. +% See also GENCODE_RVALUE, GENCODE_SUBSTRUCT, GENCODE_SUBSTRUCTCODE. % -% This code is part of a batch job configuration system for MATLAB. See -% help matlabbatch -% for a general overview. +% This code has been developed as part of a batch job configuration +% system for MATLAB. See +% http://sourceforge.net/projects/matlabbatch +% for details about the original project. %_______________________________________________________________________ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode.m 2657 2009-01-27 16:24:01Z volkmar $ +% $Id: gencode.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 2657 $'; %#ok +rev = '$Rev: 3355 $'; %#ok if nargin < 2 tag = inputname(1); end; if nargin < 3 - stoptag = tag; -end; -if nargin < 4 - tropts = cfg_tropts({{}},1,inf,1,inf,true); -end; - -%% Get variable name -% Check whether to generate code -if tropts.clvl > tropts.mlvl - % Stopping - tag based on stoptag, tag of item and expected new item count - tag = genvarname(sprintf('%s_%s_0001', stoptag, tag)); - str = {}; - cind = []; - ccnt = 0; - return; -else - % Tag based on item count - if isempty(tag) - tag = genvarname(sprintf('val_%04d', tropts.cnt)); - end; + tagctx = {}; +end +if isempty(tag) + tag = genvarname('val', tagctx); end; % Item count -ccnt = 1; cind = 1; % try to generate rvalue code @@ -101,64 +73,45 @@ subs = gensubs('()', {':',':'}, szitem(3:end)); for k = 1:numel(subs) substag = gencode_substruct(subs{k}, tag); - [str1 tag1 cind1 ccnt1] = gencode(subsref(item, subs{k}), substag{1}, stoptag, tropts); - str = {str{:} str1{:}}; + str1 = gencode(subsref(item, subs{k}), substag{1}, tagctx); + str = [str(:)' str1(:)']; end case 'cell' str = {}; szitem = size(item); subs = gensubs('{}', {}, szitem); - tropts.clvl = tropts.clvl + 1; for k = 1:numel(subs) substag = gencode_substruct(subs{k}, tag); - [str1 tag1 cind1 ccnt1] = gencode(subsref(item, subs{k}), substag{1}, stoptag, tropts); - str = {str{:} str1{:}}; + str1 = gencode(subsref(item, subs{k}), substag{1}, tagctx); + str = [str(:)' str1(:)']; end case 'struct' - fn = fieldnames(item); - if isempty(fn) - str{1} = sprintf('%s = struct([]);', tag); - elseif isempty(item) - fn = strcat('''', fn, '''', ', {}'); - str{1} = sprintf('%s = struct(', tag); - for k = 1:numel(fn)-1 - str{1} = sprintf('%s%s, ', str{1}, fn{k}); - end - str{1} = sprintf('%s%s);', str{1}, fn{end}); - elseif numel(item) == 1 - str = {}; - tropts.clvl = tropts.clvl + 1; - for l = 1:numel(fn) - [str1 tag1 cind1 ccnt1] = gencode(item.(fn{l}), sprintf('%s.%s', tag, fn{l}), stoptag, tropts); - str = {str{:} str1{:}}; - end - else - str = {}; - szitem = size(item); - subs = gensubs('()', {}, szitem); - tropts.clvl = tropts.clvl + 1; - for k = 1:numel(subs) - for l = 1:numel(fn) - csubs = [subs{k} substruct('.', fn{l})]; - substag = gencode_substruct(csubs, tag); - [str1 tag1 cind1 ccnt1] = gencode(subsref(item, csubs), substag{1}, stoptag, tropts); - str = {str{:} str1{:}}; - end - end - end + str = gencode_structobj(item, tag, tagctx); otherwise if isobject(item) || ~(isnumeric(item) || islogical(item)) - str = {sprintf('warning(''%s: No code generated for object of class %s.'')', tag, class(item))}; - % Objects need to have their own gencode method implemented - cfg_message('matlabbatch:gencode:unknown', ... - '%s: Code generation for objects of class ''%s'' must be implemented as object method.', tag, class(item)); + % This branch is hit for objects without a gencode method + try + % try to generate code in a struct-like fashion + str = gencode_structobj(item, tag, tagctx); + catch + % failed - generate a warning in generated code and + % warn directly + str = {sprintf('warning(''%s: No code generated for object of class %s.'')', tag, class(item))}; + if any(exist('cfg_message') == 2:6) + cfg_message('matlabbatch:gencode:unknown', ... + '%s: Code generation for objects of class ''%s'' must be implemented as object method.', tag, class(item)); + else + warning('gencode:unknown', ... + '%s: Code generation for objects of class ''%s'' must be implemented as object method.', tag, class(item)); + end + end elseif issparse(item) % recreate sparse matrix from indices [tmpi tmpj tmps] = find(item); - [stri tagi cindi ccnti] = gencode(tmpi); - [strj tagj cindj ccntj] = gencode(tmpj); - [strs tags cinds ccnts] = gencode(tmps); - str = {stri{:} strj{:} strs{:}}; + [stri tagi cindi] = gencode(tmpi); + [strj tagj cindj] = gencode(tmpj); + [strs tags cinds] = gencode(tmps); + str = [stri(:)' strj(:)' strs(:)']; cind = cind + cindi + cindj + cinds; str{end+1} = sprintf('%s = sparse(tmpi, tmpj, tmps);', tag); else @@ -167,8 +120,8 @@ subs = gensubs('()', {':',':'}, szitem(3:end)); for k = 1:numel(subs) substag = gencode_substruct(subs{k}, tag); - [str1 tag1 cind1 ccnt1] = gencode(subsref(item, subs{k}), substag{1}, stoptag, tropts); - str = {str{:} str1{:}}; + str1 = gencode(subsref(item, subs{k}), substag{1}, tagctx); + str = [str(:)' str1(:)']; end end end @@ -193,9 +146,78 @@ end; end; +subs = cell(1,size(ind,2)); % for each column of ind, generate a separate subscript structure for k = 1:size(ind,2) cellind = num2cell(ind(:,k)); - subs{k} = substruct(type, {initdims{:} cellind{:}}); + subs{k} = substruct(type, [initdims(:)' cellind(:)']); end; +function str = gencode_structobj(item, tag, tagctx) + +% Create code for a struct array. Also used as fallback for object +% arrays, if the object does not provide its own gencode implementation. + +citem = class(item); +% try to figure out fields/properties that can be set +if isobject(item) && exist('metaclass','builtin') + mobj = metaclass(item); + % Only create code for properties which are + % * not dependent or dependent and have a SetMethod + % * not constant + % * not abstract + % * have public SetAccess + sel = cellfun(@(cProp)(~cProp.Constant && ... + ~cProp.Abstract && ... + (~cProp.Dependent || ... + (cProp.Dependent && ... + ~isempty(cProp.SetMethod))) && ... + strcmp(cProp.SetAccess,'public')),mobj.Properties); + fn = cellfun(@(cProp)subsref(cProp,substruct('.','Name')),mobj.Properties(sel),'uniformoutput',false); +else + % best guess + fn = fieldnames(item); +end +if isempty(fn) + if isstruct(item) + str{1} = sprintf('%s = struct([]);', tag); + else + str{1} = sprintf('%s = %s;', tag, citem); + end +elseif isempty(item) + if isstruct(item) + fn = strcat('''', fn, '''', ', {}'); + str{1} = sprintf('%s = struct(', tag); + for k = 1:numel(fn)-1 + str{1} = sprintf('%s%s, ', str{1}, fn{k}); + end + str{1} = sprintf('%s%s);', str{1}, fn{end}); + else + str{1} = sprintf('%s = %s.empty;', tag, citem); + end +elseif numel(item) == 1 + if isstruct(item) + str = {}; + else + str{1} = sprintf('%s = %s;', tag, citem); + end + for l = 1:numel(fn) + str1 = gencode(item.(fn{l}), sprintf('%s.%s', tag, fn{l}), tagctx); + str = [str(:)' str1(:)']; + end +else + str = {}; + szitem = size(item); + subs = gensubs('()', {}, szitem); + for k = 1:numel(subs) + if ~isstruct(item) + str{end+1} = sprintf('%s = %s;', gencode_substruct(subs{k}, tag), citem); + end + for l = 1:numel(fn) + csubs = [subs{k} substruct('.', fn{l})]; + substag = gencode_substruct(csubs, tag); + str1 = gencode(subsref(item, csubs), substag{1}, tagctx); + str = [str(:)' str1(:)']; + end + end +end diff --git a/matlabbatch/gencode_rvalue.m b/matlabbatch/gencode_rvalue.m index 325d728..6ca73f4 100644 --- a/matlabbatch/gencode_rvalue.m +++ b/matlabbatch/gencode_rvalue.m @@ -1,30 +1,33 @@ function [str, sts] = gencode_rvalue(item) -% function [str, sts] = gencode_rvalue(item) -% Generate the right hand side for a valid MATLAB variable assignment. + +% GENCODE_RVALUE Code for right hand side of MATLAB assignment +% Generate the right hand side for a valid MATLAB variable +% assignment. This function is a helper to GENCODE, but can be used on +% its own to generate code for the following types of variables: +% * scalar, 1D or 2D numeric, logical or char arrays +% * scalar or 1D cell arrays, where each item can be one of the supported +% array types (i.e. nested cells are allowed) % +% function [str, sts] = gencode_rvalue(item) % Input argument: -% item - value to generate code for -% +% item - value to generate code for % Output arguments: -% str - cellstr with generated code, line per line -% sts - true, if successful, false if code could not be generated +% str - cellstr with generated code, line per line +% sts - true, if successful, false if code could not be generated % -% This function is a helper to GENCODE, but can be used on its own to -% generate a code for the following types of variables: -% * scalar, 1D or 2D numeric, logical or char arrays -% * scalar or 1D cell arrays, where each item can be one of the supported -% array types (i.e. nested cells are allowed) +% See also GENCODE, GENCODE_SUBSTRUCT, GENCODE_SUBSTRUCTCODE. % -% This code is part of a batch job configuration system for MATLAB. See -% help matlabbatch -% for a general overview. +% This code has been developed as part of a batch job configuration +% system for MATLAB. See +% http://sourceforge.net/projects/matlabbatch +% for details about the original project. %_______________________________________________________________________ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode_rvalue.m 2417 2008-10-30 08:24:13Z volkmar $ +% $Id: gencode_rvalue.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 2417 $'; %#ok +rev = '$Rev: 3355 $'; %#ok str = {}; sts = true; @@ -42,7 +45,7 @@ str = str1; else % String array, print brackets and concatenate str1 - str = {'[' str1{:} ']'}; + str = [ {'['} str1(:)' {']'} ]; end else % not an rvalue @@ -58,7 +61,7 @@ if ~sts break; end - str1 = {str1{:} str2{:}}; + str1 = [str1(:)' str2(:)']; end if sts if numel(str1) == 1 @@ -67,11 +70,11 @@ else % Cell vector, print braces and concatenate str1 if size(item,1) == 1 - endstr = '}'''; + endstr = {'}'''}; else - endstr = '}'; + endstr = {'}'}; end - str = {'{' str1{:} endstr}; + str = [{'{'} str1(:)' endstr]; end end else @@ -87,7 +90,7 @@ str{1} = sprintf('@%s', fstr); end otherwise - if isobject(item) || ~(isnumeric(item) || islogical(item)) || ndims(item) > 2 + if isobject(item) || ~(isnumeric(item) || islogical(item)) || issparse(item) || ndims(item) > 2 sts = false; else % treat item as numeric or logical, don't create 'class'(...) diff --git a/matlabbatch/gencode_substruct.m b/matlabbatch/gencode_substruct.m index 8f59a01..98f139d 100644 --- a/matlabbatch/gencode_substruct.m +++ b/matlabbatch/gencode_substruct.m @@ -1,20 +1,36 @@ -function [str, name, ind] = gencode_substruct(subs, name) +function str = gencode_substruct(subs, name) -% Generate MATLAB code that shows the subscript reference subs. If name is -% given, it is prepended to the string, otherwise it is set to ''. -% str is a 1-line cellstr, name is the used name and ind is 1 (to conform -% to general gencode calling syntax). +% GENCODE_SUBSTRUCT String representation of subscript structure. +% Generate MATLAB code equivalent to subscript structure subs. See help +% on SUBSTRUCT, SUBSASGN and SUBSREF for details how subscript structures +% are used. +% +% str = gencode_substruct(subs, name) +% Input arguments: +% subs - a subscript structure +% name - optional: name of variable to be dereferenced +% Output arguments: +% str - a one-line cellstr containing a string representation of the +% subscript structure +% If name is given, it is prepended to the string. % For '()' and '{}' also pseudo subscripts are allowed: if subs.subs{...} % is a string, it will be printed literally, even if it is not equal to -% ':'. This way, one can create code snippets that contain e.g. references -% to a loop variable by name. +% ':'. This way, it is possible create code snippets that contain +% e.g. references to a loop variable by name. +% +% See also GENCODE, GENCODE_RVALUE, GENCODE_SUBSTRUCTCODE. +% +% This code has been developed as part of a batch job configuration +% system for MATLAB. See +% http://sourceforge.net/projects/matlabbatch +% for details about the original project. %_______________________________________________________________________ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode_substruct.m 1862 2008-06-30 14:12:49Z volkmar $ +% $Id: gencode_substruct.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 1862 $'; %#ok +rev = '$Rev: 3355 $'; %#ok ind = 1; if nargin < 2 @@ -22,7 +38,11 @@ end if ~isstruct(subs) || ~all(isfield(subs, {'type','subs'})) - cfg_message('matlabbatch:usage', 'Item is not a substruct.'); + if any(exist('cfg_message') == 2:6) + cfg_message('matlabbatch:usage', 'Item is not a substruct.'); + else + warning('gencode_substruct:usage', 'Item is not a substruct.'); + end else str = {name}; for k = 1:numel(subs) diff --git a/matlabbatch/gencode_substructcode.m b/matlabbatch/gencode_substructcode.m index 5261e2d..604cc7e 100644 --- a/matlabbatch/gencode_substructcode.m +++ b/matlabbatch/gencode_substructcode.m @@ -1,21 +1,38 @@ -function [str, name, ind] = gencode_substructcode(subs, name) +function str = gencode_substructcode(subs, name) -% Generate MATLAB code that recreates a given subscript structure using -% a substruct call. -% str is a 1-line cellstr, name is the used name and ind is 1 (to conform -% to general gencode calling syntax). If name is not supplied or empty, +% GENCODE_SUBSTRUCTCODE Create code for a subscript structure +% Generate MATLAB code (using SUBSTRUCT) to create subscript structure +% subs. See help on SUBSTRUCT, SUBSASGN and SUBSREF for details how +% subscript structures are used. +% +% str = gencode_substructcode(subs, name) +% Input arguments: +% subs - a subscript structure +% name - optional: name of variable +% Output arguments: +% str - a one-line cellstr containing a call to SUBSTRUCT that returns +% an substruct equivalent to subs. +% If name is supplied as input argument, the generated code will assign +% the output of SUBSTRUCT to the variable 'name'. % then only the rhs of the expression will be returned. % For '()' and '{}' also pseudo subscripts are allowed: if subs.subs{...} % is a string, it will be printed literally, even if it is not equal to % ':'. This way, one can create code snippets that contain e.g. references % to a loop variable by name. +% +% See also GENCODE, GENCODE_RVALUE, GENCODE_SUBSTRUCT. +% +% This code has been developed as part of a batch job configuration +% system for MATLAB. See +% http://sourceforge.net/projects/matlabbatch +% for details about the original project. %_______________________________________________________________________ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode_substructcode.m 1862 2008-06-30 14:12:49Z volkmar $ +% $Id: gencode_substructcode.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 1862 $'; %#ok +rev = '$Rev: 3355 $'; %#ok ind = 1; if nargin < 2 @@ -23,7 +40,12 @@ end if ~isstruct(subs) || ~all(isfield(subs, {'type','subs'})) - cfg_message('matlabbatch:usage', 'Item is not a substruct.'); + if any(exist('cfg_message') == 2:6) + cfg_message('matlabbatch:usage', 'Item is not a substruct.'); + else + warning('gencode_substructcode:usage', ... + 'Item is not a substruct.'); + end else if isempty(subs) str = {'struct(''type'',{},''subs'',{})'}; diff --git a/matlabbatch/help2cell.m b/matlabbatch/help2cell.m index 691feb4..00ede1d 100644 --- a/matlabbatch/help2cell.m +++ b/matlabbatch/help2cell.m @@ -1,4 +1,4 @@ -function cellhelp = vgtbx_help2cell(topic); +function cellhelp = help2cell(topic) % VGTBX_HELP2CELL - translate help texts into cell arrays % cellhelp = help2cell(topic) % Create a cell array of help strings from the MATLAB help on 'topic'. diff --git a/matlabbatch/hgsave_pre2008a.m b/matlabbatch/hgsave_pre2008a.m index 4589252..fb9688c 100644 --- a/matlabbatch/hgsave_pre2008a.m +++ b/matlabbatch/hgsave_pre2008a.m @@ -1,5 +1,5 @@ -function hgsave_pre2008a(figname,doreplace) -% function HGSAVE_PRE2008A(figname,doreplace) +function outfile = hgsave_pre2008a(figname,doreplace) +% HGSAVE_PRE2008A % Starting with MATLAB 2008a, GUIDE saves figures with '%automatic' % functions (e.g. Callbacks, ResizeFcn ...) as anonymous function handles, % where previous versions used strings instead. The problem is that MATLAB @@ -10,15 +10,21 @@ function hgsave_pre2008a(figname,doreplace) % b) generating code with anonymous function handles which must be run in % MATLAB R14SP3 to save a valid .fig or .mat file. % +% function outfile = hgsave_pre2008a(figname,doreplace) % Input arguments: -% - figname: string containing full path and file of .fig/.mat file to -% repair -% - doreplace: true - try to replace function handles with strings. Useful +% figname - string containing full path and file of .fig/.mat file to +% repair +% doreplace - how to treat function handles +% true - try to replace function handles with strings. Useful % if one needs to be compatible, but has no R14SP3 at % hand. -% false - create code that must be run in MATLAB R14SP3 to +% false - create .m file that must be run in MATLAB R14SP3 to % save a compatible .mat file. +% Output argument: +% outfile - file name of output file. Depending on doreplace, this is +% either a .fig/.mat file, or a .m file. % +% Details of the correction procedure: % 1) load a MATLAB 2008a .fig or .mat file as variable % 2) generate code for it using GENCODE % if doreplace @@ -41,16 +47,19 @@ function hgsave_pre2008a(figname,doreplace) % corrected. After editing, this m-file can be run to save the corrected % figure. % -% This code is part of a batch job configuration system for MATLAB. See -% help matlabbatch -% for a general overview. +% See also GENCODE, GENCODE_RVALUE, GENCODE_SUBSTRUCT, GENCODE_SUBSTRUCTCODE. +% +% This code has been developed as part of a batch job configuration +% system for MATLAB. See +% http://sourceforge.net/projects/matlabbatch +% for details about the original project. %_______________________________________________________________________ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: hgsave_pre2008a.m 2180 2008-09-25 08:17:13Z volkmar $ +% $Id: hgsave_pre2008a.m 3355 2009-09-04 09:37:35Z volkmar $ -rev = '$Rev: 2180 $'; %#ok +rev = '$Rev: 3355 $'; %#ok hvar = load(figname,'-mat'); hstr = gencode(hvar); @@ -67,13 +76,14 @@ function hgsave_pre2008a(figname,doreplace) % check, whether there may be other anonymous callbacks left anon = ~cellfun(@isempty,regexp(hstr1,'^[^=]*= @\(')); if ~doreplace || any(anon) + scriptname = fullfile(p, sprintf('%s_R14SP3.m', fign)); if doreplace && any(anon) warning('hgsave_pre2008a:anon', ... ['There may be anonymous function handles left. ',... 'An m-file\n ''%s''\n will be created. Please check this file, adjust the offending lines and run it once.'], ... - fullfile(p, sprintf('%s_R14SP3.m', fign))); + scriptname); end - fid = fopen(fullfile(p, sprintf('%s_R14SP3.m', fign)),'w'); + fid = fopen(scriptname,'w'); % preamble, usage instructions fprintf(fid, '%% m-file generated by %s using GENCODE\n', upper(mfilename)); fprintf(fid, '%% This file contains code to generate figure/variable ''%s_R14SP3''.\n', fign); @@ -95,8 +105,10 @@ function hgsave_pre2008a(figname,doreplace) 'catch\n',... 'save(''%s'', ''-struct'',''hvar'');\n',... 'end\n'], nfigname, nfigname); + outfile = scriptname; else % generated code will overwrite hvar eval(sprintf('%s\n',hstr1{:})); save(nfigname,'-v7','-struct','hvar'); + outfile = nfigname; end \ No newline at end of file diff --git a/matlabbatch/private/cfg_maxextent.m b/matlabbatch/private/cfg_maxextent.m index 1443b7a..60b915d 100644 --- a/matlabbatch/private/cfg_maxextent.m +++ b/matlabbatch/private/cfg_maxextent.m @@ -13,12 +13,12 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_maxextent.m 2131 2008-09-22 06:04:53Z volkmar $ +% $Id: cfg_maxextent.m 3282 2009-07-23 07:44:16Z volkmar $ -rev = '$Rev: 2131 $'; %#ok +rev = '$Rev: 3282 $'; %#ok ext = zeros(size(str)); for k = 1:numel(str) - set(obj,'String',str{k}); + set(obj,'String',str(k)); next = get(obj, 'Extent'); ext(k) = next(3); end; diff --git a/spm.m b/spm.m index 82f7f31..9584855 100644 --- a/spm.m +++ b/spm.m @@ -63,7 +63,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Andrew Holmes -% $Id: spm.m 3136 2009-05-20 07:34:23Z volkmar $ +% $Id: spm.m 3401 2009-09-14 18:33:23Z guillaume $ %======================================================================= @@ -341,7 +341,7 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality %-Setup for batch system %----------------------------------------------------------------------- spm_jobman('initcfg'); -spm_select('prevdirs',spm('Dir')); +spm_select('prevdirs',[spm('Dir') filesep]); %-Draw SPM windows %----------------------------------------------------------------------- @@ -513,8 +513,12 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality %-Set toolbox %----------------------------------------------------------------------- xTB = spm('tbs'); -set(findobj(Fmenu,'Tag', 'Toolbox'),'String',{'Toolbox:' xTB.name }); -set(findobj(Fmenu,'Tag', 'Toolbox'),'UserData',xTB); +if ~isempty(xTB) + set(findobj(Fmenu,'Tag', 'Toolbox'),'String',{'Toolbox:' xTB.name }); + set(findobj(Fmenu,'Tag', 'Toolbox'),'UserData',xTB); +else + set(findobj(Fmenu,'Tag', 'Toolbox'),'Visible','off') +end set(Fmenu,'Visible',Vis); varargout = {Fmenu}; @@ -601,7 +605,7 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality offset = 1; try - if ismac, offset = 1.4; end + %if ismac, offset = 1.4; end end sf = offset + 0.85*(min(spm('WinScale'))-1); @@ -798,7 +802,10 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality if isdeployed ind = findstr(SPMdir,'_mcr')-1; - SPMdir = fileparts(SPMdir(1:ind(1))); + if ~isempty(ind) + % MATLAB 2008a/2009a doesn't need this + SPMdir = fileparts(SPMdir(1:ind(1))); + end end varargout = {SPMdir}; @@ -985,6 +992,23 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality tmp(4),tmp(5),floor(tmp(6)),tmp(3),tmp(2),tmp(1)), tmp}; +%======================================================================= +case 'memory' +%======================================================================= +% m = spm('Memory') +%----------------------------------------------------------------------- +maxmemdef = 200*1024*1024; % 200 MB for all other platforms +if ispc + try + evalc('m=feature(''memstats'');'); + catch + m = maxmemdef; + end +else + m = maxmemdef; +end +varargout = {m}; + %======================================================================= case 'pointer' %-Set mouse pointer in all MATLAB windows %======================================================================= @@ -994,6 +1018,8 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality set(get(0,'Children'),'Pointer',Pointer) + + %======================================================================= case {'alert','alert"','alert*','alert!'} %-Alert dialogs %======================================================================= @@ -1134,18 +1160,27 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality str = 'Can''t obtain SPM Revision information.'; if isempty(SPM_VER) || (nargin > 0 && ReDo) - v = struct('Name','','Version','','Release','','Date',''); - try - fid = fopen(fullfile(spm('Dir'),'Contents.m'),'rt'); - if fid == -1, error(str); end - l1 = fgetl(fid); l2 = fgetl(fid); - fclose(fid); - l1 = strtrim(l1(2:end)); l2 = strtrim(l2(2:end)); - t = strread(l2,'%s','delimiter',' '); - v.Name = l1; v.Date = t{4}; - v.Version = t{2}; v.Release = t{3}(2:end-1); - catch - error(str); + if isdeployed && ispc + % fake version for isdeployed PCWIN - .m files seem to be + % compressed/pcoded/encrypted on this target + v.Name = 'Statistical Parametric Mapping'; + v.Version = '8'; + v.Release = 'SPM8'; + v.Date = date; + else + v = struct('Name','','Version','','Release','','Date',''); + try + fid = fopen(fullfile(spm('Dir'),'Contents.m'),'rt'); + if fid == -1, error(str); end + l1 = fgetl(fid); l2 = fgetl(fid); + fclose(fid); + l1 = strtrim(l1(2:end)); l2 = strtrim(l2(2:end)); + t = strread(l2,'%s','delimiter',' '); + v.Name = l1; v.Date = t{4}; + v.Version = t{2}; v.Release = t{3}(2:end-1); + catch + error(str); + end end SPM_VER = v; end diff --git a/spm_ADEM_M_set.m b/spm_ADEM_M_set.m index 147a1a0..a2d9fe4 100644 --- a/spm_ADEM_M_set.m +++ b/spm_ADEM_M_set.m @@ -49,7 +49,7 @@ % Copyright (C) 2005 Wellcome Department of Imaging Neuroscience % Karl Friston -% $Id: spm_ADEM_M_set.m 1961 2008-07-26 09:38:46Z karl $ +% $Id: spm_ADEM_M_set.m 3333 2009-08-25 16:12:44Z karl $ % order %-------------------------------------------------------------------------- @@ -127,7 +127,7 @@ % Assume fixed parameters if not specified %---------------------------------------------------------------------- - if length(M(i).pC) == 0 + if isempty(M(i).pC) p = length(spm_vec(M(i).pE)); M(i).pC = sparse(p,p); end @@ -154,12 +154,12 @@ catch v = sparse(0,0); end -if ~length(v) +if isempty(v) try v = sparse(M(g - 1).m,1); end end -if ~length(v) +if isempty(v) try v = sparse(M(g).l,1); end @@ -175,7 +175,7 @@ catch a = sparse(0,0); end - if ~length(a) + if isempty(a) try a = sparse(M(i).k,1); end @@ -193,7 +193,7 @@ catch x = sparse(M(i).n,1); end - if ~length(x) && M(i).n + if isempty(x) && M(i).n x = sparse(M(i).n,1); end @@ -205,7 +205,7 @@ try f = feval(M(i).f,x,v,a,M(i).pE); if length(spm_vec(x)) ~= length(spm_vec(f)) - errordlg('please check nargout: M(%i).f(x,v,a,P)',i); + errordlg(sprintf('please check nargout: M(%i).f(x,v,a,P)',i)); end catch errordlg(sprintf('evaluation failure: M(%i).f(x,v,a,P)',i)) @@ -236,7 +236,7 @@ % remove empty levels %-------------------------------------------------------------------------- try - g = min(find(~spm_vec(M.m))); + g = find(~spm_vec(M.m),1); M = M(1:g); catch errordlg('please specify number of variables') @@ -263,8 +263,8 @@ % make sure components are cell arrays %---------------------------------------------------------------------- - if length(M(i).Q) & ~iscell(M(i).Q), M(i).Q = {M(i).Q}; end - if length(M(i).R) & ~iscell(M(i).R), M(i).R = {M(i).R}; end + if ~isempty(M(i).Q) && ~iscell(M(i).Q), M(i).Q = {M(i).Q}; end + if ~isempty(M(i).R) && ~iscell(M(i).R), M(i).R = {M(i).R}; end % check hyperpriors %====================================================================== @@ -276,16 +276,16 @@ % check hyperpriors (expectations) %---------------------------------------------------------------------- - if ~length(M(i).hE), M(i).hE = sparse(length(M(i).Q),1); end - if ~length(M(i).gE), M(i).gE = sparse(length(M(i).R),1); end + if isempty(M(i).hE), M(i).hE = sparse(length(M(i).Q),1); end + if isempty(M(i).gE), M(i).gE = sparse(length(M(i).R),1); end % check hyperpriors (covariances) %---------------------------------------------------------------------- try, M(i).hC*M(i).hE; catch, M(i).hC = speye(length(M(i).hE))*256; end try, M(i).gC*M(i).gE; catch, M(i).gC = speye(length(M(i).gE))*256; end - if ~length(M(i).hC), M(i).hC = speye(length(M(i).hE))*256; end - if ~length(M(i).gC), M(i).gC = speye(length(M(i).gE))*256; end + if isempty(M(i).hC), M(i).hC = speye(length(M(i).hE))*256; end + if isempty(M(i).gC), M(i).gC = speye(length(M(i).gE))*256; end % check Q and R (precision components) %====================================================================== @@ -339,7 +339,7 @@ % remove fixed components if hyperparameters exist %---------------------------------------------------------------------- - if length(M(i).hE) + if ~isempty(M(i).hE) M(i).V = sparse(M(i).l,M(i).l); end @@ -355,7 +355,7 @@ % remove fixed components if hyperparameters exist %---------------------------------------------------------------------- - if length(M(i).gE) + if ~isempty(M(i).gE) M(i).W = sparse(M(i).n,M(i).n); end diff --git a/spm_BMS.m b/spm_BMS.m index 788239f..0cbc3a4 100644 --- a/spm_BMS.m +++ b/spm_BMS.m @@ -10,7 +10,7 @@ % (default: 1e6) % do_plot - 1 to plot p(r|y) % sampling - use sampling to compute exact alpha -% ecp - compute exceedance probability +% ecp - 1 to compute exceedance probability % alpha0 - [1 x Nk] vector of prior model counts % % OUTPUT: @@ -19,13 +19,13 @@ % xp - exceedance probabilities % % REFERENCE: -% Stephan KE, Penny WD, Daunizeau J, Moran RJ, Friston KJ -% Bayesian Model Selection for Group Studies. NeuroImage (under review) +% Stephan KE, Penny WD, Daunizeau J, Moran RJ, Friston KJ (2009) +% Bayesian Model Selection for Group Studies. NeuroImage 46:1004-1017 %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Klaas Enno Stephan & Will Penny -% $Id: spm_BMS.m 3155 2009-05-28 14:21:43Z will $ +% $Id: spm_BMS.m 3187 2009-06-07 18:01:43Z klaas $ if nargin < 2 || isempty(Nsamp) Nsamp = 1e6; diff --git a/spm_BMS_gibbs.m b/spm_BMS_gibbs.m new file mode 100644 index 0000000..0d5f6b6 --- /dev/null +++ b/spm_BMS_gibbs.m @@ -0,0 +1,94 @@ +function [exp_r,xp,r_samp] = spm_BMS_gibbs (lme, alpha0, Nsamp) +% Bayesian model selection for group studies using Gibbs sampling +% FORMAT [exp_r,xp,r_samp] = spm_BMS_gibbs (lme, alpha0, Nsamp) +% +% INPUT: +% lme - array of log model evidences +% rows: subjects +% columns: models (1..Nk) +% alpha0 - [1 x Nk] vector of prior model counts +% Nsamp - number of samples (default: 1e6) +% +% OUTPUT: +% exp_r - [1 x Nk] expectation of the posterior p(r|y) +% xp - exceedance probabilities +% r_samp - [Nsamp x Nk] matrix of samples from posterior +% +%__________________________________________________________________________ +% Copyright (C) 2009 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_BMS_gibbs.m 3260 2009-07-09 10:52:42Z will $ + +if nargin < 3 || isempty(Nsamp) + Nsamp = 1e3; +end + +Ni = size(lme,1); % number of subjects +Nk = size(lme,2); % number of models + +% prior observations +%-------------------------------------------------------------------------- +if nargin < 3 || isempty(alpha0) + alpha0 = ones(1,Nk); +end +alpha0 = alpha0(:)'; + +% Initialise; sample r from prior +r = zeros(1,Nk); +for k = 1:Nk + r(:,k) = spm_gamrnd(alpha0(k),1); +end +sr = sum(r,2); +for k = 1:Nk + r(:,k) = r(:,k)./sr; +end + +% Subtract subject means +lme=lme-mean(lme,2)*ones(1,Nk); + +% Gibbs sampling +r_samp = zeros(Nsamp,Nk); +for samp=1:2*Nsamp + + mod_vec=sparse(Ni,Nk); + % Sample m's given y, r + for i=1:Ni + % Pick a model for this subject + u=exp(lme(i,:)+log(r))+eps; + g=u/sum(u); + modnum=spm_multrnd(g,1); + mod_vec(i,modnum)=1; + end + + % Sample r's given y, m + beta=sum(mod_vec,1); + alpha=alpha0+beta; + for k = 1:Nk + r(:,k) = spm_gamrnd(alpha(k),1); + end + sr = sum(r,2); + for k = 1:Nk + r(:,k) = r(:,k)./sr; + end + + % Only keep last Nsamp samples + if samp > Nsamp + r_samp(samp-Nsamp,:)=r; + end + + if mod(samp,1e4)==0 + disp(sprintf('%d samples out of %d',samp,2*Nsamp)); + end + +end + +% Posterior mean +exp_r = mean(r_samp,1); + +% Exceedence probs +xp = zeros(1,Nk); +[y,j]=max(r_samp,[],2); +tmp=histc(j,1:Nk)'; +xp=tmp/Nsamp; + \ No newline at end of file diff --git a/spm_DEM_M_set.m b/spm_DEM_M_set.m index d261ee8..0c09604 100644 --- a/spm_DEM_M_set.m +++ b/spm_DEM_M_set.m @@ -47,7 +47,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_DEM_M_set.m 3058 2009-04-09 18:17:53Z karl $ +% $Id: spm_DEM_M_set.m 3333 2009-08-25 16:12:44Z karl $ % order %-------------------------------------------------------------------------- @@ -126,7 +126,7 @@ % Assume fixed parameters if not specified %---------------------------------------------------------------------- - if length(M(i).pC) == 0 + if isempty(M(i).pC) p = length(spm_vec(M(i).pE)); M(i).pC = sparse(p,p); end @@ -187,9 +187,7 @@ try f = feval(M(i).f,x,v,M(i).pE); if length(spm_vec(x)) ~= length(spm_vec(f)) - str = sprintf('please check: M(%i).f(x,v,P)',i); - msgbox(str) - error(' ') + errordlg(sprintf('please check: M(%i).f(x,v,P)',i)); end catch @@ -243,8 +241,8 @@ % make sure components are cell arrays %---------------------------------------------------------------------- - if ~isempty(M(i).Q) & ~iscell(M(i).Q), M(i).Q = {M(i).Q}; end - if ~isempty(M(i).R) & ~iscell(M(i).R), M(i).R = {M(i).R}; end + if ~isempty(M(i).Q) && ~iscell(M(i).Q), M(i).Q = {M(i).Q}; end + if ~isempty(M(i).R) && ~iscell(M(i).R), M(i).R = {M(i).R}; end % check hyperpriors %====================================================================== diff --git a/spm_DisplayTimeSeries.m b/spm_DisplayTimeSeries.m index a437feb..eb688f8 100644 --- a/spm_DisplayTimeSeries.m +++ b/spm_DisplayTimeSeries.m @@ -57,7 +57,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jean Daunizeau -% $Id: spm_DisplayTimeSeries.m 2925 2009-03-23 20:49:24Z jean $ +% $Id: spm_DisplayTimeSeries.m 3248 2009-07-03 16:17:30Z vladimir $ if ~exist('options','var') @@ -245,10 +245,18 @@ My = ud.v.M*y(:,ud.v.ind); ud.v.y2 = sum(My.^2,1); end + + mi = min(ud.v.y2); ma = max(ud.v.y2); -mi = mi - mi.*1e-3; -ma = ma + ma.*1e-3; + +if mi == 0 && ma == 0 + mi = -eps; + ma = eps; +else + mi = mi - mi.*1e-3; + ma = ma + ma.*1e-3; +end % Create axes ud.v.handles.axes = axes('parent',hp,... diff --git a/spm_Menu.fig b/spm_Menu.fig index f4fc1d6..dd5e55a 100644 Binary files a/spm_Menu.fig and b/spm_Menu.fig differ diff --git a/spm_ROI.m b/spm_ROI.m new file mode 100644 index 0000000..1b5e28e --- /dev/null +++ b/spm_ROI.m @@ -0,0 +1,144 @@ +function [xY, XYZmm, j] = spm_ROI(xY, XYZmm) +% Region of Interest specification +% FORMAT xY = spm_ROI(xY) +% xY - VOI structure +% xY.def - VOI definition [sphere, box, mask, cluster, ...] +% xY.rej - cell array of disabled VOI definition options +% xY.xyz - centre of VOI {mm} +% xY.spec - VOI definition parameters +% +% FORMAT [xY, XYZmm, j] = spm_ROI(xY, XYZmm) +% XYZmm - [3xm] locations of voxels {mm} +% +% XYZmm - [3xn] filtered locations of voxels {mm} (m>=n) within VOI xY +% j - [1xn] indices of input locations XYZmm within VOI xY +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Karl Friston, Guillaume Flandin +% $Id: spm_ROI.m 3354 2009-09-03 15:25:12Z guillaume $ + +if nargin < 2 && nargout > 1 + error('Too many output arguments.'); +end + +try + xY; +catch + xY = []; +end + +%-Specify ROI +%========================================================================== +if ~isfield(xY,'def') + def = {'sphere','box','cluster','mask'}; + if isfield(xY,'rej') + if ~isfield(xY,'M') + xY.rej = {xY.rej 'cluster'}; + end + else + if isfield(xY,'M') + xY.rej = {}; + else + xY.rej = {'cluster'}; + end + end + [q, i] = setdiff(def,xY.rej); + def = def(sort(i)); + xY.def = spm_input('VOI definition...','!+1','b',def,[],1); +end + +%-ROI parameters +%-------------------------------------------------------------------------- +switch lower(xY.def) + + case 'sphere' + %---------------------------------------------------------------------- + if ~isfield(xY,'xyz') + xY.xyz = spm_input('sphere centre [x y z] {mm}',... + '!+0','r','0 0 0',3); + end + if ~isfield(xY,'spec') + xY.spec = spm_input('sphere radius (mm)','!+0','r',0,1,[0,Inf]); + end + xY.str = sprintf('%0.1fmm sphere',xY.spec); + + case 'box' + %---------------------------------------------------------------------- + if ~isfield(xY,'xyz') + xY.xyz = spm_input('box centre [x y z] {mm}',... + '!+0','r','0 0 0',3); + end + if ~isfield(xY,'spec') + xY.spec = spm_input('box dimensions [x y z] {mm}',... + '!+0','r','0 0 0',3); + end + if length(xY.spec) < 3 + xY.spec = xY.spec(1)*[1 1 1]; + end + xY.str = sprintf('%0.1f x %0.1f x %0.1f mm box',xY.spec); + + case 'mask' + %---------------------------------------------------------------------- + if ~isfield(xY,'spec') + xY.spec = spm_vol(spm_select(1,'image','Specify Mask')); + else + if ~isstruct(xY.spec) + xY.spec = spm_vol(xY.spec); + end + end + str = strrep(spm_str_manip(xY.spec.fname,'a30'),'\','\\'); + str = strrep(str,'^','\^'); str = strrep(str,'_','\_'); + str = strrep(str,'{','\{'); str = strrep(str,'}','\}'); + xY.str = sprintf('image mask: %s',str); + + case 'cluster' + %---------------------------------------------------------------------- + if ~isfield(xY,'xyz') + xY.xyz = spm_input('seed voxel [x y z] {mm}',... + '!+0','r','0 0 0',3); + end + if ~isfield(xY,'M') + xY.M = spm_input('affine transformation matrix',... + '!+0','r','0 0 0',[4 4]); + end + xY.spec = []; + xY.str = sprintf('cluster (seed voxel: %0.1f %0.1f %0.1f)',xY.xyz); + + otherwise + %---------------------------------------------------------------------- + error('Unknown VOI type.'); + +end + +if nargin < 2, return; end + +%-'Estimate' ROI +%========================================================================== +Q = ones(1,size(XYZmm,2)); + +switch lower(xY.def) + + case 'sphere' + %---------------------------------------------------------------------- + j = find(sum((XYZmm - xY.xyz*Q).^2) <= xY.spec^2); + + case 'box' + %---------------------------------------------------------------------- + j = find(all(abs(XYZmm - xY.xyz*Q) <= xY.spec(:)*Q/2)); + + case 'mask' + %---------------------------------------------------------------------- + XYZ = xY.spec.mat \ [XYZmm; Q]; + j = find(spm_sample_vol(xY.spec, XYZ(1,:), XYZ(2,:), XYZ(3,:),0) > 0); + + case 'cluster' + %---------------------------------------------------------------------- + [x i] = spm_XYZreg('NearestXYZ',xY.xyz,XYZmm); + XYZ = round(xY.M \ [XYZmm; Q]); + A = spm_clusters(XYZ); + j = find(A == A(i)); + +end + +XYZmm = XYZmm(:,j); diff --git a/spm_add.mexmaci b/spm_add.mexmaci index 0ca8598..cd76b7b 100644 Binary files a/spm_add.mexmaci and b/spm_add.mexmaci differ diff --git a/spm_bias_mex.mexmaci b/spm_bias_mex.mexmaci index 1c81944..670d8a9 100644 Binary files a/spm_bias_mex.mexmaci and b/spm_bias_mex.mexmaci differ diff --git a/spm_bms_display.m b/spm_bms_display.m index bc5eb55..20a6e01 100644 --- a/spm_bms_display.m +++ b/spm_bms_display.m @@ -10,7 +10,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Maria Joao Rosa -% $Id: spm_bms_display.m 2765 2009-02-19 15:30:54Z guillaume $ +% $Id: spm_bms_display.m 3288 2009-07-27 09:23:54Z maria $ % Main options (action) % ========================================================================= @@ -184,6 +184,7 @@ 'thres', BMS.xSPM.thres,... 'scale', BMS.xSPM.scale,... 'vols', BMS.xSPM.vols,... + 'k', BMS.xSPM.k,... 'BMS', BMS)); set([hX,hY,hZ],'UserData',hFxyz); @@ -421,6 +422,7 @@ job.file{1} = user_data.BMS.fname; job.thres = user_data.thres; job.scale = user_data.scale; + job.k = user_data.k; spm_run_bms_vis(job); % Change threshold @@ -433,6 +435,7 @@ job.thres = []; job.file{1} = user_data.BMS.fname; job.scale = user_data.scale; + job.k = user_data.k; spm_run_bms_vis(job); % Change scale @@ -444,6 +447,7 @@ job.img{1} = user_data.vols; job.thres = user_data.thres; job.scale = []; + job.k = user_data.k; job.file{1} = user_data.BMS.fname; spm_run_bms_vis(job); diff --git a/spm_bms_display_vox.m b/spm_bms_display_vox.m index 076ebb3..b7ed533 100644 --- a/spm_bms_display_vox.m +++ b/spm_bms_display_vox.m @@ -8,7 +8,7 @@ function spm_bms_display_vox(BMS,xyz) % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Maria Joao Rosa -% $Id: spm_bms_display_vox.m 2915 2009-03-20 19:15:44Z maria $ +% $Id: spm_bms_display_vox.m 3288 2009-07-27 09:23:54Z maria $ % Find graphics window % ------------------------------------------------------------------------- @@ -75,12 +75,12 @@ function spm_bms_display_vox(BMS,xyz) if isfield(BMS.map,'rfx') - nmodels = size(BMS.map.rfx.alpha,2); + nmodels = size(BMS.map.rfx.ppm,2); models = []; exp_r_vox = zeros(nmodels,1); - epm_exists = isfield(BMS.map.rfx,'epm'); + epm_exists = isfield(BMS.map.rfx,'epm'); - if epm_exists, xp_vox = zeros(nmodels,1); end + if epm_exists, xp_vox = zeros(nmodels,1); end % Get values for i = 1:nmodels, @@ -136,7 +136,7 @@ function spm_bms_display_vox(BMS,xyz) else - msgbox('Error: no FFX analysis in current BMS.mat!') + msgbox('Error: no RFX analysis in current BMS.mat!') return end diff --git a/spm_brainwarp.mexmaci b/spm_brainwarp.mexmaci index 92397f4..e57672a 100644 Binary files a/spm_brainwarp.mexmaci and b/spm_brainwarp.mexmaci differ diff --git a/spm_bsplinc.mexmaci b/spm_bsplinc.mexmaci index 3b435c1..9bab2d2 100644 Binary files a/spm_bsplinc.mexmaci and b/spm_bsplinc.mexmaci differ diff --git a/spm_cat.m b/spm_cat.m index d387221..4cac96a 100644 --- a/spm_cat.m +++ b/spm_cat.m @@ -22,7 +22,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_cat.m 1172 2008-02-27 20:14:47Z karl $ +% $Id: spm_cat.m 3333 2009-08-25 16:12:44Z karl $ % check x is not already a matrix %-------------------------------------------------------------------------- @@ -80,7 +80,7 @@ [n m] = size(x); for i = 1:n for j = 1:m - if ~length(x{i,j}) + if isempty(x{i,j}) x{i,j} = sparse(I(i),J(j)); end end diff --git a/spm_changepath.m b/spm_changepath.m index 91fff16..23c888b 100644 --- a/spm_changepath.m +++ b/spm_changepath.m @@ -15,14 +15,14 @@ % Copyright (C) 2009 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_changepath.m 3148 2009-05-26 17:33:41Z guillaume $ +% $Id: spm_changepath.m 3277 2009-07-15 11:47:40Z guillaume $ if ~nargin Sf = spm_select(Inf,'mat','Select MAT files to fix'); end if ischar(Sf) - S = cell(1,size(Sf,1)); + S = cell(1,size(Sf,1)); for i=1:size(Sf,1) try S{i} = load(deblank(Sf(i,:))); diff --git a/spm_compare_families.m b/spm_compare_families.m index 70979ec..e02b10a 100644 --- a/spm_compare_families.m +++ b/spm_compare_families.m @@ -1,49 +1,91 @@ -function [family,model] = spm_compare_families (lme,partition,names,ecp) -% Bayesian comparison of model families for group studies -% FORMAT [family,model] = spm_compare_families (lme,partition,names,ecp) +function [family,model] = spm_compare_families (lme,family) +% Bayesian comparison of model families for group studies +% FORMAT [family,model] = spm_compare_families (lme,family) +% +% INPUT: % % lme - array of log model evidences % rows: subjects % columns: models (1..N) -% partition - [1 x N] vector such that partition(m)=k signifies that -% model m belongs to family k (out of K) eg. [1 1 2 2 2 3 3] -% names - cell array of K family names eg, {'fam1','fam2','fam3'} -% ecp - compute exceedence probs ? (1 or 0, default=0) % -% family - family posterior -% .alpha0 prior counts -% .alpha posterior counts -% .r expected values -% .xp exceedance probs +% family - data structure containing family definition and inference parameters: +% .infer='RFX' or 'FFX' (default) +% .partition [1 x N] vector such that partition(m)=k signifies that +% model m belongs to family k (out of K) eg. [1 1 2 2 2 3 3] +% .names cell array of K family names eg, {'fam1','fam2','fam3'} +% .Nsamp RFX only: Number of samples to get (default=1e4) +% .prior RFX only: 'F-unity' alpha0=1 for each family (default) +% or 'M-unity' alpha0=1 for each model (not advised) +% +% OUTPUT: +% +% family - RFX only: +% .alpha0 prior counts +% .exp_r expected value of r +% .s_samp samples from posterior +% .xp exceedance probs +% - FFX only: +% .prior family priors +% .post family posteriors % -% model - model posterior -% .alpha0 prior counts -% .alpha posterior counts -% .r expected values +% model - RFX only: +% .alpha0 prior counts +% .exp_r expected value of r +% .r_samp samples from posterior +% - FFX only: +% .subj_lme log model ev without subject effects +% .prior model priors +% .like model likelihoods +% .posts model posteriors % -% This function assumes a uniform prior over model families (using a -% prior count of unity for each family). It then -% adjusts model priors accordingly, uses spm_BMS to get model posteriors, -% and computes family posteriors via aggregation. %__________________________________________________________________________ % Copyright (C) 2009 Wellcome Trust Centre for Neuroimaging % Will Penny -% $Id: spm_compare_families.m 3158 2009-05-28 16:28:27Z will $ +% $Id: spm_compare_families.m 3347 2009-09-02 16:04:20Z will $ + +try + infer=family.infer; +catch + disp('Error in spm_compare_families: inference method not specified'); + return +end -if nargin < 4 | isempty(ecp) - ecp=0; +try + partition=family.partition; +catch + disp('Error in spm_compare_families: partition not specified'); + return end -% Number of samples for computing exceedance probs if K > 2 -Nsamp=1e6; +try + names=family.names; +catch + disp('Error in spm_compare_families: names not specified'); + return +end + +if strcmp(infer,'RFX') + try + Nsamp=family.Nsamp; + catch + Nsamp=1e4; + family.Nsamp=Nsamp; + end + + try + prior=family.prior; + catch + prior='F-unity'; + family.prior='F-unity'; + end +end % Number of models N=length(partition); % Number of families in partition K=length(unique(partition)); -family.alpha0=ones(1,K); % Size of families for i=1:K, @@ -51,33 +93,68 @@ fam_size(i)=length(ind{i}); end -% Set model priors to give uniform family prior -model.alpha0=zeros(1,N); -for i=1:K, - model.alpha0(ind{i})=1/fam_size(i); +if strcmp(infer,'FFX') + + % Family priors + for i=1:K, + family.prior(i)=1/K; + end + + % Model priors + for i=1:N, + model.prior(i)=1/fam_size(partition(i)); + end + + % Model likelihoods + lme=lme-mean(lme,2)*ones(1,N); % Subtract subject effects + model.subj_lme=lme; + model.like=sum(lme,1); + model.like=exp(model.like); + + % Model posterior + num=model.prior.*model.like; + model.post=num/sum(num); + + % Family posterior + for i=1:K, + family.post(i)=sum(model.post(ind{i})); + end + + return; +end + +% Set model priors +switch prior, + case 'F-unity', + for i=1:K, + model.alpha0(ind{i})=1/fam_size(i); + end + family.alpha0=ones(1,K); + case 'M-unity', + model.alpha0=ones(1,N); + for i=1:K, + family.alpha0(i)=fam_size(i); + end + otherwise + disp('Error in spm_compare_families:Unknown prior'); end % Get model posterior -[alpha,exp_r,xp] = spm_BMS(lme, [], 0, 0, 0, model.alpha0); -model.alpha=alpha; +[exp_r,xp,r_samp]=spm_BMS_gibbs(lme,model.alpha0,Nsamp); model.exp_r=exp_r; +model.xp=xp; +model.r_samp=r_samp; % Get stats from family posterior for i=1:K, - family.alpha(i)=sum(model.alpha(ind{i})); -end -for i=1:K, - family.exp_r(i)=family.alpha(i)/sum(family.alpha); -end -if ecp - if N == 2 - % comparison of 2 families - family.xp(1) = spm_Bcdf(0.5,family.alpha(2),family.alpha(1)); - family.xp(2) = spm_Bcdf(0.5,family.alpha(1),family.alpha(2)); - else - % comparison of >2 families: use sampling approach - family.xp = spm_dirichlet_exceedance(family.alpha,Nsamp); - end -else - family.xp=[]; + ri=r_samp(:,ind{i}); + family.s_samp(:,i)=sum(ri,2); + family.exp_r(i)=mean(family.s_samp(:,i)); end + +% Family exceedence probs +xp = zeros(1,K); +r=family.s_samp; +[y,j]=max(r,[],2); +tmp=histc(j,1:K)'; +family.xp=tmp/Nsamp; diff --git a/spm_cond_units.m b/spm_cond_units.m index b30a731..2473681 100644 --- a/spm_cond_units.m +++ b/spm_cond_units.m @@ -7,7 +7,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_cond_units.m 1131 2008-02-06 11:17:09Z spm $ +% $Id: spm_cond_units.m 3205 2009-06-16 10:15:00Z vladimir $ % default n = 3 %-------------------------------------------------------------------------- @@ -16,6 +16,6 @@ % rescale %-------------------------------------------------------------------------- d = spm_vec(y); -scalefactor = mean(abs(d - mean(d))); +scalefactor = norm(d(~isnan(d)),1); scalefactor = (10^n)^-round(log10(scalefactor)/n); y = spm_unvec(d*scalefactor,y); diff --git a/spm_conv_vol.mexmaci b/spm_conv_vol.mexmaci index 2ba9661..88838b3 100644 Binary files a/spm_conv_vol.mexmaci and b/spm_conv_vol.mexmaci differ diff --git a/spm_cva.m b/spm_cva.m index c94e7a9..34012af 100644 --- a/spm_cva.m +++ b/spm_cva.m @@ -1,8 +1,9 @@ -function [CVA] = spm_cva(xSPM,SPM,hReg) +function [CVA] = spm_cva(xSPM,SPM,hReg,CVA) % VOI extraction of adjusted data and CVA -% FORMAT [CVA] = spm_cva(xSPM,SPM,hReg); +% FORMAT [CVA] = spm_cva(xSPM,SPM,hReg,CVA) % % xSPM - structure containing specific SPM details +% xSPM.Ic - indice of contrast (in SPM.xCon) % SPM - structure containing generic analysis details % hReg - Handle of results section XYZ registry (see spm_results_ui.m) % @@ -74,85 +75,75 @@ % % A multivariate analysis of evoked responses in EEG and MEG data. Friston % KJ, Stephan KM, Heather JD, Frith CD, Ioannides AA, Liu LC, Rugg MD, -% Vieth J, Keber H, Hunter K, Frackowiak RS. NeuroImage. 1996 Jun;3(3 Pt -% 1):167-74. +% Vieth J, Keber H, Hunter K, Frackowiak RS. NeuroImage. 1996 Jun; +% 3(3):167-174. %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_cva.m 2958 2009-03-26 11:19:20Z guillaume $ +% $Id: spm_cva.m 3354 2009-09-03 15:25:12Z guillaume $ +% get figure handles +%-------------------------------------------------------------------------- +Finter = spm_figure('FindWin','Interactive'); +spm_results_ui('Clear'); +spm_input('!DeleteInputObj'); +header = get(Finter,'Name'); +set(Finter,'Name','Canonical Variates analysis') + % review old analysis or proceed with a new one %-------------------------------------------------------------------------- -switch questdlg('new canonical variates analysis?'); - case {'No'} +if nargin < 4 + action = spm_input('Canonical Variates Analysis','!+1','b', ... + {'New Analysis','Results'},{'A','R'},1); + if strcmpi(action,'r') CVA = spm_cva_results; return - case {'Cancel'} - return + end end - -% get figure handles -%-------------------------------------------------------------------------- -Fcva = spm_figure('GetWin','MVB'); -Finter = spm_figure('GetWin','Interactive'); -header = get(Finter,'Name'); -set(Finter,'Name','Canonical Variates analysis') - -% contrast and VOI specification +%-Contrast and VOI specification %========================================================================== -%-Get contrast +% get contrast %-------------------------------------------------------------------------- con = SPM.xCon(xSPM.Ic).name; c = SPM.xCon(xSPM.Ic).c; c = full(c); -%-Get VOI name +% get VOI name %-------------------------------------------------------------------------- -name = ['CVA_' spm_input('name','-8','s',con)]; +try + name = CVA.name; +catch + name = spm_input('name','-8','s',con); +end +name = strrep(name,' ','_'); +name = ['CVA_' name]; -%-Get current location {mm} +% get current location {mm} %-------------------------------------------------------------------------- -xyzmm = spm_results_ui('GetCoords'); - -%-Specify search volume +try + xyzmm = CVA.xY.xyz; +catch + xyzmm = spm_results_ui('GetCoords'); +end + +% specify search volume %-------------------------------------------------------------------------- -SPACE = spm_input('Search volume...','!+1','b',... - {'Sphere','Box','Image'},['S','B','I']); -Q = ones(1,size(SPM.xVol.XYZ, 2)); -XYZmm = SPM.xVol.M*[SPM.xVol.XYZ; Q]; -XYZmm = XYZmm(1:3,:); - -switch SPACE - - case 'S' %-Sphere - %---------------------------------------------------------------------- - D = spm_input('radius of VOI {mm}','!+1'); - str = sprintf('%0.1fmm sphere',D); - j = find(sum((XYZmm - xyzmm*Q).^2) <= D^2); - - case 'B' %-Box - %---------------------------------------------------------------------- - D = spm_input('box dimensions [k l m] {mm}','!+1'); - if length(D) < 3 - D = D(1)*[1 1 1]; - end - str = sprintf('%0.1f x %0.1f x %0.1f mm box',D(1),D(2),D(3)); - j = find(all(abs(XYZmm - xyzmm*Q) <= D(:)*Q/2)); - - case 'I' %-Mask Image - %---------------------------------------------------------------------- - Msk = spm_select(1,'image','Image defining search volume'); - D = spm_vol(Msk); - str = sprintf('image mask: %s',spm_str_manip(Msk,'a30')); - XYZ = D.mat \ [XYZmm; Q]; - j = find(spm_sample_vol(D, XYZ(1,:), XYZ(2,:), XYZ(3,:),0) > 0); - +try + xY = CVA.xY; + CVA = rmfield(CVA,'xY'); +catch + xY = []; end - +xY.xyz = xyzmm; + +Q = ones(1,size(SPM.xVol.XYZ, 2)); +XYZmm = SPM.xVol.M(1:3,:)*[SPM.xVol.XYZ; Q]; + +[xY, XYZ, j] = spm_ROI(xY, XYZmm); % voxels defined %-------------------------------------------------------------------------- @@ -163,11 +154,11 @@ % get explanatory variables (data) %-------------------------------------------------------------------------- -XYZ = XYZmm(:,j); Y = spm_get_data(SPM.xY.VY,SPM.xVol.XYZ(:,j)); if isempty(Y) - warndlg({'No voxels in this VOI';'Please use a larger volume'}) + spm('alert*',{'No voxels in this VOI';'Please use a larger volume'},... + 'Canonical Variates analysis'); return end @@ -240,9 +231,11 @@ %-------------------------------------------------------------------------- p = max(p,exp(-16)); -% save results +%-Save results %========================================================================== - +M = SPM.xVol.M(1:3,1:3); %-voxels to mm matrix +VOX = sqrt(diag(M'*M))'; %-voxel dimensions + % assemble results %-------------------------------------------------------------------------- CVA.contrast = con; % contrast name @@ -254,7 +247,7 @@ CVA.XYZ = XYZ; % locations of voxels (mm) CVA.xyz = xyzmm; % seed voxel location (mm) -CVA.VOX = xSPM.VOX; % dimension of voxels (mm) +CVA.VOX = VOX; % dimension of voxels (mm) CVA.V = V; % canonical vectors (data) CVA.v = v; % canonical variates (data) @@ -269,14 +262,18 @@ % save %-------------------------------------------------------------------------- -save(fullfile(SPM.swd,name),'CVA') +if spm_matlab_version_chk('7') >= 0 + save(fullfile(SPM.swd,name),'-V6','CVA') +else + save(fullfile(SPM.swd,name),'CVA') +end assignin('base','CVA',CVA) % display results %-------------------------------------------------------------------------- spm_cva_results(CVA); -%-Reset title +% reset title %-------------------------------------------------------------------------- set(Finter,'Name',header) spm('Pointer','Arrow') diff --git a/spm_data_id.m b/spm_data_id.m new file mode 100644 index 0000000..672c53f --- /dev/null +++ b/spm_data_id.m @@ -0,0 +1,46 @@ +function ID = spm_data_id(varargin) +% generates a specific real number in a deterministic way +% from any data structure +% FORMAT ID = spm_data_id(X); +% X - numeric, character, cell or stucture array[s] +% ID - specific ID +%__________________________________________________________________________ +% Copyright (C) 2009 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak (based on Karl's spm_vec) +% $Id: spm_data_id.m 3177 2009-06-03 08:47:41Z vladimir $ + +X = varargin; + +if length(X) == 1 + X = X{1}; +end + +if ischar(X) % For now strings are not taken into account + ID = 0; +elseif isnumeric(X) + Y = double(X(:)); + ID = sum(abs(Y(~isnan(Y) & ~isinf(Y)))); +elseif isstruct(X) || isobject(X) + X = struct(X); + f = fieldnames(X); + X = X(:); + ID = 0; + for i = 1:length(f) + ID = ID + spm_data_id({X.(f{i})}); + end +elseif iscell(X) + X = X(:); + ID = 0; + for i = 1:length(X) + ID = ID + spm_data_id(X{i}); + end +end + +if isempty(ID) + ID = 0; +end + +if ID > 0 + ID = 10^-(floor(log10(ID))-2)*ID; +end \ No newline at end of file diff --git a/spm_dcm_bma.m b/spm_dcm_bma.m new file mode 100644 index 0000000..eac476b --- /dev/null +++ b/spm_dcm_bma.m @@ -0,0 +1,113 @@ +function [theta] = spm_dcm_bma (post,subj,Nsamp,oddsr) +% Model-independent samples from DCM posterior +% FORMAT [theta] = spm_dcm_bma (post,subj,Nsamp,oddsr) +% +% post [Nd x M] vector of posterior model probabilities +% If Nd>1 then inference is based on a RFX posterior p(r|Y) +% subj subj(n).model(m).fname: DCM filename +% Nsamp Number of samples (default = 1e3) +% oddsr posterior odds ratio for defining Occam's window (default=0, ie +% all models used in average) +% +% theta [Np x Nsamp] posterior density matrix. Parameter vector is of +% dimension Np and there are Nsamp samples +% +% This routine implements Bayesian averaging over models and subjects +%__________________________________________________________________________ +% Copyright (C) 2009 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_dcm_bma.m 3348 2009-09-03 10:32:01Z guillaume $ + +if nargin < 3 | isempty(Nsamp) + Nsamp=1e3; +end +if nargin < 4 | isempty(oddsr) + oddsr=0; +end + +Nsub=length(subj); + +[Nd M]=size(post); +if Nd > 1 + rfx=1; + if Nsamp>Nd, + disp('Error in spm_dcm_bma: not enough samples'); + end +else + rfx=0; +end + +if rfx + mean_post=mean(post); + mp=max(mean_post); + post_ind=find(mean_post>mp*oddsr); + Nocc=length(post_ind); + disp(' '); + disp(sprintf('%d models in Occams window',Nocc)); + for occ=1:Nocc, + m=post_ind(occ); + disp(sprintf('Model %d, =%1.2f',m,mean_post(m))); + end + + % Renormalise post prob to Occam group + post=post(:,post_ind); + sp=sum(post,2); + post=post./(sp*ones(1,Nocc)); +else + % Find models in Occam's window + mp=max(post); + post_ind=find(post>mp*oddsr); + Nocc=length(post_ind); + disp(' '); + disp(sprintf('%d models in Occams window',Nocc)); + for occ=1:Nocc, + m=post_ind(occ); + disp(sprintf('Model %d, p(m|Y)=%1.2f',m,post(m))); + end + + % Renormalise post prob to Occam group + post=post(post_ind); + post=post/sum(post); +end + +% Load DCM posteriors for models in Occam's window +for n=1:Nsub, + for kk=1:Nocc, + sel=post_ind(kk); + load_str=['load ',subj(n).model(sel).fname]; + eval(load_str); + subj(n).model(kk).Ep=DCM.Ep; + subj(n).model(kk).Cp=full(DCM.Cp); + end +end + +% Pre-allocate sample arrays +Np=length(subj(n).model(kk).Ep); +theta=zeros(Np,Nsamp); +theta_all=zeros(Np,Nsub); + +for i=1:Nsamp, + % Pick a model + if rfx + m=spm_multrnd(post(i,:),1); + else + m=spm_multrnd(post,1); + end + + % Pick parameters from model for each subject + for n=1:Nsub, + mu=subj(n).model(m).Ep; + sig=subj(n).model(m).Cp; + tmp=spm_samp_gauss (mu,sig,1); + theta_all(:,n)=tmp(:); + end + + % Average over subjects + if Nsub>1 + theta(:,i)=mean(theta_all,2); + else + theta(:,i)=theta_all; + end +end + diff --git a/spm_dcm_compare.m b/spm_dcm_compare.m index e1875cb..79c92f5 100644 --- a/spm_dcm_compare.m +++ b/spm_dcm_compare.m @@ -7,7 +7,7 @@ function spm_dcm_compare(P) % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Klaas Enno Stephan -% $Id: spm_dcm_compare.m 2254 2008-09-30 15:22:45Z rosalyn $ +% $Id: spm_dcm_compare.m 3363 2009-09-04 15:11:19Z christophe $ % Get DCM filenames @@ -95,7 +95,7 @@ function spm_dcm_compare(P) set(gca,'YTick',1:n) set(gca,'YTickLabel',name) title({'conditional model probability';'under uniform model priors'}) -xlabel('psoterior probability') +xlabel('posterior probability') axis square grid on diff --git a/spm_dcm_create.m b/spm_dcm_create.m index 6af57f4..df90d45 100644 --- a/spm_dcm_create.m +++ b/spm_dcm_create.m @@ -22,7 +22,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Will Penny & Klaas Enno Stephan -% $Id: spm_dcm_create.m 2746 2009-02-14 16:43:58Z klaas $ +% $Id: spm_dcm_create.m 3363 2009-09-04 15:11:19Z christophe $ Finter = spm_figure('GetWin','Interactive'); @@ -259,7 +259,7 @@ % Import existing model - prompt user to choose it %================================================= P = spm_select(1,'^DCM.*\.mat$','Select source DCM_???.mat'); - load(P{:}) + load(P) otherwise @@ -289,7 +289,11 @@ case 'GUI' Y.dt = SPM.xY.RT; otherwise - Y.dt = DCM.Y.dt; + try + Y.dt = DCM.Y.dt; + catch + Y.dt = DCM.delays(1); + end end Y.X0 = X0; for i = 1:DCM.n, diff --git a/spm_dcm_estimate.m b/spm_dcm_estimate.m index 992d086..7390730 100644 --- a/spm_dcm_estimate.m +++ b/spm_dcm_estimate.m @@ -7,7 +7,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Will Penny -% $Id: spm_dcm_estimate.m 2748 2009-02-15 12:20:21Z klaas $ +% $Id: spm_dcm_estimate.m 3177 2009-06-03 08:47:41Z vladimir $ % load DCM structure @@ -145,6 +145,19 @@ [Ep,Cp,Ce,H0,H1,H2,M0,M1,L1,L2,F] = spm_nlsi(M,U,Y); +% Data ID +%========================================================================== +if isfield(M,'FS') + try + ID = spm_data_id(feval(M.FS,Y.y,M)); + catch + ID = spm_data_id(feval(M.FS,Y.y)); + end +else + ID = spm_data_id(Y.y); +end + + % predicted responses and residuals %-------------------------------------------------------------------------- y = feval(M.IS,Ep,M,U); @@ -225,6 +238,7 @@ %-------------------------------------------------------------------------- evidence = spm_dcm_evidence(DCM); DCM.F = F; +DCM.ID = ID; % data ID DCM.AIC = evidence.aic_overall; DCM.BIC = evidence.bic_overall; diff --git a/spm_dcm_generate.m b/spm_dcm_generate.m index 3e4fcbc..5589c97 100644 --- a/spm_dcm_generate.m +++ b/spm_dcm_generate.m @@ -15,7 +15,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Will Penny & Klaas Enno Stephan -% $Id: spm_dcm_generate.m 2942 2009-03-24 18:18:07Z klaas $ +% $Id: spm_dcm_generate.m 3363 2009-09-04 15:11:19Z christophe $ % Check parameters and load specified DCM %-------------------------------------------------------------------------- @@ -125,6 +125,7 @@ Y.y(:,i) = Y.y(:,i)-Xp*Y.y(:,i); end DCM.Y = Y; +DCM.y = Y.y; % Save synthetic DCM %-------------------------------------------------------------------------- diff --git a/spm_defaults.m b/spm_defaults.m index f94a995..fd6bddf 100644 --- a/spm_defaults.m +++ b/spm_defaults.m @@ -14,7 +14,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % John Ashburner, Andrew Holmes -% $Id: spm_defaults.m 2959 2009-03-26 11:21:02Z guillaume $ +% $Id: spm_defaults.m 3299 2009-07-30 18:21:53Z guillaume $ global defaults @@ -34,7 +34,7 @@ % Stats defaults %======================================================================= -defaults.stats.maxmem = 2^20; +defaults.stats.maxmem = 2^26; defaults.stats.maxres = 64; defaults.stats.fmri.ufp = 0.001; defaults.stats.pet.ufp = 0.05; diff --git a/spm_defs.m b/spm_defs.m index 66f60fd..63a7c73 100644 --- a/spm_defs.m +++ b/spm_defs.m @@ -1,18 +1,22 @@ -function spm_defs(job) +function out = spm_defs(job) % Various deformation field utilities. -% FORMAT spm_defs(job) +% FORMAT out = spm_defs(job) % job - a job created via spm_config_defs.m and spm_jobman.m +% out - a struct with fields +% .def - file name of created deformation field +% .warped - file names of warped images % % See spm_config_defs.m for more information. %_______________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_defs.m 1790 2008-06-05 11:27:02Z spm $ +% $Id: spm_defs.m 3392 2009-09-11 14:13:38Z guillaume $ [Def,mat] = get_comp(job.comp); -save_def(Def,mat,strvcat(job.ofname)); -apply_def(Def,mat,strvcat(job.fnames),job.interp); +[dpath ipath] = get_paths(job); +out.def = save_def(Def,mat,strvcat(job.ofname),dpath); +out.warped = apply_def(Def,mat,strvcat(job.fnames),ipath,job.interp); %_______________________________________________________________________ %_______________________________________________________________________ @@ -230,18 +234,62 @@ function spm_defs(job) %_______________________________________________________________________ %_______________________________________________________________________ -function save_def(Def,mat,ofname) +function [dpath,ipath] = get_paths(job) +switch char(fieldnames(job.savedir)) + case 'savepwd' + dpath = pwd; + ipath = pwd; + case 'savesrc' + dpath = get_dpath(job); + ipath = ''; + case 'savedef' + dpath = get_dpath(job); + ipath = dpath; + case 'saveusr' + dpath = job.savedir.saveusr{1}; + ipath = dpath; +end +%_______________________________________________________________________ + +%_______________________________________________________________________ +function dpath = get_dpath(job) +% Determine what is required, and pass the relevant bit of the +% job out to the appropriate function. + +fn = fieldnames(job); +fn = fn{1}; +switch fn +case {'comp'} + dpath = get_dpath(job.(fn){1}); +case {'def'} + dpath = fileparts(job.(fn){1}); +case {'dartel'} + dpath = fileparts(job.(fn).flowfield{1}); +case {'sn2def'} + dpath = fileparts(job.(fn).matname{1}); +case {'inv'} + dpath = fileparts(job.(fn).space{1}); +case {'id'} + dpath = fileparts(job.(fn).space{1}); +otherwise + error('Unrecognised job type'); +end; + +%_______________________________________________________________________ + +%_______________________________________________________________________ +function fname = save_def(Def,mat,ofname,odir) % Save a deformation field as an image -if isempty(ofname), return; end; +if isempty(ofname), fname = {}; return; end; -fname = fullfile(pwd,['y_' ofname '.nii']); +fname = {fullfile(odir,['y_' ofname '.nii'])}; dim = [size(Def{1},1) size(Def{1},2) size(Def{1},3) 1 3]; -dtype = 'FLOAT32-BE'; +dtype = 'FLOAT32-LE'; off = 0; scale = 1; inter = 0; -dat = file_array(fname,dim,dtype,off,scale,inter); +dat = file_array(fname{1},dim,dtype,off,scale,inter); N = nifti; N.dat = dat; @@ -260,17 +308,25 @@ function save_def(Def,mat,ofname) %_______________________________________________________________________ %_______________________________________________________________________ -function apply_def(Def,mat,fnames,intrp) +function ofnames = apply_def(Def,mat,fnames,odir,intrp) % Warp an image or series of images according to a deformation field intrp = [intrp*[1 1 1], 0 0 0]; +ofnames = cell(size(fnames,1),1); for i=1:size(fnames,1), V = spm_vol(fnames(i,:)); M = inv(V.mat); [pth,nam,ext] = spm_fileparts(fnames(i,:)); - ofname = fullfile(pwd,['w',nam,ext]); - Vo = struct('fname',ofname,... + if isempty(odir) + % use same path as source image + opth = pth; + else + % use prespecified path + opth = odir; + end + ofnames{i} = fullfile(opth,['w',nam,ext]); + Vo = struct('fname',ofnames{i},... 'dim',[size(Def{1},1) size(Def{1},2) size(Def{1},3)],... 'dt',V.dt,... 'pinfo',V.pinfo,... diff --git a/spm_design_contrasts.m b/spm_design_contrasts.m index a05be15..21e943d 100644 --- a/spm_design_contrasts.m +++ b/spm_design_contrasts.m @@ -18,7 +18,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Will Penny -% $Id: spm_design_contrasts.m 1143 2008-02-07 19:33:33Z spm $ +% $Id: spm_design_contrasts.m 3249 2009-07-03 16:43:55Z guillaume $ if isempty(SPM.factor) % Can't create contrasts if factorial design has not been specified @@ -33,6 +33,7 @@ end icon=spm_make_contrasts(kf); +if isempty(icon), con = []; return; end % Get number of basis functions per condition if isfield(SPM,'xBF') @@ -108,4 +109,4 @@ con(6).name=['Interaction: ',SPM.factor(1).name,' x ',SPM.factor(3).name]; con(7).name=['Interaction: ',SPM.factor(2).name,' x ',SPM.factor(3).name]; con(8).name=['Interaction: ',SPM.factor(1).name,' x ',SPM.factor(2).name,' x ',SPM.factor(3).name]; -end \ No newline at end of file +end diff --git a/spm_dicom_convert.m b/spm_dicom_convert.m index a3c65c1..3c701ba 100644 --- a/spm_dicom_convert.m +++ b/spm_dicom_convert.m @@ -31,7 +31,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % John Ashburner & Jesper Andersson -% $Id: spm_dicom_convert.m 3096 2009-05-04 11:30:25Z volkmar $ +% $Id: spm_dicom_convert.m 3307 2009-08-06 11:16:09Z volkmar $ if nargin<2, opts = 'all'; end @@ -630,35 +630,49 @@ % y increases posterior to anterior % z increases inferior to superior -analyze_to_dicom = [diag([1 -1 1]) [0 (dim(2)-1) 0]'; 0 0 0 1]*[eye(4,3) [-1 -1 -1 1]']; +analyze_to_dicom = [diag([1 -1 1]) [0 (dim(2)+1) 0]'; 0 0 0 1]; % Flip voxels in y +patient_to_tal = diag([-1 -1 1 1]); % Flip mm coords in x and y directions +shift_vx = [eye(4,3) [.5; .5; 0; 1]]; + orient = reshape(get_numaris4_numval(privdat,... 'ImageOrientationPatient'),[3 2]); -orient(:,3) = null(orient'); -if det(orient)<0, orient(:,3) = -orient(:,3); end; +try + ps(1) = get_numaris4_numval(privdat,... + 'VoiReadoutFoV'); + ps(2) = get_numaris4_numval(privdat,... + 'VoiPhaseFoV'); +catch + ps = get_numaris4_numval(privdat,'PixelSpacing'); +end +pos = get_numaris4_numval(privdat,'ImagePositionPatient'); + +R = [orient*diag(ps); 0 0]; +x1 = [1;1;1;1]; +y1 = [pos; 1]; + if length(hdr)>1, - z = zeros(length(hdr),1); - for i=1:length(hdr), - z(i) = get_numaris4_numval(privdat,... - 'ImagePositionPatient')*orient(:,3); - end; - z = mean(diff(z)); + error('spm_dicom_convert:spectroscopy',... + 'Don''t know how to handle multislice spectroscopy data.'); else + orient(:,3) = null(orient'); + if det(orient)<0, orient(:,3) = -orient(:,3); end; try z = get_numaris4_numval(privdat,... - 'SliceThickness'); + 'VoiThickness'); catch - z = 1; + try + z = get_numaris4_numval(privdat,... + 'SliceThickness'); + catch + z = 1; + end end; -end; - -ps = get_numaris4_numval(privdat,'PixelSpacing'); -vox = [ps(:)' z]; -pos = get_numaris4_numval(privdat,'ImagePositionPatient')'; -%dicom_to_patient = [orient*diag(vox) pos-1.5*orient*([0 vox(2) 0]') ; 0 0 0 1]; -dicom_to_patient = [orient*diag(vox) pos ; 0 0 0 1]; -patient_to_tal = diag([-1 -1 1 1]); + x2 = [0;0;1;0]; + y2 = [orient*[0;0;z];0]; +end +dicom_to_patient = [y1 y2 R]/[x1 x2 eye(4,2)]; warning('Don''t know exactly what positions in spectroscopy files should be - just guessing!') -mat = patient_to_tal*dicom_to_patient*analyze_to_dicom; +mat = patient_to_tal*dicom_to_patient*shift_vx*analyze_to_dicom; % Possibly useful information %------------------------------------------------------------------- @@ -696,7 +710,7 @@ volume = zeros(dim); for i=1:length(hdr), - plane = read_spect_data(hdr{i}); + plane = read_spect_data(hdr{i},privdat); if pinfo(1)~=1, plane = plane*pinfo(1); end; if pinfo(2)~=0, plane = plane+pinfo(2); end; plane = fliplr(plane); @@ -874,11 +888,11 @@ %_______________________________________________________________________ %_______________________________________________________________________ -function img = read_spect_data(hdr) +function img = read_spect_data(hdr,privdat) % Image dimensions %------------------------------------------------------------------- -nc = get_numaris4_numval(hdr.Private_0029_1210,'Columns'); -nr = get_numaris4_numval(hdr.Private_0029_1210,'Rows'); +nc = get_numaris4_numval(privdat,'Columns'); +nr = get_numaris4_numval(privdat,'Rows'); img = ones(nr,nc); %_______________________________________________________________________ diff --git a/spm_dicom_headers.m b/spm_dicom_headers.m index 074694a..d03b55a 100644 --- a/spm_dicom_headers.m +++ b/spm_dicom_headers.m @@ -15,7 +15,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_dicom_headers.m 3096 2009-05-04 11:30:25Z volkmar $ +% $Id: spm_dicom_headers.m 3224 2009-06-25 17:28:21Z volkmar $ if nargin<2, essentials = false; end @@ -67,8 +67,14 @@ fseek(fp,0,'bof'); end; end; -ret = read_dicom(fp, 'il',dict); -ret.Filename = fopen(fp); +try + ret = read_dicom(fp, 'il',dict); + ret.Filename = fopen(fp); +catch + fprintf('Trouble reading DICOM file %s, skipping.\n', fopen(fp)); + l = lasterror; + disp(l.message); +end fclose(fp); return; %_______________________________________________________________________ @@ -441,7 +447,7 @@ case char([0 0]) %_______________________________________________________________________ function t = decode_csa1(fp,lim) n = fread(fp,1,'uint32'); -if isempty(n) || n>128 || n <= 0, +if isempty(n) || n>1024 || n <= 0, fseek(fp,lim-4,'cof'); t = struct('name','JUNK: Don''t know how to read this damned file format'); return; @@ -486,9 +492,9 @@ case char([0 0]) unused2 = fread(fp,4,'uint8'); % Unused n = fread(fp,1,'uint32'); opos = ftell(fp); -if n>128 || n < 0, +if isempty(n) || n>1024 || n < 0, fseek(fp,lim-4,'cof'); - t = struct('junk','Don''t know how to read this damned file format'); + t = struct('name','Don''t know how to read this damned file format'); return; end; unused = fread(fp,1,'uint32')'; % Unused "M" or 77 for some reason diff --git a/spm_eeg_artefact.m b/spm_eeg_artefact.m index ce43095..845d90c 100644 --- a/spm_eeg_artefact.m +++ b/spm_eeg_artefact.m @@ -2,47 +2,38 @@ % Simple artefact detection, optionally with robust averaging % FORMAT D = spm_eeg_artefact(S) % -% S - input structure (optional) -% (optional) fields of S: +% S - input structure +% +% fields of S: % S.D - MEEG object or filename of M/EEG mat-file with -% continuous data -% S.artefact with entries (all optional): -% External_list - (0: no/1: yes) flag, whether to flag trials as -% artefactual or clean -% out_list - vector of artefactual trial indices (if -% External_list yes) -% in_list - vector of clean trial indices (if -% External_list yes) -% Weighted - (0: no/1: yes) flag, whether to use robust -% averaging -% Weightingfunction - parameter used for robust averaging -% Smoothing - parameter used for robust averaging -% Check_Threshold - (0: no/1: yes) flag, whether to threshold trials -% channels_threshold - vector of indices which channels to threshold (if -% Check_Threshold yes) -% threshold - vector of thresholds, with which the absolute -% data values are compared against (if Check_Threshold yes). -% Vector length must be either number of selected -% channels, or a single number applied to all channels. +% S.badchanthresh - fraction of trials with artefacts above which a +% channel is declared as bad (default: 0.2) % +% S.methods - a structure array with configuration parameters +% for artefact detection plugins. % Output: % D - MEEG object (also written on disk) %__________________________________________________________________________ +% This is a modular function for which plugins can be developed to detect +% artefacts with any algorithm. There are 3 basic plugins presently +% implemented and they can be used as templates for new plugins. +% The name of a plugin function should start with 'spm_eeg_artefact_' +% +% peak2peak (spm_eeg_artefact_peak2peak) - thresholds peak-to-peak +% amplitude +% +% jump (spm_eeg_artefact_jump) - thresholds the difference +% between adjacent samples. % -% spm_eeg_artefact is an artefact detection routine. The user can specify -% clean or artefactual trials as vector indices. These trials are not -% checked by SPM. -% The function uses simple thresholding to detect artefactual trials and -% bad channels. Optionally, 'robust averaging' can be used to estimate how -% much weight each trial should have in the average to compute the evoked -% response. +% flat (spm_eeg_artefact_flat) - detects flat segments in the +% data %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging -% Stefan Kiebel, Rik Henson & James Kilner -% $Id: spm_eeg_artefact.m 3071 2009-04-21 11:22:19Z vladimir $ +% Vladimir Litvak +% $Id: spm_eeg_artefact.m 3384 2009-09-10 18:36:56Z vladimir $ -SVNrev = '$Rev: 3071 $'; +SVNrev = '$Rev: 3384 $'; %-Startup %-------------------------------------------------------------------------- @@ -61,298 +52,103 @@ D = spm_eeg_load(D); -%-User specified list of artefacted and clean trials -%-------------------------------------------------------------------------- -try - artefact.External_list = S.artefact.External_list; -catch - artefact.External_list = ... - spm_input('Read own artefact list?','+1','yes|no',[1 0]); - S.artefact.External_list = artefact.External_list; +if isequal(D.type, 'continuous') + error('Artefact detection can only be applied to epoched data'); end -MustDoWork = 1; % indicate whether user already specified full artefact list - -if artefact.External_list - try - artefact.out_list = S.artefact.out_list; - catch - artefact.out_list = ... - spm_input('List artefactual trials (0 for none)', '+1', 'w', '', Inf); - S.artefact.out_list = artefact.out_list; - end - - if artefact.out_list == 0 - artefact.out_list = []; - end - - try - artefact.in_list = S.artefact.in_list; - catch - artefact.in_list = ... - spm_input('List clean trials (0 for none)', '+1', 'w', '', Inf); - S.artefact.in_list = artefact.in_list; - end - - if artefact.in_list == 0 - artefact.in_list = []; - end - - if any([artefact.out_list; artefact.in_list] < 1 | ... - [artefact.out_list; artefact.in_list] > D.ntrials) - error('Trial numbers cannot be smaller than 1 or greater than %d.', D.ntrials); - end - - % check the lists - tmp = intersect(artefact.out_list, artefact.in_list); - if ~isempty(tmp) - error('These trials were listed as both artefactual and clean: %s', mat2str(tmp)); - end - - % Check whether user has specified all trials - Iuser = [artefact.out_list; artefact.in_list]; - if length(Iuser) == D.ntrials - MustDoWork = 0; - end -end - -%-Robust averaging? +%-Backward compatibility %-------------------------------------------------------------------------- -try - artefact.Weighted = S.artefact.Weighted; -catch - artefact.Weighted = spm_input('Robust average?','+1','yes|no',[1 0]); - S.artefact.Weighted = artefact.Weighted; +persistent runonce +if isfield(S, 'artefact') + if isempty(runonce) + warning(['The old version of the artefact function will be deprecated in the future']); + runonce = 1; + end + D = spm_eeg_artefact5(S); + return; end -if artefact.Weighted == 1 - try - artefact.Weightingfunction = S.artefact.Weightingfunction; - catch - artefact.Weightingfunction = ... - spm_input('Offset weighting function by', '+1', 'r', '3', 1); - S.artefact.Weightingfunction = artefact.Weightingfunction; - end - try - artefact.Smoothing = S.artefact.Smoothing; - catch - artefact.Smoothing = ... - spm_input('FWHM for residual smoothing (ms)', '+1', 'r', '20', 1); - S.artefact.Smoothing = artefact.Smoothing; - end - artefact.Smoothing = round(artefact.Smoothing / 1000 * D.fsample); +if ~isfield(S, 'badchanthresh') + S.badchanthresh = 0.2; end -%-Thresholding details +%-Create a copy of the dataset %-------------------------------------------------------------------------- -if MustDoWork - try - artefact.Check_Threshold = S.artefact.Check_Threshold; - catch - artefact.Check_Threshold = ... - spm_input('Threshold channels?', '+1', 'yes|no', [1 0]); - S.artefact.Check_Threshold = artefact.Check_Threshold; - end +S1 =[]; +S1.D = D; +S1.newname = ['a' D.fname]; +S1.updatehistory = 0; +D = spm_eeg_copy(S1); - if artefact.Check_Threshold - try - artefact.channels_threshold = S.artefact.channels_threshold; - catch - artefact.channels_threshold = ... - spm_input('Select channels', '+1', 'i', num2str(sort([D.meegchannels D.eogchannels]))); - S.artefact.channels_threshold = artefact.channels_threshold; - end - try - artefact.threshold = S.artefact.threshold; - if length(artefact.threshold) == 1 - artefact.threshold = repmat(artefact.threshold, 1, ... - length(artefact.channels_threshold)); - end - catch - str = 'threshold[s]'; - Ypos = -1; - while 1 - if Ypos == -1 - [artefact.threshold, Ypos] = spm_input(str, '+1', 'r', [], [1 Inf]); - else - artefact.threshold = spm_input(str, Ypos, 'r', [], [1 Inf]); - end - if length(artefact.threshold) == 1 - artefact.threshold = repmat(artefact.threshold, 1, ... - length(artefact.channels_threshold)); - end - if length(artefact.threshold) == length(artefact.channels_threshold), break, end - str = sprintf('Enter a scalar or [%d] vector', length(artefact.channels_threshold)); - end - S.artefact.threshold = artefact.threshold; - end - else - artefact.channels_threshold = 1: nchannels(D); - artefact.threshold = Inf(1, length(artefact.channels_threshold)); - end -end % MustDoWork - -%-Artefact detection +%-Run the artefact detection routines %-------------------------------------------------------------------------- -if MustDoWork - - % matrix used for detecting bad channels - Mbad = zeros(D.nchannels, D.ntrials); - % flag channels that were already marked as bad - Mbad(D.badchannels, :) = 1; - - % cell vectors of channel-wise indices for thresholded trials - thresholded = cell(1, length(artefact.channels_threshold)); - - Tchannel = artefact.threshold; - - %-First flag bad channels based on thresholding - %---------------------------------------------------------------------- - spm_progress_bar('Init', D.ntrials, '1st pass - Trials thresholded'); - if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials, 100)); - else Ibar = [1:D.ntrials]; end - - for i = 1:D.ntrials - - d = squeeze(D(artefact.channels_threshold, :, i)); - - % indices of channels that are above threshold and not marked as bad - Id = find(max(abs(d')) > Tchannel & ~Mbad(artefact.channels_threshold, i)'); - Mbad(intersect(artefact.channels_threshold(Id), D.meegchannels), i) = 1; +bad = zeros(D.nchannels, D.ntrials); - if ismember(i, Ibar), spm_progress_bar('Set', i); end - - end - - spm_progress_bar('Clear'); - - %-Flag channels as bad if 20% of trials above threshold - %---------------------------------------------------------------------- - ind = find(mean(Mbad, 2) > 0.2); - - Mbad = zeros(D.nchannels, D.ntrials); - Mbad(ind, :) = 1; - - %-Report on command line and set badchannels - %---------------------------------------------------------------------- - if isempty(ind) - fprintf('There isn''t a bad channel.\n'); %-# - D = badchannels(D, [D.meegchannels], zeros(length(D.meegchannels), 1)); +for i = 1:numel(S.methods) + if iscell(S.methods(i).channels) + chanind = indchannel(D, S.methods(i).channels); else - lbl = D.chanlabels(ind); - if ~iscell(lbl), lbl = {lbl}; end - disp(['Bad channels: ', sprintf('%s ', lbl{:})]); %-# - D = badchannels(D, ind, ones(length(ind), 1)); - end - - %-Weighted averaging - %---------------------------------------------------------------------- - if artefact.Weighted == 1 - - cl = condlist(D); - - allWf = zeros(D.nchannels, D.ntrials * D.nsamples); - tloops = setdiff(artefact.channels_threshold, ind); - - for i = 1:D.nconditions - - nbars = D.nconditions * length(tloops); - spm_progress_bar('Init', nbars, '2nd pass - robust averaging'); - if nbars > 100, Ibar = floor(linspace(1, nbars,100)); - else Ibar = [1:nbars]; end - - trials = pickconditions(D, deblank(cl{i}), 0); - - for j = tloops %loop across electrodes - if ismember((i-1)*length(tloops)+j, Ibar) - spm_progress_bar('Set', (i-1)*length(tloops)+j); - end - tempdata = max(abs(squeeze(D(j, :, trials)))); - itrials = trials; - - itrials(tempdata>Tchannel(j)) = ''; - tdata = squeeze(D(j, :, itrials)); - [B, bc] = spm_eeg_robust_averaget(tdata, ... - artefact.Weightingfunction, artefact.Smoothing); - bc = bc(:); - ins = 0; - - for n = itrials' - ins = ins + 1; - allWf(j, (n-1)*D.nsamples+1 : n*D.nsamples) = ... - bc((ins-1)*D.nsamples+1:ins*D.nsamples)'; - end - end - end - - spm_progress_bar('Clear'); - - artefact.weights = allWf; - D.artefact = artefact; - - else % if artefact.Weighted == 1 - - %-2nd round of thresholding, but excluding bad channels - %------------------------------------------------------------------ - index = []; - - spm_progress_bar('Init', D.ntrials, '2nd pass - Trials thresholded'); - if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials,100)); - else Ibar = [1:D.ntrials]; end - - for i = 1:D.ntrials - - d = squeeze(D(artefact.channels_threshold, :, i)); - - % indices of channels that are above threshold - Id = find(max(abs(d')) > Tchannel & ~Mbad(artefact.channels_threshold, i)'); - Mbad(artefact.channels_threshold(Id), i) = 1; - - if any(Id), index = [index i]; end % reject - - % stow away event indices for which good channels were - % above threshold - for j = Id - thresholded{j} = [thresholded{j} i]; - end - - if ismember(i, Ibar), spm_progress_bar('Set', i); end - + switch upper(S.methods(i).channels) + case 'ALL' + chanind = 1:D.nchannels; + case 'EOG' + chanind = eogchannels(D); + case 'ECG' + chanind = ecgchannels(D); + case 'EMG' + chanind = emgchannels(D); + otherwise + chanind = meegchannels(D, S.methods(i).channels); end + end - spm_progress_bar('Clear'); - - fprintf('%d rejected trials: %s\n', length(index), mat2str(index)); %-# + chanind = setdiff(chanind, D.badchannels); + + if ~isempty(chanind) + S1 = S.methods(i).settings; + S1.D = D; + S1.chanind = chanind; - if ~isempty(index) - D = reject(D, index, 1); - end + bad = bad | feval(['spm_eeg_artefact_' S.methods(i).fun], S1); end +end - D.thresholded = thresholded; +%-Classify MEEG channels as bad if the fraction of bad trials exceeds threshold +%------------------------------------------------------------------------------- +badchanind = intersect(find(mean(bad, 2)>S.badchanthresh), meegchannels(D)); +badchanind = union(badchanind, D.badchannels); +goodchanind = setdiff(1:D.nchannels, badchanind); -end % MustDoWork +%-Classify trials as bad if they have artefacts in good M/EEG channels +%-or in non-M/EEG channels +%-------------------------------------------------------------------------- +badtrialind = find(any(bad(goodchanind, :))); -%-User-specified lists override any artefact classification +%-Update and save new dataset %-------------------------------------------------------------------------- -if artefact.External_list - if ~isempty(artefact.out_list) - D = reject(D, artefact.out_list, 1); - end - if ~isempty(artefact.in_list) - D = reject(D, artefact.in_list, 0); - end +if ~isempty(badtrialind) + D = reject(D, badtrialind, 1); +end + +if ~isempty(badchanind) + D = badchannels(D, badchanind, ones(size(badchanind))); end -%-Save new dataset -%-------------------------------------------------------------------------- D = D.history('spm_eeg_artefact', S); -copyfile(fullfile(D.path, D.fnamedat), fullfile(D.path, ['a' D.fnamedat])); -D = fnamedat(D, ['a' D.fnamedat]); -D = fname(D, ['a' D.fname]); save(D); +%-Report on command line +%-------------------------------------------------------------------------- +if isempty(badchanind) + fprintf('There isn''t a bad channel.\n'); +else + lbl = D.chanlabels(badchanind); + if ~iscell(lbl), lbl = {lbl}; end + fprintf('%d bad channels: %s\n', numel(lbl), sprintf('%s ', lbl{:})); +end +fprintf('%d rejected trials: %s\n', length(badtrialind), num2str(badtrialind)); + %-Cleanup %-------------------------------------------------------------------------- spm('FigName','M/EEG artefact detection: done'); spm('Pointer','Arrow'); diff --git a/spm_eeg_artefact5.m b/spm_eeg_artefact5.m new file mode 100644 index 0000000..da6bf30 --- /dev/null +++ b/spm_eeg_artefact5.m @@ -0,0 +1,358 @@ +function D = spm_eeg_artefact5(S) +% Simple artefact detection, optionally with robust averaging +% FORMAT D = spm_eeg_artefact5(S) +% +% S - input structure (optional) +% (optional) fields of S: +% S.D - MEEG object or filename of M/EEG mat-file with +% continuous data +% S.artefact with entries (all optional): +% External_list - (0: no/1: yes) flag, whether to flag trials as +% artefactual or clean +% out_list - vector of artefactual trial indices (if +% External_list yes) +% in_list - vector of clean trial indices (if +% External_list yes) +% Weighted - (0: no/1: yes) flag, whether to use robust +% averaging +% Weightingfunction - parameter used for robust averaging +% Smoothing - parameter used for robust averaging +% Check_Threshold - (0: no/1: yes) flag, whether to threshold trials +% channels_threshold - vector of indices which channels to threshold (if +% Check_Threshold yes) +% threshold - vector of thresholds, with which the absolute +% data values are compared against (if Check_Threshold yes). +% Vector length must be either number of selected +% channels, or a single number applied to all channels. +% +% Output: +% D - MEEG object (also written on disk) +%__________________________________________________________________________ +% +% spm_eeg_artefact is an artefact detection routine. The user can specify +% clean or artefactual trials as vector indices. These trials are not +% checked by SPM. +% The function uses simple thresholding to detect artefactual trials and +% bad channels. Optionally, 'robust averaging' can be used to estimate how +% much weight each trial should have in the average to compute the evoked +% response. +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Stefan Kiebel, Rik Henson & James Kilner +% $Id: spm_eeg_artefact5.m 3258 2009-07-08 17:46:54Z vladimir $ + +SVNrev = '$Rev: 3258 $'; + +%-Startup +%-------------------------------------------------------------------------- +spm('FnBanner', mfilename, SVNrev); +spm('FigName','M/EEG artefact detection'); spm('Pointer','Watch'); + +%-Get MEEG object +%-------------------------------------------------------------------------- +try + D = S.D; +catch + [D, sts] = spm_select(1, 'mat', 'Select M/EEG mat file'); + if ~sts, D = []; return; end + S.D = D; +end + +D = spm_eeg_load(D); + +%-User specified list of artefacted and clean trials +%-------------------------------------------------------------------------- +try + artefact.External_list = S.artefact.External_list; +catch + artefact.External_list = ... + spm_input('Read own artefact list?','+1','yes|no',[1 0]); + S.artefact.External_list = artefact.External_list; +end + +MustDoWork = 1; % indicate whether user already specified full artefact list + +if artefact.External_list + try + artefact.out_list = S.artefact.out_list; + catch + artefact.out_list = ... + spm_input('List artefactual trials (0 for none)', '+1', 'w', '', Inf); + S.artefact.out_list = artefact.out_list; + end + + if artefact.out_list == 0 + artefact.out_list = []; + end + + try + artefact.in_list = S.artefact.in_list; + catch + artefact.in_list = ... + spm_input('List clean trials (0 for none)', '+1', 'w', '', Inf); + S.artefact.in_list = artefact.in_list; + end + + if artefact.in_list == 0 + artefact.in_list = []; + end + + if any([artefact.out_list; artefact.in_list] < 1 | ... + [artefact.out_list; artefact.in_list] > D.ntrials) + error('Trial numbers cannot be smaller than 1 or greater than %d.', D.ntrials); + end + + % check the lists + tmp = intersect(artefact.out_list, artefact.in_list); + if ~isempty(tmp) + error('These trials were listed as both artefactual and clean: %s', mat2str(tmp)); + end + + % Check whether user has specified all trials + Iuser = [artefact.out_list; artefact.in_list]; + if length(Iuser) == D.ntrials + MustDoWork = 0; + end +end + +%-Robust averaging? +%-------------------------------------------------------------------------- +try + artefact.Weighted = S.artefact.Weighted; +catch + artefact.Weighted = spm_input('Robust average?','+1','yes|no',[1 0]); + S.artefact.Weighted = artefact.Weighted; +end + +if artefact.Weighted == 1 + try + artefact.Weightingfunction = S.artefact.Weightingfunction; + catch + artefact.Weightingfunction = ... + spm_input('Offset weighting function by', '+1', 'r', '3', 1); + S.artefact.Weightingfunction = artefact.Weightingfunction; + end + try + artefact.Smoothing = S.artefact.Smoothing; + catch + artefact.Smoothing = ... + spm_input('FWHM for residual smoothing (ms)', '+1', 'r', '20', 1); + S.artefact.Smoothing = artefact.Smoothing; + end + artefact.Smoothing = round(artefact.Smoothing / 1000 * D.fsample); +end + +%-Thresholding details +%-------------------------------------------------------------------------- +if MustDoWork + try + artefact.Check_Threshold = S.artefact.Check_Threshold; + catch + artefact.Check_Threshold = ... + spm_input('Threshold channels?', '+1', 'yes|no', [1 0]); + S.artefact.Check_Threshold = artefact.Check_Threshold; + end + + if artefact.Check_Threshold + try + artefact.channels_threshold = S.artefact.channels_threshold; + catch + artefact.channels_threshold = ... + spm_input('Select channels', '+1', 'i', num2str(sort([D.meegchannels D.eogchannels]))); + S.artefact.channels_threshold = artefact.channels_threshold; + end + + try + artefact.threshold = S.artefact.threshold; + if length(artefact.threshold) == 1 + artefact.threshold = repmat(artefact.threshold, 1, ... + length(artefact.channels_threshold)); + end + catch + str = 'threshold[s]'; + Ypos = -1; + while 1 + if Ypos == -1 + [artefact.threshold, Ypos] = spm_input(str, '+1', 'r', [], [1 Inf]); + else + artefact.threshold = spm_input(str, Ypos, 'r', [], [1 Inf]); + end + if length(artefact.threshold) == 1 + artefact.threshold = repmat(artefact.threshold, 1, ... + length(artefact.channels_threshold)); + end + if length(artefact.threshold) == length(artefact.channels_threshold), break, end + str = sprintf('Enter a scalar or [%d] vector', length(artefact.channels_threshold)); + end + S.artefact.threshold = artefact.threshold; + end + else + artefact.channels_threshold = 1: nchannels(D); + artefact.threshold = Inf(1, length(artefact.channels_threshold)); + end +end % MustDoWork + +%-Artefact detection +%-------------------------------------------------------------------------- +if MustDoWork + + % matrix used for detecting bad channels + Mbad = zeros(D.nchannels, D.ntrials); + % flag channels that were already marked as bad + Mbad(D.badchannels, :) = 1; + + % cell vectors of channel-wise indices for thresholded trials + thresholded = cell(1, length(artefact.channels_threshold)); + + Tchannel = artefact.threshold; + + %-First flag bad channels based on thresholding + %---------------------------------------------------------------------- + spm_progress_bar('Init', D.ntrials, '1st pass - Trials thresholded'); + if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials, 100)); + else Ibar = [1:D.ntrials]; end + + for i = 1:D.ntrials + + d = squeeze(D(artefact.channels_threshold, :, i)); + + % indices of channels that are above threshold and not marked as bad + Id = find(max(abs(d')) > Tchannel & ~Mbad(artefact.channels_threshold, i)'); + Mbad(intersect(artefact.channels_threshold(Id), D.meegchannels), i) = 1; + + if ismember(i, Ibar), spm_progress_bar('Set', i); end + + end + + spm_progress_bar('Clear'); + + %-Flag channels as bad if 20% of trials above threshold + %---------------------------------------------------------------------- + ind = find(mean(Mbad, 2) > 0.2); + + Mbad = zeros(D.nchannels, D.ntrials); + Mbad(ind, :) = 1; + + %-Report on command line and set badchannels + %---------------------------------------------------------------------- + if isempty(ind) + fprintf('There isn''t a bad channel.\n'); %-# + D = badchannels(D, [D.meegchannels], zeros(length(D.meegchannels), 1)); + else + lbl = D.chanlabels(ind); + if ~iscell(lbl), lbl = {lbl}; end + disp(['Bad channels: ', sprintf('%s ', lbl{:})]); %-# + D = badchannels(D, ind, ones(length(ind), 1)); + end + + %-Weighted averaging + %---------------------------------------------------------------------- + if artefact.Weighted == 1 + + cl = condlist(D); + + allWf = zeros(D.nchannels, D.ntrials * D.nsamples); + tloops = setdiff(artefact.channels_threshold, ind); + + for i = 1:D.nconditions + + nbars = D.nconditions * length(tloops); + spm_progress_bar('Init', nbars, '2nd pass - robust averaging'); + if nbars > 100, Ibar = floor(linspace(1, nbars,100)); + else Ibar = [1:nbars]; end + + trials = pickconditions(D, deblank(cl{i}), 0); + + for j = tloops %loop across electrodes + if ismember((i-1)*length(tloops)+j, Ibar) + spm_progress_bar('Set', (i-1)*length(tloops)+j); + end + tempdata = max(abs(squeeze(D(j, :, trials)))); + itrials = trials; + + itrials(tempdata>Tchannel(j)) = ''; + tdata = squeeze(D(j, :, itrials)); + [B, bc] = spm_eeg_robust_averaget(tdata, ... + artefact.Weightingfunction, artefact.Smoothing); + bc = bc(:); + ins = 0; + + for n = itrials' + ins = ins + 1; + allWf(j, (n-1)*D.nsamples+1 : n*D.nsamples) = ... + bc((ins-1)*D.nsamples+1:ins*D.nsamples)'; + end + end + end + + spm_progress_bar('Clear'); + + artefact.weights = allWf; + D.artefact = artefact; + + else % if artefact.Weighted == 1 + + %-2nd round of thresholding, but excluding bad channels + %------------------------------------------------------------------ + index = []; + + spm_progress_bar('Init', D.ntrials, '2nd pass - Trials thresholded'); + if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials,100)); + else Ibar = [1:D.ntrials]; end + + for i = 1:D.ntrials + + d = squeeze(D(artefact.channels_threshold, :, i)); + + % indices of channels that are above threshold + Id = find(max(abs(d')) > Tchannel & ~Mbad(artefact.channels_threshold, i)'); + Mbad(artefact.channels_threshold(Id), i) = 1; + + if any(Id), index = [index i]; end % reject + + % stow away event indices for which good channels were + % above threshold + for j = Id + thresholded{j} = [thresholded{j} i]; + end + + if ismember(i, Ibar), spm_progress_bar('Set', i); end + + end + + spm_progress_bar('Clear'); + + fprintf('%d rejected trials: %s\n', length(index), mat2str(index)); %-# + + if ~isempty(index) + D = reject(D, index, 1); + end + end + + D.thresholded = thresholded; + +end % MustDoWork + +%-User-specified lists override any artefact classification +%-------------------------------------------------------------------------- +if artefact.External_list + if ~isempty(artefact.out_list) + D = reject(D, artefact.out_list, 1); + end + if ~isempty(artefact.in_list) + D = reject(D, artefact.in_list, 0); + end +end + +%-Save new dataset +%-------------------------------------------------------------------------- +D = D.history('spm_eeg_artefact', S); +copyfile(fullfile(D.path, D.fnamedat), fullfile(D.path, ['a' D.fnamedat])); +D = fnamedat(D, ['a' D.fnamedat]); +D = fname(D, ['a' D.fname]); +save(D); + +%-Cleanup +%-------------------------------------------------------------------------- +spm('FigName','M/EEG artefact detection: done'); spm('Pointer','Arrow'); diff --git a/spm_eeg_artefact_flat.m b/spm_eeg_artefact_flat.m new file mode 100644 index 0000000..d8d46e0 --- /dev/null +++ b/spm_eeg_artefact_flat.m @@ -0,0 +1,87 @@ +function res = spm_eeg_artefact_flat(S) +% Plugin for spm_eeg_artefact doing flat channel detection. +% S - input structure +% fields of S: +% S.D - M/EEG object +% S.chanind - vector of indices of channels that this plugin will look at. +% +% Additional parameters can be defined specific for each plugin +% Output: +% res - +% If no input is provided the plugin returns a cfg branch for itself +% +% If input is provided the plugin returns a matrix of size D.nchannels x D.ntrials +% with zeros for clean channel/trials and ones for artefacts. +%______________________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: spm_eeg_artefact_flat.m 3258 2009-07-08 17:46:54Z vladimir $ + + +%-This part if for creating a config branch that plugs into spm_cfg_eeg_artefact +% Any parameters can be specified and they are then passed to the plugin +% when it's called. +%-------------------------------------------------------------------------- +if nargin == 0 + threshold = cfg_entry; + threshold.tag = 'threshold'; + threshold.name = 'Threshold'; + threshold.strtype = 'r'; + threshold.val = {0}; + threshold.num = [1 1]; + threshold.help = {'Threshold for difference between adjacent samples'}; + + + seqlength = cfg_entry; + seqlength.tag = 'seqlength'; + seqlength.name = 'Flat segment length'; + seqlength.strtype = 'r'; + seqlength.num = [1 1]; + seqlength.val = {4}; + seqlength.help = {'Minimal number of adjacent samples with the same value to reject.'}; + + flat = cfg_branch; + flat.tag = 'flat'; + flat.name = 'Flat segments'; + flat.val = {threshold, seqlength}; + + res = flat; + + return +end + +SVNrev = '$Rev: 3258 $'; + +%-Startup +%-------------------------------------------------------------------------- +spm('sFnBanner', mfilename, SVNrev); +spm('FigName','M/EEG flat data detection'); + +D = spm_eeg_load(S.D); + +chanind = S.chanind; +threshold = S.threshold; +seqlength = S.seqlength; +res = zeros(D.nchannels, D.ntrials); + +%-Artefact detection +%-------------------------------------------------------------------------- + +spm_progress_bar('Init', D.ntrials, 'Trials checked'); +if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials,100)); +else Ibar = [1:D.ntrials]; end + +for i = 1:D.ntrials + for j = 1:length(chanind) + tmp = find(abs(diff(squeeze(D(chanind(j), :, i)), [], 2))>threshold); + if max(diff([0 tmp D.nsamples]))>=seqlength + res(chanind(j), i) = 1; + end + end + if ismember(i, Ibar), spm_progress_bar('Set', i); end +end + +spm_progress_bar('Clear'); + +spm('FigName','M/EEG flat data detection: done'); \ No newline at end of file diff --git a/spm_eeg_artefact_jump.m b/spm_eeg_artefact_jump.m new file mode 100644 index 0000000..ba529fe --- /dev/null +++ b/spm_eeg_artefact_jump.m @@ -0,0 +1,71 @@ +function res = spm_eeg_artefact_jump(S) +% Plugin for spm_eeg_artefact doing jump detection. +% S - input structure +% fields of S: +% S.D - M/EEG object +% S.chanind - vector of indices of channels that this plugin will look at. +% +% Additional parameters can be defined specific for each plugin +% Output: +% res - +% If no input is provided the plugin returns a cfg branch for itself +% +% If input is provided the plugin returns a matrix of size D.nchannels x D.ntrials +% with zeros for clean channel/trials and ones for artefacts. +%______________________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: spm_eeg_artefact_jump.m 3258 2009-07-08 17:46:54Z vladimir $ + + +%-This part if for creating a config branch that plugs into spm_cfg_eeg_artefact +% Any parameters can be specified and they are then passed to the plugin +% when it's called. +%-------------------------------------------------------------------------- +if nargin == 0 + threshold = cfg_entry; + threshold.tag = 'threshold'; + threshold.name = 'Threshold'; + threshold.strtype = 'r'; + threshold.num = [1 1]; + threshold.help = {'Threshold value to apply to all channels'}; + + jump = cfg_branch; + jump.tag = 'jump'; + jump.name = 'Difference between adjacent samples'; + jump.val = {threshold}; + + res = jump; + + return +end + +SVNrev = '$Rev: 3258 $'; + +%-Startup +%-------------------------------------------------------------------------- +spm('sFnBanner', mfilename, SVNrev); +spm('FigName','M/EEG jump detection'); + +D = spm_eeg_load(S.D); + +chanind = S.chanind; +threshold = S.threshold; +res = zeros(D.nchannels, D.ntrials); + +%-Artefact detection +%-------------------------------------------------------------------------- + +spm_progress_bar('Init', D.ntrials, 'Trials checked'); +if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials,100)); +else Ibar = [1:D.ntrials]; end + +for i = 1:D.ntrials + res(chanind, i) = max(abs(diff(squeeze(D(chanind, :, i)), [], 2)), [], 2)>threshold; + if ismember(i, Ibar), spm_progress_bar('Set', i); end +end + +spm_progress_bar('Clear'); + +spm('FigName','M/EEG jump detection: done'); \ No newline at end of file diff --git a/spm_eeg_artefact_peak2peak.m b/spm_eeg_artefact_peak2peak.m new file mode 100644 index 0000000..51f0743 --- /dev/null +++ b/spm_eeg_artefact_peak2peak.m @@ -0,0 +1,71 @@ +function res = spm_eeg_artefact_peak2peak(S) +% Plugin for spm_eeg_artefact doing artefact detection based on peak-to-peak amplitude. +% S - input structure +% fields of S: +% S.D - M/EEG object +% S.chanind - vector of indices of channels that this plugin will look at. +% +% Additional parameters can be defined specific for each plugin +% Output: +% res - +% If no input is provided the plugin returns a cfg branch for itself +% +% If input is provided the plugin returns a matrix of size D.nchannels x D.ntrials +% with zeros for clean channel/trials and ones for artefacts. +%______________________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: spm_eeg_artefact_peak2peak.m 3259 2009-07-09 10:19:57Z vladimir $ + + +%-This part if for creating a config branch that plugs into spm_cfg_eeg_artefact +% Any parameters can be specified and they are then passed to the plugin +% when it's called. +%-------------------------------------------------------------------------- +if nargin == 0 + threshold = cfg_entry; + threshold.tag = 'threshold'; + threshold.name = 'Threshold'; + threshold.strtype = 'r'; + threshold.num = [1 1]; + threshold.help = {'Threshold value to apply to all channels'}; + + peak2peak = cfg_branch; + peak2peak.tag = 'peak2peak'; + peak2peak.name = 'Peak to peak amplitude'; + peak2peak.val = {threshold}; + + res = peak2peak; + + return +end + +SVNrev = '$Rev: 3259 $'; + +%-Startup +%-------------------------------------------------------------------------- +spm('sFnBanner', mfilename, SVNrev); +spm('FigName','M/EEG peak to peak artefact detection'); + +D = spm_eeg_load(S.D); + +chanind = S.chanind; +threshold = S.threshold; +res = zeros(D.nchannels, D.ntrials); + +%-Artefact detection +%-------------------------------------------------------------------------- + +spm_progress_bar('Init', D.ntrials, 'Trials checked'); +if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials,100)); +else Ibar = [1:D.ntrials]; end + +for i = 1:D.ntrials + res(chanind, i) = (squeeze(max(D(chanind, :, i), [], 2) - min(D(chanind, :, i), [], 2)))>threshold; + if ismember(i, Ibar), spm_progress_bar('Set', i); end +end + +spm_progress_bar('Clear'); + +spm('FigName','M/EEG peak to peak artefact detection: done'); \ No newline at end of file diff --git a/spm_eeg_artefact_threshchan.m b/spm_eeg_artefact_threshchan.m new file mode 100644 index 0000000..c5acfcf --- /dev/null +++ b/spm_eeg_artefact_threshchan.m @@ -0,0 +1,71 @@ +function res = spm_eeg_artefact_threshchan(S) +% Plugin for spm_eeg_artefact doing artefact detection by chanel thresholding. +% S - input structure +% fields of S: +% S.D - M/EEG object +% S.chanind - vector of indices of channels that this plugin will look at. +% +% Additional parameters can be defined specific for each plugin +% Output: +% res - +% If no input is provided the plugin returns a cfg branch for itself +% +% If input is provided the plugin returns a matrix of size D.nchannels x D.ntrials +% with zeros for clean channel/trials and ones for artefacts. +%______________________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: spm_eeg_artefact_threshchan.m 3262 2009-07-09 12:10:53Z vladimir $ + + +%-This part if for creating a config branch that plugs into spm_cfg_eeg_artefact +% Any parameters can be specified and they are then passed to the plugin +% when it's called. +%-------------------------------------------------------------------------- +if nargin == 0 + threshold = cfg_entry; + threshold.tag = 'threshold'; + threshold.name = 'Threshold'; + threshold.strtype = 'r'; + threshold.num = [1 1]; + threshold.help = {'Threshold value to apply to all channels'}; + + threshchan = cfg_branch; + threshchan.tag = 'threshchan'; + threshchan.name = 'Threshold channels'; + threshchan.val = {threshold}; + + res = threshchan; + + return +end + +SVNrev = '$Rev: 3262 $'; + +%-Startup +%-------------------------------------------------------------------------- +spm('sFnBanner', mfilename, SVNrev); +spm('FigName','M/EEG threshold channels'); + +D = spm_eeg_load(S.D); + +chanind = S.chanind; +threshold = S.threshold; +res = zeros(D.nchannels, D.ntrials); + +%-Artefact detection +%-------------------------------------------------------------------------- + +spm_progress_bar('Init', D.ntrials, 'Trials checked'); +if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials,100)); +else Ibar = [1:D.ntrials]; end + +for i = 1:D.ntrials + res(chanind, i) = squeeze(max(abs(D(chanind, :, i)), [], 2))>threshold; + if ismember(i, Ibar), spm_progress_bar('Set', i); end +end + +spm_progress_bar('Clear'); + +spm('FigName', 'M/EEG threshold channels: done'); \ No newline at end of file diff --git a/spm_eeg_average.m b/spm_eeg_average.m index ca3fc1c..d72b1c6 100644 --- a/spm_eeg_average.m +++ b/spm_eeg_average.m @@ -5,6 +5,11 @@ % S - optional input struct % (optional) fields of S: % D - MEEG object or filename of M/EEG mat-file with epoched data +% S.robust - (optional) - use robust averaging +% .savew - save the weights in an additional dataset +% .bycondition - compute the weights by condition (1, +% default) or from all trials (0) +% .ks - offset of the weighting function (default: 3) % review - review data after averaging [default: true] % % Output: @@ -16,9 +21,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_average.m 2882 2009-03-16 11:12:57Z vladimir $ +% $Id: spm_eeg_average.m 3321 2009-08-12 13:10:15Z vladimir $ -SVNrev = '$Rev: 2882 $'; +SVNrev = '$Rev: 3321 $'; %-Startup %-------------------------------------------------------------------------- @@ -41,157 +46,232 @@ %-------------------------------------------------------------------------- if ntrials(D)==0 || all(reject(D)) warning('No good trials for averaging were found. Nothing to do.'); + return; +end + +%-Redirect to Time-Frequency averaging if necessary +%-------------------------------------------------------------------------- +if strncmpi(D.transformtype,'TF',2) % TF and TFphase + D = spm_eeg_average_TF(S); + return +end + +%-Backward compatibility +%-------------------------------------------------------------------------- +persistent runonce +if isfield(D, 'artefact') + if isempty(runonce) + warning(['Robust averaging in spm_eeg_artefact has been deprecated. ' ... + 'Use the ''robust'' option in spm_eeg_average instead.']); + runonce = 1; + end + D = spm_eeg_average5(S); + return; +end + +%-Configure robust averaging +%-------------------------------------------------------------------------- +if ~isfield(S, 'robust') + robust = spm_input('Use robust averaging?','+1','yes|no',[1 0]); + if robust + S.robust = []; + else + S.robust = false; + end else + robust = isa(S.robust, 'struct'); +end - %-Redirect to Time-Frequency averaging if necessary - %-------------------------------------------------------------------------- - if strncmpi(D.transformtype,'TF',2) % TF and TFphase - D = spm_eeg_average_TF(S); - return +if robust + if ~isfield(S.robust, 'savew') + S.robust.savew = spm_input('Save weights?','+1','yes|no',[1 0]); end + savew = S.robust.savew; - %-Generate new MEEG object with new files - %-------------------------------------------------------------------------- - Dnew = clone(D, ['m' fnamedat(D)], [D.nchannels D.nsamples D.nconditions]); - Dnew = type(Dnew, 'evoked'); + if ~isfield(S.robust, 'bycondition') + S.robust.bycondition = spm_input('Compute weights by condition?','+1','yes|no',[1 0]); + end - %-Do the averaging - %-------------------------------------------------------------------------- - cl = D.condlist; - try artefact = D.artefact; catch artefact = []; end - if isfield(artefact, 'weights') + bycondition = S.robust.bycondition; - %-Weighted averaging - %====================================================================== - weights = artefact.weights; - try thresholded = D.thresholded; catch thresholded = []; end + if ~isfield(S.robust, 'ks') + S.robust.ks = spm_input('Offset of the weighting function', '+1', 'r', '3', 1); + end - d = zeros(D.nchannels, D.nsamples); + ks = S.robust.ks; - for i = 1:D.nconditions +end + +%-Generate new MEEG object with new files +%-------------------------------------------------------------------------- +Dnew = clone(D, ['m' fnamedat(D)], [D.nchannels D.nsamples D.nconditions]); +Dnew = type(Dnew, 'evoked'); - for j = 1:D.nchannels - w = pickconditions(D, cl{i}, true)'; - ni(i) = length(w); +if robust && savew + Dw = clone(D, ['W' fnamedat(D)], size(D)); +end - ti = 0; - ts = 0; - while ts==0 - ti = ti+1; - ts = (j == thresholded{ti}); - end - if isempty(ts) - ind = pickconditions(D, cl{i}, 1); - data = squeeze(D(j, :, ind))'; - tempwf = []; - for nl = ind' - tempwf = [tempwf, weights(j, (nl-1)*D.nsamples+1:nl*D.nsamples)]; - end - data = data'; - tempwf = reshape(tempwf, D.nsamples, length(ind)); +%-Do the averaging +%-------------------------------------------------------------------------- +cl = D.condlist; + +ni = zeros(1, D.nconditions); +for i = 1:D.nconditions + w = pickconditions(D, deblank(cl{i}), 1)'; + ni(i) = length(w); + if ni(i) == 0 + warning('%s: No trials for trial type %d', D.fname, cl{i}); + end +end - for t = 1:size(data,1) - B(t) = sum(tempwf(t,:).*data(t,:))/sum(tempwf(t,:)); - end +goodtrials = pickconditions(D, cl, 1); +chanind = meegchannels(D); - if isfield(artefact, 'Smoothing') - sm = gausswin(artefact.Smoothing); - sm = sm/sum(sm); - mB = mean(B); - B = conv(sm,B-mean(B)); - B = B(floor(artefact.Smoothing/2):end-ceil(artefact.Smoothing/2)); - B = B + mB; - end - d(j, :) = B; - else - d(j,:) = zeros(1,D.nsamples); +if prod(size(D))*8 < spm('memory') + + %-Average everything at once if there is enough memory + %====================================================================== + spm_progress_bar('Init', D.nconditions, 'Averages done'); + if D.nconditions > 100, Ibar = floor(linspace(1, D.nconditions, 100)); + else Ibar = [1:D.nconditions]; end + + if robust && ~bycondition + W1 = ones(D.nchannels, D.nsamples, length(goodtrials)); + [Y, W2] = spm_robust_average(D(chanind, :, goodtrials), 3, ks); + W1(chanind, :, :) = W2; + if savew + Dw(:, :, goodtrials) = W1; + end + W = zeros(size(D)); + W(:, :, goodtrials) = W1; + end + + for i = 1:D.nconditions + + w = pickconditions(D, deblank(cl{i}), true)'; + + if isempty(w) + continue; + end + + if ~robust + Dnew(:, :, i) = mean(D(:, :, w), 3); + else + if bycondition + W = ones(D.nchannels, D.nsamples, length(w)); + [Y, W1] = spm_robust_average(D(chanind, :, w), 3, ks); + W(chanind, :, :) = W1; + Dnew(chanind, :, i) = Y; + if length(chanind) 100, Ibar = floor(linspace(1, D.nchannels, 100)); + else Ibar = [1:D.nchannels]; end + for j = 1:D.nchannels + if robust && ~bycondition + if ismember(j, chanind) + [Y, W1] = spm_robust_average(D(j, :, goodtrials), 3, ks); + W = zeros([1 D.nsamples D.ntrials]); + W(1, :, goodtrials) = W1; + else + W1 = ones(1, D.nsamples, length(goodtrials)); + end - else - %-Averaging - %====================================================================== - spm_progress_bar('Init', D.nconditions, 'Averages done'); - if D.nconditions > 100, Ibar = floor(linspace(1, D.nconditions, 100)); - else Ibar = [1:D.nconditions]; end + if savew + Dw(j, :, goodtrials) = W1; + end + end - ni = zeros(1,D.nconditions); for i = 1:D.nconditions - d = zeros(D.nchannels, D.nsamples); + w = pickconditions(D, deblank(cl{i}), true)'; - w = pickconditions(D, deblank(cl{i}), true)'; - c = zeros(1, D.ntrials); - c(w) = 1; + if isempty(w) + continue; + end - ni(i) = length(w); - if ni(i) == 0 - warning('%s: No trials for trial type %s', D.fname, cl{i}); + if ~robust || ~ismember(j, chanind) + Dnew(j, :, i) = mean(D(j, :, w), 3); else - c = c ./ sum(c); % vector of trial-wise weights - for j = 1:D.nchannels - d(j, :) = c * squeeze(D(j, :, :))'; + if bycondition + [Y, W] = spm_robust_average(D(j, :, w), 3, ks); + Dnew(j, :, i) = Y; + if savew + Dw(j, :, w) = W; + end + else + X = D(j, :, w); + X(isnan(X)) = 0; + Dnew(j, :, i) = ... + sum(W(1, :, w).*X, 3)./sum(W(1, :, w), 3); end end - Dnew(1:Dnew.nchannels, 1:Dnew.nsamples, i) = d; - - if ismember(i, Ibar), spm_progress_bar('Set', i); end - - end % for i = 1:D.nconditions + end % for i = 1:D.nconditions + if ismember(j, Ibar), spm_progress_bar('Set', j); end end +end - spm_progress_bar('Clear'); +spm_progress_bar('Clear'); - %-Reorganise trial structure - %-------------------------------------------------------------------------- - sD = struct(Dnew); +%-Update some header information +%-------------------------------------------------------------------------- +Dnew = conditions(Dnew, [], cl); +Dnew = repl(Dnew, [], ni); - [sD.trials.label] = deal([]); - for i = 1:D.nconditions - sD.trials(i).label = cl{i}; +%-Display averaging statistics +%-------------------------------------------------------------------------- +disp(sprintf('%s: Number of replications per contrast:', Dnew.fname)); %-# +s = []; +for i = 1:D.nconditions + s = [s sprintf('average %s: %d trials', cl{i}, ni(i))]; + if i < D.nconditions + s = [s ', ']; + else + s = [s '\n']; end +end +disp(sprintf(s)); %-# - sD.trials = sD.trials(1:D.nconditions); +if robust + disp('Robust averaging might have introduced high frequencies in the data. It is advised to re-apply low-pass filter'); +end - for i = 1:D.nconditions, sD.trials(i).repl = ni(i); end - if isfield(sD.other, 'artefact'); - sD.other = rmfield(sD.other, 'artefact'); - end +%-Save new evoked M/EEG dataset +%-------------------------------------------------------------------------- +Dnew = Dnew.history(mfilename, S); +save(Dnew); - Dnew = meeg(sD); +if robust && savew + Dw = Dw.history(mfilename, S); + save(Dw); +end - %-Display averaging statistics - %-------------------------------------------------------------------------- - disp(sprintf('%s: Number of replications per contrast:', Dnew.fname)); %-# - s = []; - cl = D.condlist; - for i = 1:D.nconditions - s = [s sprintf('average %s: %d trials', cl{i}, ni(i))]; - if i < D.nconditions - s = [s ', ']; - else - s = [s '\n']; - end - end - disp(sprintf(s)); %-# - - %-Save new evoked M/EEG dataset - %-------------------------------------------------------------------------- - D = Dnew; - D = D.history(mfilename, S); - save(D); - - %-Eventually display it - %-------------------------------------------------------------------------- - if ~isfield(S, 'review') || S.review - spm_eeg_review(D); - end +D = Dnew; + +%-Eventually display it +%-------------------------------------------------------------------------- +if ~isfield(S, 'review') || S.review + spm_eeg_review(D); end %-Cleanup diff --git a/spm_eeg_average5.m b/spm_eeg_average5.m new file mode 100644 index 0000000..9075fcf --- /dev/null +++ b/spm_eeg_average5.m @@ -0,0 +1,199 @@ +function D = spm_eeg_average(S) +% Average each channel over trials or trial types +% FORMAT D = spm_eeg_average(S) +% +% S - optional input struct +% (optional) fields of S: +% D - MEEG object or filename of M/EEG mat-file with epoched data +% review - review data after averaging [default: true] +% +% Output: +% D - MEEG object (also written on disk) +%__________________________________________________________________________ +% +% spm_eeg_average averages single trial data within trial type. +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Stefan Kiebel +% $Id: spm_eeg_average5.m 3258 2009-07-08 17:46:54Z vladimir $ + +SVNrev = '$Rev: 3258 $'; + +%-Startup +%-------------------------------------------------------------------------- +spm('FnBanner', mfilename, SVNrev); +spm('FigName','M/EEG averaging'); spm('Pointer','Watch'); + +%-Get MEEG object +%-------------------------------------------------------------------------- +try + D = S.D; +catch + [D, sts] = spm_select(1, 'mat', 'Select M/EEG mat file'); + if ~sts, D = []; return; end + S.D = D; +end + +D = spm_eeg_load(D); + +%-Check that there is any good data available +%-------------------------------------------------------------------------- +if ntrials(D)==0 || all(reject(D)) + warning('No good trials for averaging were found. Nothing to do.'); +else + + + %-Redirect to Time-Frequency averaging if necessary + %-------------------------------------------------------------------------- + if strncmpi(D.transformtype,'TF',2) % TF and TFphase + D = spm_eeg_average_TF(S); + return + end + + %-Generate new MEEG object with new files + %-------------------------------------------------------------------------- + Dnew = clone(D, ['m' fnamedat(D)], [D.nchannels D.nsamples D.nconditions]); + Dnew = type(Dnew, 'evoked'); + + %-Do the averaging + %-------------------------------------------------------------------------- + cl = D.condlist; + try artefact = D.artefact; catch artefact = []; end + if isfield(artefact, 'weights') + + %-Weighted averaging + %====================================================================== + weights = artefact.weights; + try thresholded = D.thresholded; catch thresholded = []; end + + d = zeros(D.nchannels, D.nsamples); + + for i = 1:D.nconditions + + for j = 1:D.nchannels + w = pickconditions(D, cl{i}, true)'; + ni(i) = length(w); + + ti = 0; + ts = 0; + while ts==0 + ti = ti+1; + ts = (j == thresholded{ti}); + end + if isempty(ts) + ind = pickconditions(D, cl{i}, 1); + data = squeeze(D(j, :, ind))'; + tempwf = []; + for nl = ind' + tempwf = [tempwf, weights(j, (nl-1)*D.nsamples+1:nl*D.nsamples)]; + end + data = data'; + tempwf = reshape(tempwf, D.nsamples, length(ind)); + + for t = 1:size(data,1) + B(t) = sum(tempwf(t,:).*data(t,:))/sum(tempwf(t,:)); + end + + if isfield(artefact, 'Smoothing') + sm = gausswin(artefact.Smoothing); + sm = sm/sum(sm); + mB = mean(B); + B = conv(sm,B-mean(B)); + B = B(floor(artefact.Smoothing/2):end-ceil(artefact.Smoothing/2)); + B = B + mB; + end + d(j, :) = B; + else + d(j,:) = zeros(1,D.nsamples); + end + end % for j = 1:D.nchannels + + Dnew(1:Dnew.nchannels, 1:Dnew.nsamples, i) = d; + + end % for i = 1:D.nconditions + + else + %-Averaging + %====================================================================== + spm_progress_bar('Init', D.nconditions, 'Averages done'); + if D.nconditions > 100, Ibar = floor(linspace(1, D.nconditions, 100)); + else Ibar = [1:D.nconditions]; end + + ni = zeros(1,D.nconditions); + for i = 1:D.nconditions + + d = zeros(D.nchannels, D.nsamples); + + w = pickconditions(D, deblank(cl{i}), true)'; + c = zeros(1, D.ntrials); + c(w) = 1; + + ni(i) = length(w); + if ni(i) == 0 + warning('%s: No trials for trial type %s', D.fname, cl{i}); + else + c = c ./ sum(c); % vector of trial-wise weights + for j = 1:D.nchannels + d(j, :) = c * squeeze(D(j, :, :))'; + end + end + + Dnew(1:Dnew.nchannels, 1:Dnew.nsamples, i) = d; + + if ismember(i, Ibar), spm_progress_bar('Set', i); end + + end % for i = 1:D.nconditions + end + + spm_progress_bar('Clear'); + + %-Reorganise trial structure + %-------------------------------------------------------------------------- + sD = struct(Dnew); + + [sD.trials.label] = deal([]); + for i = 1:D.nconditions + sD.trials(i).label = cl{i}; + end + + sD.trials = sD.trials(1:D.nconditions); + + for i = 1:D.nconditions, sD.trials(i).repl = ni(i); end + if isfield(sD.other, 'artefact'); + sD.other = rmfield(sD.other, 'artefact'); + end + + Dnew = meeg(sD); + + %-Display averaging statistics + %-------------------------------------------------------------------------- + disp(sprintf('%s: Number of replications per contrast:', Dnew.fname)); %-# + s = []; + cl = D.condlist; + for i = 1:D.nconditions + s = [s sprintf('average %s: %d trials', cl{i}, ni(i))]; + if i < D.nconditions + s = [s ', ']; + else + s = [s '\n']; + end + end + disp(sprintf(s)); %-# + + %-Save new evoked M/EEG dataset + %-------------------------------------------------------------------------- + D = Dnew; + D = D.history(mfilename, S); + save(D); + + %-Eventually display it + %-------------------------------------------------------------------------- + if ~isfield(S, 'review') || S.review + spm_eeg_review(D); + end +end + +%-Cleanup +%-------------------------------------------------------------------------- +spm('FigName','M/EEG averaging: done'); spm('Pointer','Arrow'); diff --git a/spm_eeg_average_TF.m b/spm_eeg_average_TF.m index e270a49..a899854 100644 --- a/spm_eeg_average_TF.m +++ b/spm_eeg_average_TF.m @@ -7,6 +7,12 @@ % S.D - MEEG object or filename of M/EEG mat-file with epoched TF data % S.circularise - flag that indicates whether average is straight (0) or % vector (1) of phase angles. +% S.robust - (optional) - use robust averaging (only for power) +% .savew - save the weights in an additional dataset +% .bycondition - compute the weights by condition (1, +% default) or from all trials (0) +% .ks - offset of the weighting function (default: 3) +% % Output: % D - MEEG object (also written to disk) %__________________________________________________________________________ @@ -16,9 +22,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_average_TF.m 2868 2009-03-12 12:01:03Z vladimir $ +% $Id: spm_eeg_average_TF.m 3371 2009-09-08 14:15:52Z vladimir $ -SVNrev = '$Rev: 2868 $'; +SVNrev = '$Rev: 3371 $'; %-Startup %-------------------------------------------------------------------------- @@ -58,71 +64,113 @@ S.circularise = circularise; end + +%-Configure robust averaging +%-------------------------------------------------------------------------- + +if strcmp(D.transformtype, 'TFphase') + S.robust = 0; +end + +if ~isfield(S, 'robust') + robust = spm_input('Use robust averaging?','+1','yes|no',[1 0]); + if robust + S.robust = []; + else + S.robust = false; + end +else + robust = isa(S.robust, 'struct'); +end + + +if robust + if ~isfield(S.robust, 'savew') + S.robust.savew = spm_input('Save weights?','+1','yes|no',[1 0]); + end + savew = S.robust.savew; + + if ~isfield(S.robust, 'bycondition') + S.robust.bycondition = spm_input('Compute weights by condition?','+1','yes|no',[1 0]); + end + + bycondition = S.robust.bycondition; + + if ~isfield(S.robust, 'ks') + S.robust.ks = spm_input('Offset of the weighting function', '+1', 'r', '3', 1); + end + + ks = S.robust.ks; + +end + %-Generate new MEEG object with new files %-------------------------------------------------------------------------- Dnew = clone(D, ['m' fnamedat(D)], [D.nchannels D.nfrequencies D.nsamples D.nconditions]); + +if robust && savew + Dw = clone(D, ['W' fnamedat(D)], size(D)); +end + cl = D.condlist; -spm_progress_bar('Init', D.nconditions, 'Averages done'); +spm_progress_bar('Init', D.nchannels, 'Channels completed'); ni = zeros(1,D.nconditions); - for i = 1:D.nconditions - w = pickconditions(D, deblank(cl{i}), 1)'; - ni(i) = length(w); - if ni(i) == 0 warning('%s: No trials for trial type %d', D.fname, cl{i}); - else + end +end + +goodtrials = pickconditions(D, cl, 1); + +for j = 1:D.nchannels + if robust && ~bycondition + [Y, W1] = spm_robust_average(D(j, :, :, goodtrials), 4, ks); + if savew + Dw(j, :, :, goodtrials) = W1; + end + W = zeros([1 D.nfrequencies D.nsamples D.ntrials]); + W(1, :, :, goodtrials) = W1; + end + for i = 1:D.nconditions + + w = pickconditions(D, deblank(cl{i}), 1)'; + + if isempty(w) + continue; + end %-Straight average %------------------------------------------------------------------ if ~circularise - - for j = 1:D.nchannels - Dnew(j, 1:Dnew.nfrequencies, 1:Dnew.nsamples, i) = mean(D(j, :, :, w), 4); - end + Dnew(j, :, :, i) = mean(D(j, :, :, w), 4); - %-Vector average (eg PLV for phase) - %------------------------------------------------------------------ + %-Vector average (eg PLV for phase) + %------------------------------------------------------------------ else - - for j = 1:D.nchannels - tmp = D(j, :, :, w); - tmp = cos(tmp) + sqrt(-1)*sin(tmp); - Dnew(j, 1:Dnew.nsamples, i) = squeeze(abs(mean(tmp,4)) ./ mean(abs(tmp),4)); - end - + tmp = D(j, :, :, w); + tmp = exp(sqrt(-1)*tmp); + Dnew(j, :, :, i) = abs(mean(tmp,4)); end - - spm_progress_bar('Set', i); end + spm_progress_bar('Set', j); end spm_progress_bar('Clear'); Dnew = type(Dnew, 'evoked'); -%-Reorganise trial structure +%-Update some header information %-------------------------------------------------------------------------- -sD = struct(Dnew); - -[sD.trials.label] = deal([]); -for i = 1:D.nconditions - sD.trials(i).label = cl{i}; -end - -sD.trials = sD.trials(1:D.nconditions); - -for i = 1:D.nconditions, sD.trials(i).repl = ni(i); end - -Dnew = meeg(sD); +Dnew = conditions(Dnew, [], cl); +Dnew = repl(Dnew, [], ni); %-Display averaging statistics %-------------------------------------------------------------------------- -cl = D.condlist; disp(sprintf('%s: Number of replications per contrast:', Dnew.fname)); %-# s = []; for i = 1:D.nconditions @@ -137,9 +185,15 @@ %-Save new evoked M/EEG dataset %-------------------------------------------------------------------------- +Dnew = Dnew.history(mfilename, S); +save(Dnew); + +if robust && savew + Dw = Dw.history(mfilename, S); + save(Dw); +end + D = Dnew; -D = D.history(mfilename, S); -save(D); %-Cleanup %-------------------------------------------------------------------------- diff --git a/spm_eeg_bc.m b/spm_eeg_bc.m index c15ef7f..0a4d4f3 100644 --- a/spm_eeg_bc.m +++ b/spm_eeg_bc.m @@ -6,7 +6,8 @@ % (optional) fields of S: % S.D - MEEG object or filename of M/EEG mat-file with epoched data % S.time - 2-element vector with start and end of baseline period [ms] -% S.save - save the baseline corrected data on disk [default: false] +% S.save - save the baseline corrected data in a separate file [default: true] +% S.updatehistory - update history information [default: true] % % D - MEEG object (also saved on disk if requested) %__________________________________________________________________________ @@ -16,9 +17,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_bc.m 2890 2009-03-17 12:04:18Z vladimir $ +% $Id: spm_eeg_bc.m 3262 2009-07-09 12:10:53Z vladimir $ -SVNrev = '$Rev: 2890 $'; +SVNrev = '$Rev: 3262 $'; %-Startup %-------------------------------------------------------------------------- @@ -37,6 +38,14 @@ D = spm_eeg_load(D); + +%-Redirect to Time-Frequency baseline correction if necessary +%-------------------------------------------------------------------------- +if strncmpi(D.transformtype,'TF',2) % TF and TFphase + D = spm_eeg_tf_rescale(S); + return; +end + %-Get input parameters %-------------------------------------------------------------------------- try @@ -46,11 +55,6 @@ S.time = time; end -try - S.save; -catch - S.save = false; -end %-Converting to sec %-------------------------------------------------------------------------- @@ -67,39 +71,34 @@ indchannels = [D.meegchannels D.eogchannels]; + +if ~isfield(S, 'save') || S.save + S1 = []; + S1.D = D; + S1.newname = ['b' D.fname]; + S1.updatehistory = 0; + D = spm_eeg_copy(S1); +end + spm_progress_bar('Init', D.ntrials, 'trials baseline-corrected'); if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials, 100)); else Ibar = 1:D.ntrials; end -switch(transformtype(D)) - case {'TF','TFphase'} - for k = 1: D.ntrials - tmp = mean(D(:, :, t(1):t(2), k), 3); - D(:, :, :, k) = D(:, :, :, k) - repmat(tmp, [1, 1, D.nsamples, 1]); - - if ismember(k, Ibar), spm_progress_bar('Set', k); end - end - - case 'time' - for k = 1: D.ntrials - tmp = mean(D(indchannels, t(1):t(2), k), 2); - D(indchannels, :, k) = D(indchannels, :, k) - repmat(tmp, 1, D.nsamples); - - if ismember(k, Ibar), spm_progress_bar('Set', k); end - end - - otherwise - error('Unknown transform type'); + +for k = 1: D.ntrials + tmp = mean(D(indchannels, t(1):t(2), k), 2); + D(indchannels, :, k) = D(indchannels, :, k) - repmat(tmp, 1, D.nsamples); + + if ismember(k, Ibar), spm_progress_bar('Set', k); end end spm_progress_bar('Clear'); %-Save data %-------------------------------------------------------------------------- -if S.save - error('Save option not handled yet.'); - %D = D.history('spm_eeg_bc', S); - %save(D); +if ~isfield(S, 'updatehistory') || S.updatehistory + D = D.history(mfilename, S); + save(D); end %-Cleanup diff --git a/spm_eeg_convert2images.m b/spm_eeg_convert2images.m index 4f49223..1970c6e 100644 --- a/spm_eeg_convert2images.m +++ b/spm_eeg_convert2images.m @@ -1,4 +1,4 @@ -function [D, S] = spm_eeg_convert2images(S) +function [D, S, Pout] = spm_eeg_convert2images(S) % User interface for conversion of M/EEG-files to SPM image file format % FORMAT [D, S] = spm_eeg_convert2images(S) % @@ -9,7 +9,7 @@ % S.images with entries (all optional): % fmt - string that determines type of input file. Currently, % it can be 'channels' or 'frequency' -% elecs - electrodes of interest (as vector of indices) +% elecs - channels of interest (as vector of indices) % region_no - region number % freqs - frequency window of interest (2-vector) [Hz] % t_win - [t1 t2] For 'frequency' option with TF data, specify @@ -37,9 +37,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % James Kilner, Stefan Kiebel -% $Id: spm_eeg_convert2images.m 3080 2009-04-22 17:05:00Z will $ +% $Id: spm_eeg_convert2images.m 3391 2009-09-11 11:45:17Z rik $ -SVNrev = '$Rev: 3080 $'; +SVNrev = '$Rev: 3391 $'; %-Startup %-------------------------------------------------------------------------- @@ -87,40 +87,66 @@ %-Select channels %-------------------------------------------------------------- try - images.electrodes_of_interest = S.images.elecs; + images.channels_of_interest = S.images.elecs; catch - str = 'electrodes[s]'; - Ypos = '+1'; - while true - [images.electrodes_of_interest, Ypos] = ... - spm_input(str, Ypos, 'r', [], [1 Inf]); - if any(ismember(images.electrodes_of_interest, [1:D.nchannels])) - break; + if D.nchannels > 1 + str = 'channels[s]'; + Ypos = '+1'; + while true + [images.channels_of_interest, Ypos] = ... + spm_input(str, Ypos, 'r', [], [1 Inf]); + if any(ismember(images.channels_of_interest, 1:D.nchannels)) + break; + end end + else + images.channels_of_interest = 1; end - S.images.elecs = images.electrodes_of_interest; + S.images.elecs = images.channels_of_interest; end - %-Attribute a region number to that channel + %-Attribute a region number to those channels %-------------------------------------------------------------- try images.Nregion = S.images.region_no; catch - str = 'region number'; - images.Nregion = spm_input(str, '+1', 'r', [], [1 Inf]); - S.images.region_no = images.Nregion; + if D.nchannels > 1 + str = 'region number'; + images.Nregion = spm_input(str, '+1', 'r', [], [1 Inf]); + S.images.region_no = images.Nregion; + else + images.Nregion = 1; + end end %-Convert to NIfTI images %-------------------------------------------------------------- cl = D.condlist; - for i = 1 : D.nconditions - Itrials = pickconditions(D, cl{i}, 1)'; + df = diff(D.frequencies); + if any(diff(df)) + warning('Irregular frequency spacing.'); + end + + %-Make output directory for each dataset + %-------------------------------------------------------------------------- + [P, F] = fileparts(S.D); + if isempty(P), P = pwd; end + [sts, msg] = mkdir(P, F); + if ~sts, error(msg); end + P = fullfile(P, F); + Pout = cell(1, D.nconditions); + for i = 1 : D.nconditions + Itrials = pickconditions(D, cl{i}, 1)'; + + Pout{i} = {}; + + %-Make subdirectory for each condition + %-------------------------------------------------------------------------- dname = sprintf('%dROI_TF_trialtype_%s', images.Nregion, cl{i}); - [sts, msg] = mkdir(D.path, dname); + [sts, msg] = mkdir(P, dname); if ~sts, error(msg); end - P = fullfile(D.path, dname); + dname = fullfile(P, dname); for l = Itrials(:)' @@ -131,22 +157,27 @@ % evoked data fname = 'average.img'; end - fname = fullfile(P,fname); + fname = fullfile(dname,fname); + + Pout{i} = [Pout{i}, {fname}]; N = nifti; dat = file_array(fname, [D.nfrequencies D.nsamples], 'FLOAT64-LE'); N.dat = dat; N.mat = [... - 1 0 0 min(D.frequencies);... - 0 1000/D.fsample 0 time(D, 1, 'ms');... - 0 0 1 0;... - 0 0 0 1]; + df(1) 0 0 min(D.frequencies);... + 0 1000/D.fsample 0 time(D, 1, 'ms');... + 0 0 1 0;... + 0 0 0 1]; + N.mat(1,4) = N.mat(1,4) - N.mat(1,1); + N.mat(2,4) = N.mat(2,4) - N.mat(2,2); N.mat_intent = 'Aligned'; create(N); - N.dat(:, :) = spm_cond_units(squeeze(mean(D(images.electrodes_of_interest, :, :, l), 1))); + N.dat(:, :) = spm_cond_units(squeeze(mean(D(images.channels_of_interest, :, :, l), 1))); end + Pout{i} = char(Pout{i}); end %-Average over frequency @@ -172,6 +203,11 @@ S.images.freqs = images.Frequency_window; end + % This is a slightly ugly fix for the problem of very small + % power values for MEG (in T^2). + megchanind = strmatch('MEG', D.chantype); + nonmegchanind = setdiff(1:D.nchannels, megchanind); + %-Generate new dataset with averaged data over frequency window %-------------------------------------------------------------- fnamedat = ['F' num2str(images.Frequency_window(1)) '_' ... @@ -180,7 +216,7 @@ if isfield(S.images,'t_win') % Only extract time points in specified window tims=time(D); - if S.images.t_win(1) < tims(1) | S.images.t_win(2) > tims(end) + if S.images.t_win(1) < tims(1) || S.images.t_win(2) > tims(end) disp('Error: Impossible specification of time extraction window'); disp(S.images.t_win); return; @@ -188,24 +224,39 @@ tind=find(tims > S.images.t_win(1) & tims < S.images.t_win(2)); Nind=length(tind); Dnew = clone(D, fnamedat, [D.nchannels Nind D.ntrials]); - Dnew(1:Dnew.nchannels, 1:Dnew.nsamples, 1:Dnew.ntrials) = ... - squeeze(mean(D(:, inds, tind(1):tind(end), :), 2)); + + if ~isempty(megchanind) + Dnew(megchanind, 1:Dnew.nsamples, 1:Dnew.ntrials) = ... + 1e30*squeeze(mean(D(megchanind, inds, tind(1):tind(end), :), 2)); + end + if ~isempty(nonmegchanind) + Dnew(nonmegchanind, 1:Dnew.nsamples, 1:Dnew.ntrials) = ... + squeeze(mean(D(nonmegchanind, inds, tind(1):tind(end), :), 2)); + end else Dnew = clone(D, fnamedat, [D.nchannels D.nsamples D.ntrials]); - Dnew(1:Dnew.nchannels, 1:Dnew.nsamples, 1:Dnew.ntrials) = ... - squeeze(mean(D(:, inds, :, :), 2)); + if ~isempty(megchanind) + Dnew(megchanind, 1:Dnew.nsamples, 1:Dnew.ntrials) = ... + 1e30*squeeze(mean(D(megchanind, inds, :, :), 2)); + end + if ~isempty(nonmegchanind) + Dnew(nonmegchanind, 1:Dnew.nsamples, 1:Dnew.ntrials) = ... + squeeze(mean(D(nonmegchanind, inds, :, :), 2)); + end end + + Dnew = transformtype(Dnew, 'time'); - + if ~isempty(megchanind) + Dnew = units(Dnew, megchanind, 'fT^2'); + end - - Dnew = transformtype(Dnew, 'time'); save(Dnew); %-Convert that dataset into images %-------------------------------------------------------------- S.Fname = fullfile(Dnew.path, Dnew.fname); - S = spm_eeg_convert2scalp(S); + [S, Pout] = spm_eeg_convert2scalp(S); %-Otherwise... %------------------------------------------------------------------ @@ -217,7 +268,7 @@ %-Time Epoched data %====================================================================== S.Fname = fullfile(D.path, D.fname); - S = spm_eeg_convert2scalp(S); + [S, Pout] = spm_eeg_convert2scalp(S); end %-Cleanup diff --git a/spm_eeg_convert2scalp.m b/spm_eeg_convert2scalp.m index 914fa53..35d20a3 100644 --- a/spm_eeg_convert2scalp.m +++ b/spm_eeg_convert2scalp.m @@ -1,4 +1,4 @@ -function S = spm_eeg_convert2scalp(S) +function [S, Pout] = spm_eeg_convert2scalp(S) % Convert epoched M/EEG data from SPM to NIfTI format by projecting % onto the scalp surface % FORMAT S = spm_eeg_convert2scalp(S) @@ -13,6 +13,7 @@ % S.modality - modality to be used % % S - output structure containing parameters used +% Pout - list of generated image files %__________________________________________________________________________ % % spm_eeg_convert2scalp converts M/EEG data from the SPM format to the @@ -28,9 +29,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_convert2scalp.m 3084 2009-04-24 17:21:04Z vladimir $ +% $Id: spm_eeg_convert2scalp.m 3367 2009-09-04 23:04:04Z vladimir $ -SVNrev = '$Rev: 3084 $'; +SVNrev = '$Rev: 3367 $'; %-Startup %-------------------------------------------------------------------------- @@ -67,9 +68,10 @@ %-Recursive call if multiple MEEG files %-------------------------------------------------------------------------- if numel(Fname) > 1 + Pout = cell(1, numel(Fname)); for i=1:numel(Fname) S.Fname = Fname{i}; - S = spm_eeg_convert2scalp(S); + [S, Pout{i}] = spm_eeg_convert2scalp(S); end S.Fname = Fname; return; @@ -112,7 +114,7 @@ Cel_magpl = []; Cind_magpl = []; for i=1:size(pairs,1) %loop over pairs - if any(Cind==pairs(i,1))&any(Cind==pairs(i,2)) %both channels are good, include them + if any(Cind==pairs(i,1)) && any(Cind==pairs(i,2)) %both channels are good, include them ind1=find(Cind==pairs(i,1)); ind2=find(Cind==pairs(i,2)); Cel_magpl = [Cel_magpl; round(mean([Cel(ind1,:); Cel(ind2,:)]))]; @@ -145,8 +147,9 @@ end cl = D.condlist; -for i = 1 : D.nconditions - +Pout = {}; +for i = 1 : D.nconditions + %-Make output directory for each condition %---------------------------------------------------------------------- dname = sprintf('type_%s', strrep(deblank(cl{i}),' ','_')); @@ -167,6 +170,9 @@ %-Create output image header (matching MNI space) %------------------------------------------------------------------ fname = fullfile(Pi, sprintf('trial%04d.img', Itrials(j))); + + Pout{i}{j} = fname; + N = nifti; DIM = [n n D.nsamples]; dat = file_array(fname,[DIM 1],'FLOAT32-LE'); @@ -176,9 +182,9 @@ N.mat = [... V(1) 0 0 -C(1);... 0 V(2) 0 -C(2);... - %0 0 V(3) -C(3);... 0 0 1000/D.fsample time(D, 1, 'ms');... 0 0 0 1]; + N.mat(3,4) = N.mat(3,4) - N.mat(3,3); N.mat_intent = 'Aligned'; create(N); @@ -195,6 +201,7 @@ end % for j = 1 : k + Pout{i} = char(Pout{i}); end % for i = 1 : D.nconditions diff --git a/spm_eeg_copy.m b/spm_eeg_copy.m index 918c632..73c8a1f 100644 --- a/spm_eeg_copy.m +++ b/spm_eeg_copy.m @@ -5,13 +5,14 @@ % (optional) fields of S: % S.D - MEEG object or filename of MEEG mat-file % S.newname - filename for the new dataset +% S.updatehistory - update history information [default: true] % % D - MEEG object of the new dataset %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: spm_eeg_copy.m 2850 2009-03-10 21:54:38Z guillaume $ +% $Id: spm_eeg_copy.m 3262 2009-07-09 12:10:53Z vladimir $ % get MEEG object %-------------------------------------------------------------------------- @@ -36,8 +37,16 @@ % copy dataset (.mat and .dat) %-------------------------------------------------------------------------- Dnew = clone(D, S.newname); -copyfile(D.fnamedat, Dnew.fnamedat, 'f'); +[r, msg] = copyfile(fullfile(D.path, D.fnamedat), ... + fullfile(Dnew.path, Dnew.fnamedat), 'f'); +if ~r + error(msg); +end D = Dnew; -D = D.history('spm_eeg_copy', S); % maybe not? + +if ~isfield(S, 'updatehistory') || S.updatehistory + D = D.history('spm_eeg_copy', S); +end + save(D); \ No newline at end of file diff --git a/spm_eeg_displayECD.m b/spm_eeg_displayECD.m index 04b0152..ea4b0d5 100644 --- a/spm_eeg_displayECD.m +++ b/spm_eeg_displayECD.m @@ -20,14 +20,14 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jean Daunizeau -% $Id: spm_eeg_displayECD.m 2925 2009-03-23 20:49:24Z jean $ +% $Id: spm_eeg_displayECD.m 3179 2009-06-03 12:41:21Z volkmar $ hfig = []; ParentAxes = []; tag = ''; query = []; handles = []; -try options; catch options=[];end +try options; catch, options=[];end try hfig = options.hfig;end try tag = options.tag;end try ParentAxes = options.ParentAxes;end @@ -41,9 +41,9 @@ ParentAxes = axes('parent',hfig); end -try Pos{1}; catch Pos={Pos};end -try Orient{1}; catch Orient={Orient};end -try Var{1}; catch Var = {Var};end +try Pos{1}; catch, Pos={Pos};end +try Orient{1}; catch, Orient={Orient};end +try Var{1}; catch, Var = {Var};end ndip = size(Pos{1},2); if ~exist('Names','var') || isempty(Names) diff --git a/spm_eeg_downsample.m b/spm_eeg_downsample.m index 3c2fec1..ccccbb7 100644 --- a/spm_eeg_downsample.m +++ b/spm_eeg_downsample.m @@ -17,9 +17,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_downsample.m 3082 2009-04-22 20:16:13Z guillaume $ +% $Id: spm_eeg_downsample.m 3401 2009-09-14 18:33:23Z guillaume $ -SVNrev = '$Rev: 3082 $'; +SVNrev = '$Rev: 3401 $'; %-Startup %-------------------------------------------------------------------------- @@ -83,14 +83,10 @@ % work on blocks of channels % determine block size, dependent on memory - try - evalc('memsz=2/3*feature(''memstats'');'); % 2/3 of largest block of contiguous memory, for Windows platforms - catch - memsz = 200*1024*1024; % 200 MB otherwise - end - datasz=nchannels(D)*nsamples(D)*8; % datapoints x 8 bytes per double value - blknum=ceil(datasz/memsz); - blksz=ceil(nchannels(D)/blknum); + memsz = 2/3*spm('Memory'); + datasz = nchannels(D)*nsamples(D)*8; % datapoints x 8 bytes per double value + blknum = ceil(datasz/memsz); + blksz = ceil(nchannels(D)/blknum); % now downsample blocks of channels chncnt=1; @@ -114,7 +110,7 @@ end else - spm_progress_bar('Init', D.ntrials, 'Events downsampled'); drawnow; + spm_progress_bar('Init', D.ntrials, 'Trials downsampled'); drawnow; if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials,100)); else Ibar = [1:D.ntrials]; end @@ -138,8 +134,7 @@ %-Save new downsampled M/EEG dataset %-------------------------------------------------------------------------- -Dnew = putfsample(Dnew, fsample_new); -Dnew = putnsamples(Dnew, nsamples_new); +Dnew = fsample(Dnew, fsample_new); D = Dnew; D = D.history('spm_eeg_downsample', S); save(D); diff --git a/spm_eeg_epochs.m b/spm_eeg_epochs.m index 1658adf..7fb7f2e 100644 --- a/spm_eeg_epochs.m +++ b/spm_eeg_epochs.m @@ -38,9 +38,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_epochs.m 2902 2009-03-19 20:44:35Z guillaume $ +% $Id: spm_eeg_epochs.m 3250 2009-07-06 09:31:13Z vladimir $ -SVNrev = '$Rev: 2902 $'; +SVNrev = '$Rev: 3250 $'; %-Startup %-------------------------------------------------------------------------- @@ -151,7 +151,7 @@ error('All trials should have identical and positive lengths'); end -inbounds = (trl(:,1)>1 & trl(:, 2)<=D.nsamples); +inbounds = (trl(:,1)>=1 & trl(:, 2)<=D.nsamples); rejected = find(~inbounds); rejected = rejected(:)'; @@ -199,9 +199,12 @@ %-Perform baseline correction if there are negative time points %-------------------------------------------------------------------------- if time(Dnew, 1) < 0 - Dnew = spm_eeg_bc(struct('D', Dnew, ... - 'time', [time(Dnew, 1, 'ms') 0],... - 'save', false)); + S1 = []; + S1.D = Dnew; + S1.time = [time(Dnew, 1, 'ms') 0]; + S1.save = false; + S1.updatehistory = false; + Dnew = spm_eeg_bc(S1); else warning('There was no baseline specified. The data is not baseline-corrected'); end diff --git a/spm_eeg_filter.m b/spm_eeg_filter.m index 3598bb5..d06facc 100644 --- a/spm_eeg_filter.m +++ b/spm_eeg_filter.m @@ -22,9 +22,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_filter.m 3086 2009-04-26 21:14:59Z christophe $ +% $Id: spm_eeg_filter.m 3401 2009-09-14 18:33:23Z guillaume $ -SVNrev = '$Rev: 3086 $'; +SVNrev = '$Rev: 3401 $'; %-Startup %-------------------------------------------------------------------------- @@ -149,12 +149,7 @@ % work on blocks of channels % determine blocksize % determine block size, dependent on memory - try - % 2/3 of largest block of contiguous memory, for Windows platforms - evalc('memsz=2/3*feature(''memstats'');'); - catch - memsz = 200*1024*1024; % 200 MB - end + memsz = 2/3*spm('Memory'); datasz = nchannels(D)*nsamples(D)*8; % datapoints x 8 bytes per double value blknum = ceil(datasz/memsz); blksz = ceil(nchannels(D)/blknum); @@ -186,7 +181,7 @@ else % single trial or epoched - spm_progress_bar('Init', D.ntrials, 'Events filtered'); drawnow; + spm_progress_bar('Init', D.ntrials, 'Trials filtered'); drawnow; if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials,100)); else Ibar = [1:D.ntrials]; end @@ -201,14 +196,13 @@ end end - % base line correction - d(Fchannels, :) = d(Fchannels, :) - repmat(mean(d(Fchannels, 1:indsample(D,0)), 2), 1, D.nsamples); - Dnew(:, 1:Dnew.nsamples, i) = d; if ismember(i, Ibar), spm_progress_bar('Set', i); end end + + disp('Baseline correction is no longer done automatically by spm_eeg_filter. Use spm_eeg_bc if necessary.'); end spm_progress_bar('Clear'); diff --git a/spm_eeg_ft2spm.m b/spm_eeg_ft2spm.m index 895c5d5..d1c08de 100644 --- a/spm_eeg_ft2spm.m +++ b/spm_eeg_ft2spm.m @@ -5,7 +5,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: spm_eeg_ft2spm.m 2720 2009-02-09 19:50:46Z vladimir $ +% $Id: spm_eeg_ft2spm.m 3196 2009-06-11 12:54:47Z vladimir $ + +isTF = 0; % If raw format if iscell(ftdata.time) @@ -33,7 +35,10 @@ end ftdata.time = ftdata.time{1}; -else % timelockanalysis format +else + Nchannels = numel(ftdata.label); + Nsamples = length(ftdata.time); + rptind=strmatch('rpt', tokenize(ftdata.dimord, '_')); if isempty(rptind) rptind=strmatch('subj', tokenize(ftdata.dimord, '_')); @@ -42,21 +47,31 @@ timeind=strmatch('time', tokenize(ftdata.dimord, '_')); chanind=strmatch('chan', tokenize(ftdata.dimord, '_')); - if ~isempty(rptind) - if isfield(ftdata, 'trial') - Ntrials = size(ftdata.trial, rptind); - data =permute(ftdata.trial, [chanind, timeind, rptind]); + if any(ismember({'trial', 'individual', 'avg'}, fieldnames(ftdata) )) % timelockanalysis + if ~isempty(rptind) + if isfield(ftdata, 'trial') + Ntrials = size(ftdata.trial, rptind); + data =permute(ftdata.trial, [chanind, timeind, rptind]); + else + Ntrials = size(ftdata.individual, rptind); + data =permute(ftdata.individual, [chanind, timeind, rptind]); + end else - Ntrials = size(ftdata.individual, rptind); - data =permute(ftdata.individual, [chanind, timeind, rptind]); + Ntrials = 1; + data =permute(ftdata.avg, [chanind, timeind]); + end + elseif isfield(ftdata, 'powspctrm') + isTF = 1; + Nfrequencies = numel(ftdata.freq); + freqind = strmatch('freq', tokenize(ftdata.dimord, '_')); + if ~isempty(rptind) + Ntrials = size(ftdata.powspctrm, rptind); + data = permute(ftdata.powspctrm, [chanind, freqind, timeind, rptind]); + else + Ntrials = 1; + data = permute(ftdata.powspctrm, [chanind, freqind, timeind]); end - else - Ntrials = 1; - data =permute(ftdata.avg, [chanind, timeind]); end - - Nchannels = size(ftdata.trial, chanind); - Nsamples = size(ftdata.trial, timeind); end %--------- Start making the header @@ -64,7 +79,11 @@ D = []; % sampling rate in Hz -D.Fsample = ftdata.fsample; +if isfield(ftdata, 'fsample') + D.Fsample = ftdata.fsample; +else + D.Fsample = 1./mean(diff(ftdata.time)); +end D.timeOnset = ftdata.time(1); @@ -84,24 +103,37 @@ D.data.fnamedat = [fname '.dat']; D.data.datatype = 'float32-le'; -if Ntrials == 1 - datafile = file_array(fullfile(D.path, D.data.fnamedat), [Nchannels Nsamples], D.data.datatype); - % physically initialise file - datafile(end,end) = 0; - datafile(:, :) = data; -else - datafile = file_array(fullfile(D.path, D.data.fnamedat), [Nchannels Nsamples Ntrials], D.data.datatype); - % physically initialise file - datafile(end,end) = 0; +if ~isTF + if Ntrials == 1 + datafile = file_array(fullfile(D.path, D.data.fnamedat), [Nchannels Nsamples], D.data.datatype); + % physically initialise file + datafile(end,end) = 0; + datafile(:, :) = data; + else + datafile = file_array(fullfile(D.path, D.data.fnamedat), [Nchannels Nsamples Ntrials], D.data.datatype); + % physically initialise file + datafile(end,end) = 0; - for i = 1:Ntrials - datafile(:, :, i) = data(:, :, i); + datafile(:, :, :) = data; end +else + if Ntrials == 1 + datafile = file_array(fullfile(D.path, D.data.fnamedat), [Nchannels Nfrequencies Nsamples], D.data.datatype); + % physically initialise file + datafile(end,end) = 0; + datafile(:, :, :) = data; + else + datafile = file_array(fullfile(D.path, D.data.fnamedat), [Nchannels Nfrequencies Nsamples Ntrials], D.data.datatype); + % physically initialise file + datafile(end,end) = 0; + + datafile(:, :, :, :) = data; + end + D.transform.ID = 'TF'; + D.transform.frequencies = ftdata.freq; end -if isfield(ftdata, 'hdr') && isfield(ftdata.hdr, 'grad') - D.sensors.meg = forwinv_convert_units(ftdata.hdr.grad, 'mm'); -end +D.data.y = datafile; D = meeg(D); @@ -118,6 +150,20 @@ D = type(D, 'single'); end +if isfield(ftdata, 'hdr') && isfield(ftdata.hdr, 'grad') + D = sensors(forwinv_convert_units(ftdata.hdr.grad, 'mm'), 'MEG'); + + S = []; + S.task = 'project3D'; + S.modality = 'MEG'; + S.updatehistory = 0; + S.D = D; + + D = spm_eeg_prep(S); +end + +[ok D] = check(D); + save(D); diff --git a/spm_eeg_grandmean.m b/spm_eeg_grandmean.m index 714d76c..6d022b0 100644 --- a/spm_eeg_grandmean.m +++ b/spm_eeg_grandmean.m @@ -26,9 +26,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_grandmean.m 3046 2009-04-02 14:28:31Z vladimir $ +% $Id: spm_eeg_grandmean.m 3364 2009-09-04 18:31:15Z vladimir $ -SVNrev = '$Rev: 3046 $'; +SVNrev = '$Rev: 3364 $'; %-Startup %-------------------------------------------------------------------------- @@ -213,8 +213,11 @@ types = unique([types, D{i}.conditions]); end -Ntypes = numel(types); +% The order of the conditions will be consistent with the first file +[sel1, sel2] = spm_match_str(D{1}.condlist, types); +types = types([sel2, setdiff(1:length(types), sel2)]); +Ntypes = numel(types); % how many repetitons per trial type nrepl = zeros(Nfiles, Ntypes); diff --git a/spm_eeg_history.m b/spm_eeg_history.m index 291f450..2250855 100644 --- a/spm_eeg_history.m +++ b/spm_eeg_history.m @@ -22,7 +22,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_history.m 2902 2009-03-19 20:44:35Z guillaume $ +% $Id: spm_eeg_history.m 3262 2009-07-09 12:10:53Z vladimir $ try h = S.history; @@ -161,6 +161,10 @@ function hist2script(h,fname) ' filter ' num2str(h(i).args.filter.PHz(:)', '%g %g') ' Hz']; case 'spm_eeg_downsample' hh{i} = ['Downsample to ' num2str(h(i).args.fsample_new) ' Hz']; + case 'spm_eeg_bc' + hh{i} = ['Baseline correction ' mat2str(h(i).args.time(:)') ' ms']; + case 'spm_eeg_copy' + hh{i} = 'Copy dataset'; case 'spm_eeg_montage' hh{i} = 'Change montage'; case 'spm_eeg_artefact' diff --git a/spm_eeg_inv_checkdatareg.m b/spm_eeg_inv_checkdatareg.m index f3c2e12..24bbef4 100644 --- a/spm_eeg_inv_checkdatareg.m +++ b/spm_eeg_inv_checkdatareg.m @@ -8,7 +8,7 @@ function spm_eeg_inv_checkdatareg(varargin) % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jeremie Mattout -% $Id: spm_eeg_inv_checkdatareg.m 2760 2009-02-18 18:38:20Z vladimir $ +% $Id: spm_eeg_inv_checkdatareg.m 3388 2009-09-11 08:44:35Z vladimir $ % SPM graphics figure %-------------------------------------------------------------------------- @@ -73,16 +73,6 @@ function spm_eeg_inv_checkdatareg(varargin) return end -% EEG fiducials or MEG coils (coreg.) -%-------------------------------------------------------------------------- -h_fid = plot3(Lfid(:,1),Lfid(:,2),Lfid(:,3),'oc'); -set(h_fid,'MarkerFaceColor','c','MarkerSize',12,'MarkerEdgeColor','k'); - -% MRI fiducials -%-------------------------------------------------------------------------- -h_fidmr = plot3(Lfidmri(:,1),Lfidmri(:,2),Lfidmri(:,3),'dm'); -set(h_fidmr,'MarkerFaceColor','m','MarkerSize',12,'MarkerEdgeColor','k'); - % headshape locations %-------------------------------------------------------------------------- if ~isempty(Lhsp) @@ -95,6 +85,17 @@ function spm_eeg_inv_checkdatareg(varargin) h_sens = plot3(Lsens(:,1),Lsens(:,2),Lsens(:,3),'og'); set(h_sens,'MarkerFaceColor','g','MarkerSize', 12,'MarkerEdgeColor','k'); % + +% EEG fiducials or MEG coils (coreg.) +%-------------------------------------------------------------------------- +h_fid = plot3(Lfid(:,1),Lfid(:,2),Lfid(:,3),'oc'); +set(h_fid,'MarkerFaceColor','c','MarkerSize',12,'MarkerEdgeColor','k'); + +% MRI fiducials +%-------------------------------------------------------------------------- +h_fidmr = plot3(Lfidmri(:,1),Lfidmri(:,2),Lfidmri(:,3),'dm'); +set(h_fidmr,'MarkerFaceColor','m','MarkerSize',12,'MarkerEdgeColor','k'); + % camera view %-------------------------------------------------------------------------- axis image off diff --git a/spm_eeg_inv_checkmeshes.m b/spm_eeg_inv_checkmeshes.m index 4296c40..a9ddb61 100644 --- a/spm_eeg_inv_checkmeshes.m +++ b/spm_eeg_inv_checkmeshes.m @@ -13,7 +13,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jeremie Mattout -% $Id: spm_eeg_inv_checkmeshes.m 2914 2009-03-20 18:30:31Z guillaume $ +% $Id: spm_eeg_inv_checkmeshes.m 3328 2009-08-21 17:00:32Z vladimir $ % initialise @@ -22,9 +22,10 @@ try disp(D.inv{val}.mesh); mesh = spm_eeg_inv_transform_mesh(eye(4), D.inv{val}.mesh); - Mctx = mesh.tess_ctx; - Mskl = mesh.tess_iskull; - Mslp = mesh.tess_scalp; + Mctx = mesh.tess_ctx; + Miskl = mesh.tess_iskull; + Moskl = mesh.tess_oskull; + Mslp = mesh.tess_scalp; catch warndlg('please create meshes') return @@ -41,12 +42,17 @@ % Inner-skull Mesh Display %-------------------------------------------------------------------------- -h_skl = patch('vertices',Mskl.vert,'faces',Mskl.face,'EdgeColor','r','FaceColor','none'); +h_iskl = patch('vertices',Miskl.vert,'faces',Miskl.face,'EdgeColor','r','FaceColor','none'); -% Scalp Mesh Display +% Outer-skull Mesh Display +%-------------------------------------------------------------------------- +h_oskl = patch('vertices',Moskl.vert,'faces',Moskl.face,'EdgeColor',[1 .5 .35],'FaceColor','none'); + +% Inner Scalp Mesh Display %-------------------------------------------------------------------------- h_slp = patch('vertices',Mslp.vert,'faces',Mslp.face,'EdgeColor',[1 .7 .55],'FaceColor','none'); + pls=0.05:0.2:0.9; N = nifti(mesh.sMRI); d=size(N.dat); diff --git a/spm_eeg_inv_imag_api.m b/spm_eeg_inv_imag_api.m index 34acae6..7837c75 100644 --- a/spm_eeg_inv_imag_api.m +++ b/spm_eeg_inv_imag_api.m @@ -7,7 +7,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jeremie Mattout -% $Id: spm_eeg_inv_imag_api.m 3075 2009-04-22 10:56:29Z vladimir $ +% $Id: spm_eeg_inv_imag_api.m 3324 2009-08-14 11:39:18Z vladimir $ spm('Clear'); @@ -445,7 +445,11 @@ function CheckContrast_Callback(hObject, eventdata, handles) %-------------------------------------------------------------------------- function Vis3D_Callback(hObject, eventdata, handles) Exit_Callback(hObject, eventdata, handles) -spm_eeg_review(handles.D,6,handles.D.val) +try + spm_eeg_inv_visu3D_api(handles.D); +catch + spm_eeg_review(handles.D,6,handles.D.val); +end Reset(hObject, eventdata, handles); % --- Executes on button press in CheckImage. diff --git a/spm_eeg_inv_meshdist.m b/spm_eeg_inv_meshdist.m index a14509a..aea777b 100644 --- a/spm_eeg_inv_meshdist.m +++ b/spm_eeg_inv_meshdist.m @@ -13,7 +13,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jeremie Mattout -% $Id: spm_eeg_inv_meshdist.m 1131 2008-02-06 11:17:09Z spm $ +% $Id: spm_eeg_inv_meshdist.m 3173 2009-06-02 14:50:19Z karl $ % default order is 2nd %-------------------------------------------------------------------------- @@ -48,7 +48,7 @@ %-------------------------------------------------------------------------- Mdist = edge; if ~order - Mdist = ~~Mdist; + Mdist = Mdist > 0; end % 2nd order connectivity diff --git a/spm_eeg_inv_results.m b/spm_eeg_inv_results.m index b114869..34afa2d 100644 --- a/spm_eeg_inv_results.m +++ b/spm_eeg_inv_results.m @@ -12,10 +12,10 @@ % the power in D.inv{i}.contrast.GW corresponds to the evoked power %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging - + % Karl Friston -% $Id: spm_eeg_inv_results.m 2720 2009-02-09 19:50:46Z vladimir $ - +% $Id: spm_eeg_inv_results.m 3175 2009-06-02 16:26:12Z karl $ + % SPM data structure %========================================================================== try @@ -24,14 +24,14 @@ model = D.inv{end}; D.val = numel(D.inv); end - + % defaults %-------------------------------------------------------------------------- try, woi = model.contrast.woi; catch, woi = [80 120]; end try, foi = model.contrast.fboi; catch, foi = []; end try, type = model.contrast.type; catch, type = 'evoked'; end - - + + % Check contrast woi is within inversion woi %-------------------------------------------------------------------------- if woi(1) < D.inv{D.val}.inverse.woi(1) || woi(2) > D.inv{D.val}.inverse.woi(2) @@ -40,9 +40,9 @@ if ~any(foi) foi = []; end - + fprintf('\ncomputing contrast - please wait\n') - + % inversion parameters %-------------------------------------------------------------------------- J = model.inverse.J; % Trial average MAP estimate @@ -56,23 +56,23 @@ Nd = model.inverse.Nd; % number of mesh dipoles Nb = size(T,1); % number of time bins Nc = size(U,1); % number of channels - + try scale = model.inverse.scale; % Trial average MAP estimate catch scale = 1; end - + % time-frequency contrast %========================================================================== - + % get [Gaussian] time window %-------------------------------------------------------------------------- fwhm = max(diff(woi),8); t = exp(-4*log(2)*(pst(:) - mean(woi)).^2/(fwhm^2)); t = t/sum(t); - - + + % get frequency space and put PST subspace into contrast (W -> T*T'*W) %-------------------------------------------------------------------------- if ~isempty(foi) @@ -88,7 +88,7 @@ end TW = T'*W; TTW = T*TW; - + % MAP projector and conditional covariance %========================================================================== M = model.inverse.M; @@ -96,8 +96,8 @@ qC = model.inverse.qC*trace(TTW'*V*TTW); qC = max(qC,0); MAP = M*U'*R; - - + + % cycle over trial types %========================================================================== try @@ -106,60 +106,71 @@ trial = D.condlist; end for i = 1:length(J) - + % induced or evoked %---------------------------------------------------------------------- switch(type) - + % energy of conditional mean %------------------------------------------------------------------ case{'evoked'} - + JW{i} = J{i}*TW(:,1); GW{i} = sum((J{i}*TW).^2,2) + qC; - - % mean energy over trials - %------------------------------------------------------------------ + + % mean energy over trials + %------------------------------------------------------------------ case{'induced'} - + JW{i} = sparse(0); JWWJ = sparse(0); - + c = D.pickconditions(trial{i}); - + % conditional expectation of contrast (J*W) and its energy - %------------------------------------------------------------------ + %-------------------------------------------------------------- Nt = length(c); for j = 1:Nt fprintf('evaluating trial %i, condition %i\n',j,i) - Y = D(Ic,It,c(j))*scale; + try + % unimodal data + %------------------------------------------------------ + Y = D(Ic,It,c(j))*scale; + catch + % multimodal data + %------------------------------------------------------ + for k = 1:length(Ic) + Yk{k,1} = D(Ic{k},It,c(j))*scale(k); + end + Y = spm_cat(Yk); + end MYW = MAP*Y*TTW; JW{i} = JW{i} + MYW(:,1); JWWJ = JWWJ + sum(MYW.^2,2); end - + % conditional expectation of total energy (source space GW) %-------------------------------------------------------------- JW{i} = JW{i}/Nt; GW{i} = JWWJ/Nt + qC; end - + end - - - + + + % Save results %========================================================================== model.contrast.woi = woi; model.contrast.fboi = foi; - + model.contrast.W = W; model.contrast.JW = JW; model.contrast.GW = GW; - + D.inv{D.val} = model; - - + + % Display %========================================================================== spm_eeg_inv_results_display(D); diff --git a/spm_eeg_inv_vbecd.m b/spm_eeg_inv_vbecd.m index 460a603..2f374c0 100644 --- a/spm_eeg_inv_vbecd.m +++ b/spm_eeg_inv_vbecd.m @@ -1,5 +1,5 @@ function P = spm_eeg_inv_vbecd(P) -% Model inversion routine for ECDs using "variational Bayes" +% Model inversion routine for ECDs using variational Bayesian approach % % FORMAT P = spm_eeg_inv_vbecd(P) % @@ -9,16 +9,14 @@ % and "sens" structure in a FT compatible format % bad - list of bad channels, not to use. % y - data vector -% Nc - +% % Niter - maximum number of iterations -% threshold_dF - threshold on free energy improvement to stop iterating -% priors - priors on parameters, hard and soft, as filled in (and +% priors - priors on parameters, as filled in (and % described) in spm_eeg_inv_vbecd_gui.m. % % Output: % same structure with extra fields % init - initial valuse used for mu_w/s -% ok - flags indicating if everything was ok % dF - successive (relative) improvement of F % post - posterior value of estimated parameters and ther variance % Fi - successive values of F @@ -27,501 +25,86 @@ % Reference: % Kiebel et al., Variational Bayesian inversion of the equivalent current % dipole model in EEG/MEG., NeuroImage, 39:728-741, 2008 +% (Although this algorithm uses a function for general Bayesian inversion of +% a non-linear model - see spm_nlsi_gn) %__________________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging - -% Christophe Phillips & Stefan Kiebel -% $Id: spm_eeg_inv_vbecd.m 3062 2009-04-17 14:07:40Z jean $ - +% Copyright (C) 2009 Wellcome Trust Centre for Neuroimaging +% Gareth Barnes +% $Id: spm_eeg_inv_vbecd.m 3372 2009-09-08 14:33:45Z gareth $ % unpack model, priors, data -%--------------------------- -a10 = P.priors.a10; b10 = P.priors.b10; -a20 = P.priors.a20; b20 = P.priors.b20; -a30 = P.priors.a30; b30 = P.priors.b30; Nd = length(P.priors.mu_w0)/3; mu_w0 = P.priors.mu_w0; mu_s0 = P.priors.mu_s0; -iS_w0 = P.priors.iS_w0; -iS_s0 = P.priors.iS_s0; - -try - Tw = P.priors.Tw; -catch - Tw = eye(length(mu_w0)); -end +S_w0 = P.priors.S_w0; +S_s0 = P.priors.S_s0; -try - Ts = P.priors.Ts; -catch - Ts = eye(length(mu_s0)); -end - -Vw = full(spm_svd(Tw)); -Vs = full(spm_svd(Ts)); +if strcmp(upper(P.modality),'EEG'), + P.y=P.y-mean(P.y); %% subtracting mean level from eeg data +end; % if y = P.y; +sc_y=1/std(y); %% rescale data to fit into minimisation routine (same magnitude for EEG and MEG means same threshold and exit criteria) +y = y*sc_y; -P.dv = 10^-2; % used to compute step-size for gradients +[u,s,v] = svd(S_w0); +%% set random moment, scaled by prior variances +mu_w = mu_w0 + u*diag(sqrt(diag(s+eps)))*v'*randn(size(mu_w0)); %% source units -%--------------- -% initialization -%--------------- +[u,s,v] = svd(S_s0); %% +%% a random guess for the location, based on variance of the prior -% ensure data have apropriate scale: ADDED BACK BY GRB - if strcmp(P.modality,'MEG') - % sc_y = norm(y)/10; - sc_y = 1e-8; - y = y/sc_y; - else - y = y-mean(y); % Re-reference data to mean - sc_y = 1; - end -% sc_y = 1; -% Initialize posterior with prior -b10 = b10*sc_y.^2; -a1 = a10; -b1 = b10; -a2 = a20; -b2 = b20; %*sc_y.^2; -a3 = a30; -b3 = b30; +outside=1; +while (outside), + outside=0; + mu_s = mu_s0 + u*diag(sqrt(diag(s+eps)))*v'*randn(size(mu_s0)); % + for i=1:3:length(mu_s), %% check each dipole is inside the head + pos=mu_s(i:i+2); + outside = outside+ ~forwinv_inside_vol(pos',P.forward.vol); + end; +end; -[u,s,v] = svd((a2/b2)*iS_w0); -mu_w = mu_w0 + 1e-8*u*diag(sqrt(diag(s)))*v'*randn(size(mu_w0)); -mu_w = mu_w./sqrt(sum(mu_w.^2)); -S_w = u*diag(diag((s+eps).^-1))*v'; -[u,s,v] = svd((a3/b3)*iS_s0); -mu_s = mu_s0 + 1e-8*u*diag(sqrt(diag(s)))*v'*randn(size(mu_s0)); -S_s = u*diag(diag((s+eps).^-1))*v'; + % get lead fields -[gmn, gm, dgm] = spm_eeg_inv_vbecd_getLF(mu_s, P.forward.sens, P.forward.vol,... - P.dv.*ones(1, length(mu_w0))); - -[Nc, Np] = size(gmn); -DE = kron(S_w+mu_w*mu_w', eye(Nc)); -P.init.mu_s = mu_s; -P.init.mu_w = mu_w; - - -% Calulate free energy with priors -F(1) = -Nc/2*log(2*pi) + Nc/2*(psi(a1) - log(b1))... - -a1/(2*b1)*(y'*y - 2*mu_w'*gmn'*y + gm'*DE*gm + trace(S_s*dgm'*DE*dgm))... - -spm_kl_normal(Vw'*mu_w, Vw'*S_w*Vw, Vw'*mu_w0, Vw'*b2/a2*inv(iS_w0)*Vw)... - -spm_kl_normal(Vs'*mu_s, Vs'*S_s*Vs, Vs'*mu_s0, Vs'*b3/a3*inv(iS_s0)*Vs)... - -spm_kl_gamma(1/b1,a1,1/b10,a10)... - -spm_kl_gamma(1/b2,a2,1/b20,a20)... - -spm_kl_gamma(1/b3,a3,1/b30,a30); - -P.gmn = gmn; - - -P.handles.hfig = spm_figure('GetWin','Graphics'); -spm_clf(P.handles.hfig) -P.handles.SPMdefaults.col = get(P.handles.hfig,'colormap'); -P.handles.SPMdefaults.renderer = get(P.handles.hfig,'renderer'); -set(P.handles.hfig,'userdata',P) - -[P] = displayVBupdate(y,a1,b1,a2,b2,a3,b3,mu_w,mu_s,S_s,S_w,P,1,[],F); - -P.ok = 1; -for i = 1:P.Niter - - % orientation parameters w - SD = zeros(Np, Np); - Dm = dgm*S_s*dgm'; - for j = 1:Nc - ind = j + [0:Np-1]*Nc; - d = Dm(ind, ind); - SD = SD + d; - end -% S_w = Tw*pinv(a2/b2*iS_w0 + a1/b1*(gmn'*gmn))*Tw'; - S_w = Tw*pinv(a2/b2*iS_w0 + a1/b1*(gmn'*gmn + SD))*Tw'; - mu_w = Tw*S_w*(a1/b1*gmn'*y + a2/b2*iS_w0*mu_w0); - - % location parameters s - [mu_s,S_s,DE,gmn,gm,dgm,P] = ... - modifiedGN4s(mu_s,S_s,iS_s0,mu_s0,mu_w,S_w,a1,b1,a3,b3,y,P,Ts); - - [P] = displayVBupdate(y,a1,b1,a2,b2,a3,b3,mu_w,mu_s,S_s,S_w,P,i+1,'data',F(end)); - - % precision on y - a1 = Nc/2 + a10; - b1 = 0.5*(y'*y... - - 2*mu_w'*gmn'*y... - + gm'*DE*gm... - + trace(S_s*dgm'*DE*dgm))... - + b10; - - % precision on w - a2 = size(Vw,2)/2 + a20; - b2 = 0.5*((Vw'*(mu_w-mu_w0))'*Vw'*iS_w0*Vw*(Vw'*(mu_w-mu_w0)) + ... - trace(Vw'*iS_w0*S_w*Vw)) + b20; - - % precision on s - a3 = size(Vs,2)/2 + a30; - b3 = 0.5*((Vs'*(mu_s-mu_s0))'*Vs'*iS_s0*Vs*(Vs'*(mu_s-mu_s0)) + ... - trace(Vs'*iS_s0*Vs*Vs'*S_s*Vs)) + b30; - - % compute neg free energy - F(i+1) = -Nc/2*log(2*pi) + Nc/2*(psi(a1) - log(b1))... - -a1/(2*b1)*(y'*y - 2*mu_w'*gmn'*y + gm'*DE*gm + trace(S_s*dgm'*DE*dgm))... - -spm_kl_normal(Vw'*mu_w, Vw'*S_w*Vw, Vw'*mu_w0, Vw'*b2/a2*inv(iS_w0)*Vw)... - -spm_kl_normal(Vs'*mu_s, Vs'*S_s*Vs, Vs'*mu_s0, Vs'*b3/a3*inv(iS_s0)*Vs)... - -spm_kl_gamma(1/b1,a1,1/b10,a10)... - -spm_kl_gamma(1/b2,a2,1/b20,a20)... - -spm_kl_gamma(1/b3,a3,1/b30,a30); - - % update display - P.gmn = gmn; - [P] = displayVBupdate(y,a1,b1,a2,b2,a3,b3,mu_w,mu_s,S_s,S_w,P,i+1,[],F(end)); - pause(0.1) - - % check termination condition - dF = (F(i+1)-F(i));%/abs(F(i)); - if abs(dF) < P.threshold_dF - P.dF(i) = dF; - str = sprintf('%3d/%d, F: %f\t dFr: %f', i, P.Niter, F(i+1), dF); - fprintf('%s\n', str) - break; - end - P.dF(i) = dF; - str = sprintf('%3d/%d, F: %f\t dFr: %f', i, P.Niter, F(i+1), dF); - fprintf('%s\n', str) - -end - -try - set(P.handles.hte(2),'string','VB for ECDs: done.') - drawnow - pause -catch - P.ok = 0; -end -set(P.handles.hfig,'userdata',[]) -P = rmfield(P,'handles'); - - -% rescale back to original units -mu_w = mu_w*sc_y; -S_w = S_w*sc_y^2; - -% save results -post.mu_w = mu_w; -post.mu_s = mu_s; -post.S_w = S_w; -post.S_s = S_s; -post.a1 = a1; post.b1 = b1; -post.a2 = a2; post.b2 = b2; -post.a3 = a3; post.b3 = b3; -post.gmn = gmn; % lead field - -P.post = post; -P.Fi = F; -P.F = F(end); - - -function [mu,Sigma,DE,gmn,gm,dgm,P] = ... - modifiedGN4s(mu,S_s,iS_s0,mu_s0,mu_w,S_w,a1,b1,a3,b3,y,P,Ts) - - -% Compute variational energy and GN step from previous mode -Sigma = S_s; -PreviousMu = mu; -[PreviousI,Sigma,deltaMu,DE,gmn,gm,dgm] = ... - logVarQ(PreviousMu,Sigma,iS_s0,mu_s0,mu_w,S_w,a1,b1,a3,b3,y,P,Ts); - -maxIter = 16; -rdI = 1e-4; -stop = 0; -it = 0; -while stop == 0 - it = it+1; - % make a move - mu = Ts*(PreviousMu + deltaMu); - % get variational energy (as well as next move) - [I,nextSigma,NextdeltaMu,nextDE,nextgmn,nextgm,nextdgm] = ... - logVarQ(mu,Sigma,iS_s0,mu_s0,mu_w,S_w,a1,b1,a3,b3,y,P,Ts); - % calculate relative variational energy improvement - deltaI = I-PreviousI; - % check whether to stop, to accept move or to halve step - if it <= maxIter && abs(deltaI./PreviousI)>rdI - if deltaI<0 % halve step size - deltaMu = 0.5*deltaMu; - P.pc = 100*it./maxIter; - P = displayVBupdate(y,a1,b1,[],[],a3,b3,mu_w,mu,Sigma,S_w,P,[],'mGN'); - else % accept move - % propose a new GN move from there on - deltaMu = NextdeltaMu; - DE = nextDE; - gmn = nextgmn; - gm = nextgm; - dgm = nextdgm; - Sigma = Ts*nextSigma*Ts'; - PreviousMu = mu; - PreviousI = I; - P.pc = 100*it./maxIter; - P.gmn = gmn; - P = displayVBupdate(y,a1,b1,[],[],a3,b3,mu_w,mu,Sigma,S_w,P,[],'ecd'); - end - else % stop Gauss-Newton search - stop = 1; - end -end -mu = PreviousMu; % ensures mode is not updated if no improvement - -function [I,Sigma,deltaMu,DE,gmn,gm,dgm] = ... - logVarQ(PreviousMu,S_s,iS_s0,mu_s0,mu_w,S_w,a1,b1,a3,b3,y,P,Ts) - -Nc = size(y,1); -In = speye(Nc); - -vol = P.forward.vol; -sens = P.forward.sens; -dv = P.dv; - -% first check if inside head -outside = ~forwinv_inside_vol(reshape(PreviousMu,3,[])',vol); -if ~all(~outside) - I = -Inf; - deltaMu = mu_s0-PreviousMu+eps; % to correct for unluky initialization - Sigma = []; - DE = []; - gmn = []; - gm = []; - dgm = []; -else - % Compute lead fields (and gradients) at the mode - [gmn, gm, dgm] =... - spm_eeg_inv_vbecd_getLF(PreviousMu, sens, vol,dv.*sqrt(diag(S_s))); - % Compute variational energy at the mode - Gw = gmn*mu_w; - dmu0 = mu_s0-PreviousMu; - iSdmu0 = iS_s0*dmu0; - dy = y-Gw; - I = -0.5*(a3/b3)*dmu0'*iSdmu0 ... - -0.5*(a1/b1)*( dy'*dy + trace(gmn'*gmn*S_w) ); - % Get local curvature of variational energy (-> cov matrix) - DE = kron(S_w+mu_w*mu_w', eye(Nc)); - Sigma = pinv(a3/b3*iS_s0 + a1/b1*(dgm'*DE*dgm)); - if any(diag(Sigma)<0) - error('More regularization is required to invert the model!') - end - % standard Gauss-Newton move from mode and curvature - deltaMu = Sigma*( (a3/b3)*iSdmu0 ... - + (a1/b1)*dgm'*(kron(mu_w,dy)) );%+ kron(S_w,In)*gm) ); -end - - - - -function [P] = displayVBupdate(y,a1,b1,a2,b2,a3,b3,mu_w,mu_s,S_s,S_w,P,it,flag,F) - -if ~exist('flag','var') - flag = []; -end - - -if isempty(flag) || isequal(flag,'ecd') - % plot dipoles - try - opt.ParentAxes = P.handles.axesECD; - opt.hfig = P.handles.hfig; - opt.handles.hp = P.handles.hp; - opt.handles.hq = P.handles.hq; - opt.handles.hs = P.handles.hs; - opt.handles.ht = P.handles.ht; - opt.query = 'replace'; - catch - P.handles.axesECD = axes(... - 'parent',P.handles.hfig,... - 'Position',[0.13 0.55 0.775 0.4],... - 'hittest','off',... - 'visible','off',... - 'deleteFcn',@back2defaults); - opt.ParentAxes = P.handles.axesECD; - opt.hfig = P.handles.hfig; - end - w = reshape(mu_w,3,[]); - s = reshape(mu_s, 3, []); - [out] = spm_eeg_displayECD(... - s,w,reshape(diag(S_s),3,[]),[],opt); - P.handles.hp = out.handles.hp; - P.handles.hq = out.handles.hq; - P.handles.hs = out.handles.hs; - P.handles.ht = out.handles.ht; -end - -% plot data and predicted data -pos = P.forward.sens.prj; -ChanLabel = P.channels; -in.f = P.handles.hfig; -in.noButtons = 1; -try - P.handles.axesY; -catch - figure(P.handles.hfig) - P.handles.axesY = axes(... - 'Position',[0.02 0.3 0.3 0.2],... - 'hittest','off'); - in.ParentAxes = P.handles.axesY; - spm_eeg_plotScalpData(y,pos,ChanLabel,in); - title(P.handles.axesY,'measured data') -end -if isempty(flag) || isequal(flag,'data') || isequal(flag,'ecd') - yHat = P.gmn*mu_w; - miY = min([yHat;y]); - maY = max([yHat;y]); - try - P.handles.axesYhat; - d = get(P.handles.axesYhat,'userdata'); - yHat = yHat(d.goodChannels); - clim = [min(yHat(:))-( max(yHat(:))-min(yHat(:)) )/63,... - max(yHat(:))]; - ZI = griddata(... - d.interp.pos(1,:),d.interp.pos(2,:),full(double(yHat)),... - d.interp.XI,d.interp.YI); - set(d.hi,'Cdata',flipud(ZI)); - caxis(P.handles.axesYhat,clim); - delete(d.hc) - [C,d.hc] = contour(P.handles.axesYhat,flipud(ZI),... - 'linecolor',0.5.*ones(3,1)); - set(P.handles.axesYhat,... - 'userdata',d); - catch - figure(P.handles.hfig) - P.handles.axesYhat = axes(... - 'Position',[0.37 0.3 0.3 0.2],... - 'hittest','off'); - in.ParentAxes = P.handles.axesYhat; - spm_eeg_plotScalpData(yHat,pos,ChanLabel,in); - title(P.handles.axesYhat,'predicted data') - end - try - P.handles.axesYhatY; - catch - figure(P.handles.hfig) - P.handles.axesYhatY = axes(... - 'Position',[0.72 0.3 0.25 0.2],... - 'NextPlot','replace',... - 'box','on'); - end - plot(P.handles.axesYhatY,y,yHat,'.') - set(P.handles.axesYhatY,... - 'nextplot','add') - plot(P.handles.axesYhatY,[miY;maY],[miY;maY],'r') - set(P.handles.axesYhatY,... - 'nextplot','replace') - title(P.handles.axesYhatY,'predicted vs measured data') - axis(P.handles.axesYhatY,'square','tight') - grid(P.handles.axesYhatY,'on') - -end - -if isempty(flag) || isequal(flag,'var') - % plot precision hyperparameters - try - P.handles.axesVar1; - catch - figure(P.handles.hfig) - P.handles.axesVar1 = axes(... - 'Position',[0.05 0.05 0.25 0.2],... - 'NextPlot','add',... - 'box','on'); - end - plot(P.handles.axesVar1,it,log(a1./b1),'.') - title(P.handles.axesVar1,'measurement noise precision (log)') - axis(P.handles.axesVar1,'square','tight') - grid(P.handles.axesVar1,'on') - - try - P.handles.axesVar2; - catch - figure(P.handles.hfig) - P.handles.axesVar2 = axes(... - 'Position',[0.37 0.05 0.25 0.2],... - 'NextPlot','add',... - 'box','on'); - end - plot(P.handles.axesVar2,it,log(a2./b2),'.') - title(P.handles.axesVar2,'ECD orientations precision (log)') - axis(P.handles.axesVar2,'square','tight') - grid(P.handles.axesVar2,'on') - - try - P.handles.axesVar3; - catch - figure(P.handles.hfig) - P.handles.axesVar3 = axes(... - 'Position',[0.72 0.05 0.25 0.2],... - 'NextPlot','add',... - 'box','on'); - end - plot(P.handles.axesVar3,it,log(a3./b3),'.') - title(P.handles.axesVar3,'ECD location precision (log)') - axis(P.handles.axesVar3,'square','tight') - grid(P.handles.axesVar3,'on') - -end - -if ~isempty(flag) && (isequal(flag,'ecd') || isequal(flag,'mGN') ) - try - P.handles.hte(2); - catch - figure(P.handles.hfig) - P.handles.hte(2) = uicontrol('style','text',... - 'units','normalized',... - 'position',[0.2,0.91,0.6,0.02],... - 'backgroundcolor',[1,1,1]); - end - set(P.handles.hte(2),'string',... - ['ECD locations: Modified Gauss-Newton scheme... ',num2str(floor(P.pc)),'%']) -else - try - set(P.handles.hte(2),'string','VB updates on hyperparameters') - end -end - -try - P.handles.hte(1); -catch - figure(P.handles.hfig) - P.handles.hte(1) = uicontrol('style','text',... - 'units','normalized',... - 'position',[0.2,0.94,0.6,0.02],... - 'backgroundcolor',[1,1,1]); -end -try - set(P.handles.hte(1),'string',... - ['Model evidence: p(y|m) >= ',num2str(F(end),'%10.3e\n')]) -end - -try - P.handles.hti; -catch - figure(P.handles.hfig) - P.handles.hti = uicontrol('style','text',... - 'units','normalized',... - 'position',[0.3,0.97,0.4,0.02],... - 'backgroundcolor',[1,1,1],... - 'string',['VB ECD inversion: trial #',num2str(P.ltr(P.ii))]); -end -drawnow - -function back2defaults(e1,e2) -hf = spm_figure('FindWin','Graphics'); -P = get(hf,'userdata'); -try - set(hf,'colormap',P.handles.SPMdefaults.col); - set(hf,'renderer',P.handles.SPMdefaults.renderer); -end +Y.y=y; +U.u=1; + +M.pE = [mu_s;mu_w]; %% prior parameter estimate +M.pC = blkdiag(S_s0,S_w0); % %% prior covariance estimate +M.pC + +M.IS='spm_eeg_wrap_dipfit_vbecd'; + + + + startguess=M.pE; + M.Setup=P; %% pass volume conductor and sensor locations on + M.sc_y=sc_y; %% pass on scaling factor + outsideflag=1; + while outsideflag==1, %% don't use sources which end up outside the head + [starty]=spm_eeg_wrap_dipfit_vbecd(startguess,M,U); + [Ep,Cp,S,F] = spm_nlsi_GN(M,U,Y); + P.Ep=Ep; + P.Cp=Cp; + P.S=S; + P.F=F; + [P.ypost,outsideflag]=spm_eeg_wrap_dipfit_vbecd(P.Ep,M,U); + P.ypost=P.ypost./sc_y; %% scale it back + if outsideflag + disp('running again, one or more dipoles outside head.'); + end; + end; % while +P.post_mu_s=Ep(1:length(mu_s)); +P.post_mu_w=Ep(length(mu_s)+1:end); +P.post_S_s=Cp(1:length(mu_s),1:length(mu_s)); +P.post_S_w=Cp(length(mu_s)+1:end,length(mu_s)+1:end); diff --git a/spm_eeg_inv_vbecd_getLF.m b/spm_eeg_inv_vbecd_getLF.m index 185fbbd..df300d0 100644 --- a/spm_eeg_inv_vbecd_getLF.m +++ b/spm_eeg_inv_vbecd_getLF.m @@ -1,6 +1,8 @@ function [gmn, gm, dgm] = spm_eeg_inv_vbecd_getLF(s, sens, vol, step) % Estimation of the leadfield matrix and its spatial derivative if required % for a set of dipoles used in the VB-ECD solution +%% scales non-eeg data up by a fixed factor (1e8) for compatibility of +%% units % % FORMAT [gmn, gm, dgm] = spm_eeg_inv_vbecd_getLF(s, sens, vol, step) % @@ -16,19 +18,29 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Christophe Phillips & Stefan Kiebel -% $Id: spm_eeg_inv_vbecd_getLF.m 3051 2009-04-06 14:47:09Z jean $ +% $Id: spm_eeg_inv_vbecd_getLF.m 3222 2009-06-25 14:09:48Z gareth $ +%%% now does rank reduction (to 2) for non eeg data + +MEGRANK=2; +MEGSCALE=1e10; %% should be the same as in spm_eeg_inv_vbecd.m + if nargin<4 step = 0; end gm = []; for i = 1:length(s)/3 - [tmp] = forwinv_compute_leadfield(s(1+(i-1)*3:i*3)', sens, vol); + + % mean correction of LF, only for EEG data. if forwinv_senstype(sens, 'eeg') + [tmp] = forwinv_compute_leadfield(s(1+(i-1)*3:i*3)', sens, vol); tmp = tmp - repmat(mean(tmp), size(tmp,1), 1); + else %% reduce rank of leadfield for MEG- assume one direction (radial) is silent + [tmp] = forwinv_compute_leadfield(s(1+(i-1)*3:i*3)', sens, vol,'reducerank',MEGRANK); + tmp=tmp.*MEGSCALE; end gm = [gm tmp]; end @@ -41,7 +53,10 @@ end -if all(step > 0) && nargout == 3 +if all(step > 0) && nargout == 3, +% if isempty(step), +% step=randn(size(s)); +% end; % if isempty dgm = []; for j = 1:length(s) ds = s; @@ -49,10 +64,14 @@ dtmp = []; for i = 1:length(s)/3 if ceil(j/3) == i - [tmp] = forwinv_compute_leadfield(ds(1+(i-1)*3:i*3)', sens, vol); + % mean correction of LF, only for EEG data. if forwinv_senstype(sens, 'eeg') + [tmp] = forwinv_compute_leadfield(ds(1+(i-1)*3:i*3)', sens, vol); tmp = tmp - repmat(mean(tmp), size(tmp,1), 1); + else % MEG + [tmp] = forwinv_compute_leadfield(ds(1+(i-1)*3:i*3)', sens, vol,'reducerank',MEGRANK); + tmp=tmp.*MEGSCALE; end dtmp = [dtmp tmp]; else diff --git a/spm_eeg_inv_vbecd_gui.m b/spm_eeg_inv_vbecd_gui.m index 5575f38..80bb166 100644 --- a/spm_eeg_inv_vbecd_gui.m +++ b/spm_eeg_inv_vbecd_gui.m @@ -1,20 +1,13 @@ function D = spm_eeg_inv_vbecd_gui(D,val) -% GUI function for the VB-ECD inversion +% GUI function for Bayesian ECD inversion % - load the necessary data, if not provided % - fill in all the necessary bits for the VB-ECD inversion routine, -% especially the priors -% - launch the VB_ECD routine, aka. spm_eeg_inv_vbecd +% - launch the B_ECD routine, aka. spm_eeg_inv_vbecd % - displays the results. -% See the comments in the function and the article for further details -% -% Reference: -% Kiebel et al., Variational Bayesian inversion of the equivalent current -% dipole model in EEG/MEG., NeuroImage, 39:728-741, 2008 %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging - -% Christophe Phillips -% $Id: spm_eeg_inv_vbecd_gui.m 3051 2009-04-06 14:47:09Z jean $ +% +% $Id: spm_eeg_inv_vbecd_gui.m 3372 2009-09-08 14:33:45Z gareth $ %% % Load data, if necessary @@ -81,9 +74,7 @@ %% Struct that collects the inputs for vbecd code P = []; -P.modality = 'EEG'; -% Uncomment the line below to try other modalities P.modality = spm_eeg_modality_ui(D, 1, 1); if isfield(D.inv{val}, 'forward') && isfield(D.inv{val}, 'datareg') @@ -103,7 +94,9 @@ [U, L, V] = svd(M1(1:3, 1:3)); M1(1:3,1:3) =U*V'; end - + % disp('Undoing transformation to Tal space !'); + % M1=eye(4) + P.forward.sens = forwinv_transform_sens(M1, P.forward.sens); P.forward.vol = forwinv_transform_vol(M1, P.forward.vol); @@ -154,6 +147,7 @@ ltb = ltb(1):ltb(2); % list of time bins 'tb' to use end + % trial type if D.ntrials>1 msg_tr = ['Trial type number [1 ',num2str(D.ntrials),']']; @@ -165,7 +159,47 @@ end % data, averaged over time window considered -dat_y = squeeze(mean(D(P.Ic,ltb,ltr),2)); + + +EEGscale=1; + +%% SORT OUT EEG UNITS AND CONVERT VALUES TO VOLTS +if strcmp(upper(P.modality),'EEG'), + allunits=strvcat('uV','mV','V'); + allscales=[1e-6, 1e-3, 1]; %% + EEGscale=0; + eegunits = unique(D.units(D.meegchannels('EEG'))); + Neegchans=numel(D.units(D.meegchannels('EEG'))); + for j=1:length(allunits), + if strcmp(deblank(allunits(j,:)),deblank(eegunits)); + EEGscale=allscales(j); + end; % if + end; % for j + +if EEGscale==0, + warning('units unspecified'); + if mean(std(D(P.Ic,ltb,ltr)))>1e-2, + guess_ind=[1 2 3]; + else + guess_ind=[3 2 1]; + end; + msg_str=sprintf('Units of EEG are %s ? (rms=%3.2e)',allunits(guess_ind(1),:),mean(std(D(P.Ic,ltb,ltr)))); + dip_ch = sprintf('%s|%s|%s',allunits(guess_ind(1),:),allunits(guess_ind(2),:),allunits(guess_ind(3),:)); + dip_val = [1,2,3]; + def_opt=1; + unitind= spm_input(msg_str,2,'b',dip_ch,dip_val,def_opt); + %ans=spm_input(msg_str,1,'s','yes'); + allunits(guess_ind(unitind),:) + D = units(D, 1:Neegchans, allunits(guess_ind(unitind),:)); + EEGscale=allscales(guess_ind(unitind)); + D.save; %% Save the new units + end; %if EEGscale==0 + +end; % if eeg data + + +dat_y = squeeze(mean(D(P.Ic,ltb,ltr)*EEGscale,2)); + %% % Other bits of the P structure, apart for priors and #dipoles @@ -173,10 +207,8 @@ P.ltr = ltr; P.Nc = length(P.Ic); -P.Niter = 200; % \_ Using SK default values here... -P.threshold_dF = 1e-2; %1e-4 % / -%% + % Deal with dipoles number and priors %==================================== dip_q = 0; % number of dipole 'elements' added (single or pair) @@ -184,22 +216,25 @@ adding_dips = 1; clear dip_pr -% These are defaults value picked from SK's example data set ran for the -% paper, values should proabably be tweaked a bit... -def_ab_noninfo = [1e-3 1e-12]; -def_ab_info = [3 1e-12]; -corr = .999; + + +priorlocvardefault=[100, 100, 100]; %% location variance default in mm +nopriorlocvardefault=[80*80, 80*80, 80*80]; +nopriormomvardefault=[10, 10, 10]; %% moment variance in nAM +priormomvardefault=[1, 1, 1]; %% + + while adding_dips if dip_q>0, msg_dip =['Add dipoles to ',num2str(dip_c),' or stop?']; - dip_ch = 'Single|Pair|Stop'; + dip_ch = 'Single|Symmetric Pair|Stop'; dip_val = [1,2,0]; def_opt=3; else msg_dip =['Add dipoles to model']; def_opt=1; - dip_ch = 'Single|Pair'; + dip_ch = 'Single|Symmetric Pair'; dip_val = [1,2]; end a_dip = spm_input(msg_dip,2+tr_q+dip_q,'b',dip_ch,dip_val,def_opt); @@ -209,8 +244,8 @@ % add a single dipole to the model dip_q = dip_q+1; dip_pr(dip_q) = struct( 'a_dip',a_dip, ... - 'mu_w0',[],'mu_s0',[],'S_s0',eye(3),'S_w0',eye(3), ... - 'ab20',[],'ab30',[],'Tw',eye(3),'Ts',eye(3)); + 'mu_w0',[],'mu_s0',[],'S_s0',eye(3),'S_w0',eye(3)); + %% 'ab20',[],'ab30',[]); %% ,'Tw', [],'Ts', []); % Location prior spr_q = spm_input('Location prior ?',1+tr_q+dip_q+1,'b', ... 'Informative|Non-info',[1,0],2); @@ -220,36 +255,48 @@ while 1 s0 = spm_input(str, 1+tr_q+dip_q+2,'e',[0 0 0])'; outside = ~forwinv_inside_vol(s0',P.forward.vol); + str2='Prior location variance (mm2)'; + diags_s0 = spm_input(str2, 1+tr_q+dip_q+2,'e',priorlocvardefault)'; + if all(~outside), break, end - str = 'Prior location must be inside head'; - end - dip_pr(dip_q).mu_s0 = s0; - dip_pr(dip_q).ab30 = def_ab_info; + str = 'Prior location must be inside head'; + end + dip_pr(dip_q).mu_s0 = s0; else % no location prior dip_pr(dip_q).mu_s0 = zeros(3,1); - dip_pr(dip_q).ab30 = def_ab_noninfo; + diags_s0= nopriorlocvardefault'; end + %% prior cov matrix for single dipole location (i.e. no crosstalk) + dip_pr(dip_q).S_s0=eye(length(diags_s0)).*repmat(diags_s0,1,length(diags_s0)); + % Moment prior wpr_q = spm_input('Moment prior ?',1+tr_q+dip_q+spr_q+2,'b', ... 'Informative|Non-info',[1,0],2); if wpr_q % informative moment prior - dip_pr(dip_q).mu_w0 = spm_input('Moment prior', ... + w0= spm_input('Moment prior', ... 1+tr_q+dip_q+spr_q+3,'e',[0 0 0])'; - dip_pr(dip_q).ab20 = def_ab_info; + str2='Prior moment variance (nAm2)'; + diags_w0 = spm_input(str2, 1+tr_q+dip_q+2,'e',priormomvardefault)'; + dip_pr(dip_q).mu_w0 =w0; + else % no location prior dip_pr(dip_q).mu_w0 = zeros(3,1); - dip_pr(dip_q).ab20 = def_ab_noninfo; + diags_w0= nopriormomvardefault'; end + %% set up covariance matrix for orientation with no crosstalk terms (for single + %% dip) + dip_pr(dip_q).S_w0=eye(length(diags_w0)).*repmat(diags_w0,1,length(diags_w0)); dip_c = dip_c+1; else % add a pair of symmetric dipoles to the model dip_q = dip_q+1; dip_pr(dip_q) = struct( 'a_dip',a_dip, ... - 'mu_w0',[],'mu_s0',[],'S_s0',eye(6),'S_w0',eye(6), ... - 'ab20',[],'ab30',[],'Tw',eye(6),'Ts',eye(6)); + 'mu_w0',[],'mu_s0',[],'S_s0',eye(6),'S_w0',eye(6)); + %%... + % 'ab20',[],'ab30',[]); %%,'Tw',eye(6),'Ts',eye(6)); % Location prior spr_q = spm_input('Location prior ?',1+tr_q+dip_q+1,'b', ... 'Informative|Non-info',[1,0],2); @@ -257,105 +304,83 @@ % informative location prior str = 'Location prior (right only)'; while 1 - tmp = spm_input(str, 1+tr_q+dip_q+2,'e',[0 0 0])'; - outside = ~forwinv_inside_vol(tmp',P.forward.vol); + tmp_s0 = spm_input(str, 1+tr_q+dip_q+2,'e',[0 0 0])'; + str2='Prior location variance (mm2)'; + tmp_diags_s0 = spm_input(str2, 1+tr_q+dip_q+2,'e',priorlocvardefault)'; + + outside = ~forwinv_inside_vol(tmp_s0',P.forward.vol); if all(~outside), break, end str = 'Prior location must be inside head'; end - tmp = [tmp ; tmp] ; tmp(4) = -tmp(4); - dip_pr(dip_q).mu_s0 = tmp ; - dip_pr(dip_q).ab30 = def_ab_info; + tmp_s0 = [tmp_s0 ; tmp_s0] ; tmp_s0(4) = -tmp_s0(4); + tmp_diags_s0 = [tmp_diags_s0 ; tmp_diags_s0] ; + + dip_pr(dip_q).mu_s0 = tmp_s0 ; + else % no location prior dip_pr(dip_q).mu_s0 = zeros(6,1); - dip_pr(dip_q).ab30 = def_ab_noninfo; - end + tmp_diags_s0 = [nopriorlocvardefault';nopriorlocvardefault']; + end %% end of if informative prior + %% setting up a covariance matrix where there is covariance between + %% the x parameters negatively coupled, y,z positively. + dip_pr(dip_q).S_s0 = eye(length(tmp_diags_s0)).*repmat(tmp_diags_s0,1,length(tmp_diags_s0)); + dip_pr(dip_q).S_s0(4,1)=-dip_pr(dip_q).S_s0(4,4); % reflect in x + dip_pr(dip_q).S_s0(5,2)=dip_pr(dip_q).S_s0(5,5); % maintain y and z + dip_pr(dip_q).S_s0(6,3)=dip_pr(dip_q).S_s0(6,6); + + dip_pr(dip_q).S_s0(1,4)=dip_pr(dip_q).S_s0(4,1); + dip_pr(dip_q).S_s0(2,5)=dip_pr(dip_q).S_s0(5,2); + dip_pr(dip_q).S_s0(3,6)=dip_pr(dip_q).S_s0(6,3); + % Moment prior wpr_q = spm_input('Moment prior ?',1+tr_q+dip_q+spr_q+2,'b', ... 'Informative|Non-info',[1,0],2); if wpr_q % informative moment prior - tmp = spm_input('Moment prior (right only)', ... - 1+tr_q+dip_q+spr_q+3,'e',[1 1 1])'; - tmp = [tmp ; tmp] ; tmp(4) = -tmp(4); + tmp= spm_input('Moment prior (right only)', ... + 1+tr_q+dip_q+spr_q+3,'e',[1 1 1])'; + tmp = [tmp ; tmp] ; tmp(4) = tmp(4); dip_pr(dip_q).mu_w0 = tmp; - dip_pr(dip_q).ab20 = def_ab_info; + + str2='Prior moment variance (nAm2)'; + diags_w0 = spm_input(str2, 1+tr_q+dip_q+spr_q+3,'e',priormomvardefault)'; + tmp_diags_w0=[diags_w0; diags_w0]; + else - % no location prior + % no moment prior dip_pr(dip_q).mu_w0 = zeros(6,1); - dip_pr(dip_q).ab20 = def_ab_noninfo; - end - % Symmetry priors, soft or hard for both location and moment. - mpr_q = spm_input('Symmetry prior ?', ... - 1+tr_q+dip_q+spr_q+wpr_q+3,'b','Soft|Hard',[1,0],1); - if mpr_q - % Soft prior, i.e. parameter correlation - tmp = eye(6); - tmp2 = eye(3); tmp2(1,1) = -1; - tmp(4:6,1:3) = tmp2*corr; - tmp(1:3,4:6) = tmp2*corr; - dip_pr(dip_q).S_s0 = tmp; - dip_pr(dip_q).S_w0 = tmp; - else - % hard prior, i.e. parameter reduction - T = [eye(3) ; eye(3)]; T(4,1) = -1; - dip_pr(dip_q).Tw = .5*T*T'; - dip_pr(dip_q).Ts = .5*T*T'; + tmp_diags_w0 = [nopriormomvardefault'; nopriormomvardefault']; end + %dip_pr(dip_q).S_w0=eye(length(diags_w0)).*repmat(diags_w0,1,length(diags_w0)); + %% couple all orientations positively or leave for now... + dip_pr(dip_q).S_w0 = eye(length(tmp_diags_w0)).*repmat(tmp_diags_w0,1,length(tmp_diags_w0)); + dip_pr(dip_q).S_w0(4,1)=dip_pr(dip_q).S_w0(4,4); % + dip_pr(dip_q).S_w0(5,2)=dip_pr(dip_q).S_w0(5,5); % + dip_pr(dip_q).S_w0(6,3)=dip_pr(dip_q).S_w0(6,6); % + dip_pr(dip_q).S_w0(1,4)=dip_pr(dip_q).S_w0(4,1); % + dip_pr(dip_q).S_w0(2,5)=dip_pr(dip_q).S_w0(5,2); % + dip_pr(dip_q).S_w0(3,6)=dip_pr(dip_q).S_w0(6,3); % + + + dip_c = dip_c+2; end end + str2='Number of iterations'; + Niter = spm_input(str2, 1+tr_q+dip_q+2+1,'e',10)'; + %% -% Get all the priors together and build structure to pass to inv_vbecd ! +% Get all the priors together and build structure to pass to inv_becd + %============================ + priors = struct('mu_w0',cat(1,dip_pr(:).mu_w0), ... 'mu_s0',cat(1,dip_pr(:).mu_s0), ... - 'iS_w0',[],'iS_s0',[], ... - 'Tw',blkdiag(dip_pr(:).Tw), ... - 'Ts',blkdiag(dip_pr(:).Ts),... - 'a20',[],'b20',[], ... - 'a30',[],'b30',[]); -% PROBLEM: -% How to ensure different precision level, i.e. informative vs -% non-informative, for different priors !!! -% Trying to impose that with an a priori scaling of the prior covariance -% matrices but I still think this is not the right way to go... - -tmp_ab20 = cat(1,dip_pr(:).ab20); -% assume that parameter b20/30 have always the same value... -if length(unique(tmp_ab20(:,1)))==1 - % all model element have same prior precision, easy ! - priors.a20 = unique(tmp_ab20(:,1)); - priors.b20 = unique(tmp_ab20(:,2)); - priors.iS_w0 = inv(blkdiag(dip_pr(:).S_w0)); -else - % Not equal "informativeness" -> tricky - % assume 2 levels, and use their ratio to weight variance mtx - priors.a20 = min(tmp_ab20(:,1)); - priors.b20 = min(tmp_ab20(:,2)); - for ii=1:dip_c - tmp_iS{ii} = inv(dip_pr(ii).S_w0*min(tmp_ab20(:,1))/tmp_ab20(ii,1)); - end - priors.iS_w0 = blkdiag(tmp_iS{:}); -end - -tmp_ab30 = cat(1,dip_pr(:).ab30); -if length(unique(tmp_ab30(:,1)))==1 - % all model element have same prior precision, easy ! - priors.a30 = unique(tmp_ab30(:,1)); - priors.b30 = unique(tmp_ab30(:,2)); - priors.iS_s0 = inv(blkdiag(dip_pr(:).S_s0)); -else - % Not equal "informativeness" -> tricky - % assume 2 levels, and use their ratio to weight variance mtx - priors.a30 = min(tmp_ab30(:,1)); - priors.b30 = min(tmp_ab30(:,2)); - for ii=1:dip_q - tmp_iS{ii} = inv(dip_pr(ii).S_s0*min(tmp_ab30(:,1))/tmp_ab30(ii,1)); - end - priors.iS_s0 = blkdiag(tmp_iS{:}); -end + 'S_w0',blkdiag(dip_pr(:).S_w0),'S_s0',blkdiag(dip_pr(:).S_s0)); + + P.priors = priors; %% @@ -383,29 +408,46 @@ P.y = dat_y(:,ii); P.ii = ii; - %----------------------------------------------------------------% - %- Temporary changes in the priors for precisionhyperparameters -% - P.priors.a10 = numel(P.y); - P.priors.b10 = numel(P.y)*1e-18; - P.priors.a20 = 1; - P.priors.b20 = 1e8; % 1e8 - P.priors.a30 = 1e1; - P.priors.b30 = 1e4; - %- These have to be modified to account for informative priors! -% - %----------------------------------------------------------------% + %% set up figures + P.handles.hfig = spm_figure('GetWin','Graphics'); + spm_clf(P.handles.hfig) + P.handles.SPMdefaults.col = get(P.handles.hfig,'colormap'); + P.handles.SPMdefaults.renderer = get(P.handles.hfig,'renderer'); + set(P.handles.hfig,'userdata',P) - fprintf('\nLocalising source nr %d.\n',ii) - P = spm_eeg_inv_vbecd(P); + for j=1:Niter, + Pout(j) = spm_eeg_inv_vbecd(P); + close(gcf); + varresids(j)=var(Pout(j).y-Pout(j).ypost); + pov(j)=100*(1-varresids(j)/var(Pout(j).y)); %% percent variance explained + allF(j)=Pout(j).F; + dip_mom=reshape(Pout(j).post_mu_w,3,length(Pout(j).post_mu_w)/3); + dip_amp(j,:)=sqrt(dot(dip_mom,dip_mom)); + % display + displayVBupdate2(Pout(j).y,pov,allF,Niter,dip_amp,Pout(j).post_mu_w,Pout(j).post_mu_s,Pout(j).post_S_s,Pout(j).post_S_w,P,j,[],Pout(j).F,Pout(j).ypost,[]); + + end; % for j + allF=[Pout.F]; + [maxFvals,maxind]=max(allF); + + P=Pout(maxind); %% take best F % Get the results out. inverse.pst = tb*1e3; inverse.F(ii) = P.F; % free energy - inverse.loc{ii} = reshape(P.post.mu_s,3,dip_c); % loc of dip (3 x n_dip) - inverse.j{ii} = P.post.mu_w; % dipole(s) orient/ampl, in 1 column - inverse.cov_loc{ii} = P.post.S_s; % cov matrix of source location - inverse.cov_j{ii} = P.post.S_w; % cov matrix of source orient/ampl - inverse.exitflag(ii) = P.ok; % Converged (1) or not (0) + inverse.loc{ii} = reshape(P.post_mu_s,3,length(P.post_mu_s)/3); % loc of dip (3 x n_dip) + inverse.j{ii} = P.post_mu_w; % dipole(s) orient/ampl, in 1 column + inverse.cov_loc{ii} = P.post_S_s; % cov matrix of source location + inverse.cov_j{ii} = P.post_S_w; % cov matrix of source orient/ampl + inverse.exitflag(ii) = 1; % Converged (1) or not (0) inverse.P{ii} = P; % save all kaboodle too. + %% show final result + pause(1); + + + spm_clf(P.handles.hfig) + displayVBupdate2(Pout(maxind).y,pov,allF,Niter,dip_amp,Pout(maxind).post_mu_w,Pout(maxind).post_mu_s,Pout(maxind).post_S_s,Pout(maxind).post_S_w,P,j,[],Pout(maxind).F,Pout(maxind).ypost,maxind); + % end D.inv{val}.inverse = inverse; @@ -413,8 +455,266 @@ % Save results and display %------------------------- save(D) -P.handles.hfig = spm_figure('GetWin','Graphics'); -close(P.handles.hfig) -spm_eeg_inv_vbecd_disp('init',D) + return + + + + + +function [P] = displayVBupdate2(y,pov_iter,F_iter,maxit,dipamp_iter,mu_w,mu_s,S_s,S_w,P,it,flag,F,yHat,maxind) + +%% yHat is estimate of y based on dipole position +if ~exist('flag','var') + flag = []; +end +if ~exist('maxind','var') + maxind = []; +end + +if isempty(flag) || isequal(flag,'ecd') + % plot dipoles + try + opt.ParentAxes = P.handles.axesECD; + opt.hfig = P.handles.hfig; + opt.handles.hp = P.handles.hp; + opt.handles.hq = P.handles.hq; + opt.handles.hs = P.handles.hs; + opt.handles.ht = P.handles.ht; + opt.query = 'replace'; + catch + P.handles.axesECD = axes(... + 'parent',P.handles.hfig,... + 'Position',[0.13 0.55 0.775 0.4],... + 'hittest','off',... + 'visible','off',... + 'deleteFcn',@back2defaults); + opt.ParentAxes = P.handles.axesECD; + opt.hfig = P.handles.hfig; + end + w = reshape(mu_w,3,[]); + s = reshape(mu_s, 3, []); + [out] = spm_eeg_displayECD(... + s,w,reshape(diag(S_s),3,[]),[],opt); + P.handles.hp = out.handles.hp; + P.handles.hq = out.handles.hq; + P.handles.hs = out.handles.hs; + P.handles.ht = out.handles.ht; +end + +% plot data and predicted data +pos = P.forward.sens.prj; +ChanLabel = P.channels; +in.f = P.handles.hfig; +in.noButtons = 1; +try + P.handles.axesY; +catch + figure(P.handles.hfig) + P.handles.axesY = axes(... + 'Position',[0.02 0.3 0.3 0.2],... + 'hittest','off'); + in.ParentAxes = P.handles.axesY; + spm_eeg_plotScalpData(y,pos,ChanLabel,in); + title(P.handles.axesY,'measured data') +end +if isempty(flag) || isequal(flag,'data') || isequal(flag,'ecd') + %yHat = P.gmn*mu_w; + miY = min([yHat;y]); + maY = max([yHat;y]); + try + P.handles.axesYhat; + d = get(P.handles.axesYhat,'userdata'); + yHat = yHat(d.goodChannels); + clim = [min(yHat(:))-( max(yHat(:))-min(yHat(:)) )/63,... + max(yHat(:))]; + ZI = griddata(... + d.interp.pos(1,:),d.interp.pos(2,:),full(double(yHat)),... + d.interp.XI,d.interp.YI); + set(d.hi,'Cdata',flipud(ZI)); + caxis(P.handles.axesYhat,clim); + delete(d.hc) + [C,d.hc] = contour(P.handles.axesYhat,flipud(ZI),... + 'linecolor',0.5.*ones(3,1)); + set(P.handles.axesYhat,... + 'userdata',d); + catch + figure(P.handles.hfig) + P.handles.axesYhat = axes(... + 'Position',[0.37 0.3 0.3 0.2],... + 'hittest','off'); + in.ParentAxes = P.handles.axesYhat; + spm_eeg_plotScalpData(yHat,pos,ChanLabel,in); + title(P.handles.axesYhat,'predicted data') + end + try + P.handles.axesYhatY; + catch + figure(P.handles.hfig) + P.handles.axesYhatY = axes(... + 'Position',[0.72 0.3 0.25 0.2],... + 'NextPlot','replace',... + 'box','on'); + end + plot(P.handles.axesYhatY,y,yHat,'.') + set(P.handles.axesYhatY,... + 'nextplot','add') + plot(P.handles.axesYhatY,[miY;maY],[miY;maY],'r') + set(P.handles.axesYhatY,... + 'nextplot','replace') + title(P.handles.axesYhatY,'predicted vs measured data') + axis(P.handles.axesYhatY,'square','tight') + grid(P.handles.axesYhatY,'on') + +end + + +if isempty(flag) || isequal(flag,'var') + % plot precision hyperparameters + try + P.handles.axesVar1; + catch + figure(P.handles.hfig) + P.handles.axesVar1 = axes(... + 'Position',[0.05 0.05 0.25 0.2],... + 'NextPlot','replace',... + 'box','on'); + end + plot(P.handles.axesVar1,F_iter,'o-'); + if ~isempty(maxind), + hold on; + h=plot(P.handles.axesVar1,maxind,F_iter(maxind),'rd'); + set(h,'linewidth',4); + end; + set(P.handles.axesVar1,'Xlimmode','manual'); + set(P.handles.axesVar1,'Xlim',[1 maxit]); + set(P.handles.axesVar1,'Xtick',1:maxit); + set(P.handles.axesVar1,'Xticklabel',num2str([1:maxit]')); + set(P.handles.axesVar1,'Yticklabel',''); + title(P.handles.axesVar1,'Free energy ') + axis(P.handles.axesVar1,'square'); + set(P.handles.axesVar1,'Ylimmode','auto'); %,'tight') + + grid(P.handles.axesVar1,'on') + + try + P.handles.axesVar2; + catch + figure(P.handles.hfig) + P.handles.axesVar2 = axes(... + 'Position',[0.37 0.05 0.25 0.2],... + 'NextPlot','replace',... + 'box','on'); + end + + + plot(P.handles.axesVar2,pov_iter,'*-') + if ~isempty(maxind), + hold on; + h=plot(P.handles.axesVar2,maxind,pov_iter(maxind),'rd'); + set(h,'linewidth',4); + end; + set(P.handles.axesVar2,'Xlimmode','manual'); + set(P.handles.axesVar2,'Xlim',[1 maxit]); + set(P.handles.axesVar2,'Xtick',1:maxit); + set(P.handles.axesVar2,'Xticklabel',num2str([1:maxit]')); + set(P.handles.axesVar2,'Ylimmode','manual'); %,'tight') + set(P.handles.axesVar2,'Ylim',[0 100]); + set(P.handles.axesVar2,'Ytick',[0:20:100]); + set(P.handles.axesVar2,'Yticklabel',num2str([0:20:100]')); + + %set(P.handles.axesVar2,'Yticklabel',''); + title(P.handles.axesVar2,'Percent variance explained'); + axis(P.handles.axesVar2,'square'); + + + grid(P.handles.axesVar2,'on') + + + try + P.handles.axesVar3; + catch + figure(P.handles.hfig) + P.handles.axesVar3 = axes(... + 'Position',[0.72 0.05 0.25 0.2],... + 'NextPlot','replace',... + 'box','on'); + end + plot(P.handles.axesVar3,1:it,dipamp_iter','o-'); + if ~isempty(maxind), + hold on; + h=plot(P.handles.axesVar3,maxind,dipamp_iter(maxind,:)','rd'); + set(h,'linewidth',4); + end; + + set(P.handles.axesVar3,'Xlimmode','manual'); + set(P.handles.axesVar3,'Xlim',[1 maxit]); + set(P.handles.axesVar3,'Xtick',1:maxit); + set(P.handles.axesVar3,'Xticklabel',num2str([1:maxit]')); + set(P.handles.axesVar3,'Yticklabel',''); + title(P.handles.axesVar3,'Dipole amp (nAm) ') + axis(P.handles.axesVar3,'square'); + set(P.handles.axesVar3,'Ylimmode','auto'); %,'tight') + grid(P.handles.axesVar3,'on') + + + +end + + + +if ~isempty(flag) && (isequal(flag,'ecd') || isequal(flag,'mGN') ) + try + P.handles.hte(2); + catch + figure(P.handles.hfig) + P.handles.hte(2) = uicontrol('style','text',... + 'units','normalized',... + 'position',[0.2,0.91,0.6,0.02],... + 'backgroundcolor',[1,1,1]); + end + set(P.handles.hte(2),'string',... + ['ECD locations: Modified Gauss-Newton scheme... ',num2str(floor(P.pc)),'%']) +else + try + set(P.handles.hte(2),'string','VB updates on hyperparameters') + end +end + +try + P.handles.hte(1); +catch + figure(P.handles.hfig) + P.handles.hte(1) = uicontrol('style','text',... + 'units','normalized',... + 'position',[0.2,0.94,0.6,0.02],... + 'backgroundcolor',[1,1,1]); +end +try + set(P.handles.hte(1),'string',... + ['Model evidence: p(y|m) >= ',num2str(F(end),'%10.3e\n')]) +end + +try + P.handles.hti; +catch + figure(P.handles.hfig) + P.handles.hti = uicontrol('style','text',... + 'units','normalized',... + 'position',[0.3,0.97,0.4,0.02],... + 'backgroundcolor',[1,1,1],... + 'string',['VB ECD inversion: trial #',num2str(P.ltr(P.ii))]); +end +drawnow + +function back2defaults(e1,e2) +hf = spm_figure('FindWin','Graphics'); +P = get(hf,'userdata'); +try + set(hf,'colormap',P.handles.SPMdefaults.col); + set(hf,'renderer',P.handles.SPMdefaults.renderer); +end + + + diff --git a/spm_eeg_inv_visu3D_api.m b/spm_eeg_inv_visu3D_api.m index ac44da0..b54ce71 100644 --- a/spm_eeg_inv_visu3D_api.m +++ b/spm_eeg_inv_visu3D_api.m @@ -10,7 +10,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jeremie Mattout -% $Id: spm_eeg_inv_visu3D_api.m 2932 2009-03-24 11:05:15Z guillaume $ +% $Id: spm_eeg_inv_visu3D_api.m 3323 2009-08-14 11:29:27Z vladimir $ % INITIALISATION CODE %-------------------------------------------------------------------------- @@ -81,7 +81,7 @@ function spm_eeg_inv_visu3D_api_OpeningFcn(hObject, eventdata, handles) try, val = D.val; catch, val = 1; D.val = 1; end try, con = D.con; catch, con = 1; D.con = 1; end -if D.con > length(D.inv{D.val}.inverse.J) +if (D.con == 0) || (D.con > length(D.inv{D.val}.inverse.J)) con = 1; D.con = 1; end handles.D = D; @@ -136,7 +136,7 @@ function spm_eeg_inv_visu3D_api_OpeningFcn(hObject, eventdata, handles) handles.pred_data = U*L*J(Is,:); catch - warndlg({'Please invert your model';'inverse.J not found'}); + warndlg({'Please invert your model';'inverse solution not valid'}); return end diff --git a/spm_eeg_invert.m b/spm_eeg_invert.m index 54dac0e..79f01f3 100644 --- a/spm_eeg_invert.m +++ b/spm_eeg_invert.m @@ -47,7 +47,7 @@ % % This routine solves ill-posed linear models of the following form % -% [AY{1}...AY{n}] = L(1} * [J{1}...J{1}] + [e{1}...e{n}] +% [AY{1}...AY{n}] = L(1} * [J{1}...J{n}] + [e{1}...e{n}] % % where AY{i} are the spatially normalized or adjusted data from subject i % that would have been seen if the lead-field L{i} = L{1}. The ensuing @@ -68,19 +68,16 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_eeg_invert.m 2720 2009-02-09 19:50:46Z vladimir $ +% $Id: spm_eeg_invert.m 3380 2009-09-10 09:32:50Z rik $ % check whether this is a group inversion %-------------------------------------------------------------------------- if ~iscell(D), D = {D}; end -Nl = length(D); % number of forward models +Nl = length(D); % number of forward models % D - SPM data structure %========================================================================== -if nargin == 1 - val = 1; -end - +if nargin == 1, val = 1; end inverse = D{1}.inv{D{1}.val}.inverse; % defaults @@ -89,16 +86,16 @@ try, s = inverse.smooth; catch, s = 0.6; end try, Np = inverse.Np; catch, Np = 256; end try, Nm = inverse.Nm; catch, Nm = 128; end -try, Nr = inverse.Nr; catch, Nr = 8; end +try, Nr = inverse.Nr; catch, Nr = 16; end try, xyz = inverse.xyz; catch, xyz = [0 0 0]; end try, rad = inverse.rad; catch, rad = 128; end try, lpf = inverse.lpf; catch, lpf = 1; end -try, hpf = inverse.hpf; catch, hpf = 256; end +try, hpf = inverse.hpf; catch, hpf = 48; end try, sdv = inverse.sdv; catch, sdv = 4; end try, Han = inverse.Han; catch, Han = 1; end try, Na = inverse.Na; catch, Na = 1024; end try, woi = inverse.woi; catch, woi = []; end - + try modality = inverse.modality; catch @@ -113,24 +110,21 @@ %-------------------------------------------------------------------------- for i = 1:Nl [L D{i}] = spm_eeg_lgainmat(D{i}); - + Ic{i} = setdiff(meegchannels(D{i}, modality), badchannels(D{i})); Nd(i) = size(L,2); % number of dipoles - - Ic{i} = setdiff(meegchannels(D{i}, modality), badchannels(D{i})); + Nc(i) = length(Ic{i}); % number of channels if isempty(Ic{i}) error(['The specified modality is missing from file ' D{i}.fname]); end - - Nc(i) = length(Ic{i}); % number of channels if any(diff(Nd)) - warndlg('please ensure the number of dipoles is the same') + warndlg('please ensure subjects have the same number of dipoles') return end end - -% chaeck restriction; assume radii are the same for all VOI + +% check restriction; assume radii are the same for all VOI %-------------------------------------------------------------------------- Nd = Nd(1); % number of dipoles Nv = size(xyz,1); % number of VOI @@ -160,10 +154,11 @@ clear Qi QG = QG.*(QG > exp(-8)); QG = QG*QG; +clear A GL fprintf(' - done\n') -% Restrict source space +% Restrict source space by eliminating dipoles %-------------------------------------------------------------------------- Is = sparse(Nd,1); for i = 1:Nv @@ -176,7 +171,8 @@ vert = vert(Is,:); QG = QG(Is,Is); Ns = length(Is); - + + % Check for null space over sensors and remove it %-------------------------------------------------------------------------- for i = 1:Nl @@ -188,22 +184,43 @@ end end +% check for (eg, empty-room) sensor components (in Qe{1}) +%========================================================================== +for i = 1:Nl + try + QE{i} = spm_cov2corr(D{i}.inv{D{i}.val}.inverse.Qe{1}); + if length(QE{i}) ~= Nc(i) + error('specified error component for subject %d does not match number of channels (%d)\n',i,Nc(i)) + end + catch + QE{i} = 1; + end +end + + +% spatial projectors (adjusting for different Lead-fields) +%========================================================================== + % Project to channel modes (U); Checking for null space over sensors %-------------------------------------------------------------------------- -TOL = 16; Nmax = Nm; L = R{1}*spm_eeg_lgainmat(D{1},Is,D{1}.chanlabels(Ic{1})); -U{1} = spm_svd(L*L',exp(-TOL)); +U{1} = spm_svd(L*L',exp(-16)); Nm = min(size(U{1},2),Nmax); U{1} = U{1}(:,1:Nm); -UL{1} = U{1}'*L; +A{1} = U{1}'; +UL = U{1}'*L; + +% Spatial projectors for subsequent subjects +%-------------------------------------------------------------------------- for i = 2:Nl L = R{i}*spm_eeg_lgainmat(D{i},Is,D{i}.chanlabels(Ic{i})); - U{i} = spm_svd(L*L',exp(-TOL)); + U{i} = spm_svd(L*L',exp(-16)); Nm(i) = min(size(U{i},2),Nmax); U{i} = U{i}(:,1:Nm(i)); - UL{i} = U{i}'*L; + A{i} = UL*pinv(full(L)); end + fprintf('Using %i spatial modes\n',Nm) @@ -212,16 +229,17 @@ % Temporal parameters %========================================================================== -% force low-pass filtering for MEG -%-------------------------------------------------------------------------- -%if strcmp(D{1}.inv{val}.modality,'MEG'), hpf = 48; end - - -Nrmax = Nr; -A = {}; -AY = {}; +Nrmax = Nr; % number of temporal modes +Nn = sparse(1,Nl); % total number of samples +Qe{1} = sparse(0); % error covariance +AY = {}; % aligned data for MVB +AYYA = sparse(0); % aligned covariance for ReML for i = 1:Nl + % covariance for i-th subject + %---------------------------------------------------------------------- + UYYU{i} = sparse(0); + % Time-window of interest %---------------------------------------------------------------------- if isempty(woi) @@ -233,13 +251,13 @@ It{i} = max(1,It{i}(1)):min(It{i}(end), length(D{i}.time)); It{i} = fix(It{i}); - % Peri-stimulus time + % Peristimulus time %---------------------------------------------------------------------- pst{i} = 1000*D{i}.time; - pst{i} = pst{i}(It{i}); % peristimulus time (ms) - dur = (pst{i}(end) - pst{i}(1))/1000; % duration (s) - dct{i} = (It{i} - It{i}(1))/2/dur; % DCT frequencies (Hz) - Nb(i) = length(It{i}); % number of time bins + pst{i} = pst{i}(It{i}); % peristimulus time (ms) + dur = (pst{i}(end) - pst{i}(1))/1000; % duration (s) + dct{i} = (It{i} - It{i}(1))/2/dur; % DCT frequencies (Hz) + Nb(i) = length(It{i}); % number of time bins % Serial correlations %---------------------------------------------------------------------- @@ -254,10 +272,11 @@ T = T(:,j); dct{i} = dct{i}(j); + % get data (with temporal filtering) %====================================================================== - % get trials and channels + % get trials %---------------------------------------------------------------------- try trial = D{i}.inv{D{i}.val}.inverse.trials; @@ -265,60 +284,81 @@ trial = unique(D{i}.conditions); end Nt(i) = length(trial); - + + % get temporal covariance (Y'*Y) to find temporal modes + %---------------------------------------------------------------------- for j = 1:Nt(i) - Y{i,j} = sparse(0); - c = D{i}.pickconditions(trial{j}); + YTY = sparse(0); + c = D{i}.pickconditions(trial{j}); Ne = length(c); for k = 1:Ne - Y{i,j} = Y{i,j} + R{i}*squeeze(D{i}(Ic{i},It{i},c(k)))*T/Ne; + Yk = R{i}*squeeze(D{i}(Ic{i},It{i},c(k))); + YTY = YTY + Yk'*Yk; end end % Hanning operator (if requested) %---------------------------------------------------------------------- if Han - W = T'*sparse(1:Nb(i),1:Nb(i),spm_hanning(Nb(i)))*T; - WY = W'*spm_cat(Y(i,:)')'; - else - WY = spm_cat(Y(i,:)')'; + W = sparse(1:Nb(i),1:Nb(i),spm_hanning(Nb(i))); + YTY = W'*YTY*W; end - % temporal projector (at most 8 modes) S = T*v - %====================================================================== - [v u] = spm_svd(WY,1); % temporal modes - Nr(i) = min(size(v,2),Nrmax); % number of temporal modes - v = v(:, 1:Nr(i)); - u = u(1:Nr(i),1:Nr(i)); - VE(i) = sum(sum(u.^2))/sum(sum(WY.^2)); % variance explained - S{i} = T*v; % temporal projector - iV{i} = inv(S{i}'*qV{i}*S{i}); % precision (mode) - Vq{i} = S{i}*iV{i}*S{i}'; % precision (time) - - % spatial projector (adjusting for different Lead-fields) + % temporal projector (at most Nrmax modes) S = T*V %====================================================================== - A{i} = UL{1}*pinv(full(UL{i}))*U{i}'; - - % spatially adjust, temporally whiten and scale under i.i.d priors + YTY = T'*YTY*T; + [V E] = spm_svd(YTY,exp(-8)); + E = diag(E)/trace(YTY); + Nr(i) = min(length(E),Nrmax); % number of temporal modes + V = V(:,1:Nr(i)); % temporal modes + VE(i) = sum(E(1:Nr(i))); % variance explained + S{i} = T*V; % temporal projector + qP = inv(S{i}'*qV{i}*S{i}); % precision (mode) + Vq{i} = S{i}*qP*S{i}'; % precision (time) + + + % get temporal covariance (Y*Y') for Gaussian process model %---------------------------------------------------------------------- for j = 1:Nt(i) - Y{i,j} = Y{i,j}*v; - AY{end + 1} = A{i}*Y{i,j}*sqrtm(iV{i}); - AY{end} = AY{end}*sqrt(trace(A{i}*A{i}'))/trace(AY{end}'*AY{end}); + UY{i,j} = sparse(0); + c = D{i}.pickconditions(trial{j}); + Ne = length(c); + for k = 1:Ne + + % get trial-specific data + %-------------------------------------------------------------- + y = squeeze(D{i}(Ic{i},It{i},c(k)))*S{i}; + Uy = U{i}'*y; + Ay = A{i} *y; + Nn(i) = Nn(i) + Nr(i); + + % accumulate first and second-order statistics + %-------------------------------------------------------------- + UY{i,j} = UY{i,j} + Uy; + UYYU{i} = UYYU{i} + Uy*Uy'; + + % and repeat for aligned data + %-------------------------------------------------------------- + AY{end + 1} = Ay; + AYYA = AYYA + Ay*Ay'; + + end end - % create sensor components (Qe) + % augment sensor component (Qe); assuming equal noise over subjects %---------------------------------------------------------------------- - Qe{i} = A{i}*A{i}'; + Qe{1} = Qe{1} + A{i}*QE{i}*A{i}'; end -% adjusted data and sample covariance +% scale data and sample covariances %-------------------------------------------------------------------------- -[Y scale] = spm_cond_units(Y); -AY = spm_cat(AY); -YY = AY*AY'; -G = UL{1}; +[UY scale] = spm_cond_units(UY); +AY = spm_cat(AY)*scale; +AYYA = AYYA*(scale^2); +for i = 1:Nl + UYYU{i} = UYYU{i}*(scale^2); +end fprintf('Using %i temporal modes\n',Nr) fprintf('accounting for %0.2f percent variance\n',full(100*VE)) @@ -341,22 +381,22 @@ %-------------------------------------------------------------- q = QG(:,Ip(i)); Qp{end + 1}.q = q; - LQpL{end + 1}.q = G*q; + LQpL{end + 1}.q = UL*q; % right hemisphere %-------------------------------------------------------------- [d j] = min(sum([vert(:,1) + vert(Ip(i),1), ... - vert(:,2) - vert(Ip(i),2), ... - vert(:,3) - vert(Ip(i),3)].^2,2)); + vert(:,2) - vert(Ip(i),2), ... + vert(:,3) - vert(Ip(i),3)].^2,2)); q = QG(:,j); Qp{end + 1}.q = q; - LQpL{end + 1}.q = G*q; + LQpL{end + 1}.q = UL*q; % bilateral %-------------------------------------------------------------- q = QG(:,Ip(i)) + QG(:,j); Qp{end + 1}.q = q; - LQpL{end + 1}.q = G*q; + LQpL{end + 1}.q = UL*q; end @@ -365,12 +405,12 @@ % create minimum norm prior %------------------------------------------------------------------ Qp{1} = speye(Ns,Ns); - LQpL{1} = G*G'; + LQpL{1} = UL*UL'; % add smoothness component in source space %------------------------------------------------------------------ Qp{2} = QG; - LQpL{2} = G*Qp{2}*G'; + LQpL{2} = UL*Qp{2}*UL'; case {'IID','MMN'} @@ -378,10 +418,43 @@ % create minimum norm prior %------------------------------------------------------------------ Qp{1} = speye(Ns,Ns); - LQpL{1} = G*G'; + LQpL{1} = UL*UL'; + +end + +% augment with exogenous (eg, fMRI) source priors in pQ +%========================================================================== +try, pQ; catch, pQ = {}; end +for i = 1:length(pQ) + + switch(type) + + case {'MSP','GS','ARD'} + %------------------------------------------------------------------ + if isvector(pQ{i}) && length(pQ{i}) == Ns + Qp{end + 1}.q = pQ{n}(:); + LQpL{end + 1}.q = UL*Qp{end}.q; + else + error('Using MSP(GS/ARD) please supply spatial priors as vectors') + end + + case {'LOR','COH','IID','MMN'} + %------------------------------------------------------------------ + if isvector(pQ{i}) && length(pQ{i}) == Ns + Qp{end + 1} = sparse(diag(pQ{i})); + LQpL{end + 1} = UL*Qp{end}*UL'; + elseif size(pQ{i},1) == Ns && size(pQ{i},2) == Ns + Qp{end + 1} = pQ{i}; + LQpL{end + 1} = UL*Qp{end}*UL'; + else + error('spatial priors are the wrong size') + end + end + fprintf('Using %d spatial priors provided...\n',length(pQ)) end + % Inverse solution %========================================================================== QP = {}; @@ -402,7 +475,7 @@ % Multivariate Bayes %------------------------------------------------------------------ - MVB = spm_mvb(AY,G,[],Q,Qe,16); + MVB = spm_mvb(AY,UL,[],Q,Qe,16); % Spatial priors (QP); eliminating minor patterns %------------------------------------------------------------------ @@ -413,64 +486,86 @@ end qp = Q(:,j)*MVB.cp(j,j)*Q(:,j)'; - % Accmulate empirical priors + % Accumulate empirical priors %------------------------------------------------------------------ QP{end + 1} = qp; end switch(type) - - case {'MSP','ARD','IID','MMN','LOR','COH'} - % or ReML - ARD - %---------------------------------------------------------------------- - qp = sparse(0); - Q = {Qe{:} LQpL{:}}; - [Cy,h,Ph,F] = spm_sp_reml(YY,[],Q,sum(Nr)*sum(Nt)); + case {'MSP','ARD'} - % Spatial priors (QP) - %---------------------------------------------------------------------- - Ne = length(Qe); - Np = length(Qp); - hp = h([1:Np] + Ne); - for i = 1:Np - if hp(i) > max(hp)/128; - try + % or ReML - ARD + %------------------------------------------------------------------ + qp = sparse(0); + Q = {Qe{:} LQpL{:}}; + [Cy,h,Ph,F] = spm_sp_reml(AYYA,[],Q,sum(Nn)); + + % Spatial priors (QP) + %------------------------------------------------------------------ + Ne = length(Qe); + Np = length(Qp); + hp = h([1:Np] + Ne); + for i = 1:Np + if hp(i) > max(hp)/128; qp = qp + hp(i)*Qp{i}.q*Qp{i}.q'; - catch - qp = qp + hp(i)*Qp{i}; end end - end - - % Accmulate empirical priors - %---------------------------------------------------------------------- - QP{end + 1} = qp; - + + % Accumulate empirical priors + %------------------------------------------------------------------ + QP{end + 1} = qp; + + + case {'IID','MMN','LOR','COH'} + + % or ReML - ARD + %------------------------------------------------------------------ + qp = sparse(0); + Q = {Qe{:} LQpL{:}}; + [Cy,h,Ph,F] = spm_reml_sc(AYYA,[],Q,sum(Nn)); + + % Spatial priors (QP) + %------------------------------------------------------------------ + Ne = length(Qe); + Np = length(Qp); + hp = h([1:Np] + Ne); + for i = 1:Np + qp = qp + hp(i)*Qp{i}; + end + + % Accumulate empirical priors + %------------------------------------------------------------------ + QP{end + 1} = qp; + end -% re-estimate (one subject at a time) + +% Re-estimate (one subject at a time) %========================================================================== for i = 1:Nl % using spatial priors from group analysis %---------------------------------------------------------------------- - L = R{i}*spm_eeg_lgainmat(D{i}, Is, D{i}.chanlabels(Ic{i})); - Qe = R(i); - Ne = length(Qe); + L = U{i}'*spm_eeg_lgainmat(D{i}, Is, D{i}.chanlabels(Ic{i})); + Qe = U{i}'*QE{i}*U{i}; + Ne = 1; Np = length(QP); LQpL = {}; for j = 1:Np LQpL{j} = L*QP{j}*L'; end - Q = {Qe{:} LQpL{:}}; - YY = spm_cat(Y(i,:))*kron(speye(Nt(i),Nt(i)),iV{i})*spm_cat(Y(i,:))'; + Q = {Qe LQpL{:}}; % re-do ReML %---------------------------------------------------------------------- - [Cy,h,Ph,F] = spm_reml_sc(YY,[],Q,Nr(i)*Nt(i)); + [Cy,h,Ph,F] = spm_reml_sc(UYYU{i},[],Q,Nn(i)); + + % Data ID + %========================================================================== + ID = spm_data_id(UYYU{i}); % Covariances: sensor space - Ce and source space - L*Cp %---------------------------------------------------------------------- @@ -499,12 +594,12 @@ % trial-type specific source reconstruction %------------------------------------------------------------------ - J{j} = M*Y{i,j}; + J{j} = M*UY{i,j}; % sum of squares %------------------------------------------------------------------ - SSR = SSR + sum(var((Y{i,j} - L*J{j}),0,2)); - SST = SST + sum(var(Y{i,j},0,2)); + SSR = SSR + sum(var((UY{i,j} - L*J{j}),0,2)); + SST = SST + sum(var( UY{i,j},0,2)); end @@ -523,13 +618,13 @@ inverse.M = M; % MAP projector (reduced) inverse.J = J; % Conditional expectation - inverse.Y = Y(i,:); % ERP data (reduced) + inverse.Y = UY(i,:); % ERP data (reduced) inverse.L = L; % Lead-field (reduced) - inverse.R = speye(Nc(i),Nc(i)); % Re-referencing matrix + inverse.R = R{i}; % Re-referencing matrix inverse.qC = Cq; % spatial covariance inverse.qV = Vq{i}; % temporal correlations inverse.T = S{i}; % temporal subspace - inverse.U = speye(Nc(i),Nc(i)); % spatial subspace + inverse.U = U{i}; % spatial subspace inverse.Is = Is; % Indices of active dipoles inverse.It = It{i}; % Indices of time bins inverse.Ic = Ic{i}; % Indices of good channels @@ -537,6 +632,7 @@ inverse.pst = pst{i}; % peristimulus time inverse.dct = dct{i}; % frequency range inverse.F = F; % log-evidence + inverse.ID = ID; % data ID inverse.R2 = R2; % variance accounted for (%) inverse.VE = VE(i); % variance explained inverse.woi = w{i}; % time-window inverted diff --git a/spm_eeg_invert_fuse.m b/spm_eeg_invert_fuse.m index 90cd453..f2d235c 100644 --- a/spm_eeg_invert_fuse.m +++ b/spm_eeg_invert_fuse.m @@ -61,16 +61,15 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_eeg_invert_fuse.m 3139 2009-05-21 18:37:29Z karl $ -[D, val] = spm_eeg_inv_check(varargin{:}); - -[mod, list] = modality(D, 1, 1); - -Nl = numel(list); % number of forward models +% $Id: spm_eeg_invert_fuse.m 3177 2009-06-03 08:47:41Z vladimir $ + % D - SPM MEEG object %========================================================================== -inverse = D.inv{val}.inverse; +[D, val] = spm_eeg_inv_check(varargin{:}); +[mod, list] = modality(D, 1, 1); +Nl = numel(list); % number of forward models +inverse = D.inv{val}.inverse; % defaults %-------------------------------------------------------------------------- @@ -105,10 +104,10 @@ %-------------------------------------------------------------------------- for i = 1:Nl - Ic{i} = setdiff(meegchannels(D, list{i}), D.badchannels); + Ic{i} = setdiff(meegchannels(D, list{i}), D.badchannels); [L{i}, D] = spm_eeg_lgainmat(D, [], D.chanlabels(Ic{i})); - Nc(i) = size(L{i},1); % number of channels - Nd(i) = size(L{i},2); % number of dipoles + Nc(i) = size(L{i},1); % number of channels + Nd(i) = size(L{i},2); % number of dipoles end % check the source space is the same @@ -136,8 +135,8 @@ Is = sparse(Nd,1); for i = 1:Nv Iv = sum([vert(:,1) - xyz(i,1), ... - vert(:,2) - xyz(i,2), ... - vert(:,3) - xyz(i,3)].^2,2) < rad(i)^2; + vert(:,2) - xyz(i,2), ... + vert(:,3) - xyz(i,3)].^2,2) < rad(i)^2; Is = Is | Iv; end Is = find(Is); @@ -173,7 +172,7 @@ %-------------------------------------------------------------------------- if isempty(Nm); Nm = zeros(1,Nl); -elseif length(Nm)==1; +elseif length(Nm) == 1; Nm = ones(1,Nl)*Nm; end for i = 1:Nl @@ -184,7 +183,7 @@ U{i} = spm_svd(L{i}*L{i}',exp(NmTOL)); Nm(i) = size(U{i},2); else - U{i} = spm_svd(L{i}*L{i}',-Inf); + U{i} = spm_svd(L{i}*L{i}',exp(-32)); U{i} = U{i}(:,1:Nm(i)); end UL{i} = U{i}'*L{i}; @@ -194,18 +193,18 @@ %========================================================================== % Temporal parameters %========================================================================== -Nt = length(trial); % number of trial types -if isempty(Nr); - Nr = zeros(1,Nl); -elseif length(Nr)~=Nl - Nr = ones(1,Nl)*Nr(1); +Nt = length(trial); % number of trial types +if isempty(Nr); + Nr = zeros(1,Nl); +elseif length(Nr) ~= Nl + Nr = ones(1,Nl)*Nr(1); end - + % Peristimulus time-window %-------------------------------------------------------------------------- w = [D.time(1) D.time(end)]*1000; -if length(woi) +if ~isempty(woi) woi(1) = max(woi(1),w(1)); woi(2) = min(woi(2),w(2)); else @@ -214,12 +213,11 @@ % get time bins to sample %-------------------------------------------------------------------------- -It = D.indsample(woi/1000); -Nb = fix(It(end) - It(1) + 1); % number of time bins +It = D.indsample(woi/1000); +Nb = fix(It(end) - It(1) + 1); % number of time bins +It = fix(linspace(It(1),It(2),Nb)); -It = fix(linspace(It(1),It(2),Nb)); - % Peri-stimulus time %-------------------------------------------------------------------------- woi = fix(woi); @@ -236,7 +234,7 @@ % Frequency subspace %-------------------------------------------------------------------------- T = spm_dctmtx(Nb,Nb); -j = find( (dct >= lpf) & (dct <= hpf) ); +j = find( (dct >= lpf) & (dct <= hpf) ); T = T(:,j); dct = dct(j); @@ -256,7 +254,7 @@ %---------------------------------------------------------------------- for j = 1:Nt UY{i,j} = sparse(0); - c = D.pickconditions(trial{j}); + c = D.pickconditions(trial{j}); Ne = length(c); for k = 1:Ne UY{i,j} = UY{i,j} + U{i}'*squeeze(D(Ic{i},It,c(k)))*T/Ne; @@ -266,10 +264,11 @@ % rescale under i.i.d. priors %-------------------------------------------------------------------------- +UL1 = UL{1}'*inv(UL{1}*UL{1}'); for i = 1:Nl - UL{i} = UL{i}/sqrt(trace(UL{i}*UL{i}')/Nm(i)); for j = 1:Nt - scale(i,j) = 1/sqrt(trace(UY{i,j}'*UY{i,j})/Nm(i)); + scale(i,j) = sign(trace(UY{i,j}'*UL{i}*UL1*UY{1,j})); + scale(i,j) = scale(i,j)/sqrt(trace(UY{i,j}'*UY{i,j})/Nm(i)); end end scale = mean(scale,2); @@ -277,28 +276,29 @@ for j = 1:Nt UY{i,j} = UY{i,j}*scale(i); end + UL{i} = UL{i}/sqrt(trace(UL{i}*UL{i}')/Nm(i)); end % and apply Hanning operator (if requested) %-------------------------------------------------------------------------- if Han - W = T'*sparse(1:Nb,1:Nb,spm_hanning(Nb))*T; + W = T'*sparse(1:Nb,1:Nb,spm_hanning(Nb))*T; else - W = 1; + W = 1; end for i = 1:Nl WY{i} = UY(i,:); WY{i} = spm_cat(WY{i}(:))*W; end -% temporal projector (at most 8 modes) S = T*v +% temporal projector (at most 8 modes) S = T*V %========================================================================== for i = 1:Nl if ~Nr(i) [v u] = spm_svd(WY{i}',NrTOL); % temporal modes Nr(i) = size(v,2); else - [v u] = spm_svd(WY{i}',-Inf); + [v u] = spm_svd(WY{i}',exp(-16)); v = v(:, 1:Nr(i)); u = u(1:Nr(i), 1:Nr(i)); end @@ -313,7 +313,7 @@ fprintf('Using %i temporal modes in total...\n',Nr) fprintf('...accounting for %0.2f percent variance on average\n',full(100*VE)) -% projection and whitening +% projection and whitening (noting that UY = U*Y*T) %-------------------------------------------------------------------------- S = T*V; % temporal projector iV = inv(S'*qV*S); % precision (mode) @@ -427,7 +427,6 @@ %------------------------------------------------------------------ MVB = spm_mvb(AY,G,[],Q,Qe,16); F1 = MVB.F; - h1 = MVB.h; % Accumulate empirical priors %------------------------------------------------------------------ @@ -440,14 +439,13 @@ switch(type) - case {'MSP','ARD','IID','MMN','LOR','COH'} + case {'MSP','ARD'} % or ReML - ARD %------------------------------------------------------------------ - Q = {Qe{:} LQpL{:}}; - [Cy,h,Ph,F1,F1a,F1c] = spm_sp_reml(YY,[],Q,Nr*Nt); - h1 = h; - + Q = {Qe{:} LQpL{:}}; + [Cy,h,Ph,F1] = spm_sp_reml(YY,[],Q,Nr*Nt); + % Spatial priors (QP) %------------------------------------------------------------------ Ne = length(Qe); @@ -456,11 +454,7 @@ qp = sparse(0); for i = 1:Np if hp(i) > max(hp)/128; - try - qp = qp + hp(i)*Qp{i}.q*Qp{i}.q'; - catch - qp = qp + hp(i)*Qp{i}; - end + qp = qp + hp(i)*Qp{i}.q*Qp{i}.q'; end end @@ -471,6 +465,33 @@ LQPL{end + 1} = LQP{end}*G'; end + +switch(type) + + case {'IID','MMN','LOR','COH'} + + % or ReML - ARD + %------------------------------------------------------------------ + Q = {Qe{:} LQpL{:}}; + [Cy,h,Ph,F1] = spm_reml_sc(YY,[],Q,Nr*Nt); + + % Spatial priors (QP) + %------------------------------------------------------------------ + Ne = length(Qe); + Np = length(Qp); + hp = h([1:Np] + Ne); + qp = sparse(0); + for i = 1:Np + qp = qp + hp(i)*Qp{i}; + end + + % Accumulate empirical priors + %------------------------------------------------------------------ + QP{end + 1} = diag(qp); + LQP{end + 1} = G*qp; + LQPL{end + 1} = LQP{end}*G'; + +end % re-estimate %========================================================================== @@ -483,6 +504,10 @@ % re-do ReML %-------------------------------------------------------------------------- [Cy,h,Ph,F] = spm_reml_sc(YY,[],Q,Nr*Nt); + +% Data ID +%========================================================================== +ID = spm_data_id(YY); % Covariance: sensor space - Ce and source space - L*Cp %-------------------------------------------------------------------------- @@ -507,8 +532,6 @@ % evaluate conditional expectation (of the sum over trials) %-------------------------------------------------------------------------- -SSR = 0; -SST = 0; for j = 1:Nt % trial-type specific source reconstruction @@ -517,15 +540,16 @@ % sum of squares %---------------------------------------------------------------------- - SSR = SSR + sum(var((UY{j} - G*J{j}),0,2)); - SST = SST + sum(var(UY{j},0,2)); + SSR(j) = sum(var((UY{j} - G*J{j}),0,2)); + SST(j) = sum(var(UY{j},0,2)); end % assess accuracy; signal to noise (over sources) %========================================================================== -R2 = 100*(SST - SSR)/SST; -fprintf('Percent variance explained %.2f (%.2f)\n',R2,full(R2*VE)) +R2 = 100*(SST - SSR)./SST +R2 = mean(R2); +fprintf('Percent variance explained %.2f (%.2f)\n',full(R2),full(R2*VE)) % Save results %========================================================================== @@ -549,16 +573,12 @@ inverse.Nd = Nd; % number of dipoles inverse.pst = pst; % peristimulus time inverse.dct = dct; % frequency range -try - inverse.F1a = F1a; - inverse.F1c = F1c; -end inverse.F1 = F1; % log-evidence (first step; with MSP complexity) inverse.F = F; % log-evidence (second step; MSP complexity ignored) +inverse.ID = ID; % data ID inverse.R2 = R2; % variance accounted for (%) inverse.VE = VE; % variance explained inverse.woi = woi; % time-window inverted -inverse.h1 = h1; % covariance hyperparameters (first step) inverse.h = h; % covariance hyperparameters (second step) inverse.Nm = Nm; % number of spatial modes inverse.Nr = Nr; % number of spatial modes @@ -578,3 +598,4 @@ %========================================================================== spm_eeg_invert_display(D); drawnow + diff --git a/spm_eeg_mask.m b/spm_eeg_mask.m new file mode 100644 index 0000000..69691ad --- /dev/null +++ b/spm_eeg_mask.m @@ -0,0 +1,81 @@ +function spm_eeg_mask(S) +% Create a mask image for scalp-level contrasts. +% FORMAT spm_eeg_mask(S) +% +% S - input structure (optional) +% (optional) fields of S: +% image - file name of an image containing an unsmoothed +% M/EEG data in voxel-space +% window - start and end of a window in peri-stimulus time [ms] +% outfile - output file name +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: spm_eeg_mask.m 3308 2009-08-06 18:19:40Z vladimir $ + +SVNrev = '$Rev: 3308 $'; + +%-Startup +%-------------------------------------------------------------------------- +spm('FnBanner', mfilename, SVNrev); +spm('FigName','M/EEG mask generation'); spm('Pointer','Watch'); + +if nargin == 0 + S = []; +end + +%-Input parameters +%-------------------------------------------------------------------------- +if ~isfield(S, 'image') + [S.image, sts] = spm_select(1, 'image', 'Select an unsmoothed M/EEG image (in voxel-space)'); + if ~sts, return; end +end + +if ~isfield(S, 'window') + S.window = spm_input('start and end of window [ms or Hz]', '+1', 'r', '', 2); +end + +if ~isfield(S, 'outfile') + [filename, pathname] = uiputfile({'*.img;*.nii'}, 'Select the mask file'); + [p,n,e] = fileparts(filename); + if isempty(e), filename = [filename '.img']; end + S.outfile = fullfile(pathname, filename); +end + +V = spm_vol(S.image); +Y = spm_read_vols(V); +Y = ~isnan(Y) & (Y~=0); + +Nt = size(Y, 3); + +begsample = inv(V.mat)*[0 0 S.window(1) 1]'; +begsample = begsample(3); + +endsample = inv(V.mat)*[0 0 S.window(2) 1]'; +endsample = endsample(3); + +if any([begsample endsample] < 0) || ... + any([begsample endsample] > Nt) + error('The window is out of limits for the image.'); +end + +[junk begsample] = min(abs(begsample-[1:Nt])); +[junk endsample] = min(abs(endsample-[1:Nt])); + +if begsample > 1 + Y(: , :, 1:(begsample-1)) = 0; +end + +if endsample 100, Ibar = floor(linspace(1, Nfiles,100)); -else Ibar = [1:Nfiles]; end - -k = 0; -for i = 1:Nfiles - - ind = union(Dout.badchannels, D{i}.badchannels); - if ~isempty(ind) - Dout = badchannels(Dout, ind, 1); - end - - % write trial-wise to avoid memory mapping error - for j = 1:D{i}.ntrials - k = k + 1; - Dout(1:Dout.nchannels, 1:Dout.nfrequencies, 1:Dout.nsamples, k) = D{i}(:,:,:,j); - Dout = reject(Dout, k, reject(D{i}, j)); - end - - if ismember(i, Ibar), spm_progress_bar('Set', i); end - - -end - -Dout = Dout.history('spm_eeg_merge_TF', S); -save(Dout); - -%-Cleanup -%-------------------------------------------------------------------------- -spm_progress_bar('Clear'); -spm('FigName','M/EEG TF merge: done'); spm('Pointer','Arrow'); +Dout = spm_eeg_merge(S); \ No newline at end of file diff --git a/spm_eeg_montage.m b/spm_eeg_montage.m index 19d1a64..f0fb5f0 100644 --- a/spm_eeg_montage.m +++ b/spm_eeg_montage.m @@ -13,15 +13,17 @@ % or as a filename of a mat-file containing the montage structure. % S.keepothers - 'yes'/'no': keep or discart the channels not % involved in the montage [default: 'yes'] -% S.blocksize - size of blocks used internally to split large +% S.blocksize - size of blocks used internally to split large % continuous files [default ~20Mb] +% S.updatehistory - if 0 the history is not updated (useful for +% functions that use montage functionality. % % Output: % D - MEEG object (also written on disk) % montage - the applied montage %__________________________________________________________________________ % -% spm_eeg_montage applies montage provided or specified by the user to +% spm_eeg_montage applies montage provided or specified by the user to % data and sensors of an MEEG file and produces a new file. It works % correctly when no sensors are specified or when data and sensors are % consistent (which is ensured by spm_eeg_prep_ui). @@ -29,9 +31,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak, Robert Oostenveld, Stefan Kiebel -% $Id: spm_eeg_montage.m 3013 2009-03-31 13:52:57Z vladimir $ +% $Id: spm_eeg_montage.m 3382 2009-09-10 15:46:31Z vladimir $ -SVNrev = '$Rev: 3013 $'; +SVNrev = '$Rev: 3382 $'; %-Startup %-------------------------------------------------------------------------- @@ -89,7 +91,7 @@ end montage = S.montage; - + if ~all(isfield(montage, {'labelnew', 'labelorg', 'tra'})) || ... any([isempty(montage.labelnew), isempty(montage.labelorg), isempty(montage.tra)]) || ... length(montage.labelnew) ~= size(montage.tra, 1) || length(montage.labelorg) ~= size(montage.tra, 2) @@ -110,7 +112,7 @@ %-------------------------------------------------------------------------- if ~isfield(S, 'keepothers') if ~isempty(add) - S.keepothers = spm_input('Keep the other channels?', '+1', 'yes|no'); + S.keepothers = spm_input('Keep the other channels?', '+1', 'yes|no'); else S.keepothers = 'yes'; end @@ -131,15 +133,15 @@ m = size(montage.tra,1); n = size(montage.tra,2); if length(unique(montage.labelnew))~=m - error('not all output channels of the montage are unique'); + error('not all output channels of the montage are unique'); end if length(unique(montage.labelorg))~=n - error('not all input channels of the montage are unique'); + error('not all input channels of the montage are unique'); end % determine whether all channels that have to be rereferenced are available if length(intersect(D.chanlabels, montage.labelorg)) ~= n - error('not all channels that are used in the montage are available'); + error('not all channels that are used in the montage are available'); end % reorder the columns of the montage matrix @@ -151,7 +153,7 @@ %-Generate new MEEG object with new filenames %-------------------------------------------------------------------------- -Dnew = clone(D, ['M' fnamedat(D)], [m D.nsamples D.ntrials]); +Dnew = clone(D, ['M' fnamedat(D)], [m D.nsamples D.ntrials], 1); nblocksamples = floor(S.blocksize/max(D.nchannels, m)); @@ -163,28 +165,28 @@ end if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials,100)); -elseif D.ntrials == 1, Ibar = [1:ceil(D.nsamples./nblocksamples)]; +elseif D.ntrials == 1, Ibar = [1:ceil(D.nsamples./nblocksamples)]; else Ibar = [1:D.ntrials]; end spm_progress_bar('Init', Ibar(end), 'applying montage'); for i = 1:D.ntrials for j = 1:nblocks - + Dnew(:, ((j-1)*nblocksamples +1) : (j*nblocksamples), i) = ... montage.tra * squeeze(D(:, ((j-1)*nblocksamples +1) : (j*nblocksamples), i)); - + if D.ntrials == 1 && ismember(j, Ibar) spm_progress_bar('Set', j); end end - + if mod(D.nsamples, nblocksamples) ~= 0 Dnew(:, (nblocks*nblocksamples +1) : D.nsamples, i) = ... montage.tra * squeeze(D(:, (nblocks*nblocksamples +1) : D.nsamples, i)); end - - + + if D.ntrials>1 && ismember(i, Ibar) spm_progress_bar('Set', i); end @@ -192,6 +194,15 @@ Dnew = chanlabels(Dnew, 1:Dnew.nchannels, montage.labelnew); +% Transfer bad flags in case there are channels with the +% same name in both files. +if ~isempty(D.badchannels) + sel = spm_match_str(Dnew.chanlabels, D.chanlabels(D.badchannels)); + if ~isempty(sel) + Dnew = badchannels(Dnew, sel, 1); + end +end + % Set channel types to default S1 = []; S1.task = 'defaulttype'; @@ -201,10 +212,11 @@ %-Apply montage to sensors %-------------------------------------------------------------------------- -sensortypes = {'MEG'}; % EEG disabled for now +sensortypes = {'MEG', 'EEG'}; for i = 1:length(sensortypes) sens = D.sensors(sensortypes{i}); - if ~isempty(sens) && length(intersect(sens.label, montage.labelorg))==length(sens.label) + if ~isempty(sens) && length(intersect(sens.label, montage.labelorg))==length(sens.label) &&... + ~isequal(sensortypes{i}, 'EEG') % EEG disabled for now sensmontage = montage; sel1 = spm_match_str(sensmontage.labelorg, sens.label); sensmontage.labelorg = sensmontage.labelorg(sel1); @@ -212,14 +224,37 @@ selempty = find(all(sensmontage.tra == 0, 2)); sensmontage.tra(selempty, :) = []; sensmontage.labelnew(selempty) = []; - sens = forwinv_apply_montage(sens, montage, 'keepunused', S.keepothers); + sens = forwinv_apply_montage(sens, sensmontage, 'keepunused', S.keepothers); + + if isfield(sens, 'balance') && ~isequal(sens.balance.current, 'none') + balance = forwinv_apply_montage(getfield(sens.balance, sens.balance.current), sensmontage, 'keepunused', S.keepothers); + else + balance = sensmontage; + end + + sens.balance.custom = balance; + sens.balance.current = 'custom'; + end + + if ~isempty(sens) && ~isempty(intersect(sens.label, Dnew.chanlabels)) + Dnew = sensors(Dnew, sensortypes{i}, sens); + else + Dnew = sensors(Dnew, sensortypes{i}, []); end - Dnew = sensors(Dnew, sensortypes{i}, sens); end -%-Assign default EEG sensor positions if possible +% Remove any inverse solutions +if isfield(Dnew, 'inv') + Dnew = rmfield(Dnew, 'inv'); +end + +%-Assign default EEG sensor positions if no positions are present or if +% default locations had been assigned before but no longer cover all the +% EEG channels. %-------------------------------------------------------------------------- -if ~isempty(Dnew.meegchannels('EEG')) && isempty(Dnew.sensors('EEG')) +if ~isempty(Dnew.meegchannels('EEG')) && (isempty(Dnew.sensors('EEG')) ||... + (all(ismember({'spmnas', 'spmlpa', 'spmrpa'}, Dnew.fiducials.fid.label)) && ... + ~isempty(setdiff(Dnew.chanlabels(Dnew.meegchannels('EEG')), getfield(Dnew.sensors('EEG'), 'label'))))) S1 = []; S1.task = 'defaulteegsens'; S1.updatehistory = 0; @@ -230,7 +265,7 @@ %-Create 2D positions for EEG by projecting the 3D positions to 2D %-------------------------------------------------------------------------- -if ~isempty(Dnew.meegchannels('EEG')) && ~isempty(Dnew.sensors('EEG')) +if ~isempty(Dnew.meegchannels('EEG')) && ~isempty(Dnew.sensors('EEG')) S1 = []; S1.task = 'project3D'; S1.modality = 'EEG'; @@ -242,7 +277,7 @@ %-Create 2D positions for MEG by projecting the 3D positions to 2D %-------------------------------------------------------------------------- -if ~isempty(Dnew.meegchannels('MEG')) && ~isempty(Dnew.sensors('MEG')) +if ~isempty(Dnew.meegchannels('MEG')) && ~isempty(Dnew.sensors('MEG')) S1 = []; S1.task = 'project3D'; S1.modality = 'MEG'; @@ -257,7 +292,11 @@ %-Save new evoked M/EEG dataset %-------------------------------------------------------------------------- D = Dnew; -D = D.history('spm_eeg_montage', S); + +if ~isfield(S, 'updatehistory') || S.updatehistory + D = D.history('spm_eeg_montage', S); +end + save(D); %-Cleanup diff --git a/spm_eeg_montage_ui.m b/spm_eeg_montage_ui.m index e7fe15c..65deb72 100644 --- a/spm_eeg_montage_ui.m +++ b/spm_eeg_montage_ui.m @@ -12,7 +12,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jean Daunizeau -% $Id: spm_eeg_montage_ui.m 2696 2009-02-05 20:29:48Z guillaume $ +% $Id: spm_eeg_montage_ui.m 3208 2009-06-16 21:00:50Z vladimir $ error(nargchk(1,1,nargin)); @@ -125,7 +125,11 @@ function doSave(obj,evd,h) doCheck(obj,evd,h); ud = get(h,'userdata'); [M,newLabels] = getM(ud.ht); -montage.tra = M; +% delete row if empty: +ind = ~any(M,2); +M(ind,:) = []; +newLabels(ind) = []; +montage.tra = M; montage.labelorg = ud.montage.labelorg; montage.labelnew = newLabels; uisave('montage','SPMeeg_montage.mat'); diff --git a/spm_eeg_prep.m b/spm_eeg_prep.m index 2a4f559..1b85039 100644 --- a/spm_eeg_prep.m +++ b/spm_eeg_prep.m @@ -15,7 +15,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: spm_eeg_prep.m 3005 2009-03-30 17:51:05Z vladimir $ +% $Id: spm_eeg_prep.m 3403 2009-09-15 13:41:17Z vladimir $ if ~nargin spm_eeg_prep_ui; @@ -59,23 +59,19 @@ type = fileio_chantype(D.chanlabels); - spmtype = repmat({'Other'}, 1, numel(type)); - - [sel1, sel2] = spm_match_str(type, dictionary(:, 1)); - - spmtype(sel1) = dictionary(sel2, 2); - % If there is useful information in the original types it % overwrites the default assignment if isfield(D, 'origchantypes') - [sel1, sel2] = spm_match_str(chanlabels(D, ind), D.origchantypes.label); + [sel1, sel2] = spm_match_str(chanlabels(D, ind), D.origchantypes.label); - type = D.origchantypes.type(sel2); - - [sel1, sel2] = spm_match_str(type, dictionary(:, 1)); - - spmtype(sel1) = dictionary(sel2, 2); - end + type(ind(sel1)) = D.origchantypes.type(sel2); + end + + spmtype = repmat({'Other'}, 1, length(ind)); + + [sel1, sel2] = spm_match_str(type(ind), dictionary(:, 1)); + + spmtype(sel1) = dictionary(sel2, 2); D = chantype(D, ind, spmtype); @@ -91,7 +87,17 @@ xy = S.xy; label = S.label; case 'project3d' - [xy, label] = spm_eeg_project3D(D.sensors(S.modality), S.modality); + if ~isfield(D, 'val') + D.val = 1; + end + if isfield(D, 'inv') && isfield(D.inv{D.val}, 'datareg') + datareg = D.inv{D.val}.datareg; + ind = strmatch(S.modality, {datareg(:).modality}, 'exact'); + sens = datareg(ind).sensors; + else + sens = D.sensors(S.modality); + end + [xy, label] = spm_eeg_project3D(sens, S.modality); end [sel1, sel2] = spm_match_str(lower(D.chanlabels), lower(label)); @@ -160,9 +166,14 @@ shape.pnt = []; end case 'locfile' - label = chanlabels(D, sort(strmatch('EEG', D.chantype, 'exact'))); + label = chanlabels(D, D.meegchannels('EEG')); elec = fileio_read_sens(S.sensfile); + + % Remove headshape points + hspind = strmatch('headshape', elec.label); + elec.pnt(hspind, :) = []; + elec.label(hspind) = []; % This handles FIL Polhemus case and other possible cases % when no proper labels are available. @@ -357,11 +368,6 @@ D = spm_eeg_inv_mesh_ui(D, val, 1, Msize); D = spm_eeg_inv_datareg_ui(D, val); - if isequal(D.modality(1, 0), 'EEG') - D = sensors(D, 'EEG', D.inv{1}.datareg.sensors); - D = fiducials(D, D.inv{1}.datareg.fid_eeg); - end - %---------------------------------------------------------------------- otherwise %---------------------------------------------------------------------- diff --git a/spm_eeg_prep_ui.m b/spm_eeg_prep_ui.m index f20e575..57d7e97 100644 --- a/spm_eeg_prep_ui.m +++ b/spm_eeg_prep_ui.m @@ -6,7 +6,7 @@ function spm_eeg_prep_ui(callback) % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: spm_eeg_prep_ui.m 2943 2009-03-24 19:09:45Z jean $ +% $Id: spm_eeg_prep_ui.m 3192 2009-06-09 08:29:22Z vladimir $ spm('Pointer','Watch'); @@ -23,7 +23,7 @@ function spm_eeg_prep_ui(callback) %========================================================================== function CreateMenu -SVNrev = '$Rev: 2943 $'; +SVNrev = '$Rev: 3192 $'; spm('FnBanner', 'spm_eeg_prep_ui', SVNrev); Finter = spm('FnUIsetup', 'M/EEG prepare', 0); @@ -569,7 +569,19 @@ function spm_eeg_prep_ui(callback) modality = 'MEG'; end -[xy, label] = spm_eeg_project3D(D.sensors(modality), modality); +if ~isfield(D, 'val') + D.val = 1; +end + +if isfield(D, 'inv') && isfield(D.inv{D.val}, 'datareg') + datareg = D.inv{D.val}.datareg; + ind = strmatch(modality, {datareg(:).modality}, 'exact'); + sens = datareg(ind).sensors; +else + sens = D.sensors(modality); +end + +[xy, label] = spm_eeg_project3D(sens, modality); plot_sensors2D(xy, label); diff --git a/spm_eeg_project3D.m b/spm_eeg_project3D.m index d21eb8b..234061b 100644 --- a/spm_eeg_project3D.m +++ b/spm_eeg_project3D.m @@ -6,15 +6,16 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel, Vladimir Litvak -% $Id: spm_eeg_project3D.m 3005 2009-03-30 17:51:05Z vladimir $ +% $Id: spm_eeg_project3D.m 3192 2009-06-09 08:29:22Z vladimir $ cfg = []; switch modality case 'EEG' - cfg.elec = sens; + cfg.elec = sens; + cfg.rotate = 0; case 'MEG' - cfg.grad = sens; + cfg.grad = sens; otherwise error('Unknown data type'); end diff --git a/spm_eeg_remove_bad_trials.m b/spm_eeg_remove_bad_trials.m new file mode 100644 index 0000000..85f4327 --- /dev/null +++ b/spm_eeg_remove_bad_trials.m @@ -0,0 +1,97 @@ +function D = spm_eeg_remove_bad_trials(S) +% Physically removes trials marked as bad from the dataset +% FORMAT D = spm_eeg_remove_bad_trials(S) +% +% S - optional input struct +% (optional) fields of S: +% D - MEEG object or filename of M/EEG mat-file with epoched data +% +% Output: +% D - MEEG object (also written on disk) +% +% The function also changes the physical order of trials to conform to +% condlist. +% +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: spm_eeg_remove_bad_trials.m 3378 2009-09-09 16:47:16Z guillaume $ + +SVNrev = '$Rev: 3378 $'; + +%-Startup +%-------------------------------------------------------------------------- +spm('FnBanner', mfilename, SVNrev); +spm('FigName','Remove bad trials'); spm('Pointer','Watch'); + +%-Get MEEG object +%-------------------------------------------------------------------------- +try + D = S.D; +catch + [D, sts] = spm_select(1, 'mat', 'Select M/EEG mat file'); + if ~sts, D = []; return; end + S.D = D; +end + +D = spm_eeg_load(D); + +%-Check that there is any good data available +%-------------------------------------------------------------------------- +if ntrials(D)==0 || all(reject(D)) + warning('No good trials were found. Nothing to do.'); + return; +end + +%-Generate new MEEG object with new files +%-------------------------------------------------------------------------- +if strncmpi(D.transformtype,'TF',2) % TF and TFphase + Dnew = clone(D, ['r' fnamedat(D)], [D.nchannels D.nfrequencies D.nsamples sum(~D.reject)]); +else + Dnew = clone(D, ['r' fnamedat(D)], [D.nchannels D.nsamples sum(~D.reject)]); +end + +cl = D.condlist; + +goodtrials = []; +for i = 1:numel(cl) + goodtrials = [goodtrials pickconditions(D, cl{i}, 1)]; +end + +%-Copy data +%-------------------------------------------------------------------------- +spm_progress_bar('Init', length(goodtrials), 'Trials copied'); +if length(goodtrials) > 100, Ibar = floor(linspace(1, length(goodtrials), 100)); +else Ibar = [1:length(goodtrials)]; end + +for i = 1:length(goodtrials) + + if strncmpi(D.transformtype,'TF',2) % TF and TFphase + Dnew(:, :, :, i) = D(:, :, :, goodtrials(i)); + else + Dnew(:, :, i) = D(:, :, goodtrials(i)); + end + + if ismember(i, Ibar), spm_progress_bar('Set', i); end +end % + +spm_progress_bar('Clear'); + +%-Copy trial-specific data. +%-------------------------------------------------------------------------- +Dnew = conditions(Dnew, [], conditions(D, goodtrials)); +Dnew = repl(Dnew, [], repl(D, goodtrials)); +Dnew = events(Dnew, [], events(D, goodtrials)); +Dnew = trialonset(Dnew, [], trialonset(D, goodtrials)); + +%-Save the new M/EEG dataset +%-------------------------------------------------------------------------- +Dnew = Dnew.history(mfilename, S); +save(Dnew); + +D = Dnew; + +%-Cleanup +%-------------------------------------------------------------------------- +spm('FigName','Remove bad trials: done'); spm('Pointer','Arrow'); diff --git a/spm_eeg_review.m b/spm_eeg_review.m index 765453a..a89f23a 100644 --- a/spm_eeg_review.m +++ b/spm_eeg_review.m @@ -11,7 +11,7 @@ function spm_eeg_review(D,flag,inv) % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jean Daunizeau -% $Id: spm_eeg_review.m 2979 2009-03-27 18:36:03Z guillaume $ +% $Id: spm_eeg_review.m 3177 2009-06-03 08:47:41Z vladimir $ if nargin == 0 [D, sts] = spm_select(1, 'mat$', 'Select M/EEG mat file'); @@ -238,7 +238,8 @@ function back2defaults(e1,e2) if Ninv>=1 labels = cell(Ninv,1); callbacks = cell(Ninv,1); - F = zeros(Ninv,1); + F = zeros(Ninv,1); + ID = zeros(Ninv,1); pst = []; for i=1:Ninv if ~isfield(D.other.inv{isInv(i)},'comment') @@ -252,6 +253,11 @@ function back2defaults(e1,e2) && isnan(D.other.inv{isInv(i)}.inverse.R2) D.other.inv{isInv(i)}.inverse.R2 = []; end + if isfield(D.other.inv{isInv(i)}.inverse, 'ID') + ID(i) = D.other.inv{isInv(i)}.inverse.ID; + else + ID(i) = nan; + end labels{i} = [D.other.inv{isInv(i)}.comment{1}]; callbacks{i} = ['spm_eeg_review_callbacks(''visu'',''inv'',',num2str(i),')']; F(i) = D.other.inv{isInv(i)}.inverse.F; @@ -273,6 +279,7 @@ function back2defaults(e1,e2) D.PSD.source.VIZU.isInv = isInv; D.PSD.source.VIZU.pst = pst; D.PSD.source.VIZU.F = F; + D.PSD.source.VIZU.ID = ID; D.PSD.source.VIZU.labels = labels; D.PSD.source.VIZU.callbacks = callbacks; D.PSD.source.VIZU.timeCourses = 1; diff --git a/spm_eeg_review_callbacks.m b/spm_eeg_review_callbacks.m index bf27814..ec67751 100644 --- a/spm_eeg_review_callbacks.m +++ b/spm_eeg_review_callbacks.m @@ -4,7 +4,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jean Daunizeau -% $Id: spm_eeg_review_callbacks.m 3099 2009-05-05 15:31:32Z jean $ +% $Id: spm_eeg_review_callbacks.m 3319 2009-08-11 10:29:58Z vladimir $ spm('pointer','watch'); drawnow expose @@ -286,8 +286,6 @@ opt.figname = 'Coregistred MEG sensor positions'; catch pos3d = [D.sensors.meg.pnt]; - indMeg = unique([D.PSD.MEG.I(:);D.PSD.MEGPLANAR.I(:)]); - pos3d = pos3d(indMeg,:); opt.figname = 'Uncoregistred MEG sensor positions'; end pos3d(1,:); @@ -1157,6 +1155,15 @@ if ~isfield(handles,'PLOT') miY = min(v_data(:)); maY = max(v_data(:)); + + if miY == 0 && maY == 0 + miY = -eps; + maY = eps; + else + miY = miY - miY.*1e-3; + maY = maY + maY.*1e-3; + end + for i=1:length(VIZU.visuSensors) cmenu = uicontextmenu; uimenu(cmenu,'Label',['channel ',num2str(VIZU.visuSensors(i)),': ',VIZU.montage.clab{i}]); @@ -1245,7 +1252,11 @@ % get model/trial info VIZU = D.PSD.source.VIZU; + isInv = VIZU.isInv; + Ninv = length(isInv); invN = VIZU.isInv(D.PSD.source.VIZU.current); + F = VIZU.F; + ID = VIZU.ID; model = D.other.inv{invN}.inverse; t0 = get(D.PSD.handles.BUTTONS.slider_step,'value'); tmp = (model.pst-t0).^2; @@ -1264,7 +1275,37 @@ % get the inverse model info str = getInfo4Inv(D,invN); set(D.PSD.handles.infoText,'string',str); - try, set(D.PSD.handles.BMCcurrent,'XData',invN); end; + + if Ninv>1 + if isnan(ID(invN)) + xF = find(isnan(ID)); + else + xF = find(abs(ID-ID(invN))1 + D.PSD.handles.hbar = bar(D.PSD.handles.BMCplot,... + xF ,F(xF)-min(F(xF)),... + 'barwidth',0.5,... + 'FaceColor',0.5*[1 1 1],... + 'visible','off',... + 'tag','plotEEG'); + D.PSD.handles.BMCcurrent = plot(D.PSD.handles.BMCplot,... + find(xF==invN),0,'ro',... + 'visible','off',... + 'tag','plotEEG'); + set(D.PSD.handles.BMCplot,... + 'xtick',xF,... + 'xticklabel',D.PSD.source.VIZU.labels(xF),... + 'xlim',[0,length(xF)+1]); + drawnow + else + cla(D.PSD.handles.BMCplot); + set(D.PSD.handles.BMCplot,... + 'xtick',[],... + 'xticklabel',{}); + end + end + % get model/trial time series D.PSD.source.VIZU.J = zeros(model.Nd,size(model.T,1)); D.PSD.source.VIZU.J(model.Is,:) = model.J{trN(1)}*model.T'; diff --git a/spm_eeg_review_switchDisplay.m b/spm_eeg_review_switchDisplay.m index 791c9e1..2d8d88a 100644 --- a/spm_eeg_review_switchDisplay.m +++ b/spm_eeg_review_switchDisplay.m @@ -4,7 +4,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jean Daunizeau -% $Id: spm_eeg_review_switchDisplay.m 3099 2009-05-05 15:31:32Z jean $ +% $Id: spm_eeg_review_switchDisplay.m 3177 2009-06-03 08:47:41Z vladimir $ try % only if already displayed stuffs handles = rmfield(D.PSD.handles,'PLOT'); @@ -212,7 +212,8 @@ end invN = isInv(D.PSD.source.VIZU.current); pst = D.PSD.source.VIZU.pst; - F = D.PSD.source.VIZU.F; + F = D.PSD.source.VIZU.F; + ID = D.PSD.source.VIZU.ID; % create uitabs for inverse solutions hInv = D.PSD.handles.tabs.hp; @@ -246,21 +247,28 @@ % plot BMC free energies in appropriate axes if Ninv>1 - D.PSD.handles.hbar = bar(D.PSD.handles.BMCplot,... - 1:Ninv,F-min(F),... - 'barwidth',0.5,... - 'FaceColor',0.5*[1 1 1],... - 'visible','off',... - 'tag','plotEEG'); - D.PSD.handles.BMCcurrent = plot(D.PSD.handles.BMCplot,... - find(isInv==invN),0,'ro',... - 'visible','off',... - 'tag','plotEEG'); - set(D.PSD.handles.BMCplot,... - 'xtick',1:Ninv,... - 'xticklabel',D.PSD.source.VIZU.labels,... - 'xlim',[0,Ninv+1]); - drawnow + if isnan(ID(invN)) + xF = find(isnan(ID)); + else + xF = find(abs(ID-ID(invN))1 + D.PSD.handles.hbar = bar(D.PSD.handles.BMCplot,... + xF ,F(xF)-min(F(xF)),... + 'barwidth',0.5,... + 'FaceColor',0.5*[1 1 1],... + 'visible','off',... + 'tag','plotEEG'); + D.PSD.handles.BMCcurrent = plot(D.PSD.handles.BMCplot,... + find(xF==invN),0,'ro',... + 'visible','off',... + 'tag','plotEEG'); + set(D.PSD.handles.BMCplot,... + 'xtick',xF,... + 'xticklabel',D.PSD.source.VIZU.labels(xF),... + 'xlim',[0,length(xF)+1]); + drawnow + end end % Create mesh and related objects diff --git a/spm_eeg_review_uis.m b/spm_eeg_review_uis.m index f9efc3f..6c06c45 100644 --- a/spm_eeg_review_uis.m +++ b/spm_eeg_review_uis.m @@ -4,7 +4,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jean Daunizeau -% $Id: spm_eeg_review_uis.m 2943 2009-03-24 19:09:45Z jean $ +% $Id: spm_eeg_review_uis.m 3335 2009-08-25 16:37:02Z vladimir $ % POS = get(D.PSD.handles.hfig,'position'); @@ -348,8 +348,7 @@ trN = D.PSD.trials.current(1); p = []; try - p(1,:) = [D.channels(I).X_plot2D]; - p(2,:) = [D.channels(I).Y_plot2D]; + p = coor2D(meeg(D), I, [], 0.03); end if ~isequal(size(p,2),Npos) % create dummy 2D channel positions @@ -373,6 +372,14 @@ end miY = min(y(:)); maY = max(y(:)); + + if miY == 0 && maY == 0 + miY = -eps; + maY = eps; + else + miY = miY - miY.*1e-3; + maY = maY + maY.*1e-3; + end % position of plotting area for eeg data in graphics figure if size(p,2) <= 16 % for aesthetics purposes Pos = [0.023 0.05 0.95 0.52]; diff --git a/spm_eeg_robust_average.m b/spm_eeg_robust_average.m deleted file mode 100644 index b4a88ae..0000000 --- a/spm_eeg_robust_average.m +++ /dev/null @@ -1,103 +0,0 @@ -function [B,Wf] = spm_eeg_robust_average(data) -% Robust averaging -%__________________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging - -% James Kilner -% $Id: spm_eeg_robust_average.m 2696 2009-02-05 20:29:48Z guillaume $ - -data=data'; -%figure(1) -%plot(mean(data)); -% clear D -% clear d1 - - ndata=reshape(data',size(data,1)*size(data,2),1); - - -Xs=sparse(repmat(speye(size(data,2)),[size(data,1),1])); - -s1=size(data,1); -s2=size(data,2); -%clear data; -Wis=speye(length(ndata)); -h=1./((1-(diag(Xs*(Xs'*Xs)^-1*Xs'))).^0.5); -ores=1; -nres=10; -n=0; -while abs(ores-nres)>sqrt(1E-8) - abs(ores-nres); - ores=nres; - n=n+1; - B=((Xs'*Wis*Xs)^-1)*Xs'*Wis*ndata; - if sum(isnan(B))>0 - break - end - if n>500 - break - end - res=ndata-Xs*B; - mad=median(abs(res-median(res))); - res=(res)./mad; - res=res.*h; - res=reshape(res,s2,s1); - res=repmat((mean(abs(res)).*sign(sum(res))),[s2,1]); - res=reshape(res,s1*s2,1); - res=abs(res)-2; - res(res<0)=0; - nres=(sum(res.^2)); - Wf=(((abs(res)<1) .* (1 - res.^2).^2)); - clear res; - Wis=spdiags(Wf,0,Wis); - - -end - - - -% -% res=ndata-Xs*B; -% res=reshape(res,s2,s1); -% res=repmat(max(abs(res)),[s2,1]); -% res=reshape(res,s1*s2,1); -% mad=median(abs(res-median(res))); -% res=res./mad; -% res=res.*h; -% res=abs(res)-1; -% res(res<0)=0; -% nres=(sum(res.^2)); -% n=0; -% while abs(ores-nres)>sqrt(1E-8) -% n=n+1; -% figure(10) -% plot(n,abs(ores-nres),'.') -% hold on -% ores=nres; -% -% -% if n>50 -% break -% end -% -% -% B=((Xs'*Wis*Xs)^-1)*Xs'*Wis*ndata; -% res=ndata-Xs*B; -% mad=median(abs(res-median(res))); -% res=(res)./mad; -% res=res.*h; -% res=reshape(res,s2,s1); -% [df,inds]=(max(abs(res))); -% y=1:length(inds); -% res=repmat(diag(squeeze(res(inds,y)))',[s2,1]); -% res=reshape(res,s1*s2,1); -% -% res=abs(res)-1; -% res(res<0)=0; -% nres=(sum(res.^2)); -% Wf=(((abs(res)<1) .* (1 - res.^2).^2)); -% clear res; -% Wis=spdiags(Wf,0,Wis); -% -% -% end -% diff --git a/spm_eeg_robust_average_TF.m b/spm_eeg_robust_average_TF.m deleted file mode 100644 index afff1d9..0000000 --- a/spm_eeg_robust_average_TF.m +++ /dev/null @@ -1,134 +0,0 @@ -function D = spm_eeg_robust_average_TF(S) -% Robust averaging TF data based on weights calculated on ERP of same data. -% FORMAT D = spm_eeg_robust_average_TF(S) -% -% S - optional input struct -% (optional) fields of S: -% S.D - -% S.D2 - -% S.c - -% -% D - MEEG object (also written to disk) -%__________________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging - -% James Kilner -% $Id: spm_eeg_robust_average_TF.m 2861 2009-03-11 18:41:03Z guillaume $ - -SVNrev = '$Rev: 2861 $'; - -%-Startup -%-------------------------------------------------------------------------- -spm('FnBanner', mfilename, SVNrev); -spm('FigName','M/EEG robust average TF'); spm('Pointer','Watch'); - -%-Get MEEG objects -%-------------------------------------------------------------------------- -try - D = S.D; -catch - [D, sts] = spm_select(1, 'mat', 'Select M/EEG mat file'); - if ~sts, D = []; return; end - S.D = D; -end - -D = spm_eeg_load(D); - -try - D2 = S.D2; -catch - [D2, sts] = spm_select(1, 'mat', 'Select robust averaged erp file file'); - if ~sts, return; end - S.D2 = D2; -end - -D2 = spm_eeg_load(D2); -P = spm_str_manip(D2, 'H'); - -%-Get parameters -%-------------------------------------------------------------------------- -try - c = S.c; -catch - % if there is no S.c, assume that user wants default average within - % trial type - c = eye(D.events.Ntypes); -end - -%- -%-------------------------------------------------------------------------- -d=zeros(size(D.data,1),size(D.data,2),size(D.data,3),D.events.Ntypes); -fh=fopen(fullfile(D.path,D.fnamedat),'r'); - spm_progress_bar('Init', D.Nevents, 'averaging'); drawnow; - if D.Nevents > 100, Ibar = floor(linspace(1, D.Nevents,100)); - else, Ibar = [1:D.Nevents]; end -for n=1:D.Nevents - - if D.events.reject(n)== 0 - [m,i]=find(D.events.types==D.events.code(n)); - data=fread(fh,[1,size(D.data,1)*size(D.data,2)*size(D.data,3)],'short'); - data=reshape(data,size(D.data,1),size(D.data,2),size(D.data,3),1); - data=data.*repmat(D.scale(:,1,1,n),[1,D.Nfrequencies, D.Nsamples]); - for f=1:D.Nfrequencies - data(:,f,:)=squeeze(data(:,f,:)).*D2.weights(:,(n-1)*size(D.data,3)+1:n*size(D.data,3)); - end - d(:,:,:,i)=d(:,:,:,i)+data; - end - - if ismember(n, Ibar) - spm_progress_bar('Set', n); - drawnow; - end - - -end - - -spm_progress_bar('Clear'); -D.fnamedat = ['ma' D.fnamedat]; - -fpd = fopen(fullfile(P, D.fnamedat), 'w'); -D.scale = zeros(D.Nchannels, 1, 1, D.events.Ntypes); - -for n=1:D.events.Ntypes - dat=squeeze(d(:,:,:,n)./length(find(D.events.code==D.events.types(n) & ~D.events.reject))); - D.scale(:, 1, 1, n) = max(max(squeeze(abs(dat)), [], 3), [], 2)./32767; - dat = int16(dat./repmat(D.scale(:, 1, 1, n), [1, D.Nfrequencies, D.Nsamples])); - fwrite(fpd, dat, 'int16'); -end -fclose (fh) ; - -fclose(fpd); -D.Nevents = size(c, 2); - -% labeling of resulting contrasts, take care to keep numbers of old trial -% types -% check this again: can be problematic, when user mixes within-trialtype -% and over-trial type contrasts -D.events.code = size(1, size(c, 2)); -for i = 1:size(c, 2) - if sum(c(:, i)) == 1 & sum(~c(:, i)) == size(c, 1)-1 - D.events.code(i) = find(c(:, i)); - else - D.events.code(i) = i; - end -end - -D.events.time = []; -D.events.types = D.events.code; -D.events.Ntypes = length(D.events.types); -D.data = []; -D.events.reject = zeros(1, D.Nevents); -D.events.blinks = zeros(1, D.Nevents); -D.fname = ['ma' D.fname]; -D.weights=D2.weights; -if spm_matlab_version_chk('7') >= 0 - save(fullfile(P, D.fname), '-V6', 'D'); -else - save(fullfile(P, D.fname), 'D'); -end - -%-Cleanup -%-------------------------------------------------------------------------- -spm_progress_bar('Clear'); -spm('FigName','M/EEG robust average TF: done'); spm('Pointer','Arrow'); diff --git a/spm_eeg_tf.m b/spm_eeg_tf.m index ca3d064..6271650 100644 --- a/spm_eeg_tf.m +++ b/spm_eeg_tf.m @@ -29,9 +29,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_tf.m 3061 2009-04-17 12:17:00Z guillaume $ +% $Id: spm_eeg_tf.m 3350 2009-09-03 13:19:20Z vladimir $ -SVNrev = '$Rev: 3061 $'; +SVNrev = '$Rev: 3350 $'; %-Startup %-------------------------------------------------------------------------- @@ -133,7 +133,7 @@ Nfrequencies = length(tf.frequencies); Dtf = clone(D, ['tf1_' D.fnamedat], [Nchannels Nfrequencies D.nsamples D.ntrials]); -Dtf = frequencies(Dtf, tf.frequencies); +Dtf = Dtf.frequencies(:, tf.frequencies); % fix all channels sD = struct(Dtf); @@ -156,7 +156,7 @@ if tf.phase == 1 Dtf2 = clone(D, ['tf2_' D.fnamedat], [Nchannels Nfrequencies D.nsamples D.ntrials]); - Dtf2 = frequencies(Dtf2, tf.frequencies); + Dtf2 = Dtf2.frequencies(:, tf.frequencies); Dtf2 = transformtype(Dtf2, 'TFphase'); % fix all channels @@ -240,13 +240,6 @@ spm_progress_bar('Clear'); -%-Remove baseline over frequencies and trials -%-------------------------------------------------------------------------- -if tf.rm_baseline == 1 - Dtf = spm_eeg_bc(struct('D', Dtf, ... - 'time', tf.Sbaseline,... - 'save', false)); -end %-Save new M/EEG dataset %-------------------------------------------------------------------------- @@ -257,6 +250,14 @@ save(Dtf2); end +%-Remove baseline over frequencies and trials +%-------------------------------------------------------------------------- +if tf.rm_baseline == 1 + Dtf = spm_eeg_bc(struct('D', Dtf, ... + 'time', tf.Sbaseline,... + 'save', false)); +end + %-Cleanup %-------------------------------------------------------------------------- spm('FigName','M/EEG Time Frequency: done'); spm('Pointer','Arrow'); diff --git a/spm_eeg_tf_rescale.m b/spm_eeg_tf_rescale.m index 2d27041..27c2524 100644 --- a/spm_eeg_tf_rescale.m +++ b/spm_eeg_tf_rescale.m @@ -6,7 +6,7 @@ % (optional) fields of S: % S.D - MEEG object or filename of M/EEG mat-file % S.tf - structure with (optional) fields: -% S.tf.method - 'LogR', 'Diff', 'Log', 'Sqrt' +% S.tf.method - 'LogR', 'Diff', 'Rel', 'Log', 'Sqrt' % S.tf.Sbaseline - 2-element vector: start and stop of baseline % (need to specify this for LogR and Diff) % @@ -14,15 +14,16 @@ % written to disk with prefix r) % % For 'Log' and 'Sqrt', these functions are applied to spectrogram -% For 'LogR' and 'Diff' this function computes power in the baseline -% p_b and outputs (i) p-p_b for 'Diff' or (ii) log (p/p_b) for 'LogR' +% For 'LogR', 'Rel' and 'Diff' this function computes power in the baseline +% p_b and outputs (i) p-p_b for 'Diff' (ii) 100*(p-p_b)/p_b for 'Rel' +% (iii) log (p/p_b) for 'LogR' %__________________________________________________________________________ % Copyright (C) 2009 Wellcome Trust Centre for Neuroimaging % Will Penny -% $Id: spm_eeg_tf_rescale.m 3089 2009-04-29 17:24:39Z will $ +% $Id: spm_eeg_tf_rescale.m 3218 2009-06-22 15:45:53Z vladimir $ -SVNrev = '$Rev: 3089 $'; +SVNrev = '$Rev: 3218 $'; %-Startup %-------------------------------------------------------------------------- @@ -42,47 +43,55 @@ try S.tf.method; catch - str = {'LogR','Diff','Log','Sqrt'}; + str = {'LogR','Diff', 'Rel', 'Log','Sqrt'}; S.tf.method = spm_input('Rescale method','+1','b',str,[],1); end -Din = spm_eeg_load(D); -tims=time(Din); +Din = spm_eeg_load(D); +tims = time(Din); -Nf=length(frequencies(Din)); -D = clone(Din, ['r' Din.fnamedat], [Din.nchannels Nf Din.nsamples Din.ntrials]); +Nf = length(frequencies(Din)); +D = clone(Din, ['r' Din.fnamedat], [Din.nchannels Nf Din.nsamples Din.ntrials]); -switch lower(S.tf.method), - case {'logr','diff'} +switch lower(S.tf.method) + + case {'logr','diff', 'rel'} try S.tf.Sbaseline; catch tmp_base = spm_input('Start and stop of baseline [ms]', '+1', 'i', '', 2); S.tf.Sbaseline = tmp_base/1000; end - for c=1:D.ntrials, + for c=1:D.ntrials inds=find(tims>S.tf.Sbaseline(1) & tims 100, Ibar = floor(linspace(1, Ncontrasts, 100)); else Ibar = [1:Ncontrasts]; end @@ -114,14 +112,28 @@ disp(['Contrast ', mat2str(i),': ', mat2str(c(i,:),3)]) - d = zeros(D.nchannels, D.nsamples); + if strncmp(D.transformtype, 'TF', 2) + d = zeros(D.nchannels, D.nfrequencies, D.nsamples); - for j = 1:D.nchannels - d(j, :) = c(i,:) * squeeze(D(j, :, :))'; - end + for j = 1:D.nchannels + for f = 1:D.nfrequencies + d(j, f, :) = c(i,:) * squeeze(D(j, f, :, :))'; + end + end + + Dnew(1:Dnew.nchannels, 1:D.nfrequencies, 1:Dnew.nsamples, i) = d; - Dnew(1:Dnew.nchannels, 1:Dnew.nsamples, i) = d; + else + d = zeros(D.nchannels, D.nsamples); + + for j = 1:D.nchannels + d(j, :) = c(i,:) * squeeze(D(j, :, :))'; + end + + Dnew(1:Dnew.nchannels, 1:Dnew.nsamples, i) = d; + end + newrepl(i) = sum(D.repl(find(c(i,:)~=0))); if ismember(i, Ibar), spm_progress_bar('Set', i); end diff --git a/spm_eeg_weight_epochs_TF.m b/spm_eeg_weight_epochs_TF.m index 9e24a4e..c8c5111 100644 --- a/spm_eeg_weight_epochs_TF.m +++ b/spm_eeg_weight_epochs_TF.m @@ -1,146 +1,13 @@ -function D = spm_eeg_weight_epochs_TF(S) -% Compute contrasts over trials or trial types, for time-frequency data -% FORMAT D = spm_eeg_weight_epochs_TF(S) -% -% S - optional input struct -% (optional) fields of S: -% S.D - MEEG object or filename of M/EEG mat-file with TF data -% S.c - contrast matrix, each row computes a contrast of the data -% S.WeightAve - flag whether average should be weighted by number of -% replications (yes (1), no (0)) -% S.label - a cell array of contrast labels (default: prompt) -% -% D - MEEG object (also written to disk) -%__________________________________________________________________________ -% -% spm_eeg_weight_epochs_TF computes contrasts of data, over epochs of data, -% for time-frequency data. The input is a single MEEG file. -% The argument c must have dimensions Ncontrasts X Nepochs, where Ncontrasts -% is the number of contrasts and Nepochs the number of epochs, i.e. each -% row of c contains one contrast vector. The output is a MEEG file with -% Ncontrasts epochs. The typical use is to compute, for display purposes, -% contrasts like the difference or interaction between trial types in -% channel space. Another possible use is remove trials from the data file, -% by using a contrast that contains zeros for the to be removed file. +function D = spm_eeg_weight_epochs_TF(varargin) +%This function has been deprecated %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging +% $Id: spm_eeg_weight_epochs_TF.m 3209 2009-06-17 11:07:47Z vladimir $ -% Stefan Kiebel -% $Id: spm_eeg_weight_epochs_TF.m 2874 2009-03-13 11:52:24Z guillaume $ - -SVNrev = '$Rev: 2874 $'; - -%-Startup -%-------------------------------------------------------------------------- -spm('FnBanner', mfilename, SVNrev); -spm('FigName','M/EEG weigth epochs TF'); spm('Pointer','Watch'); - -%-Get MEEG object -%-------------------------------------------------------------------------- -try - D = S.D; -catch - [D, sts] = spm_select(1, 'mat', 'Select M/EEG mat file'); - if ~sts, D = []; return; end - S.D = D; -end - -D = spm_eeg_load(D); - -if ~strncmp(D.transformtype, 'TF', 2) - error('Use this function for time-frequency data only'); -end - -%-Get parameters -%-------------------------------------------------------------------------- -try - c = S.c; -catch - c = spm_input('Enter contrasts', '1', 'x', '', inf, eye(D.ntrials)); - S.c = c; -end - -Ncontrasts = size(c, 1); -if ~isfield(S, 'label') || numel(S.label)~=size(c, 1) - label = cell(1,Ncontrasts); - for i = 1:Ncontrasts - label{i} = spm_input(['Label of contrast ' num2str(i)], '+1', 's'); - end - S.label = label; -end - -if ~isempty(D.repl) - try - WeightAve = S.WeightAve; - catch - WeightAve = spm_input('Weight by num replications?', '+1', 'yes|no', [1 0]); - S.WeightAve = WeightAve; - end -else - WeightAve = 0; -end - -% here should be a sanity check of c - -Ncontrasts = size(c, 1); - -% generate new meeg object with new filenames -Dnew = clone(D, ['m' fnamedat(D)], [D.nchannels D.nfrequencies D.nsamples Ncontrasts]); - -spm_progress_bar('Init', Ncontrasts, 'Contrasts computed'); -if Ncontrasts > 100, Ibar = floor(linspace(1, Ncontrasts, 100)); -else Ibar = [1:Ncontrasts]; end - -for i = 1:Ncontrasts - - if WeightAve - p = find(c(i,:) == 1); - if ~isempty(p) - r = D.repl(p); - c(i,p) = r/sum(r); - end - - p = find(c(i,:) == -1); - if ~isempty(p) - r = D.repl(p); - c(i,p) = -r/sum(r); - end - end - - disp(['Contrast ', mat2str(i),': ', mat2str(c(i,:),3)]) - - d = zeros(D.nchannels, D.nfrequencies, D.nsamples); - - for j = 1:D.nchannels - for f = 1:D.nfrequencies - d(j, f, :) = c(i,:) * squeeze(D(j, f, :, :))'; - end - end - - Dnew(1:Dnew.nchannels, 1:D.nfrequencies, 1:Dnew.nsamples, i) = d; - - newrepl(i) = sum(D.repl(find(c(i,:)~=0))); - - if ismember(i, Ibar), spm_progress_bar('Set', i); end - -end - -spm_progress_bar('Clear'); - -Dnew = conditions(Dnew, [], S.label); -Dnew = trialonset(Dnew, [], []); -Dnew = reject(Dnew, [], 0); -Dnew = repl(Dnew, [], newrepl); - -% remove previous source reconsructions -if isfield(Dnew,'inv') - Dnew = rmfield(Dnew,'inv'); +persistent runonce +if isempty(runonce) + warning('spm_eeg_weight_epochs_TF is deprecated. Use spm_eeg_weight_epochs instead.'); + runonce = 1; end -D = Dnew; -D = D.history('spm_eeg_weight_epochs_TF', S); -save(D); - -%-Cleanup -%-------------------------------------------------------------------------- -spm('FigName','M/EEG weight epochs TF: done'); spm('Pointer','Arrow'); +D = spm_eeg_weight_epochs(varargin{:}); \ No newline at end of file diff --git a/spm_eeg_wrap_dipfit_vbecd.m b/spm_eeg_wrap_dipfit_vbecd.m new file mode 100644 index 0000000..dd1a4ee --- /dev/null +++ b/spm_eeg_wrap_dipfit_vbecd.m @@ -0,0 +1,64 @@ +function [y,outside]=spm_eeg_wrap_dipfit_vbecd(P,M,U) +% A cost function/wrapper to sit between non-linear optimisation spm_nlsi_gn.m +% and dipole fit routine spm__eeg_inv_vbecd.m +% sens and vol structures should be passed in M, where +% sens=M.Setup.forward.sens; +% vol=M.Setup.forward.vol; +% P contains a list of the free parameters (assuming all position +% parameters come first (in triplets) followed by all moment paameters +% (also in triplets) +% U is unused +% At the moment this removes the mean level from EEG data +% and reduces the rank of the MEG leadfield 2 dimensions. + +% Copyright (C) 2009 Wellcome Trust Centre for Neuroimaging +% +% $Id: spm_eeg_wrap_dipfit_vbecd.m 3372 2009-09-08 14:33:45Z gareth $ + +x=U.u; %% input , unused + + +sens=M.Setup.forward.sens; +vol=M.Setup.forward.vol; +posandmom=P; + +Pospars=3; +Mompars=3; +Ndippars=Pospars+Mompars; +Ndips=length(posandmom)/Ndippars; +if Ndips~=round(Ndips), + error('only works for 6 params per dipole'); +end; % if +allpos=reshape(P(1:Ndips*Pospars),Pospars,Ndips)'; +allmom=reshape(P(Ndips*Pospars+1:Ndips*Ndippars),Mompars,Ndips)'; + +MEGRANK=2; %% restricting rank of MEG data, could change this in future + +y=0; +outside=0; +for i=1:Ndips, + + pos=allpos(i,:); + mom=allmom(i,:); + + % mean correction of LF, only for EEG data. + if forwinv_senstype(sens, 'eeg') + [tmp] = forwinv_compute_leadfield(pos, sens, vol); + tmp = tmp - repmat(mean(tmp), size(tmp,1), 1); %% should this be here ? + else %% reduce rank of leadfield for MEG- assume one direction (radial) is silent + [tmp] = forwinv_compute_leadfield(pos, sens, vol,'reducerank',MEGRANK); + end + gmn=tmp; + y=y+gmn*mom'; + outside = outside+ ~forwinv_inside_vol(pos,vol); +end; % for i + + +y=y*M.sc_y; %% scale data appropriately +if outside + y=y.^2; + end; % penalise sources outside head + + + + diff --git a/spm_existfile.mexglx b/spm_existfile.mexglx index a8ce1d0..082b031 100644 Binary files a/spm_existfile.mexglx and b/spm_existfile.mexglx differ diff --git a/spm_existfile.mexw32 b/spm_existfile.mexw32 index 02b4322..1ceeb0e 100755 Binary files a/spm_existfile.mexw32 and b/spm_existfile.mexw32 differ diff --git a/spm_filter.m b/spm_filter.m index 3d449c0..f524b0e 100644 --- a/spm_filter.m +++ b/spm_filter.m @@ -16,30 +16,29 @@ % % K - filter structure % Y - filtered data -%___________________________________________________________________________ +%__________________________________________________________________________ % % spm_filter implements high-pass filtering in an efficient way by % using the residual forming matrix of X0 - low frequency confounds %.spm_filter also configures the filter structure in accord with the % specification fields if called with one argument -%___________________________________________________________________________ +%__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_filter.m 1143 2008-02-07 19:33:33Z spm $ - +% $Id: spm_filter.m 3303 2009-08-03 15:26:49Z guillaume $ % set or apply -%--------------------------------------------------------------------------- +%-------------------------------------------------------------------------- if nargin == 1 && isstruct(K) % set K.X0 - %------------------------------------------------------------------- + %---------------------------------------------------------------------- for s = 1:length(K) % make high pass filter - %----------------------------------------------------------- + %------------------------------------------------------------------ k = length(K(s).row); n = fix(2*(k*K(s).RT)/K(s).HParam + 1); X0 = spm_dctmtx(k,n); @@ -47,16 +46,16 @@ end % return structure - %------------------------------------------------------------------- + %---------------------------------------------------------------------- argout = K; else % apply - %------------------------------------------------------------------- + %---------------------------------------------------------------------- if isstruct(K) % ensure requisite fields are present - %----------------------------------------------------------- + %------------------------------------------------------------------ if ~isfield(K(1),'X0') K = spm_filter(K); end @@ -64,28 +63,26 @@ for s = 1:length(K) % select data - %--------------------------------------------------- + %-------------------------------------------------------------- y = Y(K(s).row,:); % apply high pass filter - %--------------------------------------------------- + %-------------------------------------------------------------- y = y - K(s).X0*(K(s).X0'*y); % reset filtered data in Y - %--------------------------------------------------- + %-------------------------------------------------------------- Y(K(s).row,:) = y; end % K is simply a filter matrix - %------------------------------------------------------------------- + %---------------------------------------------------------------------- else Y = K*Y; end % return filtered data - %------------------------------------------------------------------- - %if any(~isfinite(Y)), warning('Found non-finite values in Y (could be the data).'); end; + %---------------------------------------------------------------------- argout = Y; end - diff --git a/spm_gamrnd.m b/spm_gamrnd.m index b3e1ece..1eb5790 100644 --- a/spm_gamrnd.m +++ b/spm_gamrnd.m @@ -1,12 +1,12 @@ function r = spm_gamrnd(a,b,varargin) -% Random arrays from gamma distribution -% FUNCTION r = spm_gamrnd(a,b,m,n,...) +% Random arrays from gamma distribution - a compiled routine +% FORMAT r = spm_gamrnd(a,b,m,n,...) % -% a - shape parameter -% b - scale parameter -% m,n,... - dimensions of the output array [optional] +% a - shape parameter +% b - scale parameter +% m,n,... - dimensions of the output array [optional] % -% r - array of random numbers chosen from the gamma distribution +% r - array of random numbers chosen from the gamma distribution %__________________________________________________________________________ % % Reference @@ -14,89 +14,12 @@ % George Marsaglia and Wai Wan Tsang, "A Simple Method for Generating Gamma % Variables": ACM Transactions on Mathematical Software, Vol. 26, No. 3, % September 2000, Pages 363-372 -% -% Part of RANDRAW - Efficient Random Variates Generator: -% http://www.mathworks.com/matlabcentral/fileexchange/7309 -% Alex Bar Guy & Alexander Podgaetsky -% -% Version 1.1 -% -% Any comments and suggestions please send to: -% alex@wavion.co.il -% -% These programs are distributed in the hope that they will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +% http://portal.acm.org/citation.cfm?id=358414 %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging -% Alex Bar Guy & Alexander Podgaetsky -% $Id: spm_gamrnd.m 2685 2009-02-03 19:16:00Z guillaume $ - -error(nargchk(2,Inf,nargin)); - -if nargin == 2 - sampleSize = [1 1]; -else - sampleSize = [varargin{1:end}]; -end - -if a < 1 - % If a<1, one can use GAMMA(a)=GAMMA(1+a)*UNIFORM(0,1)^(1/a); - r = spm_gamrnd(1+a, 1, sampleSize) .* (rand(sampleSize).^(1/a)); - -else - - d = a - 1/3; - c = 1/sqrt(9*d); - - x = randn( sampleSize ); - v = 1+c*x; - - indxs = find(v <= 0); - while ~isempty(indxs) - indxsSize = size( indxs ); - xNew = randn( indxsSize ); - vNew = a+c*xNew; - - l = (vNew > 0); - v( indxs( l ) ) = vNew(l); - x( indxs( l ) ) = xNew(l); - indxs = indxs( ~l ); - end - - u = rand( sampleSize ); - v = v.^3; - x2 = x.^2; - r = d*v; - - indxs = find( (u>=1-0.0331*x2.^2) & (log(u)>=0.5*x2+d*(1-v+log(v))) ); - while ~isempty(indxs) - indxsSize = size( indxs ); - - x = randn( indxsSize ); - v = 1+c*x; - indxs1 = find(v <= 0); - while ~isempty(indxs1) - indxsSize1 = size( indxs1 ); - xNew = randn( indxsSize1 ); - vNew = a+c*xNew; - - l1 = (vNew > 0); - v( indxs1(l1) ) = vNew(l1); - x( indxs1(l1) ) = xNew(l1); - indxs1 = indxs1( ~l1 ); - end - - u = rand( indxsSize ); - v = v .* v .* v; - x2 = x.*x; - - l = (u<1-0.0331*x2.*x2) | (log(u)<0.5*x2+d*(1-v+log(v))); - r( indxs( l ) ) = d*v(l); - indxs = indxs( ~l ); - end % while ~isempty(indxs) - -end % if a < 1, else ... +% Guillaume Flandin +% $Id: spm_gamrnd.m 3251 2009-07-06 17:29:44Z guillaume $ -r = b .* r; +%-This is merely the help file for the compiled routine +error('spm_gamrnd.c not compiled - see Makefile'); diff --git a/spm_gamrnd.mexa64 b/spm_gamrnd.mexa64 new file mode 100755 index 0000000..017abf5 Binary files /dev/null and b/spm_gamrnd.mexa64 differ diff --git a/spm_gamrnd.mexglx b/spm_gamrnd.mexglx new file mode 100755 index 0000000..ae0bb01 Binary files /dev/null and b/spm_gamrnd.mexglx differ diff --git a/spm_gamrnd.mexmaci b/spm_gamrnd.mexmaci new file mode 100755 index 0000000..749222b Binary files /dev/null and b/spm_gamrnd.mexmaci differ diff --git a/spm_gamrnd.mexmaci64 b/spm_gamrnd.mexmaci64 new file mode 100755 index 0000000..4102f76 Binary files /dev/null and b/spm_gamrnd.mexmaci64 differ diff --git a/spm_gamrnd.mexw32 b/spm_gamrnd.mexw32 new file mode 100644 index 0000000..73321ed Binary files /dev/null and b/spm_gamrnd.mexw32 differ diff --git a/spm_gamrnd.mexw64 b/spm_gamrnd.mexw64 new file mode 100644 index 0000000..3a12797 Binary files /dev/null and b/spm_gamrnd.mexw64 differ diff --git a/spm_get_data.m b/spm_get_data.m index f382880..84ea357 100644 --- a/spm_get_data.m +++ b/spm_get_data.m @@ -1,23 +1,25 @@ -function [Y] = spm_get_data(V,XYZ) +function [Y] = spm_get_data(V,XYZ,check) % gets data from image files at specified locations % FORMAT [Y] = spm_get_data(V,XYZ); % -% V - [1 x n] struct array of file handles (or filename matrix) -% XYZ - [4 x m] or [3 x m]location matrix (voxel) +% V - [1 x n] struct array of file handles (or filename matrix) +% XYZ - [4 x m] or [3 x m]location matrix (voxel) +% check - check validity of input parameters [default: true] % -% Y - (n x m) double values +% Y - (n x m) double values % % see spm_sample_vol %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging - + % Karl Friston -% $Id: spm_get_data.m 2663 2009-01-28 20:24:36Z karl $ - - +% $Id: spm_get_data.m 3297 2009-07-29 17:20:19Z guillaume $ + +if nargin < 3, check = true; end + % ensure V is an array of handle structures %-------------------------------------------------------------------------- -if ~isstruct(V) +if check && ~isstruct(V) V = spm_vol(V); try V = cat(2,V{:}); @@ -31,7 +33,7 @@ % check files exists and try pwd %---------------------------------------------------------------------- - if ~spm_existfile(V(i).fname) + if check && ~spm_existfile(V(i).fname) [p,n,e] = fileparts(V(i).fname); V(i).fname = [n,e]; end diff --git a/spm_global.mexmaci b/spm_global.mexmaci index 82c6b78..9bcd0b2 100644 Binary files a/spm_global.mexmaci and b/spm_global.mexmaci differ diff --git a/spm_hist2.mexmaci b/spm_hist2.mexmaci index 54896c2..ff51a66 100644 Binary files a/spm_hist2.mexmaci and b/spm_hist2.mexmaci differ diff --git a/spm_int_J.m b/spm_int_J.m index c36e596..bceae2b 100644 --- a/spm_int_J.m +++ b/spm_int_J.m @@ -61,13 +61,13 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_int_J.m 2517 2008-12-02 10:36:11Z klaas $ +% $Id: spm_int_J.m 3264 2009-07-10 14:01:31Z karl $ % convert U to U.u if necessary and M(1) to M %-------------------------------------------------------------------------- if ~isstruct(U), u.u = U; U = u; end -M = M(1); +M = M(1); try, dt = U.dt; catch, dt = 1; end try, ns = M.ns; catch, ns = length(U.u); end @@ -86,7 +86,7 @@ try g = fcnchk(M.g,'x','u','P','M'); catch - g = []; + g = inline('x','x','v','P','M'); end % Initial states and inputs @@ -108,12 +108,12 @@ try f(x,u,P,M); catch - f = inline(char(f),'x','v','P','M'); + f = inline([char(f) '(x,v,P,M)'],'x','v','P','M'); end try g(x,u,P,M); catch - g = inline(char(g),'x','v','P','M'); + g = inline([char(g) '(x,v,P,M)'],'x','v','P','M'); end % check for delay operator @@ -150,11 +150,7 @@ % output - implement g(x) %---------------------------------------------------------------------- - if length(g) - y(:,i) = spm_vec(g(x,u,P,M)); - else - y(:,i) = spm_vec(x); - end + y(:,i) = spm_vec(g(x,u,P,M)); end diff --git a/spm_invdef.mexmaci b/spm_invdef.mexmaci index 1c9110a..03c244c 100644 Binary files a/spm_invdef.mexmaci and b/spm_invdef.mexmaci differ diff --git a/spm_jobman.m b/spm_jobman.m index 1a27474..4db0632 100644 --- a/spm_jobman.m +++ b/spm_jobman.m @@ -92,7 +92,7 @@ % Copyright (C) 2008 Freiburg Brain Imaging % Volkmar Glauche -% $Id: spm_jobman.m 3130 2009-05-18 14:41:31Z volkmar $ +% $Id: spm_jobman.m 3225 2009-06-25 17:29:45Z volkmar $ if nargin==0 @@ -146,15 +146,19 @@ addpath(fullfile(spm('Dir'),'config')); end cfg_util('initcfg'); % This must be the first call to cfg_util - f = cfg_ui('Visible','off'); % Create invisible batch ui - f0 = findobj(f, 'Tag','MenuFile'); % Add entries to file menu - f2 = uimenu(f0,'Label','Load SPM5 job', 'Callback',@load_job, ... - 'HandleVisibility','off', 'tag','jobs', ... - 'Separator','on'); - f3 = uimenu(f0,'Label','Bulk Convert SPM5 job(s)', ... - 'Callback',@conv_jobs, ... - 'HandleVisibility','off', 'tag','jobs'); - + if isdeployed + cfg_master; + end + if ~spm('cmdline') + f = cfg_ui('Visible','off'); % Create invisible batch ui + f0 = findobj(f, 'Tag','MenuFile'); % Add entries to file menu + f2 = uimenu(f0,'Label','Load SPM5 job', 'Callback',@load_job, ... + 'HandleVisibility','off', 'tag','jobs', ... + 'Separator','on'); + f3 = uimenu(f0,'Label','Bulk Convert SPM5 job(s)', ... + 'Callback',@conv_jobs, ... + 'HandleVisibility','off', 'tag','jobs'); + end case 'interactive', if exist('mljob', 'var') cjob = cfg_util('initjob', mljob); diff --git a/spm_list.m b/spm_list.m index 4f026f7..9f86565 100644 --- a/spm_list.m +++ b/spm_list.m @@ -115,7 +115,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston & Andrew Holmes -% $Id: spm_list.m 2821 2009-03-03 19:54:19Z guillaume $ +% $Id: spm_list.m 3342 2009-09-02 10:35:28Z guillaume $ % satellite figure global variable @@ -574,7 +574,8 @@ % Specifically changed so it properly finds hMIPax %------------------------------------------------------------------ - h = text(tCol(12),y,sprintf(TabDat.fmt{12},XYZmm(:,i)),... + tXYZmm = XYZmm(DIM,i); + h = text(tCol(12),y,sprintf(TabDat.fmt{12},tXYZmm),... 'FontWeight','Bold',... 'Tag','ListXYZ',... 'ButtonDownFcn',[... @@ -677,8 +678,9 @@ % specifically modified line to use hMIPax %------------------------------------------------------ + tXYZmm = XYZmm(DIM,d); h = text(tCol(12),y,... - sprintf(TabDat.fmt{12},XYZmm(:,d)),... + sprintf(TabDat.fmt{12},tXYZmm),... 'Tag','ListXYZ',... 'ButtonDownFcn',[... 'hMIPax = findobj(''tag'',''hMIPax'');',... diff --git a/spm_make_contrasts.m b/spm_make_contrasts.m index a6367a6..e9a1b1c 100644 --- a/spm_make_contrasts.m +++ b/spm_make_contrasts.m @@ -20,7 +20,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Will Penny -% $Id: spm_make_contrasts.m 1143 2008-02-07 19:33:33Z spm $ +% $Id: spm_make_contrasts.m 3249 2009-07-03 16:43:55Z guillaume $ % See section 5 of % http://www.fil.ion.ucl.ac.uk/~wpenny/publications/rik_anova.pdf @@ -34,7 +34,8 @@ elseif nf==2 k=[k 1]; elseif nf>3 - disp(sprintf('Error: spm_make_contrasts not written for %d-way ANOVAs !',nf)); + fprintf('spm_make_contrasts not written for %d-way ANOVAs !\n',nf); + Con = []; return end @@ -67,4 +68,4 @@ Con(6).name='Interaction, factor 1 x 3'; Con(7).name='Interaction, factor 2 x 3'; Con(8).name='Interaction, factor 1 x 2 x 3'; -end \ No newline at end of file +end diff --git a/spm_mesh_project.m b/spm_mesh_project.m new file mode 100644 index 0000000..a1c0bc2 --- /dev/null +++ b/spm_mesh_project.m @@ -0,0 +1,38 @@ +function P = spm_mesh_project(M, dat, method, varargin) +% Project volumetric data onto a mesh +% FORMAT P = spm_mesh_project(M, dat, method) +% M - a patch structure, a handle to a patch +% or a [nx3] vertices array +% dat - a structure array [1xm] with fields dim, mat, XYZ and t +% (see spm_render.m) +% method - interpolation method {'nn'} +% varargin - other parameters required by the interpolation method +% +% P - a [mxn] curvature vector +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Guillaume Flandin +% $Id: spm_mesh_project.m 3277 2009-07-15 11:47:40Z guillaume $ + +if ishandle(M) + V = get(M,'Vertices'); +elseif isstruct(M) + V = M.vertices; +else + V = M; +end + +if nargin < 3, method = 'nn'; end +if ~strcmpi(method,'nn') + error('Only Nearest Neighbours interpolation is available.'); +end + +P = zeros(length(dat),size(V,1)); +for i=1:length(dat) + Y = zeros(dat(i).dim(1:3)'); + OFF = dat(i).XYZ(1,:) + dat(i).dim(1)*(dat(i).XYZ(2,:)-1 + dat(i).dim(2)*(dat(i).XYZ(3,:)-1)); + Y(OFF) = dat(i).t .* (dat(i).t > 0); + XYZ = double(inv(dat(i).mat)*[V';ones(1,size(V,1))]); + P(i,:) = spm_sample_vol(Y,XYZ(1,:),XYZ(2,:),XYZ(3,:),0); +end diff --git a/spm_mip.m b/spm_mip.m index 76b468a..91054e8 100644 --- a/spm_mip.m +++ b/spm_mip.m @@ -34,7 +34,7 @@ function spm_mip(Z,XYZ,M,units) % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston et al. -% $Id: spm_mip.m 2829 2009-03-05 12:05:07Z guillaume $ +% $Id: spm_mip.m 3348 2009-09-03 10:32:01Z guillaume $ %-Get units and grid scaling %-------------------------------------------------------------------------- @@ -68,13 +68,19 @@ function spm_mip(Z,XYZ,M,units) %-Single slice case %-------------------------------------------------------------------------- -if isempty(units{3}) +if isempty(units{3}) && ~strcmp(units{2},'mm') + %-2d case: Time-Frequency or Frequency-Frequency + %---------------------------------------------------------------------- + mip = 4*grid_trans; + +elseif isempty(units{3}) + %-2d case %---------------------------------------------------------------------- mip = 4*grid_trans + mask_trans; -elseif units{3} == '%' +elseif strcmp(units{3},'ms') || strcmp(units{3},'Hz') %-3d case: Space-time %---------------------------------------------------------------------- diff --git a/spm_mip_ui.m b/spm_mip_ui.m index 1c32981..17d8adf 100644 --- a/spm_mip_ui.m +++ b/spm_mip_ui.m @@ -1,12 +1,13 @@ function varargout = spm_mip_ui(varargin) % GUI for displaying MIPs with interactive pointers -% FORMAT hMIPax = spm_mip_ui(Z,XYZ,M,DIM,F) +% FORMAT hMIPax = spm_mip_ui(Z,XYZ,M,DIM,F,units) % Z - {1 x ?} vector point list of SPM values for MIP % XYZ - {3 x ?} matrix of coordinates of points (Talairach coordinates) % M - voxels - > mm matrix % DIM - image dimensions {voxels} % F - Figure (or axes) to work in [Defaults to gcf] % hMIPax - handle of MIP axes +% units - units of space % % FORMAT xyz = spm_mip_ui('GetCoords',h) % h - Handle of MIP axes, or figure containing MIP axis [default gcf] @@ -68,7 +69,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Andrew Holmes -% $Id: spm_mip_ui.m 2842 2009-03-09 15:40:51Z guillaume $ +% $Id: spm_mip_ui.m 3348 2009-09-03 10:32:01Z guillaume $ %========================================================================== @@ -161,7 +162,7 @@ %-Axis offsets for 3d MIPs: %========================================================================== %-MIP pane dimensions and Talairach origin offsets -%-See spm_mip.c for derivation +%-See spm_project.c for derivation DXYZ = [182 218 182]; CXYZ = [091 127 073]; % DMIP = [DXYZ(2)+DXYZ(1), DXYZ(1)+DXYZ(3)]; @@ -210,7 +211,34 @@ xyz = spm_XYZreg('RoundCoords',[0;0;0],M,DIM); - + %-Display (MIP) transformation matrix + %---------------------------------------------------------------------- + if isequal(units,{'mm' 'mm' 'mm'}) + Md = eye(4); + elseif isequal(units,{'mm' 'mm' ''}) + Md = eye(4); + Md(3,4) = -100; % out of field of view (MNI) + elseif isequal(units,{'mm' 'mm' 'ms'}) || isequal(units,{'mm' 'mm' 'Hz'}) + Md = eye(4); + Md(3,3) = 80 / (M(3,3)*DIM(3)); + Md(3,4) = -80 * M(3,4) / (M(3,3)*DIM(3)); + elseif isequal(units,{'Hz' 'ms' ''}) || isequal(units,{'Hz' 'Hz' ''}) + Md = eye(4); + Md(1,1) = -136 / (M(1,1)*DIM(1)); + Md(1,4) = M(1,4)*136 / (M(1,1)*DIM(1)) + 68; + Md(2,2) = 172 / (M(2,2)*DIM(2)); + Md(2,4) = -M(2,4)*172 / (M(2,2)*DIM(2)) - 100; + Md(3,4) = -100; + elseif isequal(units,{'mm' 'mm' '%'}) + warning('Handling of data units changed: please re-estimate model.'); + units = {'mm' 'mm' 'ms'}; + Md = eye(4); + Md(3,3) = 80 / (M(3,3)*DIM(3)); + Md(3,4) = -80 * M(3,4) / (M(3,3)*DIM(3)); + else + error('Unknown data units.'); + end + %-Display MIP %---------------------------------------------------------------------- Funits = get(F,'Units'); @@ -223,7 +251,7 @@ %-NB: spm_mip's `image` uses a newplot, & screws stuff without the figure. figure(F) - spm_mip(Z,XYZ,M,units); + spm_mip(Z,Md(1:3,:)*[XYZ;ones(1,size(XYZ,2))],Md*M,units); hMIPim = get(gca,'Children'); @@ -241,6 +269,7 @@ %-Create point markers %---------------------------------------------------------------------- + xyz = Md(1:3,:)*[xyz(:);1]; hX1r = text(Po(1)+xyz(2),Po(2)+xyz(1),'<',... 'Color','r','Fontsize',16,... 'Tag','hX1r',... @@ -309,6 +338,7 @@ 'XYZ', XYZ,... 'Z', Z,... 'M', M,... + 'Md', Md,... 'DIM', DIM,... 'hXr', hXr)) @@ -349,7 +379,7 @@ %-Move marker points, update internal cache in hMIPxyz %------------------------------------------------------------------ - spm_mip_ui('PosnMarkerPoints',xyz,h,'r'); + spm_mip_ui('PosnMarkerPoints',MD.Md(1:3,:)*[xyz;1],h,'r'); set(MD.hMIPxyz,'UserData',reshape(xyz(1:3),3,1)) set(MD.hMIPxyz,'String',{'{\bfSPM}{\itmip}',sprintf('[%g, %g, %g]',xyz(1:3))}) @@ -371,7 +401,7 @@ if ~any(strcmp(r,{'r','g'})), error('Invalid pointer colour spec'), end if nargin<3, h=spm_mip_ui('FindMIPax'); else h=varargin{3}; end if nargin<2, xyz = spm_mip_ui('GetCoords',h); else xyz = varargin{2}; end - + %-Get handles of marker points of appropriate colour from UserData of hMIPax %------------------------------------------------------------------ hX = getfield(get(h,'UserData'),['hX',r]); @@ -483,6 +513,7 @@ 'MIPaxPos', get(hMIPax,'Position')*[1,0;0,1;0,0;0,0],... 'hMIPxyz', MD.hMIPxyz,... 'M', MD.M,... + 'Md', MD.Md,... 'DIM', MD.DIM,... 'hX', MD.hXr)) @@ -555,7 +586,8 @@ else error('Can''t work out which marker point') end - + xyz = inv(MS.Md) * [xyz(:);1]; xyz = xyz(1:3); + %-Round coordinates according to DragType & set in hMIPxyz's UserData %------------------------------------------------------------------ if DragType==0 @@ -575,9 +607,10 @@ %-Move marker points %------------------------------------------------------------------ set(MS.hX,'Units','Data') - set(MS.hX(1),'Position',[ Po(1) + xyz(2), Po(2) + xyz(1), 0]) - set(MS.hX(2),'Position',[ Po(1) + xyz(2), Po(3) - xyz(3), 0]) - set(MS.hX(3),'Position',[ Po(4) + xyz(1), Po(3) - xyz(3), 0]) + xyz2 = MS.Md(1:3,1:4) * [xyz(:);1]; + set(MS.hX(1),'Position',[ Po(1) + xyz2(2), Po(2) + xyz2(1), 0]) + set(MS.hX(2),'Position',[ Po(1) + xyz2(2), Po(3) - xyz2(3), 0]) + set(MS.hX(3),'Position',[ Po(4) + xyz2(1), Po(3) - xyz2(3), 0]) %-Update dynamic co-ordinate strings (if appropriate DragType) diff --git a/spm_multrnd.m b/spm_multrnd.m new file mode 100644 index 0000000..13fe137 --- /dev/null +++ b/spm_multrnd.m @@ -0,0 +1,19 @@ +function [m] = spm_multrnd(p,N) +% Sample from multinomial distribution +% FORMAT [m] = spm_multrnd(p,N) +% +% p - [M x 1] vector of probabilities +% N - Number of samples to generate +% +% m - [N x 1] vector of samples, where each sample is number from 1 to M +%__________________________________________________________________________ +% Copyright (C) 2009 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_multrnd.m 3190 2009-06-08 17:13:36Z guillaume $ + +cp = [0; cumsum(p(:))]; +m = zeros(N,1); +for n=1:N + m(n) = find(rand > cp, 1, 'last'); +end diff --git a/spm_mvb.m b/spm_mvb.m index 3042db6..cb1a989 100644 --- a/spm_mvb.m +++ b/spm_mvb.m @@ -16,16 +16,41 @@ % G: covariance partition indices % h: covariance hyperparameters % U: ordered patterns -% qE: conditional expectation of voxel weights +% M: MAP projector: qE = M*X +% qE: conditional expectation of voxel weights % qC: conditional variance of voxel weights -% Cp: prior covariance (ordered pattern space) -% cp: prior covariance (original pattern space) +% Cp: empirical prior covariance (ordered pattern space) +% cp: empirical prior covariance (original pattern space) %__________________________________________________________________________ % % model: X = Y*P + X0*Q + R % P = U*E; % cov(E) = h1*diag(G(:,1)) + h2*diag(G(:,2)) + ... % +% This routine uses a multivariate Bayesian (MVB) scheme to decode or +% recognise brain states from neuroimages. It resolves the ill-posed +% many-to-one mapping, from voxel values or data features to a target +% variable, using a parametric empirical or hierarchical Bayesian model. +% This model is inverted using standard variational techniques, in this +% case expectation maximisation, to furnish the model evidence and the +% conditional density of the model's parameters. This allows one to compare +% different models or hypotheses about the mapping from functional or +% structural anatomy to perceptual and behavioural consequences (or their +% deficits). The aim of MVB is not to predict (because the outcomes are +% known) but to enable inference on different models of structure-function +% mappings; such as distributed and sparse representations. This allows one +% to optimise the model itself and produce predictions that outperform +% standard pattern classification approaches, like support vector machines. +% Technically, the model inversion and inference uses the same empirical +% Bayesian procedures developed for ill-posed inverse problems (e.g., +% source reconstruction in EEG). +% +% CAUTION: MVB should not be used to establish a significant mapping +% between brain states and some classification or contrast vector. Its use +% is limited to comparison of different models under the assumption +% (hyperprior) that this mapping exists. To ensure the mapping exists, use +% CVA or related approaches. +% % See spm_mvb_ui and: % % Bayesian decoding of brain images. @@ -44,7 +69,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_mvb.m 3139 2009-05-21 18:37:29Z karl $ +% $Id: spm_mvb.m 3334 2009-08-25 16:13:38Z karl $ % defaults (use splits +/- one standard deviation by default) %-------------------------------------------------------------------------- @@ -136,6 +161,7 @@ % remove some patterns if there are too many %-------------------------------------------------------------------------- +clear M X Y qE = sum(model.qE.^2,2); [i j] = sort(-qE); try @@ -148,8 +174,15 @@ cp = model.Cp; Cp = cp(i,i); MAP = U*model.MAP(i,:); -qE = U*model.qE(i,:); - + +% try to save conditional expectations (if there is enough memory) +%-------------------------------------------------------------------------- +try + qE = U*model.qE(i,:); +catch + qE = []; +end + % remove confounds from L = Y*U %-------------------------------------------------------------------------- L = L - X0*pinv(full(X0))*L; @@ -161,7 +194,7 @@ model.F = F; model.U = U; -model.M = MAP; +model.M = MAP; % MAP projector model.qE = qE; % conditional expectation model.Cp = Cp; % prior covariance (ordered) model.cp = cp; % prior covariance (original) diff --git a/spm_mvb_G.m b/spm_mvb_G.m index 5595faf..83eaaec 100644 --- a/spm_mvb_G.m +++ b/spm_mvb_G.m @@ -38,7 +38,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_mvb_G.m 3139 2009-05-21 18:37:29Z karl $ +% $Id: spm_mvb_G.m 3337 2009-08-27 14:53:32Z karl $ % defaults %-------------------------------------------------------------------------- @@ -84,13 +84,16 @@ % inverse solution %========================================================================== -% Covariance components (with the first error Qe{1} fixed at -4) +% Covariance components (with the first error Qe{1} fixed) %-------------------------------------------------------------------------- -if size(L,2) +if size(L,2) > 0 Q = {Qe{1} Qe{:} LQpL{:}}; else Q = {Qe{1} Qe{:}}; end + +% Lower bound on noise Qe{1} relative to signal (of about 2%) +%-------------------------------------------------------------------------- m = length(Q); hE = -32*ones(m,1); hC = 256*speye(m,m); @@ -99,17 +102,16 @@ % ReML %-------------------------------------------------------------------------- -[Cy,h,P,F] = spm_reml_sc(X*X',[],Q,size(X,2),hE,hC); +[Cy,h,P,F,Fa,Fc] = spm_reml_sc(X*X',[],Q,size(X,2),hE,hC); h(2) = h(2) + h(1); h = h(2:end); - + % prior covariance: source space %-------------------------------------------------------------------------- Cp = sparse(Nk,Nk); -hp = h([1:Np] + Ne); for i = 1:Np - Cp = Cp + hp(i)*Qp{i}; + Cp = Cp + h(i + Ne)*Qp{i}; end % MAP estimates of instantaneous sources @@ -126,3 +128,4 @@ model.qE = qE; model.MAP = MAP; model.Cp = Cp; + diff --git a/spm_mvb_p.m b/spm_mvb_p.m new file mode 100644 index 0000000..f26c12f --- /dev/null +++ b/spm_mvb_p.m @@ -0,0 +1,108 @@ +function [p] = spm_mvb_p(MVB,k) +% Classical p-value for MVB using null distribution of log-odds ratio +% FORMAT [p] = spm_mvb_p(MVB,k) +% +% MVB - Multivariate Bayes structure +% k - number of smaples > 20 +% +% p - p-value: of (relative) F using an empirical null distribution +% +% spm_mvb_p evaluates an empirical null distribution for the (fee-energy) +% difference in log-evidences (the log odds ratio) by phase-shuffling the +% target vector and repeating the greedy search. It adds the p-value as a +% field (p_value) to MVB. +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_mvb_p.m 3334 2009-08-25 16:13:38Z karl $ + +%-number of samples +%-------------------------------------------------------------------------- +try + k; +catch + str = 'number of samples for null distribution'; + k = spm_input(str,'!+1','b',{'20','50','100'},[20 50 100]); +end + +%-Get figure handles and set title +%-------------------------------------------------------------------------- +Fmvb = spm_figure('GetWin','MVB'); +spm_clf(Fmvb); + +% get MVB results +%========================================================================== +try + MVB; +catch + mvb = spm_select(1,'mat','please select models',[],pwd,'MVB_*'); + MVB = load(mvb(1,:)); + MVB = MVB.MVB; +end + + +% whiten target and predictor (X) variables (Y) (i.e., remove correlations) +%-------------------------------------------------------------------------- +K = MVB.K; +X = K*MVB.X; +Y = K*MVB.Y; +X0 = K*MVB.X0; +U = MVB.M.U; + +% create orthonormal projection to remove confounds +%-------------------------------------------------------------------------- +Ns = length(X); +X0 = spm_svd(X0); +R = speye(Ns) - X0*X0'; +R = spm_svd(R); +Y = R'*Y; + +% F value (difference in log-evidence or log odds ratio) +%-------------------------------------------------------------------------- +F = MVB.M.F; +F = max(F) - F(1); + +% k-fold cross-validation +%========================================================================== +for i = 1:k + + % randomise target vector + %---------------------------------------------------------------------- + X0 = R'*spm_phase_shuffle(X); + + % Training + %====================================================================== + M = spm_mvb(X0,Y,[],U,[],MVB.Ni,MVB.sg); + + % record F and compute p-value + %---------------------------------------------------------------------- + F0(i) = max(M.F) - M.F(1); + p = 1 - sum(F0 < F)/(1 + i); + + % plot + %---------------------------------------------------------------------- + subplot(2,1,1) + q = ceil(i/4); + hist(F0,q); hold on + plot([F F],[0 q],'r'); hold off; + str = sprintf('p < %0.4f (%i samples)',p,i); + title({[MVB.name ' (' MVB.contrast ')']; str}) + xlabel('log odds ratio') + ylabel('null distribution') + axis square + drawnow + +end + + +% display and assign in base memory +%-------------------------------------------------------------------------- +fprintf('\n randomisation p-value = %.4f; \n',p) +MVB.p_value = p; + + +% save results +%-------------------------------------------------------------------------- +save(MVB.name,'MVB') +assignin('base','MVB',MVB) diff --git a/spm_mvb_ui.m b/spm_mvb_ui.m index c651dc3..d9be82b 100644 --- a/spm_mvb_ui.m +++ b/spm_mvb_ui.m @@ -1,6 +1,6 @@ -function [MVB] = spm_mvb_ui(xSPM,SPM,hReg) +function [MVB] = spm_mvb_ui(xSPM,SPM,hReg,MVB) % multivariate Bayes (Bayesian decoding of a contrast) -% % FORMAT [MVB] = spm_mvb_ui(xSPM,SPM,hReg) +% % FORMAT [MVB] = spm_mvb_ui(xSPM,SPM,hReg,MVB) % % Sets up, evaluates and saves an MVB structure: % @@ -20,6 +20,7 @@ % MVB.Sp_info % parameters of VOI % MVB.Ni % number of greedy search steps % MVB.sg % size of reedy search split +% MVB.priors % model (spatial prior) % MVB.fSPM % SPM analysis (.mat file) % % where MVB.M contins the following field: @@ -33,10 +34,35 @@ % Cp: prior covariance (ordered pattern space) % cp: prior covariance (original pattern space) % -% See: +%-------------------------------------------------------------------------- +% This routine uses a multivariate Bayesian (MVB) scheme to decode or +% recognise brain states from neuroimages. It resolves the ill-posed +% many-to-one mapping, from voxel values or data features to a target +% variable, using a parametric empirical or hierarchical Bayesian model. +% This model is inverted using standard variational techniques, in this +% case expectation maximisation, to furnish the model evidence and the +% conditional density of the model's parameters. This allows one to compare +% different models or hypotheses about the mapping from functional or +% structural anatomy to perceptual and behavioural consequences (or their +% deficits). The aim of MVB is not to predict (because the outcomes are +% known) but to enable inference on different models of structure-function +% mappings; such as distributed and sparse representations. This allows one +% to optimise the model itself and produce predictions that outperform +% standard pattern classification approaches, like support vector machines. +% Technically, the model inversion and inference uses the same empirical +% Bayesian procedures developed for ill-posed inverse problems (e.g., +% source reconstruction in EEG). +% +% CAUTION: MVB should not be used to establish a significant mapping +% between brain states and some classification or contrast vector. Its use +% is limited to comparison of different models under the assumption +% (hyperprior) that this mapping exists. To ensure the mapping exists, use +% CVA or related approaches. +% +% See: spm_mvb and % % Bayesian decoding of brain images. -% Friston K, Chu C, Mourão-Miranda J, Hulme O, Rees G, Penny W, Ashburner J. +% Friston K, Chu C, Mourao-Miranda J, Hulme O, Rees G, Penny W, Ashburner J. % Neuroimage. 2008 Jan 1;39(1):181-205 % % Multiple sparse priors for the M/EEG inverse problem. @@ -51,88 +77,86 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_mvb_ui.m 3139 2009-05-21 18:37:29Z karl $ +% $Id: spm_mvb_ui.m 3378 2009-09-09 16:47:16Z guillaume $ %-Get figure handles and set title %-------------------------------------------------------------------------- -Fmvb = spm_figure('GetWin','MVB'); +Finter = spm_figure('FindWin','Interactive'); +spm_results_ui('Clear'); +spm_input('!DeleteInputObj'); +header = get(Finter,'Name'); +Fmvb = spm_figure('GetWin','MVB'); spm_clf(Fmvb); %-Get contrast: only the first line of F-contrast %-------------------------------------------------------------------------- -contrast = SPM.xCon(xSPM.Ic).name; -c = SPM.xCon(xSPM.Ic).c(:,1); +contrast = SPM.xCon(xSPM.Ic).name; +c = SPM.xCon(xSPM.Ic).c(:,1); %-Get VOI name %-------------------------------------------------------------------------- -name = ['MVB_' spm_input('name','-8','s',contrast)]; +try + name = MVB.name; +catch + name = spm_input('name','-8','s',contrast); +end +name = strrep(name,' ','_'); +name = ['MVB_' name]; %-Get current location {mm} %-------------------------------------------------------------------------- -xyzmm = spm_results_ui('GetCoords'); - +try + xyzmm = MVB.xyzmm; +catch + xyzmm = spm_results_ui('GetCoords'); +end + %-Specify search volume %-------------------------------------------------------------------------- -str = sprintf(' at [%.0f,%.0f,%.0f]',xyzmm(1),xyzmm(2),xyzmm(3)); -SPACE = spm_input('Search volume...','!+1','m',... - {['Sphere',str],['Box',str],'Image'},['S','B','I']); -Q = ones(1,size(SPM.xVol.XYZ,2)); -XYZmm = SPM.xVol.M*[SPM.xVol.XYZ; Q]; -XYZmm = XYZmm(1:3,:); - -switch SPACE - - case 'S' %-Sphere - %--------------------------------------------------------------- - D = spm_input('radius of VOI {mm}','!+1'); - str = sprintf('%0.1fmm sphere',D); - j = find(sum((XYZmm - xyzmm*Q).^2) <= D^2); - - - case 'B' %-Box - %--------------------------------------------------------------- - D = spm_input('box dimensions [k l m] {mm}','!+1'); - if length(D) < 3 - D = D(1)*[1 1 1]; - end - str = sprintf('%0.1f x %0.1f x %0.1f mm box',D(1),D(2),D(3)); - j = find(all(abs(XYZmm - xyzmm*Q) <= D(:)*Q/2)); - - - case 'I' %-Mask Image - %--------------------------------------------------------------- - Msk = spm_select(1,'image','Image defining search volume'); - D = spm_vol(Msk); - str = sprintf('image mask: %s',spm_str_manip(Msk,'a30')); - XYZ = D.mat \ [XYZmm; Q]; - j = find(spm_sample_vol(D, XYZ(1,:), XYZ(2,:), XYZ(3,:),0) > 0); - +try + xY = MVB.xY; + MVB = rmfield(MVB,'xY'); +catch + xY = []; end +xY.xyz = xyzmm; + +Q = ones(1,size(SPM.xVol.XYZ,2)); +XYZmm = SPM.xVol.M(1:3,:)*[SPM.xVol.XYZ; Q]; + +[xY, XYZ, j] = spm_ROI(xY, XYZmm); % Get explanatory variables (data) %-------------------------------------------------------------------------- -XYZ = XYZmm(:,j); -Y = spm_get_data(SPM.xY.VY,SPM.xVol.XYZ(:,j)); +XYZ = XYZmm(:,j); +Y = spm_get_data(SPM.xY.VY,SPM.xVol.XYZ(:,j)); % Check there are intracranial voxels %-------------------------------------------------------------------------- if isempty(Y) - warndlg({'No voxels in this VOI';'Please use a larger volume'}) - return + spm('alert*',{'No voxels in this VOI';'Please use a larger volume'},... + 'Multivariate Bayes'); end %-Get model[s] %-------------------------------------------------------------------------- -str = {'compact','sparse','smooth','support'}; -Ip = spm_input('model (spatial prior)','!+1','m',str); -priors = str{Ip}; - +try + priors = lower(MVB.priors); +catch + str = {'compact','sparse','smooth','support'}; + Ip = spm_input('model (spatial prior)','!+1','m',str); + priors = str{Ip}; +end + %-Number of iterations %-------------------------------------------------------------------------- -str = 'size of successive subdivisions'; -sg = spm_input(str,'!+1','e',.5); - +try + sg = MVB.sg; +catch + str = 'size of successive subdivisions'; + sg = spm_input(str,'!+1','e',.5); +end % MVB is now specified %========================================================================== @@ -162,8 +186,12 @@ % invert %========================================================================== U = spm_mvb_U(Y,priors,X0,XYZ,xSPM.VOX); -str = 'Greedy search steps'; -Ni = spm_input(str,'!+1','i',max(8,ceil(log(size(U,2))/log(1/sg)))); +try + Ni = MVB.Ni; +catch + str = 'Greedy search steps'; + Ni = spm_input(str,'!+1','i',max(8,ceil(log(size(U,2))/log(1/sg)))); +end M = spm_mvb(X,Y,X0,U,V,Ni,sg); M.priors = priors; @@ -181,10 +209,11 @@ MVB.K = full(V)^(-1/2); % whitening matrix MVB.VOX = xSPM.M; % voxel scaling MVB.xyzmm = xyzmm; % centre of VOI (mm) -MVB.Space = SPACE; % VOI definition -MVB.Sp_info = D; % parameters of VOI +MVB.Space = xY.def; % VOI definition +MVB.Sp_info = xY.spec; % parameters of VOI MVB.Ni = Ni; % number of greedy search steps MVB.sg = sg; % size of reedy search split +MVB.priors = priors; % model (spatial prior) MVB.fSPM = fullfile(SPM.swd,'SPM.mat'); % SPM analysis (.mat file) % display @@ -198,10 +227,9 @@ else save(fullfile(SPM.swd,name),'MVB') end - +assignin('base','MVB',MVB) + %-Reset title %-------------------------------------------------------------------------- -spm('FigName',['SPM{',xSPM.STAT,'}: Results']); +set(Finter,'Name',header) spm('Pointer','Arrow') - - diff --git a/spm_nlsi_N.m b/spm_nlsi_N.m index 9302e9a..ffdab5b 100644 --- a/spm_nlsi_N.m +++ b/spm_nlsi_N.m @@ -73,7 +73,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_nlsi_N.m 2921 2009-03-23 17:59:50Z guillaume $ +% $Id: spm_nlsi_N.m 3337 2009-08-27 14:53:32Z karl $ % figure (unless disabled) %-------------------------------------------------------------------------- @@ -382,7 +382,7 @@ % prevent overflow %-------------------------------------------------------------- - h = min(max(h,-16),16); + h = min(max(h,-32),32); % convergence %-------------------------------------------------------------- @@ -546,6 +546,7 @@ Cg = Vg*Cb([1:ng] + np,[1:ng] + np)*Vg'; F = C.F; warning(sw); + return diff --git a/spm_orthviews.m b/spm_orthviews.m index 082bd86..1c1ae77 100644 --- a/spm_orthviews.m +++ b/spm_orthviews.m @@ -116,7 +116,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % John Ashburner, Matthew Brett, Tom Nichols and Volkmar Glauche -% $Id: spm_orthviews.m 2947 2009-03-25 11:23:38Z volkmar $ +% $Id: spm_orthviews.m 3386 2009-09-11 07:54:46Z volkmar $ @@ -1662,7 +1662,15 @@ function c_menu(varargin) else defstr = ''; end; - spm_orthviews('window',current_handle,spm_input('Range','+1','e',defstr,2)); + [w yp] = spm_input('Range','+1','e',defstr,[1 inf]); + while numel(w) < 1 || numel(w) > 2 + uiwait(warndlg('Window must be one or two numbers','Wrong input size','modal')); + [w yp] = spm_input('Range',yp,'e',defstr,[1 inf]); + end + if numel(w) == 1 + w(2) = w(1)+eps; + end + spm_orthviews('window',current_handle,w); end; case 'window_gl', @@ -1677,10 +1685,16 @@ function c_menu(varargin) else defstr = ''; end; - data = spm_input('Range','+1','e',defstr,2); - + [w yp] = spm_input('Range','+1','e',defstr,[1 inf]); + while numel(w) < 1 || numel(w) > 2 + uiwait(warndlg('Window must be one or two numbers','Wrong input size','modal')); + [w yp] = spm_input('Range',yp,'e',defstr,[1 inf]); + end + if numel(w) == 1 + w(2) = w(1)+eps; + end for i = 1:length(get_cm_handles), - st.vols{i}.window = data; + st.vols{i}.window = w; end; end; redraw_all; diff --git a/spm_peb_ppi.m b/spm_peb_ppi.m index 8cffb9a..8f66b16 100644 --- a/spm_peb_ppi.m +++ b/spm_peb_ppi.m @@ -1,26 +1,47 @@ -function PPI = spm_peb_ppi(SPM) +function PPI = spm_peb_ppi(varargin) % Bold deconvolution to create physio- or psycho-physiologic interactions -% FORMAT PPI = spm_peb_ppi(SPM); +% FORMAT PPI = spm_peb_ppi(SPMname,ppiflag,VOI,Uu,ppiname,showGraphics); % -% SPM - Structure containing generic details about the analysis +% SPM - Structure containing generic details about the analysis or +% the fully qualified filename of such a structure. +% ppiflag - Type of analysis. Must be one of: +% 'simple deconvolution' or 'sd' +% 'psychophysiologic interaction' or 'ppi' +% 'physiophysiologic interaction' or 'phipi' +% VOI - Structure containing details about a VOI (as produced by +% spm_regions) or the fully qualified filename of such a +% structure. If a structure, then VOI should be of size 1x1 +% in the case of simple deconvolution, and psychophysiologic +% interactions) or 1x2, in the case of physiophysiologic +% interactions. If a file name it should be 1xN or 2xN. +% Uu - Matrix of input variables and contrast weights. This is an +% [n x 3] matrix. The first column indexes SPM.Sess.U(i). The +% second column indexes the name of the input or cause, see +% SPM.Sess.U(i).name{j}. The third column is the contrast +% weight. Unless there are parametric effects the second +% colulmn will generally be a 1. +% ppiname - Filename of PPI to save. The path should not be included. +% The PPI file will be saved in the SPM working directory. +% showGraphics - empty or 1 = yes, 0 = no. % -% PPI.ppi = (PSY*xn or xn1*xn2) convolved with the HRF -% PPI.Y = Original BOLD eigenvariate. Use as covariate of no interest. -% PPI.P = PSY convolved with HRF for psychophysiologic interactions, +% +% PPI.ppi - (PSY*xn or xn1*xn2) convolved with the HRF +% PPI.Y - Original BOLD eigenvariate. Use as covariate of no interest. +% PPI.P - PSY convolved with HRF for psychophysiologic interactions, % or in the case of physiophysologic interactions contains % the eigenvariate of the second region. -% PPI.name = Name of PPI -% PPI.xY = Original VOI information -% PPI.xn = Deconvolved neural signal(s) -% PPI.U.u = Psychological variable or input function (PPIs only) -% PPI.U.w = Contrast weights for psychological variable (PPIs only) -% PPI.U.name = Names of psychological conditions (PPIs only) +% PPI.name - Name of PPI +% PPI.xY - Original VOI information +% PPI.xn - Deconvolved neural signal(s) +% PPI.U.u - Psychological variable or input function (PPIs only) +% PPI.U.w - Contrast weights for psychological variable (PPIs only) +% PPI.U.name - Names of psychological conditions (PPIs only) %--------------------------------------------------------------------- % % This routine is effectively a hemodynamic deconvolution using % full priors and EM to deconvolve the HRF from a hemodynamic % time series to give a neuronal time series [that can be found in -% PPI.xn]. This deconvolution conforms to Weiner filtering +% PPI.xn]. This deconvolution conforms to Weiner filtering. % The neuronal process is then used to form PPIs..... % % SETTING UP A PPI THAT ACCOUNTS FOR THE HRF @@ -44,7 +65,7 @@ % % It is actually the right hand side of the equation one wants. % Thus one has to work backwards, in a sense, and deconvolve the hrf -% from x to get xn. This can them be multiplied by P and the resulting +% from x to get xn. This can then be multiplied by P and the resulting % vector (or matrix) reconvolved with the hrf. % % This algorithm uses a least squares strategy to solve for xn. @@ -69,12 +90,34 @@ % % Conditional estimates of B allow for priors that ensure % uniform variance over frequencies. +%--------------------------------------------------------------------- +% +% PPI STATISTICAL MODEL +% ================================================================== +% Once the PPI.ppi interaction term has been calculated a new GLM must be +% setup to search for the interaction effects across the brain. This is +% done using a standard, first level, fMRI model, which must include 3 +% covariates, PPI.ppi (interaction), PPI.Y (main effect: source region bold +% signal) and PPI.P (main effect: "psychological" condition), plus any +% nuisance regressors according to the particular design. +% +% NB: Designs that include only the interaction term without the main +% effects are not proper as inferences on the interaction will include a +% mixture of both main and interaction effects. + +% Once the model has been setup and run, a contrast of [1 0 0 ] over the +% PPI.ppi, PPI.Y and PPI.P columns respectively, will show regions with a +% positive relationship to the interaction term, discounting any main +% effects. Negative regressions can be examined with [-1 0 0]. A PPI random +% effects analysis would involve taking the con*.img files from the [1 0 0] +% t-contrast for each subject and forwarding them to a second level +% analysis. % %--------------------------------------------------------------------- % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Darren Gitelman -% $Id: spm_peb_ppi.m 2489 2008-11-23 23:17:08Z Darren $ +% $Id: spm_peb_ppi.m 3277 2009-07-15 11:47:40Z guillaume $ % set up the graphical interface @@ -88,14 +131,20 @@ % check inputs and set up variables %---------------------------------------------------------------------- -if ~nargin - [P, sts] = spm_select(1,'^SPM\.mat$','Select SPM.mat'); - if ~sts, return; end - swd = spm_str_manip(P,'H'); - load(fullfile(swd,'SPM.mat')) - cd(swd) - clear P +% First input is the SPM.mat file or its name +if nargin >0 && isstruct(varargin{1}) + SPM = varargin{1}; + swd = SPM.swd; +else + try + load(varargin{1}) + swd = SPM.swd; + catch + swd = spm_str_manip(spm_select(1,'^SPM\.mat$','Select SPM.mat'),'H'); + load(fullfile(swd,'SPM.mat')) + end end +cd(swd) RT = SPM.xY.RT; dt = SPM.xBF.dt; NT = RT/dt; @@ -103,102 +152,166 @@ % Ask whether to perform physiophysiologic or psychophysiologic interactions %-------------------------------------------------------------------------- -set(Finter,'name','PPI Setup') -ppiflag = { 'simple deconvolution',... - 'psychophysiologic interaction',... - 'physiophysiologic interaction'}; -i = spm_input('Analysis type?',1,'m',ppiflag); -ppiflag = ppiflag{i}; +try + ppiflag = varargin{2}; +catch + set(Finter,'name','PPI Setup') + ppiflag = {'simple deconvolution',... + 'psychophysiologic interaction',... + 'physiophysiologic interaction'}; + i = spm_input('Analysis type?',1,'m',ppiflag); + ppiflag = ppiflag{i}; +end -switch ppiflag - - -case 'simple deconvolution' - %===================================================================== - spm_input('physiological variable:... ',2,'d'); - voi = spm_select(1,'^VOI.*\.mat$',{'select VOI'}); - p = load(deblank(voi(:))','xY'); - xY(1) = p.xY; - Sess = SPM.Sess(xY(1).Sess); - - - -case 'physiophysiologic interaction' % interactions between 2 regions - %===================================================================== - spm_input('physiological variables:... ',2,'d'); - voi = spm_select(2,'^VOI.*\.mat$',{'select VOIs'}); - for i = 1:2 - p = load(deblank(voi(i,:)),'xY'); - xY(i) = p.xY; - end - Sess = SPM.Sess(xY(1).Sess); - - - -case 'psychophysiologic interaction' % get hemodynamic response - %===================================================================== - spm_input('physiological variable:... ',2,'d'); - voi = spm_select(1,'^VOI.*\.mat$',{'select VOI'}); - p = load(deblank(voi(:))','xY'); - xY(1) = p.xY; - Sess = SPM.Sess(xY(1).Sess); - - % get 'causes' or inputs U - %---------------------------------------------------------------------- - spm_input('Psychological variable:... ',2,'d'); - u = length(Sess.U); - U.name = {}; - U.u = []; - U.w = []; - for i = 1:u - for j = 1:length(Sess.U(i).name) - str = ['include ' Sess.U(i).name{j} '?']; - if spm_input(str,3,'y/n',[1 0]) - U.u = [U.u Sess.U(i).u(33:end,j)]; - U.name{end + 1} = Sess.U(i).name{j}; - str = 'Contrast weight'; - U.w = [U.w spm_input(str,4,'e',[],1)]; +switch lower(ppiflag) + case {'simple deconvolution','sd'} + %===================================================================== + if nargin > 2 && isstruct(varargin{3}) + p.xY = varargin{3}; + else + try + VOI = varargin{3}; + p = load(deblank(VOI(1,:)),'xY'); + catch + spm_input('physiological variable:... ',2,'d'); + voi = spm_select(1,'^VOI.*\.mat$',{'select VOI'}); + p = load(deblank(voi(:))','xY'); end end - end - - + xY(1) = p.xY; + Sess = SPM.Sess(xY(1).Sess); + + + case {'physiophysiologic interaction','phipi'} % interactions between 2 regions + %===================================================================== + if nargin >2 && isstruct(varargin{3}) + xY = varargin{3}; + xY = xY(:)'; + if size(xY) ~= [1 2] + error('Must include 2 VOI structures for physiophysiologic interactions') + end + else + try + VOI = varargin{3}; + if size(VOI,1) ~= 2 + error('Must include 2 VOI filenames for physiophygiologic interactions') + end + for i = 1:2 + p = load(deblank(VOI(i,:)),'xY'); + xY(i) = p.xY; + end + catch + spm_input('physiological variables:... ',2,'d'); + voi = spm_select(2,'^VOI.*\.mat$',{'select VOIs'}); + for i = 1:2 + p = load(deblank(voi(i,:)),'xY'); + xY(i) = p.xY; + end + end + end + Sess = SPM.Sess(xY(1).Sess); + + + case {'psychophysiologic interaction','ppi'} % get hemodynamic response + %===================================================================== + if nargin >2 && isstruct(varargin{3}) + p.xY = varargin{3}; + else + try + VOI = varargin{3}; + p = load(deblank(VOI(1,:)),'xY'); + catch + spm_input('physiological variable:... ',2,'d'); + voi = spm_select(1,'^VOI.*\.mat$',{'select VOI'}); + p = load(deblank(voi(:))','xY'); + end + end + xY(1) = p.xY; + Sess = SPM.Sess(xY(1).Sess); + + % get 'causes' or inputs U + %---------------------------------------------------------------------- + U.name = {}; + U.u = []; + U.w = []; + try + Uu = varargin{4}; + for i = 1:size(Uu,1) + U.u = [U.u Sess.U(Uu(i,1)).u(33:end,Uu(i,2))]; + U.name{end+1} = Sess.U(Uu(i,1)).name{Uu(i,2)}; + U.w = [U.w Uu(i,3)]; + end + catch + spm_input('Psychological variable:... ',2,'d'); + u = length(Sess.U); + for i = 1:u + for j = 1:length(Sess.U(i).name) + str = ['include ' Sess.U(i).name{j} '?']; + if spm_input(str,3,'y/n',[1 0]) + str = 'Contrast weight'; + tmpw = spm_input(str,4,'e',[],1); + % if tmpw==0 then don't include the column in the + % design. This takes care of the possibility that + % the user would select to include the column but + % then give it a 0 weight. + %------------------------------------------------ + if tmpw ~= 0 + U.w = [U.w tmpw]; + U.u = [U.u Sess.U(i).u(33:end,j)]; + U.name{end + 1} = Sess.U(i).name{j}; + end + end + end + end + end + end % (switch setup) % name of PPI file to be saved %------------------------------------------------------------------------- -PPI.name = spm_input('Name of PPI',3,'s','PPI'); +try + PPI.name = varargin{5}; +catch + PPI.name = spm_input('Name of PPI',3,'s','PPI'); +end +[tmp ppiFilename] = fileparts(PPI.name); +% check if Graphical output should be shown +try + showGraphics = varargin{6}; +catch + showGraphics = 1; +end % Setup variables %------------------------------------------------------------------------- -N = length(xY(1).u); -k = 1:NT:N*NT; % microtime to scan time indices +N = length(xY(1).u); +k = 1:NT:N*NT; % microtime to scan time indices % create basis functions and hrf in scan time and microtime %------------------------------------------------------------------------- spm('Pointer','watch') -hrf = spm_hrf(dt); +hrf = spm_hrf(dt); % create convolved explanatory {Hxb} variables in scan time %------------------------------------------------------------------------- -xb = spm_dctmtx(N*NT + 128,N); -Hxb = zeros(N,N); +xb = spm_dctmtx(N*NT + 128,N); +Hxb = zeros(N,N); for i = 1:N Hx = conv(xb(:,i),hrf); Hxb(:,i) = Hx(k + 128); end -xb = xb(129:end,:); +xb = xb(129:end,:); % get confounds (in scan time) and constant term %------------------------------------------------------------------------- -X0 = xY(1).X0; -M = size(X0,2); +X0 = xY(1).X0; +M = size(X0,2); % get response variable, @@ -210,7 +323,7 @@ % remove confounds and save Y in ouput structure %------------------------------------------------------------------------- -Yc = Y - X0*inv(X0'*X0)*X0'*Y; +Yc = Y - X0*inv(X0'*X0)*X0'*Y; PPI.Y = Yc(:,1); if size(Y,2) == 2 PPI.P = Yc(:,2); @@ -220,12 +333,12 @@ % specify covariance components; assume neuronal response is white % treating confounds as fixed effects %------------------------------------------------------------------------- -Q = speye(N,N)*N/trace(Hxb'*Hxb); -Q = blkdiag(Q, speye(M,M)*1e6 ); +Q = speye(N,N)*N/trace(Hxb'*Hxb); +Q = blkdiag(Q, speye(M,M)*1e6 ); % get whitening matrix (NB: confounds have already been whitened) %------------------------------------------------------------------------- -W = SPM.xX.W(Sess.row,Sess.row); +W = SPM.xX.W(Sess.row,Sess.row); % create structure for spm_PEB %------------------------------------------------------------------------- @@ -236,170 +349,217 @@ switch ppiflag - - -case 'simple deconvolution' - %===================================================================== - C = spm_PEB(Y,P); - xn = xb*C{2}.E(1:N); - xn = spm_detrend(xn); - - % save variables - %--------------------------------------------------------------------- - PPI.xn = xn; - - % Plot so the user can see the results - %--------------------------------------------------------------------- - figure(Fgraph); - t = RT*[1:N]; - T = dt*[1:(N*NT)]; - - subplot(2,1,1) - plot(t,Yc,T,PPI.xn) - title('hemodynamic and neuronal responses') - xlabel('time (secs)') - axis tight square - grid on - legend('BOLD','neuronal') - -case 'physiophysiologic interaction' % PHYSIOPHYSIOLOGIC INTERACTIONS - %===================================================================== - C = spm_PEB(Y(:,1),P); - xn1 = xb*C{2}.E(1:N); - C = spm_PEB(Y(:,2),P); - xn2 = xb*C{2}.E(1:N); - xn1 = spm_detrend(xn1); - xn2 = spm_detrend(xn2); - xnxn = xn1.*xn2; - - % convolve and resample at each scan for bold signal - %--------------------------------------------------------------------- - ppi = conv(xnxn,hrf); - ppi = ppi(k); - - % save variables - %--------------------------------------------------------------------- - PPI.xn = [xn1 xn2]; - PPI.ppi = spm_detrend(ppi); - - - % Plot so the user can see the results - %--------------------------------------------------------------------- - figure(Fgraph); - t = RT*[1:N]; - T = dt*[1:(N*NT)]; - - subplot(2,1,1) - plot(t,PPI.ppi) - title('PPI') - xlabel('time (secs)') - axis tight square - grid on - - subplot(2,2,3) - plot(t,Yc(:,1),T,PPI.xn(:,1)) - title('hemodynamic and neuronal responses (1st)') - xlabel('time (secs)') - axis tight square - grid on - legend('BOLD','neuronal') - - - subplot(2,2,4) - plot(t,Yc(:,2),T,PPI.xn(:,2)) - title('hemodynamic and neuronal responses (2nd)') - xlabel('time (secs)') - axis tight square - grid on - legend('BOLD','neuronal') - - - -case 'psychophysiologic interaction' - %===================================================================== - - % COMPUTE PSYCHOPHYSIOLOGIC INTERACTIONS - % use basis set in microtime - %--------------------------------------------------------------------- - % get parameter estimates and neural signal; beta (C) is in scan time - % This clever trick allows us to compute the betas in scan time which is - % much quicker than with the large microtime vectors. Then the betas - % are applied to a microtime basis set generating the correct neural - % activity to convolve with the psychological variable in mircrotime - %--------------------------------------------------------------------- - C = spm_PEB(Y,P); - xn = xb*C{2}.E(1:N); - xn = spm_detrend(xn); - - % setup psychological variable from inputs and contast weights - %--------------------------------------------------------------------- - PSY = zeros(N*NT,1); - for i = 1:size(U.u,2) - PSY = PSY + full(U.u(:,i)*U.w(:,i)); - end - PSY = spm_detrend(PSY); - - % multiply psychological variable by neural signal - %--------------------------------------------------------------------- - PSYxn = PSY.*xn; - - % convolve and resample at each scan for bold signal - %--------------------------------------------------------------------- - ppi = conv(PSYxn,hrf); - ppi = ppi(k); - - % similarly for psychological effect - %--------------------------------------------------------------------- - PSYHRF = conv(PSY,hrf); - PSYHRF = PSYHRF(k); - - % save psychological variables - %--------------------------------------------------------------------- - PPI.psy = U; - PPI.P = PSYHRF; - PPI.xn = xn; - PPI.ppi = spm_detrend(ppi); - - - % Plot so the user can see the results - %--------------------------------------------------------------------- - figure(Fgraph); - t = RT*[1:N]; - T = dt*[1:(N*NT)]; - - subplot(2,1,1) - plot(t,Yc(:,1),T,PPI.xn(:,1)) - title('hemodynamic and neuronal responses') - xlabel('time (secs)') - axis tight square - grid on - legend('BOLD','neuronal') - - subplot(2,2,3) - plot(T,PSY,'LineStyle','--','Color',[0 .65 0]); - hold on - plot(t,PPI.P,'LineStyle','-','LineWidth',1.5,'Color','b'); - hold off - title('[convolved] psych. variable') - xlabel('time (secs)') - axis tight square - grid on - - subplot(2,2,4) - plot(t,PPI.ppi) - title('PPI') - xlabel('time (secs)') - axis tight square - grid on - - + + case {'simple deconvolution','sd'} + %===================================================================== + C = spm_PEB(Y,P); + xn = xb*C{2}.E(1:N); + xn = spm_detrend(xn); + + % save variables + %--------------------------------------------------------------------- + PPI.xn = xn; + + % Plot so the user can see the results + %--------------------------------------------------------------------- + if showGraphics + figure(Fgraph); + t = RT*[1:N]; + T = dt*[1:(N*NT)]; + + str = sprintf('Simple Deconvolution: %s\n',ppiFilename); + str = [str sprintf('VOI file: %s',xY.name)]; + h = annotation('textbox',[0.01 .99 .01 .01]); + set(h,'String',str,'FitBoxToText','on','EdgeColor','none'); + + ax = subplot(2,1,1); + plot(t,Yc,T,PPI.xn) + title('hemodynamic and neuronal responses') + xlabel('time (secs)') + axis tight square + grid on + legend('BOLD','neuronal') + + % Make sure axis does not overlap with the annotation + % Get position of annotation + hpos = get(h,'Position'); + % Get position of current axis + apos = get(ax,'Position'); + set(ax,'Position',[apos(1) hpos(2)-hpos(4)/2-apos(4) apos(3) apos(4)]); + end + + case {'physiophysiologic interaction','phipi'} % PHYSIOPHYSIOLOGIC INTERACTIONS + %===================================================================== + C = spm_PEB(Y(:,1),P); + xn1 = xb*C{2}.E(1:N); + C = spm_PEB(Y(:,2),P); + xn2 = xb*C{2}.E(1:N); + xn1 = spm_detrend(xn1); + xn2 = spm_detrend(xn2); + xnxn = xn1.*xn2; + + % convolve and resample at each scan for bold signal + %--------------------------------------------------------------------- + ppi = conv(xnxn,hrf); + ppi = ppi(k); + + % save variables + %--------------------------------------------------------------------- + PPI.xn = [xn1 xn2]; + PPI.ppi = spm_detrend(ppi); + + + % Plot so the user can see the results + %--------------------------------------------------------------------- + if showGraphics + figure(Fgraph); + t = RT*[1:N]; + T = dt*[1:(N*NT)]; + + str = sprintf('Physiophysiologic Interaction: %s\n',ppiFilename); + str = [str, sprintf('VOI File 1: %s\n',xY(1).name)]; + str = [str, sprintf('VOI File 2: %s',xY(2).name)]; + h = annotation('textbox',[0.01 .99 .01 .01]); + set(h,'String',str,'FitBoxToText','on','EdgeColor','none'); + + ax = subplot(2,1,1); + plot(t,PPI.ppi) + title('PPI') + xlabel('time (secs)') + axis tight square + grid on + + % Make sure axis does not overlap with the annotation + % Get position of annotation + hpos = get(h,'Position'); + % Get position of current axis + apos = get(ax,'Position'); + set(ax,'Position',[apos(1) hpos(2)-hpos(4)/3-apos(4) apos(3) apos(4)]); + + subplot(2,2,3) + plot(t,Yc(:,1),T,PPI.xn(:,1)) + title('hemodynamic and neuronal responses (1st)') + xlabel('time (secs)') + axis tight square + grid on + legend('BOLD','neuronal') + + subplot(2,2,4) + plot(t,Yc(:,2),T,PPI.xn(:,2)) + title('hemodynamic and neuronal responses (2nd)') + xlabel('time (secs)') + axis tight square + grid on + legend('BOLD','neuronal') + end + + case {'psychophysiologic interaction','ppi'} + %===================================================================== + + % COMPUTE PSYCHOPHYSIOLOGIC INTERACTIONS + % use basis set in microtime + %--------------------------------------------------------------------- + % get parameter estimates and neural signal; beta (C) is in scan time + % This clever trick allows us to compute the betas in scan time which is + % much quicker than with the large microtime vectors. Then the betas + % are applied to a microtime basis set generating the correct neural + % activity to convolve with the psychological variable in mircrotime + %--------------------------------------------------------------------- + C = spm_PEB(Y,P); + xn = xb*C{2}.E(1:N); + xn = spm_detrend(xn); + + % setup psychological variable from inputs and contast weights + %--------------------------------------------------------------------- + PSY = zeros(N*NT,1); + for i = 1:size(U.u,2) + PSY = PSY + full(U.u(:,i)*U.w(:,i)); + end +% PSY = spm_detrend(PSY); <- removed centering of psych variable +% prior to multiplication with xn. Based on discussion with Karl +% and Donald McLaren. + + % multiply psychological variable by neural signal + %--------------------------------------------------------------------- + PSYxn = PSY.*xn; + + % convolve and resample at each scan for bold signal + %--------------------------------------------------------------------- + ppi = conv(PSYxn,hrf); + ppi = ppi(k); + + % similarly for psychological effect + %--------------------------------------------------------------------- + PSYHRF = conv(PSY,hrf); + PSYHRF = PSYHRF(k); + + % save psychological variables + %--------------------------------------------------------------------- + PPI.psy = U; + PPI.P = PSYHRF; + PPI.xn = xn; + PPI.ppi = spm_detrend(ppi); + + + % Plot so the user can see the results + %--------------------------------------------------------------------- + if showGraphics + figure(Fgraph); + t = RT*[1:N]; + T = dt*[1:(N*NT)]; + + str = sprintf('Psychophyiologic Interaction: %s\n',ppiFilename); + str = [str, sprintf('VOI File: %s\n',xY(1).name)]; + str = [str, sprintf('Factors: ')]; + for i = 1:numel(U.name) + str = [str, sprintf('%s [%0.0f]',U.name{i},U.w(i))]; + if i < numel(U.name) + str = [str, sprintf('; ')]; + end + end + + h = annotation('textbox',[0.01 .99 .01 .01]); + set(h,'String',str,'FitBoxToText','on','EdgeColor','none'); + + ax = subplot(2,1,1); + plot(t,Yc(:,1),T,PPI.xn(:,1)) + title('hemodynamic and neuronal responses') + xlabel('time (secs)') + axis tight square + grid on + legend('BOLD','neuronal') + + % Make sure axis does not overlap with the annotation + % Get position of annotation + hpos = get(h,'Position'); + % Get position of current axis + apos = get(ax,'Position'); + set(ax,'Position',[apos(1) hpos(2)-hpos(4)/3-apos(4) apos(3) apos(4)]); + + subplot(2,2,3) + plot(T,PSY,'LineStyle','--','Color',[0 .65 0]); + hold on + plot(t,PPI.P,'LineStyle','-','LineWidth',1,'Color','b'); + hold off + title('[convolved] psych. variable') + xlabel('time (secs)') + axis tight square + grid on + + subplot(2,2,4) + plot(t,PPI.ppi) + title('PPI') + xlabel('time (secs)') + axis tight square + grid on + end end % (switch) -% setup other output variables +% setup other output variables and Save %------------------------------------------------------------------------- -PPI.xY = xY; -PPI.dt = dt; -str = ['PPI_' PPI.name]; +PPI.xY = xY; +PPI.dt = dt; +str = ['PPI_' PPI.name]; if spm_matlab_version_chk('7') >= 0, save(fullfile(SPM.swd,str),'-V6','PPI') @@ -411,5 +571,5 @@ %------------------------------------------------------------------------- spm('Pointer','arrow') spm('FigName',header); -fprintf('\ndone\n') +fprintf('\nCompleted PPI: %s\n',ppiFilename) return diff --git a/spm_phase_shuffle.m b/spm_phase_shuffle.m index 799a21e..c4da9ef 100644 --- a/spm_phase_shuffle.m +++ b/spm_phase_shuffle.m @@ -1,15 +1,15 @@ function [y] = spm_phase_shuffle(x,n) % phase-shuffling of a vector % FORMAT [y] = spm_phase_shuffle(x,[n]) -% x - dtate matrix (time-series in columns) -% n - optinal window length for windowed shuffling +% x - data matrix (time-series in columns) +% n - optional window length for windowed shuffling %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging - + % Karl Friston -% $Id: spm_phase_shuffle.m 1131 2008-02-06 11:17:09Z spm $ - - +% $Id: spm_phase_shuffle.m 3334 2009-08-25 16:13:38Z karl $ + + try % randomise phase - WFT @@ -34,5 +34,5 @@ p(n - i + 2,:) = -r; s = abs(s).*exp(j*p); y = real(ifft(s)); - + end diff --git a/spm_powell.m b/spm_powell.m index b10ecf3..5a02660 100644 --- a/spm_powell.m +++ b/spm_powell.m @@ -20,7 +20,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_powell.m 1143 2008-02-07 19:33:33Z spm $ +% $Id: spm_powell.m 3283 2009-07-23 17:37:36Z john $ p = p(:); @@ -66,8 +66,12 @@ pi = pi*pmin; p = p + pi; -for i=1:length(p), fprintf('%-8.4g ', p(i)); end; -fprintf('| %.5g\n', f); +if length(p)<12, + for i=1:length(p), fprintf('%-8.4g ', p(i)); end; + fprintf('| %.5g\n', f); +else + fprintf('%.5g\n', f); +end min1d_plot('Clear'); return; diff --git a/spm_regions.m b/spm_regions.m index 67fc39f..ab47b29 100644 --- a/spm_regions.m +++ b/spm_regions.m @@ -25,7 +25,7 @@ % % (See spm_getSPM for details on the SPM,VOL, xX & xSPM structures.) % -%_______________________________________________________________________ +%__________________________________________________________________________ % % spm_regions extracts a representative time course from voxel data % in terms of the first eigenvariate of the filtered and adjusted @@ -41,17 +41,16 @@ % voxel time series can be extracted from xY.y, and will be % the same as the [adjusted] data returned by the plotting routine % (spm_graph.m) for the same contrast. -%_______________________________________________________________________ +%__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_regions.m 3094 2009-05-04 11:20:13Z volkmar $ - +% $Id: spm_regions.m 3354 2009-09-03 15:25:12Z guillaume $ % get figure handles -%----------------------------------------------------------------------- -Finter = spm_figure('GetWin','Interactive'); +%-------------------------------------------------------------------------- +Finter = spm_figure('FindWin','Interactive'); Fgraph = spm_figure('GetWin','Graphics'); header = get(Finter,'Name'); set(Finter,'Name','VOI time-series extraction') @@ -61,8 +60,8 @@ xY = {}; end -%-Find nearest voxel [Euclidean distance] in point list in Y.mad -%----------------------------------------------------------------------- +%-Find nearest voxel [Euclidean distance] in point list +%-------------------------------------------------------------------------- if isempty(xSPM.XYZmm) spm('alert!','No suprathreshold voxels!',mfilename,0); Y = []; xY = []; @@ -79,12 +78,12 @@ posstr = sprintf('at [%3.0f %3.0f %3.0f]',xyz); % and update GUI location -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- spm_XYZreg('SetCoords',xyz,hReg); %-Get adjustment options and VOI name -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- spm_input(sprintf('at [%3.0f %3.0f %3.0f]',xY.xyz),1,'d',... 'VOI time-series extraction') @@ -97,7 +96,7 @@ Con = {''}; for i = 1:length(SPM.xCon) if strcmp(SPM.xCon(i).STAT,'F') - q(end + 1) = i; + q(end + 1) = i; Con{end + 1} = SPM.xCon(i).name; end end @@ -106,9 +105,8 @@ end %-if fMRI data then ask user to select session -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- if isfield(SPM,'Sess') - if ~isfield(xY,'Sess') s = length(SPM.Sess); if s > 1 @@ -120,93 +118,47 @@ %-Specify VOI -%----------------------------------------------------------------------- -if ~isfield(xY,'def') - xY.def = spm_input('VOI definition...','!+1','b',... - {'sphere','box','cluster','mask'}); -end -Q = ones(1,size(xSPM.XYZmm,2)); - - -switch xY.def - - case 'sphere' - %--------------------------------------------------------------- - if ~isfield(xY,'spec') - xY.spec = spm_input('VOI radius (mm)','!+0','r',0,1,[0,Inf]); - end - d = [xSPM.XYZmm(1,:) - xyz(1); - xSPM.XYZmm(2,:) - xyz(2); - xSPM.XYZmm(3,:) - xyz(3)]; - Q = find(sum(d.^2) <= xY.spec^2); - - case 'box' - %--------------------------------------------------------------- - if ~isfield(xY,'spec') - xY.spec = spm_input('box dimensions [x y z] {mm}',... - '!+0','r','0 0 0',3); - end - Q = find(all(abs(xSPM.XYZmm - xyz*Q) <= xY.spec(:)*Q/2)); - - case 'mask' - %--------------------------------------------------------------- - if ~isfield(xY,'spec') - xY.spec = spm_vol(spm_select(1,'image','Specify Mask')); - else - if ~isstruct(xY.spec) - xY.spec = spm_vol(xY.spec); - end; - end; - mXYZ=inv(xY.spec.mat)*[xSPM.XYZmm;ones(1,size(xSPM.XYZmm,2))]; - tmpQ = spm_sample_vol(xY.spec,mXYZ(1,:),mXYZ(2,:),mXYZ(3,:),0); - tmpQ(~isfinite(tmpQ)) = 0; - Q = find(tmpQ); - posstr = sprintf('in mask %s', xY.spec.fname); - - case 'cluster' - %--------------------------------------------------------------- - [x i] = spm_XYZreg('NearestXYZ',xyz,xSPM.XYZmm); - A = spm_clusters(xSPM.XYZ); - Q = find(A == A(i)); -end +%-------------------------------------------------------------------------- +xY.M = xSPM.M; +[xY, xY.XYZmm, Q] = spm_ROI(xY, xSPM.XYZmm); +try, xY = rmfield(xY,'M'); end % voxels defined -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- spm('Pointer','Watch') %-Extract required data from results files -%======================================================================= +%========================================================================== %-Get raw data, whiten and filter -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- y = spm_get_data(SPM.xY.VY,xSPM.XYZ(:,Q)); y = spm_filter(SPM.xX.K,SPM.xX.W*y); -xY.XYZmm = xSPM.XYZmm(:,Q); %-Computation -%======================================================================= +%========================================================================== % remove null space of contrast -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- if xY.Ic %-Parameter estimates: beta = xX.pKX*xX.K*y - %--------------------------------------------------------------- + %---------------------------------------------------------------------- beta = spm_get_data(SPM.Vbeta,xSPM.XYZ(:,Q)); %-subtract Y0 = XO*beta, Y = Yc + Y0 + e - %--------------------------------------------------------------- + %---------------------------------------------------------------------- y = y - spm_FcUtil('Y0',SPM.xCon(xY.Ic),SPM.xX.xKXs,beta); end % confounds -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- xY.X0 = SPM.xX.xKXs.X(:,[SPM.xX.iB SPM.xX.iG]); % extract session-specific rows from data and confounds -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- try i = SPM.Sess(xY.Sess).row; y = y(i,:); @@ -214,24 +166,24 @@ end % and add session-specific filter confounds -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- try xY.X0 = [xY.X0 SPM.xX.K(xY.Sess).X0]; end -%======================================================================= +%========================================================================== try xY.X0 = [xY.X0 SPM.xX.K(xY.Sess).KH]; % Compatibility check end -%======================================================================= +%========================================================================== % Remove null space of X0 -%----------------------------------------------------------------------- -xY.X0 = xY.X0(:,any(xY.X0)); +%-------------------------------------------------------------------------- +xY.X0 = xY.X0(:,any(xY.X0)); % compute regional response in terms of first eigenvariate -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- [m n] = size(y); if m > n [v s v] = svd(y'*y); @@ -250,17 +202,17 @@ Y = u*sqrt(s(1)/n); % set in structure -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- xY.y = y; xY.u = Y; xY.v = v; xY.s = s; %-Display VOI weighting and eigenvariate -%======================================================================== +%========================================================================== % show position -%------------------------------------------------------------------------ +%-------------------------------------------------------------------------- spm_results_ui('Clear',Fgraph); figure(Fgraph); subplot(2,2,3) @@ -268,7 +220,7 @@ % show dynamics -%------------------------------------------------------------------------ +%-------------------------------------------------------------------------- subplot(2,2,4) try plot(SPM.xY.RT*[1:length(xY.u)],Y) @@ -286,7 +238,7 @@ % save -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- str = ['VOI_' xY.name]; if isfield(xY,'Sess') if length(xY.Sess) == 1 @@ -300,6 +252,6 @@ end %-Reset title -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- spm('FigName',header); spm('Pointer','Arrow') diff --git a/spm_reml.m b/spm_reml.m index c6badfb..5df2473 100644 --- a/spm_reml.m +++ b/spm_reml.m @@ -33,7 +33,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % John Ashburner & Karl Friston -% $Id: spm_reml.m 2559 2008-12-12 17:10:23Z karl $ +% $Id: spm_reml.m 3297 2009-07-29 17:20:19Z guillaume $ % assume a single sample if not specified %-------------------------------------------------------------------------- @@ -141,7 +141,7 @@ % update regulariser %---------------------------------------------------------------------- dF = dFdh'*dh; - fprintf('%-30s: %i %30s%e\n',' ReML Iteration',k,'...',full(dF)); + fprintf('%-40s: %3i %14s%e\n',' ReML Iteration',k,'...',full(dF)); % final estimate of covariance (with missing data points) %---------------------------------------------------------------------- diff --git a/spm_reml_sc.m b/spm_reml_sc.m index 5e49851..c093895 100644 --- a/spm_reml_sc.m +++ b/spm_reml_sc.m @@ -40,7 +40,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_reml_sc.m 2559 2008-12-12 17:10:23Z karl $ +% $Id: spm_reml_sc.m 3264 2009-07-10 14:01:31Z karl $ % assume proportional hyperpriors not specified %-------------------------------------------------------------------------- @@ -125,7 +125,7 @@ % E-step: conditional covariance cov(B|y) {Cq} %====================================================================== iCX = iC*X; - if length(X) + if ~isempty(X) Cq = inv(X'*iCX); else Cq = sparse(0); diff --git a/spm_render.m b/spm_render.m index 656173c..dd8c973 100644 --- a/spm_render.m +++ b/spm_render.m @@ -33,9 +33,9 @@ function spm_render(dat,brt,rendfile) % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_render.m 3156 2009-05-28 16:22:52Z guillaume $ +% $Id: spm_render.m 3289 2009-07-27 15:28:24Z guillaume $ -SVNrev = '$Rev: 3156 $'; +SVNrev = '$Rev: 3289 $'; global prevrend if ~isstruct(prevrend) @@ -73,6 +73,34 @@ function spm_render(dat,brt,rendfile) end prevrend.rendfile = rendfile; +[p,f,e] = fileparts(rendfile); +loadgifti = false; +if strcmpi(e,'.mat') + load(rendfile); + if ~exist('rend','var') && ~exist('Matrixes','var') + loadgifti = true; + end +end +if ~strcmpi(e,'.mat') || loadgifti + try + rend = export(gifti(rendfile),'patch'); + catch + error('\nCannot read render file "%s".\n', rendfile); + end + if num == 1 + col = hot(256); + else + col = eye(3); + if spm_input('Which colours?','!+1','b',{'RGB','Custom'},[0 1],1) + for k = 1:num + col(k,:) = uisetcolor(col(k,:),sprintf('Colour of blob set %d',k)); + end + end + end + surf_rend(dat,rend,col); + return +end + %-Get brightness & colours %-------------------------------------------------------------------------- if nargin < 2 || isempty(prevrend.brt) @@ -87,8 +115,8 @@ function spm_render(dat,brt,rendfile) % ask for custom colours & get rgb values %------------------------------------------------------------------ if spm_input('Which colours?','!+1','b',{'RGB','Custom'},[0 1],1) - for k = 1:num, - col(k,:) = uisetcolor(col(k,:),sprintf('Color of blob set %d',k)); + for k = 1:num + col(k,:) = uisetcolor(col(k,:),sprintf('Colour of blob set %d',k)); end end else @@ -106,24 +134,6 @@ function spm_render(dat,brt,rendfile) %-Perform the rendering %========================================================================== -[p,f,e] = fileparts(rendfile); -loadgifti = false; -if strcmpi(e,'.mat') - load(rendfile); - if ~exist('rend','var') && ~exist('Matrixes','var') - loadgifti = true; - end -end -if ~strcmpi(e,'.mat') || loadgifti - try - rend = export(gifti(rendfile),'patch'); - catch - error('\nCannot read render file "%s".\n', rendfile); - end - surf_rend(dat,rend,col); - return -end - spm('Pointer','Watch'); if ~exist('rend','var') % Assume old format... @@ -299,41 +309,61 @@ function spm_render(dat,brt,rendfile) %========================================================================== function surf_rend(dat,rend,col) +%-Setup figure and axis +%-------------------------------------------------------------------------- Fgraph = spm_figure('GetWin','Graphics'); spm_results_ui('Clear',Fgraph); rdr = get(Fgraph,'Renderer'); set(Fgraph,'Renderer','OpenGL'); +ax0 = axes(... + 'Parent',Fgraph,... + 'Units','normalized',... + 'Color', [1 1 1],... + 'XTick',[],... + 'YTick',[],... + 'Position',[-0.05, -0.05, 1.05, 0.555]); + ax = axes(... 'Parent',Fgraph,... - 'units','normalized',... - 'Position',[0.025, 0.025, 0.95, 0.45],... + 'Units','normalized',... + 'Position',[0.05, 0.05, 0.9, 0.4],... 'Visible','off'); -Y = zeros(dat.dim(1:3)'); -OFF = dat.XYZ(1,:) + dat.dim(1)*(dat.XYZ(2,:)-1 + dat.dim(2)*(dat.XYZ(3,:)-1)); -Y(OFF) = dat.t .* (dat.t > 0); -XYZ = double(inv(dat.mat)*[rend.vertices';ones(1,size(rend.vertices,1))]); -v = spm_sample_vol(Y,XYZ(1,:),XYZ(2,:),XYZ(3,:),0); - -C = spm_mesh_curvature(rend) > 0; -C = 0.5 * repmat(C,1,3) + 0.3 * repmat(~C,1,3); -col = hot(256); -col(1,:) = 0.5; -if ~any(v) - cdat = 0.5*ones(length(v),3); -else - cdat = squeeze(ind2rgb(floor(v(:)/max(v(:))*size(col,1)),col)); +%-Project data onto surface mesh +%-------------------------------------------------------------------------- +v = spm_mesh_project(rend, dat); + +%-Compute mesh curvature texture +%-------------------------------------------------------------------------- +curv = spm_mesh_curvature(rend) > 0; +curv = 0.5 * repmat(curv,1,3) + 0.3 * repmat(~curv,1,3); + +%-Combine projected data and mesh curvature +%-------------------------------------------------------------------------- +cdat = zeros(size(v,2),3); +if any(v(:)) + if size(col,1)>3 + cdat = squeeze(ind2rgb(floor(v(:)/max(v(:))*size(col,1)),col)); + else + m = max(v(:)); + for i=1:size(v,1) + cdat = cdat + v(i,:)'/m * col(i,:); + end + end end -cdat = repmat(v==0,3,1)' .* C + repmat(v~=0,3,1)' .* cdat; +cdat = repmat(~any(v,1),3,1)' .* curv + repmat(any(v,1),3,1)' .* cdat; +%-Display the surface mesh with texture +%-------------------------------------------------------------------------- hp = patch(rend, 'Parent',ax,... 'FaceVertexCData',cdat, ... 'FaceColor', 'interp', ... 'EdgeColor', 'none',... 'FaceLighting', 'phong',... 'SpecularStrength' ,0.7, 'AmbientStrength', 0.1,... - 'DiffuseStrength', 0.7, 'SpecularExponent', 10); + 'DiffuseStrength', 0.7, 'SpecularExponent', 10,... + 'DeleteFcn', {@mydeletefcn,Fgraph,rdr}); set(Fgraph,'CurrentAxes',ax); view(ax,[-90 0]); @@ -343,36 +373,58 @@ function surf_rend(dat,rend,col) material(Fgraph,'dull'); setappdata(ax,'camlight',l); -%spm_mesh_inflate(hp,Inf,1); -%view(ax,[-90 0]);axis(ax,'image'); - +%-Setup context menu +%-------------------------------------------------------------------------- r = rotate3d(ax); set(r,'enable','off'); cmenu = uicontextmenu; c1 = uimenu(cmenu, 'Label', 'Inflate', 'Interruptible','off', 'Callback', @myinflate); setappdata(c1,'patch',hp); setappdata(c1,'axis',ax); -c12 = uimenu(cmenu, 'Label', 'Connected Components', 'Visible', 'off', 'Interruptible','off', 'Callback', @mycclabel); +c2 = uimenu(cmenu, 'Label', 'Connected Components', 'Interruptible','off'); C = spm_mesh_label(hp); -setappdata(c12,'patch',hp); -setappdata(c12,'cclabel',C); -c2 = uimenu(cmenu, 'Label', 'Rotate', 'Checked','on','Separator','on','Callback', @myswitchrotate); -setappdata(c2,'rotate3d',r); -c3 = uimenu(cmenu, 'Label', 'Transparency'); -setappdata(c3,'patch',hp); -uimenu(c3,'Label','0%', 'Checked','on', 'Callback',@mytransparency); -uimenu(c3,'Label','20%', 'Checked','off', 'Callback',@mytransparency); -uimenu(c3,'Label','40%', 'Checked','off', 'Callback',@mytransparency); -uimenu(c3,'Label','60%', 'Checked','off', 'Callback',@mytransparency); -uimenu(c3,'Label','80%', 'Checked','off', 'Callback',@mytransparency); -c4 = uimenu(cmenu, 'Label', 'Save As...','Separator','on','Callback', @mysave); -setappdata(c4,'patch',hp); +setappdata(c2,'patch',hp); +setappdata(c2,'cclabel',C); +for i=1:length(unique(C)) + uimenu(c2,'Label',sprintf('Component %d',i), 'Checked','on', 'Callback', @mycclabel); +end +c3 = uimenu(cmenu, 'Label', 'Rotate', 'Checked','on','Separator','on','Callback', @myswitchrotate); +setappdata(c3,'rotate3d',r); +c4 = uimenu(cmenu, 'Label', 'View'); +setappdata(c4,'axis',ax); +uimenu(c4,'Label','Go to Y-Z view (right)', 'Callback', {@myview, [90 0]}); +uimenu(c4,'Label','Go to Y-Z view (left)', 'Callback', {@myview, [-90 0]}); +uimenu(c4,'Label','Go to X-Y view (top)', 'Callback', {@myview, [0 90]}); +uimenu(c4,'Label','Go to X-Y view (bottom)', 'Callback', {@myview, [-180 -90]}); +uimenu(c4,'Label','Go to X-Z view (front)', 'Callback', {@myview, [-180 0]}); +uimenu(c4,'Label','Go to X-Z view (back)', 'Callback', {@myview, [0 0]}); +c5 = uimenu(cmenu, 'Label', 'Transparency'); +setappdata(c5,'patch',hp); +uimenu(c5,'Label','0%', 'Checked','on', 'Callback', @mytransparency); +uimenu(c5,'Label','20%', 'Checked','off', 'Callback', @mytransparency); +uimenu(c5,'Label','40%', 'Checked','off', 'Callback', @mytransparency); +uimenu(c5,'Label','60%', 'Checked','off', 'Callback', @mytransparency); +uimenu(c5,'Label','80%', 'Checked','off', 'Callback', @mytransparency); +c6 = uimenu(cmenu, 'Label', 'Background Color'); +setappdata(c6,'fig',ax0); +uimenu(c6,'Label','White', 'Callback', {@mybackgroundcolor, [1 1 1]}); +uimenu(c6,'Label','Black', 'Callback', {@mybackgroundcolor, [0 0 0]}); +uimenu(c6,'Label','Custom...', 'Callback', {@mybackgroundcolor, []}); +c7 = uimenu(cmenu, 'Label', 'Save As...','Separator','on','Callback', @mysave); +setappdata(c7,'patch',hp); +setappdata(c7,'fig',Fgraph); +setappdata(c7,'axis',ax); +setappdata(c7,'ax0',ax0); try, set(r,'uicontextmenu',cmenu); end try, set(hp,'uicontextmenu',cmenu); end set(r,'enable','on'); set(r,'ActionPostCallback',@mypostcallback); -set(hp,'DeleteFcn',{@mydeletefcn,Fgraph,rdr}); - +try + setAllowAxesRotate(r, setxor(findobj(Fgraph,'Type','axes'),ax), false); +end + +%-Register with MIP +%-------------------------------------------------------------------------- try % meaningless when called outside spm_results_ui hReg = spm_XYZreg('FindReg',spm_figure('GetWin','Interactive')); xyz = spm_XYZreg('GetCoords',hReg); @@ -387,14 +439,37 @@ function myinflate(obj,evd) %========================================================================== function mycclabel(obj,evd) -C = getappdata(obj,'cclabel'); -F = get(getappdata(obj,'patch'),'Faces'); -V = get(getappdata(obj,'patch'),'Vertices'); -V = zeros(size(V,1),1); -V(reshape(F(C==1,:),[],1)) = 1; -set(getappdata(obj,'patch'),'FaceVertexAlphaData',V); -set(getappdata(obj,'patch'),'FaceAlpha','interp'); - +C = getappdata(get(obj,'parent'),'cclabel'); +F = get(getappdata(get(obj,'parent'),'patch'),'Faces'); +ind = sscanf(get(obj,'Label'),'Component %d'); +V = get(getappdata(get(obj,'parent'),'patch'),'FaceVertexAlphaData'); +Fa = get(getappdata(get(obj,'parent'),'patch'),'FaceAlpha'); +if ~isnumeric(Fa) + if ~isempty(V), Fa = max(V); else Fa = 1; end + if Fa == 0, Fa = 1; end +end +if isempty(V) || numel(V) == 1 + Ve = get(getappdata(get(obj,'parent'),'patch'),'Vertices'); + if isempty(V) || V == 1 + V = Fa * ones(size(Ve,1),1); + else + V = zeros(size(Ve,1),1); + end +end +if strcmpi(get(obj,'Checked'),'on') + V(reshape(F(C==ind,:),[],1)) = 0; + set(obj,'Checked','off'); +else + V(reshape(F(C==ind,:),[],1)) = Fa; + set(obj,'Checked','on'); +end +set(getappdata(get(obj,'parent'),'patch'), 'FaceVertexAlphaData', V); +if all(V) + set(getappdata(get(obj,'parent'),'patch'), 'FaceAlpha', Fa); +else + set(getappdata(get(obj,'parent'),'patch'), 'FaceAlpha', 'interp'); +end + %========================================================================== function myswitchrotate(obj,evd) if strcmpi(get(getappdata(obj,'rotate3d'),'enable'),'on') @@ -405,6 +480,12 @@ function myswitchrotate(obj,evd) set(obj,'Checked','on'); end +%========================================================================== +function myview(obj,evd,varargin) +view(getappdata(get(obj,'parent'),'axis'),varargin{1}); +axis(getappdata(get(obj,'parent'),'axis'),'image'); +camlight(getappdata(getappdata(get(obj,'parent'),'axis'),'camlight')); + %========================================================================== function mytransparency(obj,evd) t = 1 - sscanf(get(obj,'Label'),'%d%%') / 100; @@ -412,17 +493,74 @@ function mytransparency(obj,evd) set(get(get(obj,'parent'),'children'),'Checked','off'); set(obj,'Checked','on'); +%========================================================================== +function mybackgroundcolor(obj,evd,varargin) +if isempty(varargin{1}) + c = uisetcolor(getappdata(get(obj,'parent'),'fig'), ... + 'Pick a background color...'); + if numel(c) == 1, return; end +else + c = varargin{1}; +end +set(getappdata(get(obj,'parent'),'fig'),'Color',c); + %========================================================================== function mysave(obj,evd) -g = gifti; -g.vertices = get(getappdata(obj,'patch'),'Vertices'); -g.faces = get(getappdata(obj,'patch'),'Faces'); -g.cdata = get(getappdata(obj,'patch'),'FaceVertexCData'); -[filename, pathname] = uiputfile({'*.gii' 'GIfTI files (*.gii)'}, 'Save as'); +[filename, pathname, filterindex] = uiputfile({... + '*.gii' 'GIfTI files (*.gii)'; ... + '*.png' 'PNG files (*.png)';... + '*.dae' 'Collada files (*.dae)'}, 'Save as'); if ~isequal(filename,0) && ~isequal(pathname,0) [pth,nam,ext] = fileparts(filename); - if ~strcmpi(ext,'.gii'), filename = [filename '.gii']; end - save(g,fullfile(pathname, filename)); + switch ext + case '.gii' + filterindex = 1; + case '.png' + filterindex = 2; + case '.dae' + filterindex = 3; + otherwise + switch filterindex + case 1 + filename = [filename '.gii']; + case 2 + filename = [filename '.png']; + case 3 + filename = [filename '.dae']; + end + end + switch filterindex + case 1 + g = gifti; + g.vertices = get(getappdata(obj,'patch'),'Vertices'); + g.faces = get(getappdata(obj,'patch'),'Faces'); + g.cdata = get(getappdata(obj,'patch'),'FaceVertexCData'); + save(g,fullfile(pathname, filename)); + case 2 + ax = getappdata(obj,'axis'); + u = get(ax,'units'); + set(ax,'units','pixels'); + p = get(ax,'Position'); + r = get(getappdata(obj,'fig'),'Renderer'); + h = figure('Position',p+[0 0 10 10], ... + 'InvertHardcopy','off', ... + 'Color',get(getappdata(obj,'ax0'),'Color'), ... + 'Renderer',r); + copyobj(getappdata(obj,'axis'),h); + set(ax,'units',u); + set(get(h,'children'),'visible','off'); + %a = get(h,'children'); + %set(a,'Position',get(a,'Position').*[0 0 1 1]+[10 10 0 0]); + print(h, '-dpng', '-opengl', fullfile(pathname, filename)); + close(h); + set(getappdata(obj,'fig'),'renderer',r); + case 3 + g = gifti; + g.vertices = get(getappdata(obj,'patch'),'Vertices'); + g.faces = get(getappdata(obj,'patch'),'Faces'); + g.cdata = get(getappdata(obj,'patch'),'FaceVertexCData'); + save(g,fullfile(pathname, filename),'collada'); + end end %========================================================================== diff --git a/spm_render_vol.mexmaci b/spm_render_vol.mexmaci index 10bec5a..f50492c 100644 Binary files a/spm_render_vol.mexmaci and b/spm_render_vol.mexmaci differ diff --git a/spm_resels_vol.mexmaci b/spm_resels_vol.mexmaci index 1ef71ad..feef211 100644 Binary files a/spm_resels_vol.mexmaci and b/spm_resels_vol.mexmaci differ diff --git a/spm_results_ui.m b/spm_results_ui.m index 698765b..f7c6629 100644 --- a/spm_results_ui.m +++ b/spm_results_ui.m @@ -135,9 +135,9 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston & Andrew Holmes -% $Id: spm_results_ui.m 3081 2009-04-22 20:15:38Z guillaume $ +% $Id: spm_results_ui.m 3348 2009-09-03 10:32:01Z guillaume $ -SCCSid = '$Rev: 3081 $'; +SCCSid = '$Rev: 3348 $'; %========================================================================== % - FORMAT specifications for embedded CallBack functions @@ -251,7 +251,7 @@ %-Condition arguments %-------------------------------------------------------------------------- -if nargin == 0, Action='SetUp'; else, Action=varargin{1}; end +if nargin == 0, Action='SetUp'; else Action=varargin{1}; end %========================================================================== @@ -270,38 +270,76 @@ spm_figure('clear',hSat); %-Get thresholded xSPM data and parameters of design - %======================================================================= - if (nargin > 1) - [SPM,xSPM] = spm_getSPM(varargin{2}); + %====================================================================== + if nargin > 1 + [SPM,xSPM] = spm_getSPM(varargin{2}); else - [SPM,xSPM] = spm_getSPM; - end; + [SPM,xSPM] = spm_getSPM; + end if isempty(xSPM) - varargout = {[],[],[]}; - return; + varargout = {[],[],[]}; + return; end + %-Ensure pwd = swd so that relative filenames are valid + %---------------------------------------------------------------------- + cd(SPM.swd) + + %-Get space information + %====================================================================== M = SPM.xVol.M; DIM = SPM.xVol.DIM; + + %-Space units + %---------------------------------------------------------------------- try units = SPM.xVol.units; catch - units = {'mm' 'mm' 'mm'}; + try + if strcmp(spm('CheckModality'),'EEG') + datatype = {... + 'Volumetric (2D/3D)',... + 'Scalp-Time',... + 'Scalp-Frequency',... + 'Time-Frequency',... + 'Frequency-Frequency'}; + selected = spm_input('Data Type: ','+1','m',datatype); + datatype = datatype{selected}; + else + datatype = 'Volumetric (2D/3D)'; + end + catch + datatype = 'Volumetric (2D/3D)'; + end + + switch datatype + case 'Volumetric (2D/3D)' + units = {'mm' 'mm' 'mm'}; + case 'Scalp-Time' + units = {'mm' 'mm' 'ms'}; + case 'Scalp-Frequency' + units = {'mm' 'mm' 'Hz'}; + case 'Time-Frequency' + units = {'Hz' 'ms' ''}; + case 'Frequency-Frequency' + units = {'Hz' 'Hz' ''}; + otherwise + error('Unknown data type.'); + end end - - % ensure pwd = swd so that relative filenames are valid - %---------------------------------------------------------------------- - cd(SPM.swd) - + if DIM(3) == 1, units{3} = ''; end + xSPM.units = units; + SPM.xVol.units = units; + + %-Setup Results User Interface; Display MIP, design matrix & parameters %====================================================================== - spm('FigName',['SPM{',xSPM.STAT,'}: Results'],Finter,CmdLine); - %-Setup results GUI %---------------------------------------------------------------------- - spm_figure('Clear',Finter) + spm_figure('Clear',Finter); + spm('FigName',['SPM{',xSPM.STAT,'}: Results'],Finter,CmdLine); hReg = spm_results_ui('SetupGUI',M,DIM,xSPM,Finter); %-Setup design interrogation menu @@ -309,7 +347,7 @@ hDesRepUI = spm_DesRep('DesRepUI',SPM); figure(Finter) - %-Setup Maximium intensity projection (MIP) & register + %-Setup Maximum intensity projection (MIP) & register %---------------------------------------------------------------------- hMIPax = axes('Parent',Fgraph,'Position',[0.05 0.60 0.55 0.36],'Visible','off'); hMIPax = spm_mip_ui(xSPM.Z,xSPM.XYZmm,M,DIM,hMIPax,units); @@ -494,7 +532,7 @@ %-p-values %------------------------------------------------------------------ uicontrol(Finter,'Style','Text','String','p-values',... - 'Position',[020 168 050 015].*WS,... + 'Position',[020 168 080 015].*WS,... 'FontAngle','Italic',... 'FontSize',FS(10),... 'HorizontalAlignment','Left',... @@ -565,8 +603,7 @@ %-Hemodynamic modelling %------------------------------------------------------------------ - global defaults - if strcmp(defaults.modality,'FMRI') + if strcmp(spm('CheckModality'),'FMRI') uicontrol(Finter,'Style','PushButton','String','Hemodynamics',... 'FontSize',FS(10),... 'ToolTipString','Hemodynamic modelling of regional response',... @@ -687,7 +724,7 @@ hFxyz = uicontrol(Finter,'Style','Pushbutton',... 'visible','off','enable','off','Position',[010 010 265 030].*WS); uicontrol(Finter,'Style','Text','String','co-ordinates',... - 'Position',[020 035 078 016].*WS,... + 'Position',[020 035 090 016].*WS,... 'FontAngle','Italic',... 'FontSize',FS(10),... 'HorizontalAlignment','Left',... @@ -717,6 +754,7 @@ 'Tag','hY',... 'Callback','spm_results_ui(''EdWidCB'')'); + if DIM(3) ~= 1 uicontrol(Finter,'Style','Text','String','z =',... 'Position',[190 015 024 018].*WS,... 'FontName',PF.times,'FontSize',FS(10),'FontAngle','Italic',... @@ -728,11 +766,14 @@ 'HorizontalAlignment','Right',... 'Tag','hZ',... 'Callback','spm_results_ui(''EdWidCB'')'); - + else + hZ = []; + end + %-Statistic value reporting pane %------------------------------------------------------------------ uicontrol(Finter,'Style','Text','String','statistic',... - 'Position',[285 035 085 016].*WS,... + 'Position',[285 035 090 016].*WS,... 'FontAngle','Italic',... 'FontSize',FS(10),... 'HorizontalAlignment','Left',... @@ -829,7 +870,7 @@ if hC <= 0 [xyz,d] = spm_XYZreg('RoundCoords',xyz,UD.M,UD.DIM); if d>0 && nargout<2, warning(sprintf(... - '%s: Co-ords rounded to neatest voxel centre: Discrepancy %.2f',... + '%s: Co-ords rounded to nearest voxel centre: Discrepancy %.2f',... mfilename,d)) end else diff --git a/spm_robust_average.m b/spm_robust_average.m new file mode 100644 index 0000000..a7541d6 --- /dev/null +++ b/spm_robust_average.m @@ -0,0 +1,109 @@ +function [Y,W] = spm_robust_average(X, dim, ks) +% Apply robust averaging routine to X sets +% FORMAT [Y,W] = spm_robust_averaget(X, dim, ks) +% X - data matrix to be averaged +% dim - the dimension along which the function will work +% ks - offset of the weighting function (default: 3) +% +% W - estimated weights +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% James Kilner +% $Id: spm_robust_average.m 3212 2009-06-19 15:20:48Z vladimir $ + +if nargin < 3 || isempty(ks) + ks = 3; +end + +if nargin < 2 || isempty(dim) + dim = 1; +end + +%-Remember the original data size and size of the mean +%-------------------------------------------------------------------------- +origsize = size(X); +morigsize = origsize; +morigsize(dim) = 1; + +%-Convert the data to repetitions x points matrix +%-------------------------------------------------------------------------- +if dim > 1 + X = shiftdim(X, dim-1); +end + +if length(origsize) > 2 + X = reshape(X, size(X, 1), []); +end + +%-Rescale the data +%-------------------------------------------------------------------------- +[X, scalefactor] = spm_cond_units(X); + + +%-Actual robust averaging +%-------------------------------------------------------------------------- +ores=1; +nres=10; +n=0; + +while max(abs(ores-nres))>sqrt(1E-8) + + ores=nres; + n=n+1; + + if n==1 + Y = nanmedian(X); + else + XX = X; + XX(isnan(XX)) = 0; + Y = sum(W.*XX)./sum(W); + end + + if n > 200 + warning('Robust averaging could not converge. Maximal number of iterations exceeded.'); + break; + end + + res = X-repmat(Y, size(X, 1), 1); + + mad = nanmedian(abs(res-repmat(nanmedian(res), size(res, 1), 1))); + res = res./repmat(mad, size(res, 1), 1); + res = abs(res)-ks; + res(res<0)=0; + nres= (sum(res(~isnan(res)).^2)); + W = (abs(res)<1) .* ((1 - res.^2).^2); + W(isnan(X)) = 0; + W(X == 0) = 0; %Assuming X is a real measurement +end + +disp(['Robust averaging finished after ' num2str(n) ' iterations.']); + +%-Restore the average and weights to the original data dimensions +%-------------------------------------------------------------------------- +Y = Y./scalefactor; + +if length(origsize) > 2 + Y = reshape(Y, circshift(morigsize, [1 -(dim-1)])); + W = reshape(W, circshift(origsize, [1 -(dim-1)])); +end + +if dim > 1 + Y = shiftdim(Y, length(origsize)-dim+1); + W = shiftdim(W, length(origsize)-dim+1); +end + +%-Helper function +%-------------------------------------------------------------------------- +function Y = nanmedian(X) +if ~any(any(isnan(X))) + Y = median(X); +else + Y = zeros(1, size(X,2)); + for i = 1:size(X, 2) + Y(i) = median(X(~isnan(X(:, i)), i)); + end +end + + + diff --git a/spm_robust_glm.m b/spm_robust_glm.m new file mode 100644 index 0000000..7c4992c --- /dev/null +++ b/spm_robust_glm.m @@ -0,0 +1,133 @@ +function [B, W] = spm_robust_glm(Y, X, dim, ks) +% Apply robust GLM +% FORMAT [B, W] = spm_robust_glm(Y, X, dim, ks) +% Y - data matrix +% X - design matrix +% dim - the dimension along which the function will work +% ks - offset of the weighting function (default: 3) +% +% OUTPUT: +% B - parameter estimates +% W - estimated weights +% +% Implementation of: +% Wager TD, Keller MC, Lacey SC, Jonides J. +% Increased sensitivity in neuroimaging analyses using robust regression. +% Neuroimage. 2005 May 15;26(1):99-113 +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% James Kilner, Vladimir Litvak +% $Id: spm_robust_glm.m 3212 2009-06-19 15:20:48Z vladimir $ + +if nargin < 3 || isempty(ks) + ks = 3; +end + +if nargin < 2 || isempty(dim) + dim = 1; +end + +%-Remember the original data size and size of the mean +%-------------------------------------------------------------------------- +origsize = size(Y); +borigsize = origsize; +borigsize(dim) = size(X, 2); + +%-Convert the data to repetitions x points matrix +%-------------------------------------------------------------------------- +if dim > 1 + Y = shiftdim(Y, dim-1); +end + +if length(origsize) > 2 + Y = reshape(Y, size(Y, 1), []); +end + +%-Check the design matrix and compute leverages +%-------------------------------------------------------------------------- + +if size(X, 1) ~= size(Y, 1) + error('The number of rows in the design matriz should match dimension of interest.'); +end + +H = diag(X*inv(X'*X)*X'); +H = repmat(H(:), 1, size(Y, 2)); + +%-Rescale the data +%-------------------------------------------------------------------------- +[Y, scalefactor] = spm_cond_units(Y); + +%-Actual robust GLM +%-------------------------------------------------------------------------- +ores=1; +nres=10; +n=0; + +YY = Y; +YY(isnan(YY)) = 0; + +while max(abs(ores-nres))>sqrt(1E-8) + + ores=nres; + n=n+1; + + if n == 1 + W = ones(size(Y)); + W(isnan(Y)) = 0; + end + + B = zeros(size(X, 2), size(Y, 2)); + + for i = 1:size(Y, 2); + B(:, i) = inv(X'*diag(W(:, i))*X)*X'*diag(W(:, i))*YY(:, i); + end + + if n > 200 + warning('Robust GLM could not converge. Maximal number of iterations exceeded.'); + break; + end + + res = Y-X*B; + + mad = nanmedian(abs(res-repmat(nanmedian(res), size(res, 1), 1))); + res = res./repmat(mad, size(res, 1), 1); + res = res.*H; + res = abs(res)-ks; + res(res<0)=0; + nres= (sum(res(~isnan(res)).^2)); + W = (abs(res)<1) .* ((1 - res.^2).^2); + W(isnan(Y)) = 0; + W(Y == 0) = 0; %Assuming X is a real measurement +end + +disp(['Robust GLM finished after ' num2str(n) ' iterations.']); + +%-Restore the betas and weights to the original data dimensions +%-------------------------------------------------------------------------- +B = B./scalefactor; + +if length(origsize) > 2 + B = reshape(B, circshift(borigsize, [1 -(dim-1)])); + W = reshape(W, circshift(origsize, [1 -(dim-1)])); +end + +if dim > 1 + B = shiftdim(B, length(origsize)-dim+1); + W = shiftdim(W, length(origsize)-dim+1); +end + +%-Helper function +%-------------------------------------------------------------------------- +function Y = nanmedian(X) +if ~any(any(isnan(X))) + Y = median(X); +else + Y = zeros(1, size(X,2)); + for i = 1:size(X, 2) + Y(i) = median(X(~isnan(X(:, i)), i)); + end +end + + + diff --git a/spm_sample_vol.mexmaci b/spm_sample_vol.mexmaci index 0c2bed7..f8f0cbe 100644 Binary files a/spm_sample_vol.mexmaci and b/spm_sample_vol.mexmaci differ diff --git a/spm_slice_vol.mexmaci b/spm_slice_vol.mexmaci index bb8c75e..40b55a8 100644 Binary files a/spm_slice_vol.mexmaci and b/spm_slice_vol.mexmaci differ diff --git a/spm_spm.m b/spm_spm.m index 2ec3170..f1c8307 100644 --- a/spm_spm.m +++ b/spm_spm.m @@ -2,9 +2,9 @@ % [Re]ML Estimation of a General Linear Model % FORMAT [SPM] = spm_spm(SPM) % -% required fields of SPM: +% Required fields of SPM: % -% xY.VY - nScan x 1 struct array of mapped image volumes +% xY.VY - nScan x 1 struct array of image handles (see spm_vol) % Images must have the same orientation, voxel size and data type % - Any scaling should have already been applied via the image handle % scalefactors. @@ -26,8 +26,8 @@ % the OLS estimates maximum likelihood % i.e. W*W' = inv(xVi.V). % -% xVi - structure describing intrinsic temporal non-sphericity -% - required fields are: +% xVi - Structure describing intrinsic temporal non-sphericity +% - Required fields are: % xVi.Vi - array of non-sphericity components % - defaults to {speye(size(xX.X,1))} - i.i.d. % - specifying a cell array of constraints (Qi) @@ -40,14 +40,14 @@ % a 1st pass to identify significant voxels over which % to estimate V. A 2nd pass is then used to re-estimate % the parameters with WLS and save the ML estimates -% (unless xX.W is already specified) +% (unless xX.W is already specified). % % xM - Structure containing masking information, or a simple column vector -% of thresholds corresponding to the images in VY. +% of thresholds corresponding to the images in VY [default: -Inf] % - If a structure, the required fields are: % xM.TH - nVar x nScan matrix of analysis thresholds, one per image % xM.I - Implicit masking (0=>none, 1 => implicit zero/NaN mask) -% xM.VM - struct array of mapped explicit mask image volumes +% xM.VM - struct array of explicit mask image handles % - (empty if no explicit masks) % - Explicit mask images are >0 for valid voxels to assess. % - Mask images can have any orientation, voxel size or data @@ -56,10 +56,21 @@ % - Note that voxels with constant data (i.e. the same value across % scans) are also automatically masked out. % +% swd - Directory where the output files will be saved [default: pwd] +% If exists, it becomes the current working directory. +% +% In addition, global SPM "defaults" variable is used (see spm_defaults): +% +% stats..UFp - critical F-threshold for selecting voxels over +% which the non-sphericity is estimated (if +% required) [default: 0.001] +% +% stats.maxres - maximum number of residual images for smoothness +% estimation +% +% stats.maxmem - maximum amount of data processed at a time (in bytes) % -% In addition, the global default UFp is used to set a critical -% F-threshold for selecting voxels over which the non-sphericity -% is estimated (if required) +% modality - SPM modality {'PET','FMRI','EEG'} % %__________________________________________________________________________ % @@ -77,31 +88,31 @@ % matrix X, (unknown) parameters B and residual errors e. The errors % are assumed to have a normal distribution. % -% Sometimes confounds (e.g. drift terms in fMRI) are necessary. These +% Sometimes confounds (e.g. drift terms in fMRI) are necessary. These % can be specified directly in the design matrix or implicitly, in terms % of a residual forming matrix K to give a generalised linear model % K*Y = K*X*B + K*e. In fact K can be any matrix (e.g. a convolution % matrix). % -% In some instances i.i.d. assumptions about errors do not hold. For +% In some instances i.i.d. assumptions about errors do not hold. For % example, with serially correlated (fMRI) data or correlations among the -% levels of a factor in repeated measures designs. This non-sphericity +% levels of a factor in repeated measures designs. This non-sphericity % can be specified in terms of components (SPM.xVi.Vi{i}). If specified % these covariance components will then be estimated with ReML (restricted -% maximum likelihood) hyperparameters. This estimation assumes the same +% maximum likelihood) hyperparameters. This estimation assumes the same % non-sphericity for voxels that exceed the global F-threshold. The ReML -% estimates can then used to whiten the data giving maximum likelihood (ML) -% or Gauss-Markov estimators. This entails a second pass of the data +% estimates can then be used to whiten the data giving maximum likelihood +% (ML) or Gauss-Markov estimators. This entails a second pass of the data % with an augmented model K*W*Y = K*W*X*B + K*W*e where W*W' = inv(xVi.V). % xVi.V is the non-sphericity based on the hyperparameter estimates. -% W is stored in xX.W and cov(K*W*e) in xX.V. The covariance of the -% parameter estimates is then xX.Bcov = pinv(K*W*X)*xX.V*pinv(K*W*X)'; +% W is stored in xX.W and cov(K*W*e) in xX.V. The covariance of the +% parameter estimates is then xX.Bcov = pinv(K*W*X)*xX.V*pinv(K*W*X)'. % % If you do not want ML estimates but want to use ordinary least squares % (OLS) then simply set SPM.xX.W to the identity matrix. Any non-sphericity % V will still be estimated but will be used to adjust the degrees of freedom % of the ensuing statistics using the Satterthwaite approximation (c.f. -% the Greenhouse-Giesser corrections) +% the Greenhouse-Geisser corrections). % % If [non-spherical] variance components Vi are not specified xVi.Vi and % xVi.V default to the identity matrix (i.e. i.i.d). The parameters are @@ -134,12 +145,10 @@ % % ---------------- % -% -% The volume analsed is the intersection of the threshold masks, +% The volume analysed is the intersection of the threshold masks, % explicit masks and implicit masks. See spm_spm_ui for further details % on masking options. % -% %-------------------------------------------------------------------------- % % The output of spm_spm takes the form of an SPM.mat file of the analysis @@ -164,6 +173,7 @@ % VM - file struct of Mask image handle (relative) % % ---------------- +% % xX.W - if not specified W*W' = inv(x.Vi.V) % xX.V - V matrix (K*W*Vi*W'*K') = correlations after K*W is applied % xX.xKXs - space structure for K*W*X, the 'filtered and whitened' @@ -172,9 +182,9 @@ % xX.pKX - pseudoinverse of K*W*X, computed by spm_sp % xX.Bcov - xX.pKX*xX.V*xX.pKX - variance-covariance matrix of % parameter estimates -% (when multiplied by the voxel-specific hyperparameter ResMS) -% (of the parameter estimates. (ResSS/xX.trRV = ResMS) ) -% xX.trRV - trace of R*V, computed efficiently by spm_SpUtil +% (when multiplied by the voxel-specific hyperparameter ResMS +% of the parameter estimates (ResSS/xX.trRV = ResMS) ) +% xX.trRV - trace of R*V % xX.trRVRV - trace of RVRV % xX.erdf - effective residual degrees of freedom (trRV^2/trRVRV) % xX.nKX - design matrix (xX.xKXs.X) scaled for display @@ -192,11 +202,6 @@ % % ---------------- % -% xCon - See Christensen for details of F-contrasts. These are specified -% at the end of spm_spm after the non-sphericity V has been defined -% or estimated. The fist contrast tests for all effects of interest -% assuming xX.iB and xX.iG index confounding or nuisance effects. -% % xCon - Contrast structure (created by spm_FcUtil.m) % xCon.name - Name of contrast % xCon.STAT - 'F', 'T' or 'P' - for F/T-contrast ('P' for PPMs) @@ -206,9 +211,9 @@ % coordinates of this matrix in the orthogonal basis % of xX.X defined in spm_sp. % xCon.iX0 - Indicates how contrast was specified: -% If by columns for reduced design matrix then iX0 contains the -% column indices. Otherwise, it's a string containing the -% spm_FcUtil 'Set' action: Usually one of {'c','c+','X0'} +% If by columns for reduced design matrix then iX0 contains +% the column indices. Otherwise, it's a string containing +% the spm_FcUtil 'Set' action: Usually one of {'c','c+','X0'} % (Usually this is the input argument F_iX0.) % xCon.X1o - Remaining design space (orthogonal to X0). % It is in the form of a matrix (spm99b) or the @@ -234,7 +239,7 @@ % ---------------- % % beta_????.{img,hdr} - parameter images -% These are 16-bit (float) images of the parameter estimates. The image +% These are 32-bit (float32) images of the parameter estimates. The image % files are numbered according to the corresponding column of the % design matrix. Voxels outside the analysis mask (mask.img) are given % value NaN. @@ -242,16 +247,22 @@ % ---------------- % % ResMS.{img,hdr} - estimated residual variance image -% This is a 32-bit (double) image of the residual variance estimate. +% This is a 64-bit (float64) image of the residual variance estimate. % Voxels outside the analysis mask are given value NaN. % % ---------------- % % RPV.{img,hdr} - estimated resels per voxel image -% This is a 32-bit (double) image of the RESELs per voxel estimate. +% This is a 64-bit (float64) image of the RESELs per voxel estimate. % Voxels outside the analysis mask are given value 0. These images % reflect the nonstationary aspects the spatial autocorrelations. % +% ---------------- +% +% ResI_????.{img,hdr} - standardised residual (temporary) images +% These are 64-bit (float64) images of standardised residuals. At most +% maxres images will be saved and used by spm_est_smoothness, after which +% they will be deleted. % %-------------------------------------------------------------------------- % @@ -260,7 +271,7 @@ % Christensen R (1996) Plane Answers to Complex Questions % Springer Verlag % -% Friston KJ, Holmes AP, Worsley KJ, Poline JP, Frith CD, Frackowiak RSJ (1995) +% Friston KJ, Holmes AP, Worsley KJ, Poline JB, Frith CD, Frackowiak RSJ (1995) % ``Statistical Parametric Maps in Functional Imaging: % A General Linear Approach'' % Human Brain Mapping 2:189-210 @@ -269,24 +280,18 @@ % ``Analysis of fMRI Time-Series Revisited - Again'' % NeuroImage 2:173-181 % -%-------------------------------------------------------------------------- -% -% For those interested, the analysis proceeds a "block" at a time, -% The block size conforms to maxMem that can be set as a global variable -% MAXMEM (in bytes) [default = 2^20] -% %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Andrew Holmes, Jean-Baptiste Poline & Karl Friston -% $Id: spm_spm.m 3151 2009-05-26 18:46:38Z guillaume $ +% $Id: spm_spm.m 3342 2009-09-02 10:35:28Z guillaume $ -SVNid = '$Rev: 3151 $'; +SVNid = '$Rev: 3342 $'; %-Say hello %-------------------------------------------------------------------------- -SPMid = spm('FnBanner',mfilename,SVNid); -Finter = spm('FigName','Stats: estimation...'); spm('Pointer','Watch'); +SPMid = spm('FnBanner',mfilename,SVNid); +Finter = spm('FigName','Stats: estimation...'); spm('Pointer','Watch'); %-Get SPM.mat[s] if necessary %-------------------------------------------------------------------------- @@ -305,6 +310,8 @@ %-------------------------------------------------------------------------- try cd(SPM.swd); +catch + SPM.swd = pwd; end %-Ensure data are assigned @@ -312,25 +319,23 @@ try SPM.xY.VY; catch - helpdlg('Please assign data to this design'); + spm('alert!','Please assign data to this design', mfilename); spm('FigName','Stats: done',Finter); spm('Pointer','Arrow') return end %-Delete files from previous analyses %-------------------------------------------------------------------------- -if exist(fullfile('.','mask.img'),'file') == 2 +if exist(fullfile(SPM.swd,'mask.img'),'file') == 2 - str = {'Current directory contains SPM estimation files:',... - 'pwd = ',pwd,... + str = {'Current directory contains SPM estimation files:',... + 'pwd = ',SPM.swd,... 'Existing results will be overwritten!'}; - - abort = spm_input(str,1,'bd','stop|continue',[1,0],1); - if abort + if spm_input(str,1,'bd','stop|continue',[1,0],1) spm('FigName','Stats: done',Finter); spm('Pointer','Arrow') return else - warning('Overwriting old results\n\t (pwd = %s) ',pwd); + warning('Overwriting old results\n\t (pwd = %s) ',SPM.swd); try, SPM = rmfield(SPM,'xVol'); end end end @@ -340,7 +345,7 @@ '^ess_.{4}\..{3}$', '^spm\w{1}_.{4}\..{3}$'}; for i=1:length(files) - j = spm_select('List',pwd,files{i}); + j = spm_select('List',SPM.swd,files{i}); for k=1:size(j,1) spm_unlink(deblank(j(k,:))); end @@ -434,12 +439,10 @@ %-Design space and projector matrix [pseudoinverse] for WLS %========================================================================== -xX.xKXs = spm_sp('Set',spm_filter(xX.K,W*xX.X)); % KWX +xX.xKXs = spm_sp('Set',spm_filter(xX.K,W*xX.X)); % KWX xX.xKXs.X = full(xX.xKXs.X); -xX.pKX = spm_sp('x-',xX.xKXs); % projector -erdf = spm_SpUtil('trRV',xX.xKXs); % Working error df - -global defaults +xX.pKX = spm_sp('x-',xX.xKXs); % projector +erdf = spm_SpUtil('trRV',xX.xKXs); % Working error df %-If xVi.V is not defined compute Hsqr and F-threshold under i.i.d. %-------------------------------------------------------------------------- @@ -452,68 +455,41 @@ trRV = spm_SpUtil('trRV',xX.xKXs); trMV = spm_SpUtil('trMV',X1o); - % Threshold for voxels entering non-sphericity esimtates + % Threshold for voxels entering non-sphericity estimates %---------------------------------------------------------------------- try - UFp = eval(['defaults.stats.' lower(defaults.modality) '.ufp']); + modality = lower(spm_get_defaults('modality')); + UFp = spm_get_defaults(['stats.' modality '.ufp']); catch - UFp = 0.001; + UFp = 0.001; end - UF = spm_invFcdf(1 - UFp,[trMV,trRV]); + UF = spm_invFcdf(1 - UFp,[trMV,trRV]); end %-Image dimensions and data %========================================================================== VY = SPM.xY.VY; -M = VY(1).mat; -DIM = VY(1).dim(1:3)'; -xdim = DIM(1); ydim = DIM(2); zdim = DIM(3); -YNaNrep = spm_type(VY(1).dt(1),'nanrep'); - -%-Set data units for non-Talairach data -%========================================================================== +spm_check_orientations(VY); -% assume mm in stardard space -%-------------------------------------------------------------------------- -units = {'mm' 'mm' 'mm'}; -MEEGscaling = false; - -try - % 3-D case, with arbitrary dimensions - % (dimension check to disambiguate 3D source reconstruction from 2D+t - % images, see spm_eeg_inv_Mesh2Voxels.m - to be improved...) +for i = 1:numel(VY) + % check files exists and try pwd %---------------------------------------------------------------------- - if strcmpi(defaults.modality,'EEG') && ~all(DIM == [91 109 91]') - - % z dimension is percent - %------------------------------------------------------------------ - Minit = M; - M(3,3) = 100 / DIM(3); - M(3,4) = 0; - [VY.mat] = deal(M); - SPM.xY.VY = VY; - units = {'mm' 'mm' '%'}; - MEEGscaling = true; + if ~spm_existfile(VY(i).fname) + [p,n,e] = fileparts(VY(i).fname); + VY(i).fname = [n,e]; end end -% 2-D case -%-------------------------------------------------------------------------- -if DIM(3) == 1 - units{3} = ''; -end +M = VY(1).mat; +DIM = VY(1).dim(1:3)'; +xdim = DIM(1); ydim = DIM(2); zdim = DIM(3); +YNaNrep = spm_type(VY(1).dt(1),'nanrep'); %-Maximum number of residual images for smoothness estimation %-------------------------------------------------------------------------- -MAXRES = defaults.stats.maxres; - -%-maxMem is the maximum amount of data processed at a time (bytes) -%-------------------------------------------------------------------------- -MAXMEM = defaults.stats.maxmem; +MAXRES = spm_get_defaults('stats.maxres'); nSres = min(nScan,MAXRES); -blksz = min(xdim*ydim,ceil(MAXMEM/8/nScan)); %-block size -nbch = ceil(xdim*ydim/blksz); %-# blocks fprintf('%s%30s\n',repmat(sprintf('\b'),1,30),'...done'); %-# @@ -547,7 +523,6 @@ for i = 1:nBeta Vbeta(i).fname = sprintf('beta_%04d.img',i); Vbeta(i).descrip = sprintf('spm_spm:beta (%04d) - %s',i,xX.name{i}); - spm_unlink(Vbeta(i).fname) end Vbeta = spm_create_vol(Vbeta); @@ -576,26 +551,34 @@ for i = 1:nSres VResI(i).fname = sprintf('ResI_%04d.img', i); VResI(i).descrip = sprintf('spm_spm:ResI (%04d)', i); - spm_unlink(VResI(i).fname); end VResI = spm_create_vol(VResI); fprintf('%s%30s\n',repmat(sprintf('\b'),1,30),'...initialised'); %-# end % (xX,'W') + %========================================================================== % - F I T M O D E L & W R I T E P A R A M E T E R I M A G E S %========================================================================== +%-MAXMEM is the maximum amount of data processed at a time (bytes) +%-------------------------------------------------------------------------- +MAXMEM = spm_get_defaults('stats.maxmem'); +mmv = MAXMEM/8/nScan; +blksz = min(xdim*ydim,ceil(mmv)); %-block size +nbch = ceil(xdim*ydim/blksz); %-# blocks +nbz = max(1,min(zdim,floor(mmv/(xdim*ydim)))); nbz = 1; %-# planes +blksz = blksz * nbz; %-Initialise variables used in the loop %========================================================================== -xords = (1:xdim)'*ones(1,ydim); xords = xords(:)'; % plane X coordinates -yords = ones(xdim,1)*(1:ydim); yords = yords(:)'; % plane Y coordinates -S = 0; % Volume (voxels) -s = 0; % Volume (voxels > UF) -Cy = 0; % spatially whitened -CY = 0; % for ReML -EY = 0; % for ReML +[xords, yords] = ndgrid(1:xdim, 1:ydim); +xords = xords(:)'; yords = yords(:)'; % plane X,Y coordinates +S = 0; % Volume (voxels) +s = 0; % Volume (voxels > UF) +Cy = 0; % spatially whitened +CY = 0; % <(Y - ) * (Y - )'> +EY = 0; % for ReML i_res = round(linspace(1,nScan,nSres))'; % Indices for residual %-Initialise XYZ matrix of in-mask voxel co-ordinates (real space) @@ -606,34 +589,44 @@ %========================================================================== spm_progress_bar('Init',100,str,''); -for z = 1:zdim %-loop over planes (2D or 3D data) +for z = 1:nbz:zdim %-loop over planes (2D or 3D data) % current plane-specific parameters %---------------------------------------------------------------------- - zords = z*ones(xdim*ydim,1)'; %-plane Z coordinates - CrBl = []; %-parameter estimates - CrResI = []; %-normalized residuals - CrResSS = []; %-residual sum of squares - Q = []; %-in mask indices for this plane + CrPl = z:min(z+nbz-1,zdim); %-plane list + zords = CrPl(:)*ones(1,xdim*ydim); %-plane Z coordinates + CrBl = []; %-parameter estimates + CrResI = []; %-normalized residuals + CrResSS = []; %-residual sum of squares + Q = []; %-in mask indices for this plane - for bch = 1:nbch %-loop over blocks + for bch = 1:nbch %-loop over blocks - %-# Print progress information in command window + %-Print progress information in command window %------------------------------------------------------------------ - str = sprintf('Plane %3d/%-3d, block %3d/%-3d',z,zdim,bch,nbch); - fprintf('\r%-40s: %30s',str,' '); %-# + if numel(CrPl) == 1 + str = sprintf('Plane %3d/%-3d, block %3d/%-3d',... + z,zdim,bch,nbch); + else + str = sprintf('Planes %3d-%-3d/%-3d',z,CrPl(end),zdim); + end + if z==1&&bch==1, str2=''; else str2=repmat(sprintf('\b'),1,72); end + fprintf('%s%-40s: %30s',str2,str,' '); %-# %-construct list of voxels in this block %------------------------------------------------------------------ - I = (1:blksz) + (bch - 1)*blksz; %-voxel indices - I = I(I <= xdim*ydim); %-truncate - xyz = [xords(I); yords(I); zords(I)]; %-voxel coordinates - nVox = size(xyz,2); %-number of voxels + I = (1:blksz) + (bch - 1)*blksz; %-voxel indices + I = I(I <= numel(CrPl)*xdim*ydim); %-truncate + xyz = [repmat(xords,1,numel(CrPl)); ... + repmat(yords,1,numel(CrPl)); ... + reshape(zords',1,[])]; + xyz = xyz(:,I); %-voxel coordinates + nVox = size(xyz,2); %-number of voxels %-Get data & construct analysis mask %================================================================= fprintf('%s%30s',repmat(sprintf('\b'),1,30),'...read & mask data') - Cm = true(1,nVox); %-current mask + Cm = true(1,nVox); %-current mask %-Compute explicit mask @@ -643,15 +636,11 @@ %-Coordinates in mask image %-------------------------------------------------------------- - if ~MEEGscaling - j = xM.VM(i).mat\M*[xyz;ones(1,nVox)]; - else - j = xM.VM(i).mat\Minit*[xyz;ones(1,nVox)]; - end + j = xM.VM(i).mat\M*[xyz;ones(1,nVox)]; %-Load mask image within current mask & update mask %-------------------------------------------------------------- - Cm(Cm) = spm_get_data(xM.VM(i),j(:,Cm)) > 0; + Cm(Cm) = spm_get_data(xM.VM(i),j(:,Cm),false) > 0; end %-Get the data in mask, compute threshold & implicit masks @@ -661,8 +650,8 @@ %-Load data in mask %-------------------------------------------------------------- - if ~any(Cm), break, end %-Break if empty mask - Y(i,Cm) = spm_get_data(VY(i),xyz(:,Cm)); + if ~any(Cm), break, end %-Break if empty mask + Y(i,Cm) = spm_get_data(VY(i),xyz(:,Cm),false); Cm(Cm) = Y(i,Cm) > xM.TH(i); %-Threshold (& NaN) mask if xM.I && ~YNaNrep && xM.TH(i) < 0 %-Use implicit mask @@ -684,27 +673,18 @@ %-Whiten/Weight data and remove filter confounds %-------------------------------------------------------------- - fprintf('%s%30s',repmat(sprintf('\b'),1,30),'filtering'); %-# + fprintf('%s%30s',repmat(sprintf('\b'),1,30),'...filtering');%-# KWY = spm_filter(xX.K,W*Y); - if isfield(xX,'W') && any(~isfinite(KWY(:))), - % Try to find the wierd Matlab 7 bug that I - % was getting on my Linux machine -JA - fprintf('\n'); - disp('Please inform the SPM developers about'); - disp('the configuration of your machine, and the'); - disp('MATLAB version that you are running.'); - warning('Found non-finite values in KWY.'); - end; %-General linear model: Weighted least squares estimation %-------------------------------------------------------------- - fprintf('%s%30s',repmat(sprintf('\b'),1,30),' estimation'); %-# + fprintf('%s%30s',repmat(sprintf('\b'),1,30),'...estimation');%-# - beta = xX.pKX*KWY; %-Parameter estimates - res = spm_sp('r',xX.xKXs,KWY); %-Residuals - ResSS = sum(res.^2); %-Residual SSQ - clear KWY %-Clear to save memory + beta = xX.pKX*KWY; %-Parameter estimates + res = spm_sp('r',xX.xKXs,KWY); %-Residuals + ResSS = sum(res.^2); %-Residual SSQ + clear KWY %-Clear to save memory %-If ReML hyperparameters are needed for xVi.V @@ -752,7 +732,7 @@ Q = [Q I(Cm)]; %-InMask XYZ voxel indices S = S + CrS; %-Volume analysed (voxels) - end % (bch) + end % (bch) %-Plane complete, write plane to image files (unless 1st pass) @@ -761,35 +741,31 @@ fprintf('%s%30s',repmat(sprintf('\b'),1,30),'...saving plane'); %-# + jj = NaN(xdim,ydim,numel(CrPl)); + %-Write Mask image %------------------------------------------------------------------ - jj = sparse(xdim,ydim); if ~isempty(Q), jj(Q) = 1; end - VM = spm_write_plane(VM, jj, z); + VM = spm_write_plane(VM, ~isnan(jj), CrPl); %-Write beta images %------------------------------------------------------------------ - % For some reason, on my machine with Matlab 7.0.1, some of the - % values from jj appear in unexpected places in KWY (but not in - % argout within spm_filter). Something strange happens on - % returning out of the function - jj = NaN*ones(xdim,ydim); for i = 1:nBeta if ~isempty(Q), jj(Q) = CrBl(i,:); end - Vbeta(i) = spm_write_plane(Vbeta(i), jj, z); + Vbeta(i) = spm_write_plane(Vbeta(i), jj, CrPl); end %-Write residual images %------------------------------------------------------------------ for i = 1:nSres if ~isempty(Q), jj(Q) = CrResI(i,:)./sqrt(CrResSS/erdf); end - VResI(i) = spm_write_plane(VResI(i), jj, z); + VResI(i) = spm_write_plane(VResI(i), jj, CrPl); end %-Write ResSS into ResMS (variance) image scaled by tr(RV) above %------------------------------------------------------------------ if ~isempty(Q), jj(Q) = CrResSS; end - VResMS = spm_write_plane(VResMS,jj,z); + VResMS = spm_write_plane(VResMS, jj, CrPl); end % (xX,'W') @@ -806,7 +782,7 @@ %========================================================================== % - P O S T E S T I M A T I O N C L E A N U P %========================================================================== -if S == 0, warndlg('No inmask voxels - empty analysis!'); return; end +if S == 0, spm('alert!','No inmask voxels - empty analysis!'); return; end %-average sample covariance and mean of Y (over voxels) %-------------------------------------------------------------------------- @@ -820,26 +796,27 @@ %-check there are signficant voxels %---------------------------------------------------------------------- - if ~s + if s == 0 spm('FigName','Stats: no significant voxels',Finter); spm('Pointer','Arrow'); - figure(Finter); if isfield(SPM.xGX,'rg')&&~isempty(SPM.xGX.rg) - plot(SPM.xGX.rg) - errordlg({'Please check your data'; ... + figure(Finter); + plot(SPM.xGX.rg); + spm('alert*',{'Please check your data'; ... 'There are no significant voxels';... 'The globals are plotted for diagnosis'}); else - errordlg({'Please check your data'; ... + spm('alert*',{'Please check your data'; ... 'There are no significant voxels'}); end - error('Please check your data: There are no significant voxels.'); + warning('Please check your data: There are no significant voxels.'); + return end - %-REML estimate of residual correlations through hyperparameters (h) + %-ReML estimate of residual correlations through hyperparameters (h) %---------------------------------------------------------------------- str = 'Temporal non-sphericity (over voxels)'; - fprintf('%-40s: %30s\n',str,'...REML estimation'); %-# + fprintf('%-40s: %30s\n',str,'...ReML estimation'); %-# Cy = Cy/s; % ReML for separable designs and covariance components @@ -872,7 +849,7 @@ % ReML %-------------------------------------------------------------- - fprintf('%-30s- %i\n',' ReML Block',i); + fprintf('%-30s\n',sprintf(' ReML Block %i',i)); [Vp,hp] = spm_reml(Cy(q,q),Xp,Qp); V(q,q) = V(q,q) + Vp; h(p) = hp; @@ -896,7 +873,7 @@ save('SPM','SPM','-V6'); else save('SPM','SPM'); - end; + end clear load SPM SPM = spm_spm(SPM); @@ -933,10 +910,9 @@ %-Delete the residuals images %========================================================================== -for i = 1:nSres, - spm_unlink([spm_str_manip(VResI(i).fname,'r') '.img']); - spm_unlink([spm_str_manip(VResI(i).fname,'r') '.hdr']); - spm_unlink([spm_str_manip(VResI(i).fname,'r') '.mat']); +j = spm_select('List',SPM.swd,'^ResI_.{4}\..{3}$'); +for k = 1:size(j,1) + spm_unlink(deblank(j(k,:))); end @@ -955,7 +931,6 @@ SPM.xVol.M = M; %-voxels -> mm SPM.xVol.iM = inv(M); %-mm -> voxels SPM.xVol.DIM = DIM; %-image dimensions -SPM.xVol.units = units; %-image units SPM.xVol.FWHM = FWHM; %-Smoothness data SPM.xVol.R = R; %-Resel counts SPM.xVol.S = S; %-Volume (voxels) @@ -969,6 +944,7 @@ SPM.xVi.CY = CY; %-<(Y - )*(Y - )'> SPM.xX = xX; %-design structure + SPM.xM = xM; %-mask structure SPM.xCon = struct([]); %-contrast structure diff --git a/spm_spm_Bayes.m b/spm_spm_Bayes.m index e02981b..30fe20e 100644 --- a/spm_spm_Bayes.m +++ b/spm_spm_Bayes.m @@ -68,7 +68,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_spm_Bayes.m 3055 2009-04-09 06:17:29Z volkmar $ +% $Id: spm_spm_Bayes.m 3310 2009-08-07 13:33:41Z guillaume $ %-Say hello @@ -78,9 +78,9 @@ %-Select SPM.mat & change directory %----------------------------------------------------------------------- if ~nargin - [P, sts] = spm_select(1,'^SPM\.mat$','Select SPM.mat'); + [Pf, sts] = spm_select(1,'^SPM\.mat$','Select SPM.mat'); if ~sts, return; end - swd = spm_str_manip(P,'H'); + swd = spm_str_manip(Pf,'H'); load(fullfile(swd,'SPM.mat')) cd(swd) end diff --git a/spm_transverse.m b/spm_transverse.m index d10cecd..dc6a213 100644 --- a/spm_transverse.m +++ b/spm_transverse.m @@ -22,7 +22,7 @@ function spm_transverse(varargin) % i.e., the updating is one way only. % % See also: spm_getSPM -%_______________________________________________________________________ +%__________________________________________________________________________ % % spm_transverse is called by the SPM results section and uses % variables in SPM and SPM to create three transverse sections though a @@ -31,65 +31,70 @@ function spm_transverse(varargin) % % Although the SPM{.} adopts the neurological convention (left = left) % the rendered images follow the same convention as the original data. -%_______________________________________________________________________ +%__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston & John Ashburner -% $Id: spm_transverse.m 3073 2009-04-21 16:33:44Z guillaume $ +% $Id: spm_transverse.m 3348 2009-09-03 10:32:01Z guillaume $ switch lower(varargin{1}) case 'set' % draw slices - %--------------------------------------------------------------- - + %---------------------------------------------------------------------- init(varargin{2},varargin{3}); case 'setcoords' % reposition - %--------------------------------------------------------------- + %---------------------------------------------------------------------- disp('Reposition'); case 'clear' % clear - %--------------------------------------------------------------- + %---------------------------------------------------------------------- clear_global; -end; -return; +end +return; +%========================================================================== +% function init(SPM,hReg) +%========================================================================== function init(SPM,hReg) %-Get figure handles -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- Fgraph = spm_figure('GetWin','Graphics'); - %-Get the image on which to render -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- spms = spm_select(1,'image','Select image for rendering on'); spm('Pointer','Watch'); %-Delete previous axis and their pagination controls (if any) -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- spm_results_ui('Clear',Fgraph); global transv transv = struct('blob',[],'V',spm_vol(spms),'h',[],'hReg',hReg,'fig',Fgraph); transv.blob = struct('xyz', round(SPM.XYZ), 't',SPM.Z, 'dim',SPM.DIM(1:3),... - 'iM',SPM.iM,... - 'vox', sqrt(sum(SPM.M(1:3,1:3).^2)), 'u', SPM.u); + 'iM',SPM.iM,... + 'vox', sqrt(sum(SPM.M(1:3,1:3).^2)), 'u', SPM.u); %-Get current location and convert to pixel co-ordinates -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- xyzmm = spm_XYZreg('GetCoords',transv.hReg); xyz = round(transv.blob.iM(1:3,:)*[xyzmm; 1]); +try + units = SPM.units; +catch + units = {'mm' 'mm' 'mm'}; +end -% extract data from SPM [at one plane separation] -% and get background slices -%---------------------------------------------------------------------- +%-Extract data from SPM [at one plane separation] and get background slices +%-------------------------------------------------------------------------- dim = ceil(transv.blob.dim(1:3)'.*transv.blob.vox); A = transv.blob.iM*transv.V.mat; hld = 0; @@ -140,7 +145,7 @@ function init(SPM,hReg) end; %-Configure {128 level} colormap -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- cmap = get(Fgraph,'Colormap'); if size(cmap,1) ~= 128 figure(Fgraph) @@ -163,7 +168,7 @@ function init(SPM,hReg) P = xyz.*transv.blob.vox'; %-Render activation foci on background images -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- if transv.blob.dim(3) > 1 zm = min([(siz(1) - 120)/(dim(1)*3),(siz(2)/2 - 60)/dim(2)]); xo = (siz(1)-(dim(1)*zm*3)-120)/2; @@ -175,7 +180,7 @@ function init(SPM,hReg) tmp = SPM.iM\[xyz(1:2)' (xyz(3)-1) 1]'; ax=transv.h(1);tpoint=get(ax,'title'); - str=sprintf('z = %0.0fmm',tmp(3)); + str=sprintf('z = %0.0f%s',tmp(3),units{3}); set(tpoint,'string',str); transv.h(3) = line([1 1]*P(1),[0 dim(2)],'Color','w','Parent',transv.h(1)); @@ -187,7 +192,7 @@ function init(SPM,hReg) tmp = SPM.iM\[xyz(1:2)' xyz(3) 1]'; ax=transv.h(5);tpoint=get(ax,'title'); - str=sprintf('z = %0.0fmm',tmp(3)); + str=sprintf('z = %0.0f%s',tmp(3),units{3}); set(tpoint,'string',str); transv.h(7) = line([1 1]*P(1),[0 dim(2)],'Color','w','Parent',transv.h(5)); @@ -199,14 +204,14 @@ function init(SPM,hReg) tmp = SPM.iM\[xyz(1:2)' (xyz(3)+1) 1]'; ax=transv.h(9);tpoint=get(ax,'title'); - str=sprintf('z = %0.0fmm',tmp(3)); + str=sprintf('z = %0.0f%s',tmp(3),units{3}); set(tpoint,'string',str); transv.h(11) = line([1 1]*P(1),[0 dim(2)],'Color','w','Parent',transv.h(9)); transv.h(12) = line([0 dim(1)],[1 1]*(dim(2)-P(2)+1),'Color','w','Parent',transv.h(9)); % colorbar - %----------------------------------------------------------------------- + %---------------------------------------------------------------------- q = [80+dim(1)*zm*3+xo 20+yo 20 dim(2)*zm]; if SPM.STAT=='P' str='Effect size'; @@ -232,12 +237,12 @@ function init(SPM,hReg) transv.h(1) = axes('Units','pixels','Parent',Fgraph,'Position',[20+xo 20+yo dim(1)*zm dim(2)*zm]); transv.h(2) = image(rot90(spm_grid(T2)),'Parent',transv.h(1)); axis image; axis off; - title(sprintf('z = %0.0fmm',xyzmm(3))); + title(sprintf('z = %0.0f%s',xyzmm(3),units{3})); transv.h(3) = line([1 1]*P(1),[0 dim(2)],'Color','w','Parent',transv.h(1)); transv.h(4) = line([0 dim(1)],[1 1]*(dim(2)-P(2)+1),'Color','w','Parent',transv.h(1)); % colorbar - %----------------------------------------------------------------------- + %---------------------------------------------------------------------- q = [40+dim(1)*zm+xo 20+yo 20 dim(2)*zm]; transv.h(5) = axes('Units','pixels','Parent',Fgraph,'Position',q,'Visible','off'); transv.h(6) = image([0 mx/32],[mn mx],(1:D)' + D,'Parent',transv.h(5)); @@ -252,20 +257,22 @@ function init(SPM,hReg) axis xy; -end; +end spm_XYZreg('Add2Reg',transv.hReg,transv.h(1), 'spm_transverse'); for h=transv.h, set(h,'DeleteFcn',@clear_global); -end; +end %-Reset pointer -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- spm('Pointer','Arrow') return; -%_______________________________________________________________________ -%_______________________________________________________________________ + +%========================================================================== +% function reposition(xyzmm) +%========================================================================== function reposition(xyzmm) global transv if ~isstruct(transv), return; end; @@ -274,13 +281,13 @@ function reposition(xyzmm) %-Get current location and convert to pixel co-ordinates -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- % xyzmm = spm_XYZreg('GetCoords',transv.hReg) xyz = round(transv.blob.iM(1:3,:)*[xyzmm; 1]); % extract data from SPM [at one plane separation] % and get background slices -%---------------------------------------------------------------------- +%-------------------------------------------------------------------------- dim = ceil(transv.blob.dim(1:3)'.*transv.blob.vox); A = transv.blob.iM*transv.V.mat; hld = 0; @@ -331,7 +338,7 @@ function reposition(xyzmm) end; %-Configure {128 level} colormap -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- cmap = get(transv.fig,'Colormap'); if size(cmap,1) ~= 128 figure(transv.fig) @@ -350,7 +357,7 @@ function reposition(xyzmm) P = xyz.*transv.blob.vox'; %-Render activation foci on background images -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- if transv.blob.dim(3) > 1 set(transv.h(2),'Cdata',rot90(spm_grid(T1))); @@ -371,7 +378,7 @@ function reposition(xyzmm) set(transv.h(12),'Xdata',[0 dim(1)],'Ydata',[1 1]*(dim(2)-P(2)+1)); % colorbar - %----------------------------------------------------------------------- + %---------------------------------------------------------------------- set(transv.h(14), 'Ydata',[mn mx], 'Cdata',(1:D)' + D); set(transv.h(13),'XTickLabel',[],'Ylim',[mn mx]); @@ -382,29 +389,31 @@ function reposition(xyzmm) set(transv.h(4),'Xdata',[0 dim(1)],'Ydata',[1 1]*(dim(2)-P(2)+1)); % colorbar - %----------------------------------------------------------------------- + %---------------------------------------------------------------------- set(transv.h(6), 'Ydata',[0 d], 'Cdata',(1:D)' + D); set(transv.h(5),'XTickLabel',[],'Ylim',[0 d]); -end; +end %-Reset pointer -%----------------------------------------------------------------------- +%-------------------------------------------------------------------------- spm('Pointer','Arrow') return; -%_______________________________________________________________________ -%_______________________________________________________________________ + +%========================================================================== +% function clear_global(varargin) +%========================================================================== function clear_global(varargin) global transv if isstruct(transv), for h = transv.h, if ishandle(h), set(h,'DeleteFcn',''); end; - end; + end for h = transv.h, if ishandle(h), delete(h); end; - end; + end transv = []; clear global transv; -end; +end return; diff --git a/spm_uw_show.m b/spm_uw_show.m index bf44a2a..33b4b63 100644 --- a/spm_uw_show.m +++ b/spm_uw_show.m @@ -1,18 +1,18 @@ -function spm_graphwip(mode,p1,p2,p3,p4,p5,p6) +function spm_uw_show(mode,p1,p2,p3,p4,p5,p6) % Manage graphical output for spm_FindFields % -% FORMAT spm_graphwip(mode,p1,...) +% FORMAT spm_uw_show(mode,p1,...) % % mode - Verb specifying action. % p1-p6 - Depends on mode. % -% FORMAT spm_graphwip('FinIter',SS,beta,fot,sot,ref,q) +% FORMAT spm_uw_show('FinIter',SS,beta,fot,sot,ref,q) % %_______________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jesper Andersson -% $Id: spm_uw_show.m 2696 2009-02-05 20:29:48Z guillaume $ +% $Id: spm_uw_show.m 3285 2009-07-23 19:25:22Z guillaume $ persistent iter; @@ -79,98 +79,92 @@ function spm_graphwip(mode,p1,p2,p3,p4,p5,p6) error('Wrong no. of arguments in call to ud_graphwip'); end fg = spm_figure('FindWin','Graphics'); - if isempty(fg) - fg = spm_figure('Create','Graphics'); - if isempty(fg) - error('Cannot find or create graphics window'); - end - end - spm_figure('Clear','Graphics'); - % - % Get mask for display of deformation maps. - % - mask = zeros(p5.dim(1:3)); - spm_smooth(reshape(p5.dat,p5.dim(1:3)),mask,5); - mask = mask > 0.3*mean(mean(mean(mask))); + if ~isempty(fg) + spm_figure('Clear','Graphics'); + % + % Get mask for display of deformation maps. + % + mask = zeros(p5.dim(1:3)); + spm_smooth(reshape(p5.dat,p5.dim(1:3)),mask,5); + mask = mask > 0.3*mean(mean(mean(mask))); - % - % Display deformation maps. - % - figure(fg); - fg_pos = get(fg,'Position'); - fs = spm('FontSizes'); - pf = spm_platform('fonts'); - - % - % Write general title. - % - uicontrol(fg,'Style','Text',... - 'Position',[fg_pos(3)/10 0.95*fg_pos(4) 8*fg_pos(3)/10 30],... - 'String','Estimation of EPI deformation fields',... - 'ForegroundColor','k','BackgroundColor','w',... - 'FontSize',fs(20),'FontName',pf.times) - - % - % Code modelled on spm_check_registration.m - % - mn = size(p2,2); - n = round(mn^0.4); - m = ceil(mn/n); - w = 1/n; - h = .75/m; - ds = (w+h)*0.02; - - spm_orthviews('Reset'); - global st; - P = p5; - for ij=1:mn, - P.dat = p2(:,ij); - P.dat = reshape(P.dat,P.dim(1:3)) .* mask; - i = 1-h*(floor((ij-1)/n)+1); - j = w*rem(ij-1,n); - gh = spm_orthviews('Image',P,[j+ds/2 i+ds/2 w-ds h-ds]); - if ij==1 - spm_orthviews('Space'); - spm_orthviews('maxBB'); - end; - if ij <= size(p3,2) % If first order effect. - string = sprintf('Derivative w.r.t. %s',eff_string{p3(ij)}); - else - string = sprintf('Second order effect w.r.t. %s and %s',... + % + % Display deformation maps. + % + figure(fg); + fg_pos = get(fg,'Position'); + fs = spm('FontSizes'); + pf = spm_platform('fonts'); + + % + % Write general title. + % + uicontrol(fg,'Style','Text',... + 'Position',[fg_pos(3)/10 0.95*fg_pos(4) 8*fg_pos(3)/10 30],... + 'String','Estimation of EPI deformation fields',... + 'ForegroundColor','k','BackgroundColor','w',... + 'FontSize',fs(20),'FontName',pf.times) + + % + % Code modelled on spm_check_registration.m + % + mn = size(p2,2); + n = round(mn^0.4); + m = ceil(mn/n); + w = 1/n; + h = .75/m; + ds = (w+h)*0.02; + + spm_orthviews('Reset'); + global st; + P = p5; + for ij=1:mn, + P.dat = p2(:,ij); + P.dat = reshape(P.dat,P.dim(1:3)) .* mask; + i = 1-h*(floor((ij-1)/n)+1); + j = w*rem(ij-1,n); + gh = spm_orthviews('Image',P,[j+ds/2 i+ds/2 w-ds h-ds]); + if ij==1 + spm_orthviews('Space'); + spm_orthviews('maxBB'); + end; + if ij <= size(p3,2) % If first order effect. + string = sprintf('Derivative w.r.t. %s',eff_string{p3(ij)}); + else + string = sprintf('Second order effect w.r.t. %s and %s',... eff_string{p4(ij-size(p3,2),1)},eff_string{p4(ij-size(p3,2),2)}); - end - if mn > 6 - fsi = 12; - elseif mn > 4 - fsi = 14; - else - fsi = 16; - end - uicontrol(fg,'Style','Text',... - 'Position',[(st.vols{gh}.area(1)+(st.vols{gh}.area(3)+ds)/2)*fg_pos(3)... - st.vols{gh}.area(2)*fg_pos(4)... - ((st.vols{gh}.area(3)-ds)/2)*fg_pos(3)... - (2*st.vols{gh}.area(4)/5)*fg_pos(4)],... - 'String',string,... - 'ForegroundColor','k','BackgroundColor','w',... - 'FontSize',fs(fsi),'FontName',pf.times) - end; - - % - % Show plot of residual squared error - % - axes('Position',[.1 .025 .375 .17]); - indx = find(p1 ~= 0); - plot(indx,p1(indx),'-k','LineWidth',2); - title('Residual error','FontSize',fs(14),'FontName',pf.times); - - % - % Show plot of relevant movement parameters. - % - axes('Position',[.575 .025 .375 .17]); - plot(p6); - title('Movement parameters','FontSize',fs(14),'FontName',pf.times); - + end + if mn > 6 + fsi = 12; + elseif mn > 4 + fsi = 14; + else + fsi = 16; + end + uicontrol(fg,'Style','Text',... + 'Position',[(st.vols{gh}.area(1)+(st.vols{gh}.area(3)+ds)/2)*fg_pos(3)... + st.vols{gh}.area(2)*fg_pos(4)... + ((st.vols{gh}.area(3)-ds)/2)*fg_pos(3)... + (2*st.vols{gh}.area(4)/5)*fg_pos(4)],... + 'String',string,... + 'ForegroundColor','k','BackgroundColor','w',... + 'FontSize',fs(fsi),'FontName',pf.times) + end; + % + % Show plot of residual squared error + % + axes('Position',[.1 .025 .375 .17]); + indx = find(p1 ~= 0); + plot(indx,p1(indx),'-k','LineWidth',2); + title('Residual error','FontSize',fs(14),'FontName',pf.times); + + % + % Show plot of relevant movement parameters. + % + axes('Position',[.575 .025 .375 .17]); + plot(p6); + title('Movement parameters','FontSize',fs(14),'FontName',pf.times); + end case 'FinTot' % Report on outcome of undeformation. spm_progress_bar('Clear'); diff --git a/spm_vb_neighbors.m b/spm_vb_neighbors.m index 8fca120..511f64a 100644 --- a/spm_vb_neighbors.m +++ b/spm_vb_neighbors.m @@ -17,7 +17,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Will Penny, Nelson Trujillo-Barreto and Lee Harrison -% $Id: spm_vb_neighbors.m 2887 2009-03-17 08:09:39Z lee $ +% $Id: spm_vb_neighbors.m 3291 2009-07-28 10:26:16Z guillaume $ if nargin<2 vol=0; @@ -25,7 +25,7 @@ nearestneighbor = 1; N = size(xyz,1); -DIM = max(xyz); +DIM = max(xyz,[],1); if vol, I = sub2ind([DIM(1),DIM(2),DIM(3)],xyz(:,1),xyz(:,2),xyz(:,3)); is = zeros(DIM(1),DIM(2),DIM(3)); @@ -77,4 +77,4 @@ kj = iS(p(q)); % all nodes in p with neighbours nj = Q(q); % all neighbours of p in direction duj vxyz(kj,j) = nj; -end \ No newline at end of file +end diff --git a/src/Makefile b/src/Makefile index b1f84dc..b195c53 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,7 +3,7 @@ # # Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging # -# $Id: Makefile 2990 2009-03-28 17:33:52Z guillaume $ +# $Id: Makefile 3251 2009-07-06 17:29:44Z guillaume $ # ############################################################################### # @@ -46,8 +46,8 @@ SPMMEX =\ spm_add.$(SUF) spm_conv_vol.$(SUF) spm_render_vol.$(SUF)\ spm_global.$(SUF) spm_resels_vol.$(SUF)\ spm_bsplinc.$(SUF) spm_bsplins.$(SUF) spm_bias_mex.$(SUF)\ - spm_unlink.$(SUF) spm_existfile.$(SUF) spm_hist.$(SUF)\ - spm_krutil.$(SUF) spm_project.$(SUF) spm_hist2.$(SUF)\ + spm_unlink.$(SUF) spm_existfile.$(SUF) spm_gamrnd.$(SUF)\ + spm_hist.$(SUF) spm_krutil.$(SUF) spm_project.$(SUF) spm_hist2.$(SUF)\ spm_dilate_erode.$(SUF) spm_bwlabel.$(SUF) spm_get_lm.$(SUF)\ spm_invdef.$(SUF) mat2file.$(SUF) file2mat.$(SUF) spm_voronoi.$(SUF) @@ -254,6 +254,9 @@ spm_dilate_erode.$(SUF): spm_dilate_erode.c spm_existfile.$(SUF): spm_existfile.c $(MEX) spm_existfile.c $(MEXEND) +spm_gamrnd.$(SUF): spm_gamrnd.c + $(MEX) spm_gamrnd.c $(MEXEND) + spm_get_lm.$(SUF): spm_get_lm.c $(MEX) spm_get_lm.c $(MEXEND) diff --git a/src/Makefile.var b/src/Makefile.var index a5f5185..f836789 100644 --- a/src/Makefile.var +++ b/src/Makefile.var @@ -2,7 +2,7 @@ # # Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging # -# $Id: Makefile.var 2885 2009-03-16 20:45:23Z guillaume $ +# $Id: Makefile.var 3297 2009-07-29 17:20:19Z guillaume $ # ############################################################################### # @@ -25,6 +25,7 @@ # * MacOS: # http://en.wikibooks.org/wiki/SPM/Installation_on_Mac_OS # http://en.wikibooks.org/wiki/SPM/Installation_on_Mac_OS_(Intel) +# http://en.wikibooks.org/wiki/SPM/Installation_on_64bit_Mac_OS_(Intel) # ############################################################################### diff --git a/src/file2mat.c b/src/file2mat.c index 839fa6f..027e1b7 100644 --- a/src/file2mat.c +++ b/src/file2mat.c @@ -1,5 +1,5 @@ /* - * $Id: file2mat.c 2894 2009-03-18 12:43:34Z guillaume $ + * $Id: file2mat.c 3392 2009-09-11 14:13:38Z guillaume $ * John Ashburner */ @@ -29,11 +29,17 @@ typedef char *caddr_t; #define fstat _fstati64 #define open _open #define close _close +#if defined _MSC_VER +#define size_t __int64 +#else +#define size_t unsigned long long +#endif #endif #else #include #include #include +#define size_t unsigned long long #endif /* @@ -58,7 +64,7 @@ return size; #define MXDIMS 256 -static int icumprod[MXDIMS], ocumprod[MXDIMS]; +static long long icumprod[MXDIMS], ocumprod[MXDIMS]; static void get_1_sat(int ndim, int idim[], int *iptr[], unsigned char idat[], int odim[], unsigned char odat[], int indi, int indo) { @@ -236,10 +242,10 @@ void get_w64(int ndim, int idim[], int *iptr[], unsigned long long idat[], } } -void swap8(int n, unsigned char d[]) +void swap8(long long n, unsigned char d[]) { /* DO NOTHING */} -void swap16(int n, unsigned char d[]) +void swap16(long long n, unsigned char d[]) { unsigned char tmp, *de; for(de=d+2*n; ddtype->bytes*siz+7)/8; else siz = siz*(map->dtype->bytes/8); + + /* On 32bit platforms, cannot map more than 2^31-1 bytes */ + if ((sizeof(map->data) == 4) && (siz > 2147483647ULL)) + mexErrMsgTxt("The total number of bytes mapped is too large."); pr = getpr(ptr, "be",1, &n); #ifdef SPM_BIGENDIAN @@ -483,7 +493,7 @@ void do_map_file(const mxArray *ptr, MTYPE *map) mexErrMsgTxt("File is smaller than the dimensions say it should be."); } offset = map->off % page_size(); - map->len = siz + offset; + map->len = siz + (size_t)offset; map->off = map->off - offset; #ifdef SPM_WIN32 (void)close(fd); @@ -598,8 +608,8 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) ocumprod[0] = 1; for(i=0; i +#include "mex.h" + + +/* Return a sample from Uniform on the unit interval + */ +static long ix = 101; +static long iy = 1001; +static long iz = 10001; +double Rand(void) +{ + static float u; + + ix = 171*(ix % 177)-2*(ix/177); + iy = 172*(iy % 176)-2*(iy/176); + iz = 170*(iz % 178)-2*(iz/178); + + if (ix<0) ix = ix + 30269; + if (iy<0) iy = iy + 30307; + if (iz<0) iz = iz + 30323; + + u = ((float) ix)/30269 + + ((float) iy)/30307 + ((float) iz)/30323; + u -= (float)(int)u; + return(u); +} + +/* Returns a sample from Normal(0,1) + */ +double RandN(void) +{ + double x,y,radius; + /* Generate a random point inside the unit circle */ + do { + x = 2*Rand()-1; + y = 2*Rand()-1; + radius = (x*x)+(y*y); + } while((radius >= 1.0) || (radius == 0.0)); + /* Box-Muller formula */ + radius = sqrt(-2*log(radius)/radius); + x *= radius; + y *= radius; + return x; +} + +/* Returns a sample from Gamma(a, 1). + */ +double GammaRand(double a) +{ + /* Algorithm: + * G. Marsaglia and W.W. Tsang, A simple method for generating gamma + * variables, ACM Transactions on Mathematical Software, Vol. 26, No. 3, + * Pages 363-372, September, 2000. + * http://portal.acm.org/citation.cfm?id=358414 + */ + double boost, d, c, v; + if(a < 1) { + /* boost using Marsaglia's (1961) method: gam(a) = gam(a+1)*U^(1/a) */ + boost = exp(log(Rand())/a); + a++; + } + else boost = 1; + d = a-1.0/3; c = 1.0/sqrt(9*d); + while(1) { + double x,u; + do { + x = RandN(); + v = 1+c*x; + } while(v <= 0); + v = v*v*v; + x = x*x; + u = Rand(); + if((u < 1-.0331*x*x) || + (log(u) < 0.5*x + d*(1-v+log(v)))) break; + } + return( boost*d*v ); +} + +/* Main gateway + */ +void mexFunction(int nlhs, mxArray *plhs[], + int nrhs, const mxArray *prhs[]) { + int ndims, len, i; + int *dims = NULL; + double a, b; + double *o = NULL; + + if (nlhs > 1) + mexErrMsgTxt("Too many output arguments."); + + if (nrhs < 2) + mexErrMsgTxt("Requires at least two input arguments."); + + ndims = (nrhs == 2) ? 1 : nrhs-2; + dims = (int*)mxMalloc(ndims*sizeof(int)); + + len = 1; + for (i=0;i