spm_eeg_prep.m 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. function D = spm_eeg_prep(S)
  2. % Prepare converted M/EEG data for further analysis
  3. % FORMAT D = spm_eeg_prep(S)
  4. % S - configuration structure (optional)
  5. % (optional) fields of S:
  6. % S.D - MEEG object or filename of M/EEG mat-file
  7. % S.task - action string. One of 'settype', 'defaulttype',
  8. % 'loadtemplate','setcoor2d', 'project3d', 'loadeegsens',
  9. % 'defaulteegsens', 'sens2chan', 'headshape',
  10. % 'coregister', 'sortconditions'
  11. %
  12. % S.updatehistory - update history information [default: true]
  13. % S.save - save MEEG object [default: false]
  14. %
  15. % D - MEEG object
  16. %__________________________________________________________________________
  17. % Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging
  18. % Vladimir Litvak
  19. % $Id: spm_eeg_prep.m 7175 2017-09-26 14:50:54Z vladimir $
  20. D = spm_eeg_load(S.D);
  21. switch lower(S.task)
  22. %----------------------------------------------------------------------
  23. case 'setbadchan'
  24. %----------------------------------------------------------------------
  25. D = badchannels(D, D.selectchannels(S.channels), S.status);
  26. %----------------------------------------------------------------------
  27. case 'settype'
  28. %----------------------------------------------------------------------
  29. D = chantype(D, S.ind, S.type);
  30. %----------------------------------------------------------------------
  31. case 'defaulttype'
  32. %----------------------------------------------------------------------
  33. if isfield(S, 'ind')
  34. ind = S.ind;
  35. else
  36. ind = 1:D.nchannels;
  37. end
  38. dictionary = {
  39. 'eog', 'EOG';
  40. 'eeg', 'EEG';
  41. 'ecg', 'ECG';
  42. 'lfp', 'LFP';
  43. 'emg', 'EMG';
  44. 'meg', 'MEG';
  45. 'ref', 'REF';
  46. 'megref' 'REF';
  47. 'megmag', 'MEGMAG';
  48. 'megplanar', 'MEGPLANAR';
  49. 'meggrad', 'MEGGRAD';
  50. 'refmag', 'REFMAG';
  51. 'refgrad', 'REFGRAD'
  52. 'refplanar' 'REFPLANAR'
  53. };
  54. D = chantype(D, ind, 'Other');
  55. type = ft_chantype(D.chanlabels);
  56. % If there is useful information in the original types it
  57. % overwrites the default assignment
  58. if isfield(D, 'origchantypes')
  59. [sel1, sel2] = spm_match_str(chanlabels(D, ind), D.origchantypes.label);
  60. type(ind(sel1)) = D.origchantypes.type(sel2);
  61. end
  62. spmtype = repmat({'Other'}, 1, length(ind));
  63. [sel1, sel2] = spm_match_str(type(ind), dictionary(:, 1));
  64. spmtype(sel1) = dictionary(sel2, 2);
  65. D = chantype(D, ind, spmtype);
  66. %----------------------------------------------------------------------
  67. case 'bidschantype'
  68. dictionary = { % Should be updates with updates to BIDS specification
  69. 'MEGMAG' 'MEGMAG'
  70. 'MEGGRAD' 'MEGPLANAR'
  71. 'MEG' 'MEG'
  72. 'MEGREF' 'REF'
  73. 'EEG' 'EEG'
  74. 'EOG' 'EOG'
  75. 'ECG' 'ECG'
  76. 'EMG' 'EMG'
  77. 'TRIG' 'Other'
  78. 'AUDIO' 'Other'
  79. 'PD' 'Other'
  80. 'ET' 'Other'
  81. 'MISC' 'Other'
  82. };
  83. bids_chan = spm_load(S.filename);
  84. [sel1, sel2] = spm_match_str(D.chanlabels, bids_chan.name);
  85. type = bids_chan.type(sel2);
  86. [sel3, sel4] = spm_match_str(type, dictionary(:, 1));
  87. type(sel3) = dictionary(sel4, 2);
  88. D = chantype(D, sel1, type);
  89. case 'bidschanstatus'
  90. bids_chan = spm_load(S.filename);
  91. [sel1, sel2] = spm_match_str(D.chanlabels, bids_chan.name);
  92. sel3 = strmatch('bad', bids_chan.status(sel2));
  93. D = badchannels(D, sel1, 0);
  94. D = badchannels(D, sel1(sel3), 1);
  95. case {'loadtemplate', 'setcoor2d', 'project3d'}
  96. %----------------------------------------------------------------------
  97. chanind = 1:D.nchannels;
  98. switch lower(S.task)
  99. case 'loadtemplate'
  100. template = load(S.P); % must contain Cpos, Cnames
  101. xy = template.Cpos;
  102. label = template.Cnames;
  103. case 'setcoor2d'
  104. xy = S.xy;
  105. label = S.label;
  106. case 'project3d'
  107. if ~isfield(D, 'val')
  108. D.val = 1;
  109. end
  110. if isfield(D, 'inv') && isfield(D.inv{D.val}, 'datareg')
  111. datareg = D.inv{D.val}.datareg;
  112. ind = strmatch(S.modality, {datareg(:).modality}, 'exact');
  113. sens = datareg(ind).sensors;
  114. else
  115. sens = D.sensors(S.modality);
  116. end
  117. [xy, label] = spm_eeg_project3D(sens, S.modality);
  118. switch S.modality
  119. case 'EEG'
  120. chanind = D.indchantype('EEG');
  121. case 'MEG'
  122. chanind = D.indchantype('MEGANY');
  123. case 'MEGCOMB'
  124. chanind = D.indchantype('MEGCOMB');
  125. end
  126. end
  127. megcombind = D.indchantype('MEGCOMB');
  128. if ~isempty(megcombind)
  129. chanset = spm_eeg_planarchannelset(D.chanlabels);
  130. [sel1, sel2] = spm_match_str(lower(chanset(:, 1)), lower(label));
  131. [sel3, sel4] = spm_match_str(lower(chanset(:, 2)), lower(label));
  132. [sel5, sel6, sel7] = intersect(sel1, sel3);
  133. label = [label(:); chanset(sel5, 3)];
  134. xy = [xy 0.5*(xy(:, sel2(sel6)) + xy(:, sel4(sel7)))];
  135. end
  136. [sel1, sel2] = spm_match_str(lower(D.chanlabels(chanind)), lower(label));
  137. sel1 = chanind(sel1);
  138. if ~isempty(sel1)
  139. megind = D.indchantype('MEG');
  140. eegind = D.indchantype('EEG');
  141. if ~isempty(intersect(megind, sel1)) && ~isempty(setdiff(megind, sel1))
  142. error('2D locations not found for all MEG channels');
  143. end
  144. if ~isempty(intersect(megcombind, sel1)) && ~isempty(setdiff(megcombind, sel1))
  145. error('2D locations not found for all MEGCOMB channels');
  146. end
  147. if ~isempty(intersect(eegind, sel1)) && ~isempty(setdiff(eegind, sel1))
  148. warning(['2D locations not found for all EEG channels, changing type of channels', ...
  149. num2str(setdiff(eegind, sel1)) ' to ''Other''']);
  150. D = chantype(D, setdiff(eegind, sel1), 'Other');
  151. end
  152. D = coor2D(D, sel1, num2cell(xy(:, sel2)));
  153. end
  154. %----------------------------------------------------------------------
  155. case 'loadeegsens'
  156. %----------------------------------------------------------------------
  157. switch S.source
  158. case 'mat'
  159. senspos = load(S.sensfile);
  160. name = fieldnames(senspos);
  161. senspos = getfield(senspos,name{1});
  162. label = chanlabels(D, D.indchantype('EEG'));
  163. if size(senspos, 1) ~= length(label)
  164. error('To read sensor positions without labels the numbers of sensors and EEG channels should match.');
  165. end
  166. elec = [];
  167. elec.chanpos = senspos;
  168. elec.elecpos = senspos;
  169. elec.label = label;
  170. headshape = load(S.headshapefile);
  171. name = fieldnames(headshape);
  172. headshape = getfield(headshape,name{1});
  173. shape = [];
  174. fidnum = 0;
  175. while ~all(isspace(S.fidlabel))
  176. fidnum = fidnum+1;
  177. [shape.fid.label{fidnum},S.fidlabel] = strtok(S.fidlabel);
  178. end
  179. if (fidnum < 3) || (size(headshape, 1) < fidnum)
  180. error('At least 3 labeled fiducials are necessary');
  181. end
  182. shape.fid.pnt = headshape(1:fidnum, :);
  183. if size(headshape, 1) > fidnum
  184. shape.pnt = headshape((fidnum+1):end, :);
  185. else
  186. shape.pnt = [];
  187. end
  188. case 'locfile'
  189. label = chanlabels(D, D.indchantype('EEG'));
  190. elec = ft_read_sens(S.sensfile);
  191. % Remove headshape points
  192. hspind = strmatch('headshape', elec.label);
  193. elec.chanpos(hspind, :) = [];
  194. elec.elecpos(hspind, :) = [];
  195. elec.chantype(hspind, :) = [];
  196. elec.chanunit(hspind, :) = [];
  197. elec.label(hspind) = [];
  198. % This handles FIL Polhemus case and other possible cases
  199. % when no proper labels are available.
  200. if isempty(intersect(label, elec.label))
  201. ind = str2num(strvcat(elec.label));
  202. if length(ind) == length(label)
  203. elec.label = label(ind);
  204. else
  205. error('To read sensor positions without labels the numbers of sensors and EEG channels should match.');
  206. end
  207. end
  208. shape = spm_eeg_fixpnt(ft_read_headshape(S.sensfile));
  209. % In case electrode file is used for fiducials, the
  210. % electrodes can be used as headshape
  211. if ~isfield(shape, 'pnt') || isempty(shape.pnt) && ...
  212. size(shape.fid.pnt, 1) > 3
  213. shape.pnt = shape.fid.pnt;
  214. end
  215. end
  216. elec = ft_convert_units(elec, 'mm');
  217. shape= ft_convert_units(shape, 'mm');
  218. if isequal(D.modality(1, 0), 'Multimodal')
  219. if ~isempty(D.fiducials) && isfield(S, 'regfid') && ~isempty(S.regfid)
  220. M1 = coreg(D.fiducials, shape, S.regfid);
  221. elec = ft_transform_sens(M1, elec);
  222. else
  223. error(['MEG fiducials matched to EEG fiducials are required '...
  224. 'to add EEG sensors to a multimodal dataset.']);
  225. end
  226. else
  227. D = fiducials(D, shape);
  228. end
  229. D = sensors(D, 'EEG', elec);
  230. %----------------------------------------------------------------------
  231. case 'defaulteegsens'
  232. %----------------------------------------------------------------------
  233. template_sfp = dir(fullfile(spm('dir'), 'EEGtemplates', '*.sfp'));
  234. template_sfp = {template_sfp.name};
  235. if ft_senstype(D.chanlabels(D.indchantype('EEG')), 'ext1020')
  236. ind = strmatch('ext1020.sfp', template_sfp, 'exact');
  237. else
  238. ind = strmatch([ft_senstype(D.chanlabels(D.indchantype('EEG'))) '.sfp'], template_sfp, 'exact');
  239. end
  240. if ~isempty(ind)
  241. fid = D.fiducials;
  242. if isequal(D.modality(1, 0), 'Multimodal') && ~isempty(fid)
  243. nzlbl = {'fidnz', 'nz', 'nas', 'nasion', 'spmnas'};
  244. lelbl = {'fidle', 'fidt9', 'lpa', 'lear', 'earl', 'le', 'l', 't9', 'spmlpa'};
  245. relbl = {'fidre', 'fidt10', 'rpa', 'rear', 'earr', 're', 'r', 't10', 'spmrpa'};
  246. [sel1, nzind] = spm_match_str(nzlbl, lower(fid.fid.label));
  247. if ~isempty(nzind)
  248. nzind = nzind(1);
  249. end
  250. [sel1, leind] = spm_match_str(lelbl, lower(fid.fid.label));
  251. if ~isempty(leind)
  252. leind = leind(1);
  253. end
  254. [sel1, reind] = spm_match_str(relbl, lower(fid.fid.label));
  255. if ~isempty(reind)
  256. reind = reind(1);
  257. end
  258. regfid = fid.fid.label([nzind, leind, reind]);
  259. if numel(regfid) < 3
  260. error('Could not automatically understand the MEG fiducial labels. Please use the GUI.');
  261. else
  262. regfid = [regfid(:) {'spmnas'; 'spmlpa'; 'spmrpa'}];
  263. end
  264. S1 = [];
  265. S1.D = D;
  266. S1.task = 'loadeegsens';
  267. S1.source = 'locfile';
  268. S1.regfid = regfid;
  269. S1.sensfile = fullfile(spm('dir'), 'EEGtemplates', template_sfp{ind});
  270. S1.updatehistory = 0;
  271. D = spm_eeg_prep(S1);
  272. else
  273. elec = ft_read_sens(fullfile(spm('dir'), 'EEGtemplates', template_sfp{ind}));
  274. [sel1, sel2] = spm_match_str(lower(D.chanlabels), lower(elec.label));
  275. sens = elec;
  276. sens.chanpos = sens.chanpos(sel2, :);
  277. sens.elecpos = sens.elecpos(sel2, :);
  278. sens.chantype = sens.chantype(sel2, :);
  279. sens.chanunit = sens.chanunit(sel2, :);
  280. % This takes care of possible case mismatch
  281. sens.label = D.chanlabels(sel1);
  282. sens.label = sens.label(:);
  283. D = sensors(D, 'EEG', sens);
  284. % Assumes that the first 3 points in standard location files
  285. % are the 3 fiducials (nas, lpa, rpa)
  286. fid = [];
  287. fid.pnt = elec.elecpos;
  288. fid.fid.pnt = elec.elecpos(1:3, :);
  289. fid.fid.label = elec.label(1:3);
  290. [xy, label] = spm_eeg_project3D(D.sensors('EEG'), 'EEG');
  291. [sel1, sel2] = spm_match_str(lower(D.chanlabels), lower(label));
  292. if ~isempty(sel1)
  293. eegind = strmatch('EEG', chantype(D), 'exact');
  294. if ~isempty(intersect(eegind, sel1)) && ~isempty(setdiff(eegind, sel1))
  295. warning(['2D locations not found for all EEG channels, changing type of channels ', ...
  296. num2str(setdiff(eegind(:)', sel1(:)')) ' to ''Other''']);
  297. D = chantype(D, setdiff(eegind, sel1), 'Other');
  298. end
  299. if any(any(coor2D(D, sel1) - xy(:, sel2)))
  300. D = coor2D(D, sel1, num2cell(xy(:, sel2)));
  301. end
  302. end
  303. if strcmp(D.modality(1, 0), 'Multimodal') && ~isempty(D.fiducials)...
  304. && isfield(S, 'regfid') && ~isempty(S.regfid)
  305. M1 = coreg(D.fiducials, fid, S.regfid);
  306. D = sensors(D, 'EEG', ft_transform_sens(M1, D.sensors('EEG')));
  307. else
  308. D = fiducials(D, fid);
  309. end
  310. end
  311. end
  312. %----------------------------------------------------------------------
  313. case 'loadmegsens'
  314. %----------------------------------------------------------------------
  315. hdr = ft_read_header(S.source);
  316. D = sensors(D, 'MEG', hdr.grad);
  317. D = fiducials(D, ft_convert_units(ft_read_headshape(S.source), 'mm'));
  318. if ~isempty(D.indchantype('MEG')) && ~isempty(D.sensors('MEG'))
  319. S1 = [];
  320. S1.task = 'project3D';
  321. S1.modality = 'MEG';
  322. S1.updatehistory = 0;
  323. S1.D = D;
  324. D = spm_eeg_prep(S1);
  325. end
  326. %----------------------------------------------------------------------
  327. case 'sens2chan'
  328. %----------------------------------------------------------------------
  329. if isfield(S, 'montage')
  330. montage = S.montage;
  331. if ischar(montage)
  332. montage = getfield(load(montage), 'montage');
  333. end
  334. elseif isfield(S, 'refelec');
  335. sens = sensors(D, 'EEG');
  336. if isempty(sens)
  337. error('The montage cannod be applied - no EEG sensors specified');
  338. end
  339. if ismember('all', S.refelec)
  340. refind = 1:numel(sens.label);
  341. else
  342. refind = spm_match_str(sens.label, S.refelec);
  343. end
  344. tra = eye(numel(sens.label));
  345. tra(:, refind) = tra(:, refind) - 1/length(refind);
  346. montage.tra = tra;
  347. montage.labelorg = sens.label;
  348. montage.labelnew = sens.label;
  349. else
  350. error('Montage or list of reference sensors should be specified');
  351. end
  352. modalities = {'EEG', 'MEG'};
  353. for m = 1:numel(modalities)
  354. sens = sensors(D, modalities{m});
  355. if ~isempty(sens) && ~isempty(intersect(sens.label, montage.labelorg))
  356. sens = sensors(D, 'EEG');
  357. sens = ft_apply_montage(sens, montage, 'keepunused', 'no');
  358. D = sensors(D, modalities{m}, sens);
  359. end
  360. end
  361. %----------------------------------------------------------------------
  362. case 'headshape'
  363. %----------------------------------------------------------------------
  364. switch S.source
  365. case 'mat'
  366. headshape = load(S.headshapefile);
  367. name = fieldnames(headshape);
  368. headshape = getfield(headshape,name{1});
  369. shape = [];
  370. fidnum = 0;
  371. while ~all(isspace(S.fidlabel))
  372. fidnum = fidnum+1;
  373. [shape.fid.label{fidnum},S.fidlabel] = strtok(S.fidlabel);
  374. end
  375. if (fidnum < 3) || (size(headshape, 1) < fidnum)
  376. error('At least 3 labeled fiducials are necessary');
  377. end
  378. shape.fid.pnt = headshape(1:fidnum, :);
  379. if size(headshape, 1) > fidnum
  380. shape.pnt = headshape((fidnum+1):end, :);
  381. else
  382. shape.pnt = [];
  383. end
  384. otherwise
  385. shape = spm_eeg_fixpnt(ft_read_headshape(S.headshapefile));
  386. % In case electrode file is used for fiducials, the
  387. % electrodes can be used as headshape
  388. if ~isfield(shape, 'pnt') || isempty(shape.pnt) && ...
  389. size(shape.fid.pnt, 1) > 3
  390. shape.pnt = shape.fid.pnt;
  391. end
  392. end
  393. shape = ft_convert_units(shape, 'mm');
  394. fid = D.fiducials;
  395. if ~isempty(fid) && isfield(S, 'regfid') && ~isempty(S.regfid)
  396. M1 = coreg(fid, shape, S.regfid);
  397. shape = ft_transform_headshape(M1, shape);
  398. end
  399. D = fiducials(D, shape);
  400. %----------------------------------------------------------------------
  401. case 'coregister'
  402. %----------------------------------------------------------------------
  403. [D, ok] = check(D, '3d');
  404. if ~ok
  405. error('Coregistration cannot be performed due to missing data');
  406. end
  407. try
  408. val = D.val;
  409. Msize = D.inv{val}.mesh.Msize;
  410. catch
  411. val = 1;
  412. Msize = 1;
  413. end
  414. D = spm_eeg_inv_mesh_ui(D, val, 1, Msize);
  415. D = spm_eeg_inv_datareg_ui(D, val);
  416. case 'sortconditions'
  417. if ischar(S.condlist)
  418. cl = getfield(load(S.condlist), 'condlist');
  419. else
  420. cl = S.condlist;
  421. end
  422. D = condlist(D, cl);
  423. %----------------------------------------------------------------------
  424. case 'loadbidsevents'
  425. if ~isequal(D.type, 'continuous')
  426. error('This operation can only be applied to continuous datasets');
  427. end
  428. ev_bids = spm_load(S.filename);
  429. ev_spm = struct('type', repmat({'BIDS'}, length(ev_bids.onset), 1), 'value', ev_bids.stim_type,...
  430. 'time', num2cell(ev_bids.onset), 'duration', num2cell(ev_bids.duration));
  431. if S.replace
  432. D = events(D, 1, ev_spm);
  433. else
  434. D = events(D, 1, spm_cat_struct(D.events(1), ev_spm));
  435. end
  436. D = D.check;
  437. otherwise
  438. %----------------------------------------------------------------------
  439. fprintf('Unknown task ''%s'' to perform: Nothing done.\n',S.task);
  440. end
  441. % When prep is called from other functions with history, history should be
  442. % disabled
  443. if ~isfield(S, 'updatehistory') || S.updatehistory
  444. Stemp = S;
  445. Stemp.D = fullfile(D.path,D.fname);
  446. Stemp.save = 1;
  447. D = D.history('spm_eeg_prep', Stemp);
  448. end
  449. if isfield(S, 'save') && S.save
  450. save(D);
  451. end
  452. %==========================================================================
  453. % function coreg
  454. %==========================================================================
  455. function M1 = coreg(fid, shape, regfid)
  456. [junk, sel1] = spm_match_str(regfid(:, 1), fid.fid.label);
  457. [junk, sel2] = spm_match_str(regfid(:, 2), shape.fid.label);
  458. S = [];
  459. S.targetfid = fid;
  460. S.targetfid.fid.pnt = S.targetfid.fid.pnt(sel1, :);
  461. S.sourcefid = shape;
  462. S.sourcefid.fid.pnt = S.sourcefid.fid.pnt(sel2, :);
  463. S.sourcefid.fid.label = S.sourcefid.fid.label(sel2);
  464. S.targetfid.fid.label = S.sourcefid.fid.label;
  465. S.template = 1;
  466. S.useheadshape = 0;
  467. M1 = spm_eeg_inv_datareg(S);