Scheduled service maintenance on November 22


On Friday, November 22, 2024, between 06:00 CET and 18:00 CET, GIN services will undergo planned maintenance. Extended service interruptions should be expected. We will try to keep downtimes to a minimum, but recommend that users avoid critical tasks, large data uploads, or DOI requests during this time.

We apologize for any inconvenience.

myeeg_SASICA.m 83 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636
  1. % EEG = eeg_SASICA(EEG,cfg)
  2. %
  3. % Suggest components to reject from an EEG dataset with ICA decomposition.
  4. %
  5. % Inputs: EEG: EEGlab structure with ICA fields.
  6. % cfg: structure describing which methods are to use for suggesting
  7. % bad components (see structure called def, in the code below)
  8. % Available methods are:
  9. % Autocorrelation: detects noisy components with weak
  10. % autocorrelation (muscle artifacts usually)
  11. % Focal components: detects components that are too focal and
  12. % thus unlikely to correspond to neural
  13. % activity (bad channel or muscle usually).
  14. % Focal trial activity: detects components with focal trial
  15. % activity, with same algorhithm as focal
  16. % components above. Results similar to trial
  17. % variability.
  18. % Signal to noise ratio: detects components with weak signal
  19. % to noise ratio between arbitrary baseline
  20. % and interest time windows.
  21. % Dipole fit residual variance: detects components with high
  22. % residual variance after subtraction of the
  23. % forward dipole model. Note that the inverse
  24. % dipole modeling using DIPFIT2 in EEGLAB
  25. % must have been computed to use this
  26. % measure.
  27. % EOG correlation: detects components whose time course
  28. % correlates with EOG channels.
  29. % Bad channel correlation: detects components whose time course
  30. % correlates with any channel(s).
  31. % ADJUST selection: use ADJUST routines to select components
  32. % (see Mognon, A., Jovicich, J., Bruzzone,
  33. % L., & Buiatti, M. (2011). ADJUST: An
  34. % automatic EEG artifact detector based on
  35. % the joint use of spatial and temporal
  36. % features. Psychophysiology, 48(2), 229-240.
  37. % doi:10.1111/j.1469-8986.2010.01061.x)
  38. % FASTER selection: use FASTER routines to select components
  39. % (see Nolan, H., Whelan, R., & Reilly, R. B.
  40. % (2010). FASTER: Fully Automated Statistical
  41. % Thresholding for EEG artifact Rejection.
  42. % Journal of Neuroscience Methods, 192(1),
  43. % 152-162. doi:16/j.jneumeth.2010.07.015)
  44. % MARA selection: use MARA classification engine to select components
  45. % (see Winkler I, Haufe S, Tangermann M.
  46. % 2011. Automatic Classification of
  47. % Artifactual ICA-Components for Artifact
  48. % Removal in EEG Signals. Behavioral and
  49. % Brain Functions. 7:30.)
  50. %
  51. % Options: noplot: just compute and store result in EEG. Do
  52. % not make any plots.
  53. %
  54. % If you use this program in your research, please cite the following
  55. % article:
  56. % Chaumon M, Bishop DV, Busch NA. A Practical Guide to the Selection of
  57. % Independent Components of the Electroencephalogram for Artifact
  58. % Correction. Journal of neuroscience methods. 2015
  59. %
  60. % SASICA is a software that helps select independent components of
  61. % the electroencephalogram based on various signal measures.
  62. % Copyright (C) 2014 Maximilien Chaumon
  63. %
  64. % This program is free software: you can redistribute it and/or modify
  65. % it under the terms of the GNU General Public License as published by
  66. % the Free Software Foundation, either version 3 of the License, or
  67. % (at your option) any later version.
  68. %
  69. % This program is distributed in the hope that it will be useful,
  70. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  71. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  72. % GNU General Public License for more details.
  73. %
  74. % You should have received a copy of the GNU General Public License
  75. % along with this program. If not, see <http://www.gnu.org/licenses/>.
  76. function [EEG, cfg,h] = myeeg_SASICA(EEG,cfg)
  77. if nargin < 1
  78. error('Need at least one input argument')
  79. end
  80. % deal with calling pop_prop_ADJ or pop_prop_FST here
  81. if ischar(cfg) && strncmp(cfg,'pop_prop',8)
  82. [~,h,EEG] = evalc(cfg);
  83. return
  84. end
  85. if ~exist('cfg','var')
  86. cfg = struct;
  87. end
  88. %
  89. PLOTPERFIG = 35;
  90. % get SASICA defs
  91. % def = SASICA('getdefs');
  92. def.autocorr.enable = true;
  93. def.autocorr.dropautocorr = 'auto';
  94. def.autocorr.autocorrint = 20;% will compute autocorrelation with this many milliseconds lag
  95. def.focalcomp.enable = true;
  96. def.focalcomp.focalICAout = 'auto';
  97. def.trialfoc.enable = true;
  98. def.trialfoc.focaltrialout = 'auto';
  99. def.resvar.enable = false;
  100. def.resvar.thresh = 15;% %residual variance allowed
  101. def.SNR.enable = false;
  102. def.SNR.snrPOI = [0 Inf];% period of interest (signal)
  103. def.SNR.snrBL = [-Inf 0];% period of no interest (noise)
  104. def.SNR.snrcut = 1;% SNR below this threshold will be dropped
  105. def.EOGcorr.enable = true;
  106. def.EOGcorr.corthreshV = 'auto 4';% threshold correlation with vertical EOG
  107. def.EOGcorr.Veogchannames = [];% vertical channel(s)
  108. def.EOGcorr.corthreshH = 'auto 4';% threshold correlation with horizontal EOG
  109. def.EOGcorr.Heogchannames = [];% horizontal channel(s)
  110. def.chancorr.enable = false;
  111. def.chancorr.corthresh = 'auto 4';% threshold correlation
  112. def.chancorr.channames = [];% channel(s)
  113. def.FASTER.enable = true;
  114. def.FASTER.blinkchans = [];
  115. def.ADJUST.enable = true;
  116. def.MARA.enable = false;
  117. def.opts.FontSize = 14;
  118. def.opts.noplot = 0;
  119. cfg = setdef(cfg,def);
  120. if isempty(EEG.icawinv)
  121. errordlg('No ica weights in the current EEG dataset! Compute ICA on your data first.')
  122. error('No ica weights! Compute ICA on your data first.')
  123. end
  124. struct2ws(cfg.opts);
  125. rejfields = {'icarejautocorr' 'Autocorrelation' [ 0 0 1.0000]
  126. 'icarejfocalcomp' 'Focal components' [ 0 0.5000 0]
  127. 'icarejtrialfoc' 'Focal trial activity' [ 0.7500 0 0.7500]
  128. 'icarejSNR' 'Signal to noise ' [ 0.8000 0 0]
  129. 'icarejresvar' 'Residual variance' [ 0 0.7500 0.7500]
  130. 'icarejchancorr' 'Correlation with channels' [ 0.7500 0.7500 0]
  131. 'icarejADJUST' 'ADJUST selections' [ .3 .3 .3]
  132. 'icarejFASTER' 'FASTER selections' [ 0 .7 0]
  133. 'icarejMARA' 'MARA selections' [ .5 .5 0]
  134. };
  135. rejects = zeros(size(rejfields,1),1);
  136. if numel(noplot) == 1
  137. noplot = noplot * ones(1,size(rejfields,1));
  138. end
  139. if any(~noplot)
  140. figure(321541);clf;% just a random number so we always work in the same figure
  141. BACKCOLOR = [.93 .96 1];
  142. set(gcf,'numbertitle', 'off','name','Automatic component rejection measures','color',BACKCOLOR)
  143. isubplot = 1;
  144. end
  145. ncomp= size(EEG.icawinv,2); % ncomp is number of components
  146. icaacts = eeg_getdatact(EEG,'component',1:ncomp);
  147. EEG.icaact = icaacts;
  148. EEG.reject.SASICA = [];
  149. for ifield = 1:size(rejfields,1)
  150. % EEG.reject.SASICA.(rejfields{ifield}) = false(1,ncomp);
  151. EEG.reject.SASICA.([rejfields{ifield} 'col']) = rejfields{ifield,3};
  152. end
  153. fprintf('Computing selection methods...\n')
  154. if cfg.autocorr.enable
  155. rejects(1) = 1;
  156. disp('Autocorrelation.')
  157. %% Autocorrelation
  158. % Identifying noisy components
  159. %----------------------------------------------------------------
  160. struct2ws(cfg.autocorr);
  161. Ncorrint=round(autocorrint/(1000/EEG.srate)); % number of samples for lag
  162. rej = false(1,ncomp);
  163. for k=1:ncomp
  164. y=icaacts(k,:,:);
  165. yy=xcorr(mean(y,3),Ncorrint,'coeff');
  166. autocorr(k) = yy(1);
  167. end
  168. dropautocorr = readauto(dropautocorr,autocorr,'-');
  169. for k = 1:ncomp
  170. if autocorr(k) < dropautocorr
  171. rej(k)=true;
  172. end
  173. end
  174. EEG.reject.SASICA.(strrep(rejfields{1,1},'rej','')) = autocorr;
  175. EEG.reject.SASICA.(strrep(rejfields{1,1},'rej','thresh')) = dropautocorr;
  176. EEG.reject.SASICA.(rejfields{1,1}) = logical(rej);
  177. %----------------------------------------------------------------
  178. if cfg.focalcomp.enable
  179. rejects(2) = 1;
  180. disp('Focal components.')
  181. %% Focal activity
  182. %----------------------------------------------------------------
  183. struct2ws(cfg.focalcomp);
  184. rej = false(1,ncomp);
  185. clear mywt
  186. for k=1:ncomp
  187. mywt(:,k) = sort(abs(zscore(EEG.icawinv(:,k))),'descend'); %sorts standardized weights in descending order
  188. end
  189. focalICAout = readauto(focalICAout,mywt(1,:),'+');
  190. for k = 1:ncomp
  191. if mywt(1,k) > focalICAout
  192. rej(k)=true;
  193. end
  194. end
  195. EEG.reject.SASICA.(strrep(rejfields{2,1},'rej','')) = mywt(1,:);
  196. EEG.reject.SASICA.(strrep(rejfields{2,1},'rej','thresh')) = focalICAout;
  197. EEG.reject.SASICA.(rejfields{2,1}) = logical(rej);
  198. end
  199. %----------------------------------------------------------------
  200. if cfg.trialfoc.enable
  201. rejects(3) = 1;
  202. disp('Focal trial activity.');
  203. %% Focal trial activity
  204. % Find components with focal trial activity (those that have activity
  205. % on just a few trials and are almost zero on others)
  206. %----------------------------------------------------------------
  207. if ndims(icaacts) < 3
  208. error('This method cannot be used on continuous data (no ''trials''!)');
  209. end
  210. struct2ws(cfg.trialfoc);
  211. myact =sort(abs(zscore(range(icaacts,2),[],3)),3,'descend'); % sorts standardized range of trial activity
  212. focaltrialout = readauto(focaltrialout,myact(:,:,1)','+');
  213. % in descending order
  214. rej = myact(:,:,1) > focaltrialout;
  215. EEG.reject.SASICA.(strrep(rejfields{3,1},'rej','')) = myact(:,:,1)';
  216. EEG.reject.SASICA.(strrep(rejfields{3,1},'rej','thresh')) = focaltrialout;
  217. EEG.reject.SASICA.(rejfields{3,1}) = rej';
  218. %----------------------------------------------------------------
  219. end
  220. if cfg.SNR.enable
  221. rejects(4) = 1;
  222. disp('Signal to noise ratio.')
  223. %% Low Signal to noise components
  224. struct2ws(cfg.SNR);
  225. rejfields{4,2} = ['Signal to noise Time of interest ' num2str(snrPOI,'%g ') ' and Baseline ' num2str(snrBL,'%g ') ' ms.'];
  226. POIpts = timepts(snrPOI);
  227. BLpts = timepts(snrBL);
  228. zz = zscore(icaacts,[],2);% zscore along time
  229. av1 = mean(zz(:,POIpts,:),3); % average activity in POI across trials
  230. av2 = mean(zz(:,BLpts,:),3); % activity in baseline acros trials
  231. SNR = std(av1,[],2)./std(av2,[],2); % ratio of the standard deviations of activity and baseline
  232. snrcut = readauto(snrcut,SNR,'-');
  233. rej = SNR < snrcut;
  234. EEG.reject.SASICA.(strrep(rejfields{4,1},'rej','')) = SNR';
  235. EEG.reject.SASICA.(strrep(rejfields{4,1},'rej','thresh')) = snrcut;
  236. EEG.reject.SASICA.(rejfields{4,1}) = rej';
  237. %----------------------------------------------------------------
  238. end
  239. if cfg.resvar.enable
  240. rejects(5) = 1;
  241. disp('Residual variance thresholding.')
  242. %% High residual variance
  243. struct2ws(cfg.resvar);
  244. resvar = 100*[EEG.dipfit.model.rv];
  245. rej = resvar > thresh;
  246. EEG.reject.SASICA.(strrep(rejfields{5,1},'rej','')) = resvar;
  247. EEG.reject.SASICA.(strrep(rejfields{5,1},'rej','thresh')) = thresh;
  248. EEG.reject.SASICA.(rejfields{5,1}) = rej;
  249. %----------------------------------------------------------------
  250. end
  251. if cfg.EOGcorr.enable
  252. rejects(6) = 1;
  253. disp('Correlation with EOGs.');
  254. %% Correlation with EOG
  255. struct2ws(cfg.EOGcorr);
  256. noV = 0;noH = 0;
  257. try
  258. Veogchan = chnb(Veogchannames);
  259. catch
  260. Veogchan = [];
  261. end
  262. try
  263. Heogchan = chnb(Heogchannames);
  264. catch
  265. Heogchan = [];
  266. end
  267. if numel(Veogchan) == 1
  268. VEOG = EEG.data(Veogchan,:,:);
  269. elseif numel(Veogchan) == 2
  270. VEOG = EEG.data(Veogchan(1),:,:) - EEG.data(Veogchan(2),:,:);
  271. else
  272. disp('no Vertical EOG channels...');
  273. noV = 1;
  274. end
  275. if numel(Heogchan) == 1
  276. HEOG = EEG.data(Heogchan,:,:);
  277. elseif numel(Heogchan) == 2
  278. HEOG = EEG.data(Heogchan(1),:,:) - EEG.data(Heogchan(2),:,:);
  279. else
  280. disp('no Horizontal EOG channels...');
  281. noH = 1;
  282. end
  283. ICs = icaacts(:,:)';
  284. if ~noV
  285. VEOG = VEOG(:);
  286. cV = abs(corr(ICs,VEOG))';
  287. corthreshV = readauto(corthreshV,cV,'+');
  288. rejV = cV > corthreshV ;
  289. else
  290. cV = NaN(1,size(ICs,2));
  291. corthreshV = 0;
  292. rejV = false(size(cV));
  293. end
  294. if ~noH
  295. HEOG = HEOG(:);
  296. cH = abs(corr(ICs,HEOG))';
  297. corthreshH = readauto(corthreshH,cH,'+');
  298. rejH = cH > corthreshH;
  299. else
  300. cH = NaN(1,size(ICs,2));
  301. corthreshH = 0;
  302. rejH = false(size(cH));
  303. end
  304. EEG.reject.SASICA.([strrep(rejfields{6,1},'rej','') 'VEOG']) = cV;
  305. EEG.reject.SASICA.([strrep(rejfields{6,1},'rej','thresh') 'VEOG']) = corthreshV;
  306. EEG.reject.SASICA.([strrep(rejfields{6,1},'rej','') 'HEOG']) = cH;
  307. EEG.reject.SASICA.([strrep(rejfields{6,1},'rej','thresh') 'HEOG']) = corthreshH;
  308. EEG.reject.SASICA.(rejfields{6,1}) = [rejV|rejH];
  309. %----------------------------------------------------------------
  310. end
  311. if cfg.chancorr.enable
  312. rejects(6) = 1;
  313. disp('Correlation with other channels.')
  314. %% Correlation with other channels
  315. struct2ws(cfg.chancorr);
  316. if ~cfg.EOGcorr.enable
  317. rejH = false(1,ncomp);
  318. rejV = false(1,ncomp);
  319. end
  320. if ~isempty(channames)
  321. try
  322. [chan cellchannames channames] = chnb(channames);
  323. end
  324. chanEEG = EEG.data(chan,:)';
  325. ICs = icaacts(:,:)';
  326. c = abs(corr(ICs,chanEEG))';
  327. corthresh = mean(readauto(corthresh,c,'+'));
  328. rej = c > corthresh ;
  329. if size(rej,1) > 1
  330. rej = sum(rej)>=1;
  331. end
  332. EEG.reject.SASICA.([strrep(rejfields{6,1},'rej','') 'chans']) = c;
  333. EEG.reject.SASICA.([strrep(rejfields{6,1},'rej','thresh') 'chans']) = corthresh;
  334. EEG.reject.SASICA.(rejfields{6,1}) = [rej|rejH|rejV];
  335. else
  336. noplot(6) = 1;
  337. disp('Could not find the channels to compute correlation.');
  338. c = NaN(1,ncomp);
  339. EEG.reject.SASICA.([strrep(rejfields{6,1},'rej','') 'chans']) = c;
  340. EEG.reject.SASICA.([strrep(rejfields{6,1},'rej','thresh') 'chans']) = corthresh;
  341. rej = false(1,ncomp);
  342. EEG.reject.SASICA.(rejfields{6,1}) = [rej|rejV|rejH];
  343. end
  344. %----------------------------------------------------------------
  345. end
  346. if cfg.ADJUST.enable
  347. rejects(7) = 1;
  348. disp('ADJUST methods selection')
  349. %% ADJUST
  350. struct2ws(cfg.ADJUST);
  351. [art, horiz, vert, blink, disc,...
  352. soglia_DV, diff_var, soglia_K, med2_K, meanK, soglia_SED, med2_SED, SED, soglia_SAD, med2_SAD, SAD, ...
  353. soglia_GDSF, med2_GDSF, GDSF, soglia_V, med2_V, nuovaV, soglia_D, maxdin] = ADJUST (EEG);
  354. ADJ.art = art;ADJ.horiz = horiz;ADJ.vert = vert;ADJ.blink = blink;ADJ.disc = disc;
  355. ADJ.soglia_DV = soglia_DV; ADJ.diff_var = diff_var;
  356. ADJ.soglia_K = soglia_K;ADJ.med2_K = med2_K; ADJ.meanK = meanK;
  357. ADJ.soglia_SED = soglia_SED; ADJ.med2_SED = med2_SED;ADJ.SED = SED;
  358. ADJ.med2_SAD = med2_SAD;ADJ.soglia_SAD = soglia_SAD;ADJ.SAD = SAD;
  359. ADJ.soglia_GDSF = soglia_GDSF; ADJ.med2_GDSF = med2_GDSF;ADJ.GDSF = GDSF;
  360. ADJ.soglia_V = soglia_V;ADJ.med2_V = med2_V;ADJ.nuovaV = nuovaV;
  361. ADJ.soglia_D = soglia_D; ADJ.maxdin = maxdin;
  362. rej = false(1,size(EEG.icaact,1));
  363. rej([ADJ.art ADJ.horiz ADJ.vert ADJ.blink ADJ.disc]) = true;
  364. EEG.reject.SASICA.(strrep(rejfields{7,1},'rej','')) = ADJ;
  365. EEG.reject.SASICA.(rejfields{7,1}) = rej;
  366. %----------------------------------------------------------------
  367. end
  368. if cfg.FASTER.enable
  369. rejects(8) = 1;
  370. disp('FASTER methods selection')
  371. %% FASTER
  372. struct2ws(cfg.FASTER);
  373. blinkchans = chnb(blinkchans);
  374. listprops = component_properties(EEG,blinkchans);
  375. FST.rej = min_z(listprops)' ~= 0;
  376. FST.listprops = listprops;
  377. EEG.reject.SASICA.(strrep(rejfields{8,1},'rej','')) = FST;
  378. EEG.reject.SASICA.(rejfields{8,1}) = FST.rej;
  379. %----------------------------------------------------------------
  380. end
  381. if cfg.MARA.enable
  382. rejects(9) = 1;
  383. disp('MARA methods selection')
  384. %% MARA
  385. struct2ws(cfg.MARA);
  386. [rej info] = MARA(EEG);
  387. MR.rej = false(1,size(EEG.icaact,1));
  388. MR.rej(rej) = true;
  389. MR.info = info;
  390. EEG.reject.SASICA.(strrep(rejfields{9,1},'rej','')) = MR;
  391. EEG.reject.SASICA.(rejfields{9,1}) = MR.rej;
  392. %----------------------------------------------------------------
  393. end
  394. EEG.reject.SASICA.var = var(EEG.icaact(:,:),[],2);% variance of each component
  395. if (cfg.ADJUST.enable||cfg.FASTER.enable) && any(~noplot)
  396. h = uicontrol('style','text','string','for ADJUST or FASTER results, click on component buttons in the other window(s)','units','normalized','position',[0 0 1 .05],'backgroundcolor',get(gcf,'color'));
  397. uistack(h,'bottom')
  398. end
  399. % Final computations
  400. % combine in gcompreject field and pass to pop_selectcomps
  401. EEG.reject.gcompreject = false(1,ncomp);
  402. for ifield = 1:size(rejfields,1)
  403. if rejects(ifield)
  404. EEG.reject.gcompreject = [EEG.reject.gcompreject ; EEG.reject.SASICA.(rejfields{ifield})];
  405. end
  406. end
  407. EEG.reject.gcompreject = sum(EEG.reject.gcompreject) >= 1;
  408. end
  409. % pop_prop() - plot the properties of a channel or of an independent
  410. % component.
  411. % Usage:
  412. % >> pop_prop( EEG); % pops up a query window
  413. % >> pop_prop( EEG, typecomp); % pops up a query window
  414. % >> pop_prop( EEG, typecomp, chanorcomp, winhandle,spectopo_options);
  415. %
  416. % Inputs:
  417. % EEG - EEGLAB dataset structure (see EEGGLOBAL)
  418. %
  419. % Optional inputs:
  420. % typecomp - [0|1] 1 -> display channel properties
  421. % 0 -> component properties {default: 1 = channel}
  422. % chanorcomp - channel or component number[s] to display {default: 1}
  423. %
  424. % winhandle - if this parameter is present or non-NaN, buttons
  425. % allowing the rejection of the component are drawn.
  426. % If non-zero, this parameter is used to back-propagate
  427. % the color of the rejection button.
  428. % spectopo_options - [cell array] optional cell arry of options for
  429. % the spectopo() function.
  430. % For example { 'freqrange' [2 50] }
  431. %
  432. % Author: Arnaud Delorme, CNL / Salk Institute, 2001
  433. %
  434. % See also: pop_runica(), eeglab()
  435. % Copyright (C) 2001 Arnaud Delorme, Salk Institute, arno@salk.edu
  436. %
  437. % This program is free software; you can redistribute it and/or modify
  438. % it under the terms of the GNU General Public License as published by
  439. % the Free Software Foundation; either version 2 of the License, or
  440. % (at your option) any later version.
  441. %
  442. % This program is distributed in the hope that it will be useful,
  443. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  444. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  445. % GNU General Public License for more details.
  446. %
  447. % You should have received a copy of the GNU General Public License
  448. % along with this program; if not, write to the Free Software
  449. % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  450. function [fh,EEG] = pop_prop(EEG, typecomp, chanorcomp, winhandle, spec_opt,ID, flag_fig)
  451. if exist('flag_fig','var') == 0 || isempty(flag_fig)
  452. flag_fig = 1;
  453. end
  454. % assumed input is chanorcomp
  455. % -------------------------
  456. try, icadefs;
  457. catch,
  458. BACKCOLOR = [0.8 0.8 0.8];
  459. GUIBUTTONCOLOR = [0.8 0.8 0.8];
  460. end;
  461. basename = ['Component_' int2str(chanorcomp) ];
  462. if flag_fig
  463. fh = figure('name', ['SetID_' num2str(ID) '_' basename],...
  464. 'color', BACKCOLOR,...
  465. 'numbertitle', 'off',...
  466. 'visible', 'on',...
  467. 'PaperPositionMode','auto',...
  468. 'ToolBar', 'none',...
  469. 'MenuBar','none');
  470. pos = get(fh,'position');
  471. set(fh,'Position', [pos(1) pos(2)-700+pos(4) 600 800], 'visible', 'on');
  472. pos = get(gca,'position'); % plot relative to current axes
  473. hh = gca;
  474. q = [pos(1) pos(2) 0 0];
  475. s = [pos(3) pos(4) pos(3) pos(4)]./100;
  476. delete(gca);
  477. p = panel();
  478. p.margin = [10 10 10 10];
  479. p.pack('v',{.3 .25 []});
  480. p(1).margin = [5 5 5 5];
  481. p(1).pack('h',{.4 [] .01});
  482. % ---
  483. if ~exist('winhandle') || isempty(winhandle) || isnan(winhandle)
  484. winhandle = NaN;
  485. p(2).pack('v',{.98 [] })
  486. else
  487. p(2).pack('v',{.8 []})
  488. end;
  489. p(2,1).pack('h',{.01,[]});
  490. p(2,1).margin = [10 10 0 0 ] ;%[10 15 0 55];
  491. p(2,1,1).margin = 0;
  492. %p(2,1,3).margin = 0;
  493. p(2,1,2).pack('v',{.2 []});
  494. p(2,1,2,1).margin = 0;
  495. p(2,1,2,2).margintop = 5;
  496. p(2,1,2,2).select();
  497. % --- plot time series ---
  498. [dat, time] = choose_ts_seg(EEG,chanorcomp);
  499. plot(time,dat');
  500. axis on;
  501. box on;
  502. grid on;
  503. tstitle_h = title('Component Time Series', 'fontsize', 14,'fontweight','bold');
  504. % ---
  505. if ~exist('winhandle') || isempty(winhandle) || isnan(winhandle)
  506. winhandle = NaN;
  507. p(3).pack('v',{.98 [] })
  508. else
  509. p(3).pack('v',{.8 []})
  510. end;
  511. p(3,1).pack('h',{.005,[],.005});
  512. p(3,1).margin = [10 0 0 0 ] ;%[10 15 0 55];
  513. p(3,1,1).margin = 0;
  514. p(3,1,3).margin = 0;
  515. p(3,1,2).pack('v',{.001 []});
  516. p(3,1,2,1).margin = 0;
  517. p(3,1,2,2).margintop = 3;
  518. % ---
  519. % plotting topoplot
  520. p(1,1).select()
  521. p(1,1).margin = [25 25 20 25];
  522. scalpmap_norm = EEG.icawinv(:,chanorcomp)/sqrt(sum(EEG.icawinv(:,chanorcomp).^2));
  523. [~,Zi,plotrad] = topoplotFast( scalpmap_norm, EEG.chanlocs, 'chaninfo', EEG.chaninfo, ...
  524. 'shading', 'interp', 'numcontour', 3,'electrodes','on'); axis square;
  525. EEG.reject.SASICA.topo(chanorcomp,:) = Zi(~isnan(Zi));
  526. EEG.reject.SASICA.plotrad(chanorcomp)= plotrad;
  527. % h = title(basename, 'fontsize', 14);
  528. % set(h,'position',get(h,'position')+[0 1 0]);
  529. % set(gca,'fontSize',14);
  530. if ~isfield(EEG.reject.SASICA, 'icarejresvar'), rv = 'NA'; else, rv = num2str(EEG.dipfit.model(chanorcomp).rv*100); end;
  531. h = title({'Residual Variance:'; [rv '%']},'fontsize', 16,'Units','Normalized');
  532. pos = get(h,'position');
  533. h2 = text(0.10,0.95,[num2str(ID,'%06d') '_' num2str(chanorcomp,'%03d')],'Units', 'Normalized','Fontsize', 12);
  534. % % plotting erpimage
  535. %--------------------
  536. p(1,2).margin = [30 30 25 25];
  537. set(h,'position',pos+[0 -1.28 0],'fontsize', 14);
  538. set(h2,'Position',[0.35,1.15],'Interpreter','none')
  539. p(1,2).select();
  540. eeglab_options;
  541. if EEG.trials > 1
  542. % put title at top of erpimage
  543. axis off
  544. EEG.times = linspace(EEG.xmin, EEG.xmax, EEG.pnts);
  545. if EEG.trials < 6
  546. ei_smooth = 1;
  547. else
  548. ei_smooth = 3;
  549. end
  550. icaacttmp = eeg_getdatact(EEG, 'component', chanorcomp);
  551. offset = nan_mean(icaacttmp(:));
  552. era = nan_mean(squeeze(icaacttmp)')-offset;
  553. era_limits=get_era_limits(era);
  554. [~,~,~,~,axhndls] = myerpimage( icaacttmp-offset, ones(1,EEG.trials)*10000, EEG.times*1000, ...
  555. '', ei_smooth, 1, 'caxis', 2/3, 'cbar','erp', 'yerplabel', '','erp_vltg_ticks',era_limits);
  556. % title(sprintf('%s activity \\fontsize{10}(global offset %3.3f)', basename, offset));
  557. title('Epoched Data', 'fontsize', 14 );
  558. else
  559. % put title at top of erpimage
  560. EI_TITLE = 'Continous data';
  561. ERPIMAGELINES = 200; % show 200-line erpimage
  562. while size(EEG.data,2) < ERPIMAGELINES*EEG.srate
  563. ERPIMAGELINES = 0.9 * ERPIMAGELINES;
  564. end
  565. ERPIMAGELINES = round(ERPIMAGELINES);
  566. if ERPIMAGELINES > 2 % give up if data too small
  567. if ERPIMAGELINES < 10
  568. ei_smooth = 1;
  569. else
  570. ei_smooth = 3;
  571. end
  572. erpimageframes = floor(size(EEG.data,2)/ERPIMAGELINES);
  573. erpimageframestot = erpimageframes*ERPIMAGELINES;
  574. eegtimes = linspace(0, erpimageframes-1, EEG.srate/1000);
  575. if typecomp == 1 % plot channel
  576. offset = nan_mean(EEG.data(chanorcomp,:));
  577. % Note: we don't need to worry about ERP limits, since ERPs
  578. % aren't visualized for continuous data
  579. [~,~,~,~,axhndls] = myerpimage( reshape(EEG.data(chanorcomp,1:erpimageframestot),erpimageframes,ERPIMAGELINES)-offset, ones(1,ERPIMAGELINES)*10000, eegtimes , ...
  580. EI_TITLE, ei_smooth, 1, 'caxis', 2/3, 'cbar');
  581. else % plot component
  582. icaacttmp = eeg_getdatact(EEG, 'component', chanorcomp);
  583. offset = nan_mean(icaacttmp(:));
  584. [~,~,~,~,axhndls] = myerpimage(reshape(icaacttmp(:,1:erpimageframestot),erpimageframes,ERPIMAGELINES)-offset,ones(1,ERPIMAGELINES)*10000, eegtimes , ...
  585. EI_TITLE, ei_smooth, 1, 'caxis', 2/3, 'cbar','yerplabel', '');
  586. end
  587. else
  588. axis off;
  589. text(0.1, 0.3, [ 'No erpimage plotted' 10 'for small continuous data']);
  590. end;
  591. end;
  592. axhndls{1}.FontSize = 12;
  593. axhndls{1}.YLabel.FontSize = 14;
  594. axhndls{3}.FontSize = 12;
  595. axhndls{3}.XLabel.FontSize =14;
  596. set(tstitle_h,'FontSize',15);
  597. p(2,1,2,2).select();
  598. set(gca,'FontSize',12);
  599. set(gca,'Xlabel');
  600. xlabel('Time (s)','fontsize', 13);
  601. ylabel('\muV');
  602. % plotting spectrum
  603. % -----------------s
  604. p(3,1,2,2).select();
  605. try
  606. spectopo( EEG.icaact(chanorcomp,:), EEG.pnts, EEG.srate, 'mapnorm', EEG.icawinv(:,chanorcomp), spec_opt{:} );
  607. % set( get(gca, 'ylabel'), 'string', 'Power 10*log_{10}(\muV^{2}/Hz)', 'fontsize', 12);
  608. % set( get(gca, 'xlabel'), 'string', 'Frequency (Hz)', 'fontsize', 12);
  609. axis on;
  610. box on;
  611. grid on;
  612. xlim([3 80]);
  613. xlabel('Frequency (Hz)')
  614. clc
  615. text(0.35,1.04,'Activity power spectrum','units','normalized', 'fontsize', 15);
  616. catch
  617. axis off;
  618. lasterror
  619. text(0.1, 0.3, [ 'Error: no spectrum plotted' 10 ' make sure you have the ' 10 'signal processing toolbox']);
  620. end;
  621. else
  622. fh = [];
  623. scalpmap_norm = EEG.icawinv(:,chanorcomp)/sqrt(sum(EEG.icawinv(:,chanorcomp).^2));
  624. [~,Zi,plotrad] = topoplotFast( scalpmap_norm, EEG.chanlocs, 'chaninfo', EEG.chaninfo, ...
  625. 'shading', 'interp', 'numcontour', 3,'electrodes','on','noplot','on');
  626. EEG.reject.SASICA.topo(chanorcomp,:) = Zi(~isnan(Zi));
  627. EEG.reject.SASICA.plotrad(chanorcomp)= plotrad;
  628. end
  629. function out = nan_mean(in)
  630. nans = find(isnan(in));
  631. in(nans) = 0;
  632. sums = sum(in);
  633. nonnans = ones(size(in));
  634. nonnans(nans) = 0;
  635. nonnans = sum(nonnans);
  636. nononnans = find(nonnans==0);
  637. nonnans(nononnans) = 1;
  638. out = sum(in)./nonnans;
  639. out(nononnans) = NaN;
  640. function era_limits=get_era_limits(era)
  641. %function era_limits=get_era_limits(era)
  642. %
  643. % Returns the minimum and maximum value of an event-related
  644. % activation/potential waveform (after rounding according to the order of
  645. % magnitude of the ERA/ERP)
  646. %
  647. % Inputs:
  648. % era - [vector] Event related activation or potential
  649. %
  650. % Output:
  651. % era_limits - [min max] minimum and maximum value of an event-related
  652. % activation/potential waveform (after rounding according to the order of
  653. % magnitude of the ERA/ERP)
  654. mn=min(era);
  655. mx=max(era);
  656. mn=orderofmag(mn)*round(mn/orderofmag(mn));
  657. mx=orderofmag(mx)*round(mx/orderofmag(mx));
  658. era_limits=[mn mx];
  659. function ord=orderofmag(val)
  660. %function ord=orderofmag(val)
  661. %
  662. % Returns the order of magnitude of the value of 'val' in multiples of 10
  663. % (e.g., 10^-1, 10^0, 10^1, 10^2, etc ...)
  664. % used for computing erpimage trial axis tick labels as an alternative for
  665. % plotting sorting variable
  666. val=abs(val);
  667. if val>=1
  668. ord=1;
  669. val=floor(val/10);
  670. while val>=1,
  671. ord=ord*10;
  672. val=floor(val/10);
  673. end
  674. return;
  675. else
  676. ord=1/10;
  677. val=val*10;
  678. while val<1,
  679. ord=ord/10;
  680. val=val*10;
  681. end
  682. return;
  683. end
  684. function thresh = readauto(thresh,dat,comp)
  685. % if thresh starts with 'auto'
  686. % compute auto threshold as mean(dat) +/- N std(dat)
  687. % with N read in the string thresh = 'auto N'
  688. % if not, use thresh as a value
  689. if isstr(thresh) && strncmp(thresh,'auto',4)
  690. if numel(thresh) > 4
  691. threshsigma = str2num(thresh(5:end));
  692. else
  693. threshsigma = 2;
  694. end
  695. thresh = eval(['mean(dat,2)' comp 'threshsigma * std(dat,[],2)']);
  696. end
  697. function [nb,channame,strnames] = chnb(channame, varargin)
  698. % chnb() - return channel number corresponding to channel names in an EEG
  699. % structure
  700. %
  701. % Usage:
  702. % >> [nb] = chnb(channameornb);
  703. % >> [nb,names] = chnb(channameornb,...);
  704. % >> [nb,names,strnames] = chnb(channameornb,...);
  705. % >> [nb] = chnb(channameornb, labels);
  706. %
  707. % Input:
  708. % channameornb - If a string or cell array of strings, it is assumed to
  709. % be (part of) the name of channels to search. Either a
  710. % string with space separated channel names, or a cell
  711. % array of strings.
  712. % Note that regular expressions can be used to match
  713. % several channels. See regexp.
  714. % If only one channame pattern is given and the string
  715. % 'inv' is attached to it, the channels NOT matching the
  716. % pattern are returned.
  717. % labels - Channel names as found in {EEG.chanlocs.labels}.
  718. %
  719. % Output:
  720. % nb - Channel numbers in labels, or in the EEG structure
  721. % found in the caller workspace (i.e. where the function
  722. % is called from) or in the base workspace, if no EEG
  723. % structure exists in the caller workspace.
  724. % names - Channel names, cell array of strings.
  725. % strnames - Channel names, one line character array.
  726. error(nargchk(1,2,nargin));
  727. if nargin == 2
  728. labels = varargin{1};
  729. else
  730. try
  731. EEG = evalin('caller','EEG');
  732. catch
  733. try
  734. EEG = evalin('base','EEG');
  735. catch
  736. error('Could not find EEG structure');
  737. end
  738. end
  739. if not(isfield(EEG,'chanlocs'))
  740. error('No channel list found');
  741. end
  742. EEG = EEG(1);
  743. labels = {EEG.chanlocs.labels};
  744. end
  745. if iscell(channame) || ischar(channame)
  746. if ischar(channame) || iscellstr(channame)
  747. if iscellstr(channame) && numel(channame) == 1 && isempty(channame{1})
  748. channame = '';
  749. end
  750. tmp = regexp(channame,'(\S*) ?','tokens');
  751. channame = {};
  752. for i = 1:numel(tmp)
  753. if iscellstr(tmp{i}{1})
  754. channame{i} = tmp{i}{1}{1};
  755. else
  756. channame{i} = tmp{i}{1};
  757. end
  758. end
  759. if isempty(channame)
  760. nb = [];
  761. return
  762. end
  763. end
  764. if numel(channame) == 1 && not(isempty(strmatch('inv',channame{1})))
  765. cmd = 'exactinv';
  766. channame{1} = strrep(channame{1},'inv','');
  767. else
  768. channame{1} = channame{1};
  769. cmd = 'exact';
  770. end
  771. nb = regexpcell(labels,channame,[cmd 'ignorecase']);
  772. elseif isnumeric(channame)
  773. nb = channame;
  774. if nb > numel(labels)
  775. nb = [];
  776. end
  777. end
  778. channame = labels(nb);
  779. strnames = sprintf('%s ',channame{:});
  780. if not(isempty(strnames))
  781. strnames(end) = [];
  782. end
  783. function idx = regexpcell(c,pat, cmds)
  784. % idx = regexpcell(c,pat, cmds)
  785. %
  786. % Return indices idx of cells in c that match pattern(s) pat (regular expression).
  787. % Pattern pat can be char or cellstr. In the later case regexpcell returns
  788. % indexes of cells that match any pattern in pat.
  789. %
  790. % cmds is a string that can contain one or several of these commands:
  791. % 'inv' return indexes that do not match the pattern.
  792. % 'ignorecase' will use regexpi instead of regexp
  793. % 'exact' performs an exact match (regular expression should match the whole strings in c).
  794. % 'all' (default) returns all indices, including repeats (if several pat match a single cell in c).
  795. % 'unique' will return unique sorted indices.
  796. % 'intersect' will return only indices in c that match ALL the patterns in pat.
  797. %
  798. % v1 Maximilien Chaumon 01/05/09
  799. % v1.1 Maximilien Chaumon 24/05/09 - added ignorecase
  800. % v2 Maximilien Chaumon 02/03/2010 changed input method.
  801. % inv,ignorecase,exact,combine are replaced by cmds
  802. error(nargchk(2,3,nargin))
  803. if not(iscellstr(c))
  804. error('input c must be a cell array of strings');
  805. end
  806. if nargin == 2
  807. cmds = '';
  808. end
  809. if not(isempty(regexpi(cmds,'inv', 'once' )))
  810. inv = true;
  811. else
  812. inv = false;
  813. end
  814. if not(isempty(regexpi(cmds,'ignorecase', 'once' )))
  815. ignorecase = true;
  816. else
  817. ignorecase = false;
  818. end
  819. if not(isempty(regexpi(cmds,'exact', 'once' )))
  820. exact = true;
  821. else
  822. exact = false;
  823. end
  824. if not(isempty(regexpi(cmds,'unique', 'once' )))
  825. combine = 2;
  826. elseif not(isempty(regexpi(cmds,'intersect', 'once' )))
  827. combine = 3;
  828. else
  829. combine = 1;
  830. end
  831. if ischar(pat)
  832. pat = cellstr(pat);
  833. end
  834. if exact
  835. for i_pat = 1:numel(pat)
  836. pat{i_pat} = ['^' pat{i_pat} '$'];
  837. end
  838. end
  839. for i_pat = 1:length(pat)
  840. if ignorecase
  841. trouv = regexpi(c,pat{i_pat}); % apply regexp on each pattern
  842. else
  843. trouv = regexp(c,pat{i_pat}); % apply regexp on each pattern
  844. end
  845. idx{i_pat} = [];
  846. for i = 1:numel(trouv)
  847. if not(isempty(trouv{i}))% if there is a match, store index
  848. idx{i_pat}(end+1) = i;
  849. end
  850. end
  851. end
  852. switch combine
  853. case 1
  854. idx = [idx{:}];
  855. case 2
  856. idx = unique([idx{:}]);
  857. case 3
  858. for i_pat = 2:length(pat)
  859. idx{1} = intersect(idx{1},idx{i_pat});
  860. end
  861. idx = idx{1};
  862. end
  863. if inv % if we want to invert result, then do so.
  864. others = 1:numel(trouv);
  865. others(idx) = [];
  866. idx = others;
  867. end
  868. function s = setdef(s,d)
  869. % s = setdef(s,d)
  870. % Merges the two structures s and d recursively.
  871. % Adding the default field values from d into s when not present or empty.
  872. if isstruct(s) && not(isempty(s))
  873. fields = fieldnames(d);
  874. for i_f = 1:numel(fields)
  875. if isfield(s,fields{i_f})
  876. s.(fields{i_f}) = setdef(s.(fields{i_f}),d.(fields{i_f}));
  877. else
  878. s.(fields{i_f}) = d.(fields{i_f});
  879. end
  880. end
  881. elseif not(isempty(s))
  882. s = s;
  883. elseif isempty(s);
  884. s = d;
  885. end
  886. function struct2ws(s,varargin)
  887. % struct2ws(s,varargin)
  888. %
  889. % Description : This function returns fields of scalar structure s in the
  890. % current workspace
  891. % __________________________________
  892. % Inputs :
  893. % s (scalar structure array) : a structure that you want to throw in
  894. % your current workspace.
  895. % re (string optional) : a regular expression. Only fields
  896. % matching re will be returned
  897. % Outputs :
  898. % No output : variables are thrown directly in the caller workspace.
  899. %
  900. %
  901. % _____________________________________
  902. % See also : ws2struct ; regexp
  903. %
  904. % Maximilien Chaumon v1.0 02/2007
  905. if nargin == 0
  906. cd('d:\Bureau\work')
  907. s = dir('pathdef.m');
  908. end
  909. if length(s) > 1
  910. error('Structure should be scalar.');
  911. end
  912. if not(isempty(varargin))
  913. re = varargin{1};
  914. else
  915. re = '.*';
  916. end
  917. vars = fieldnames(s);
  918. vmatch = regexp(vars,re);
  919. varsmatch = [];
  920. for i = 1:length(vmatch)
  921. if isempty(vmatch{i})
  922. continue
  923. end
  924. varsmatch(end+1) = i;
  925. end
  926. for i = varsmatch
  927. assignin('caller',vars{i},s.(vars{i}));
  928. end
  929. function [sortie] = ws2struct(varargin)
  930. % [s] = ws2struct(varargin)
  931. %
  932. % Description : This function returns a structure containing variables
  933. % of the current workspace.
  934. % __________________________________
  935. % Inputs :
  936. % re (string optional) : a regular expression matching the variables to
  937. % be returned.
  938. % Outputs :
  939. % s (structure array) : a structure containing all variables of the
  940. % calling workspace. If re input is specified,
  941. % only variables matching re are returned.
  942. % _____________________________________
  943. % See also : struct2ws ; regexp
  944. %
  945. % Maximilien Chaumon v1.0 02/2007
  946. if not(isempty(varargin))
  947. re = varargin{1};
  948. else
  949. re = '.*';
  950. end
  951. vars = evalin('caller','who');
  952. vmatch = regexp(vars,re);
  953. varsmatch = [];
  954. for i = 1:length(vmatch)
  955. if isempty(vmatch{i}) || not(vmatch{i} == 1)
  956. continue
  957. end
  958. varsmatch{end+1} = vars{i};
  959. end
  960. for i = 1:length(varsmatch)
  961. dat{i} = evalin('caller',varsmatch{i});
  962. end
  963. sortie = cell2struct(dat,varsmatch,2);
  964. function [tpts tvals] = timepts(timein, varargin)
  965. % timepts() - return time points corresponding to a certain latency range
  966. % in an EEG structure.
  967. %
  968. % Usage:
  969. % >> [tpts] = timepts(timein);
  970. % >> [tpts tvals] = timepts(timein, times);
  971. % Note: this last method also works with any type of numeric
  972. % data entered under times (ex. frequencies, trials...)
  973. %
  974. % Input:
  975. % timein - latency range [start stop] (boundaries included). If
  976. % second argument 'times' is not provided, EEG.times will
  977. % be evaluated from the EEG structure found in the caller
  978. % workspace (or base if not in caller).
  979. % times - time vector as found in EEG.times
  980. %
  981. % Output:
  982. % tpts - index numbers corresponding to the time range.
  983. % tvals - values of EEG.times at points tpts
  984. %
  985. error(nargchk(1,2,nargin));
  986. if nargin == 2
  987. times = varargin{1};
  988. else
  989. try
  990. EEG = evalin('caller','EEG');
  991. catch
  992. try
  993. EEG = evalin('base','EEG');
  994. catch
  995. error('Could not find EEG structure');
  996. end
  997. end
  998. if not(isfield(EEG,'times'))
  999. error('No time list found');
  1000. end
  1001. times = EEG.times;
  1002. if isempty(times)
  1003. times = EEG.xmin:1/EEG.srate:EEG.xmax;
  1004. end
  1005. end
  1006. if isempty(times)
  1007. error('could not find times');
  1008. end
  1009. if numel(timein) == 1
  1010. [dum tpts] = min(abs(times - timein));% find the closest one
  1011. if tpts == numel(times)
  1012. warning('Strange time is last index of times')
  1013. end
  1014. elseif numel(timein) == 2
  1015. tpts = find(times >= timein(1) & times <= timein(2));% find times within bounds
  1016. else
  1017. error('timein should be a scalar or a 2 elements vector');
  1018. end
  1019. tvals = times(tpts);
  1020. function tw = strwrap(t,n)
  1021. % tw = strwrap(t,n)
  1022. %
  1023. % wrap text array t at n characters taking non alphanumeric characters as
  1024. % breaking characters (i.e. not cutting words strangely).
  1025. t = deblank(t(:)');
  1026. seps = '[\s-]';
  1027. tw = '';
  1028. while not(isempty(t))
  1029. breaks = regexp(t,seps);
  1030. breaks(end+1) = numel(t);
  1031. idx = 1:min(n,breaks(find(breaks < n, 1,'last')));
  1032. if isempty(idx)
  1033. idx = 1:min(n,numel(t));
  1034. end
  1035. tw(end+1,:) = char(padarray(double(t(idx)),[0 n-numel(idx)],32,'post'));
  1036. t(idx)= [];
  1037. t = strtrim(t);
  1038. end
  1039. function h = hline(y,varargin)
  1040. % h = hline(y,varargin)
  1041. % add horizontal line(s) on the current axes at y
  1042. % all varargin arguments are passed to plot...
  1043. y = y(:);
  1044. ho = ishold;
  1045. hold on
  1046. h = plot(repmat(xlim,numel(y),1)',[y y]',varargin{:});
  1047. if not(ho)
  1048. hold off
  1049. end
  1050. if nargout == 0
  1051. clear h
  1052. end
  1053. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1054. %%%%%%%%%%%%%%%%%%%%%%% BELOW IS ADJUST CODE%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1055. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1056. % ADJUST() - Automatic EEG artifact Detector
  1057. % with Joint Use of Spatial and Temporal features
  1058. %
  1059. % Usage:
  1060. % >> [art, horiz, vert, blink, disc,...
  1061. % soglia_DV, diff_var, soglia_K, med2_K, meanK, soglia_SED, med2_SED, SED, soglia_SAD, med2_SAD, SAD, ...
  1062. % soglia_GDSF, med2_GDSF, GDSF, soglia_V, med2_V, nuovaV, soglia_D, maxdin]=ADJUST (EEG,out)
  1063. %
  1064. % Inputs:
  1065. % EEG - current dataset structure or structure array (has to be epoched)
  1066. % out - (string) report file name
  1067. %
  1068. % Outputs:
  1069. % art - List of artifacted ICs
  1070. % horiz - List of HEM ICs
  1071. % vert - List of VEM ICs
  1072. % blink - List of EB ICs
  1073. % disc - List of GD ICs
  1074. % soglia_DV - SVD threshold
  1075. % diff_var - SVD feature values
  1076. % soglia_K - TK threshold
  1077. % meanK - TK feature values
  1078. % soglia_SED - SED threshold
  1079. % SED - SED feature values
  1080. % soglia_SAD - SAD threshold
  1081. % SAD - SAD feature values
  1082. % soglia_GDSF- GDSF threshold
  1083. % GDSF - GDSF feature values
  1084. % soglia_V - MEV threshold
  1085. % nuovaV - MEV feature values
  1086. %
  1087. %
  1088. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1089. %
  1090. % ADJUST
  1091. % Automatic EEG artifact Detector based on the Joint Use of Spatial and Temporal features
  1092. %
  1093. % Developed 2007-2014
  1094. % Andrea Mognon (1) and Marco Buiatti (2),
  1095. % (1) Center for Mind/Brain Sciences, University of Trento, Italy
  1096. % (2) INSERM U992 - Cognitive Neuroimaging Unit, Gif sur Yvette, France
  1097. %
  1098. % Last update: 02/05/2014 by Marco Buiatti
  1099. %
  1100. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1101. %
  1102. % Reference paper:
  1103. % Mognon A, Bruzzone L, Jovicich J, Buiatti M,
  1104. % ADJUST: An Automatic EEG artifact Detector based on the Joint Use of Spatial and Temporal features.
  1105. % Psychophysiology 48 (2), 229-240 (2011).
  1106. %
  1107. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1108. % Copyright (C) 2009-2014 Andrea Mognon (1) and Marco Buiatti (2),
  1109. % (1) Center for Mind/Brain Sciences, University of Trento, Italy
  1110. % (2) INSERM U992 - Cognitive Neuroimaging Unit, Gif sur Yvette, France
  1111. %
  1112. % This program is free software; you can redistribute it and/or modify
  1113. % it under the terms of the GNU General Public License as published by
  1114. % the Free Software Foundation; either version 2 of the License, or
  1115. % (at your option) any later version.
  1116. %
  1117. % This program is distributed in the hope that it will be useful,
  1118. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  1119. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1120. % GNU General Public License for more details.
  1121. %
  1122. % You should have received a copy of the GNU General Public License
  1123. % along with this program; if not, write to the Free Software
  1124. % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  1125. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1126. % VERSIONS LOG
  1127. %
  1128. % 02/05/14: Modified text in Report.txt (MB).
  1129. %
  1130. % 30/03/14: Removed 'message to the user' (redundant). (MB)
  1131. %
  1132. % 22/03/14: kurtosis is replaced by kurt for compatibility if signal processing
  1133. % toolbox is missing (MB).
  1134. %
  1135. % V2 (07 OCTOBER 2010) - by Andrea Mognon
  1136. % Added input 'nchannels' to compute_SAD and compute_SED_NOnorm;
  1137. % this is useful to differentiate the number of ICs (n) and the number of
  1138. % sensors (nchannels);
  1139. % bug reported by Guido Hesselman on October, 1 2010.
  1140. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1141. % function [art, horiz, vert, blink, disc,...
  1142. % soglia_DV, diff_var, soglia_K, meanK, soglia_SED, SED, soglia_SAD, SAD, ...
  1143. % soglia_GDSF, GDSF, soglia_V, nuovaV, soglia_D, maxdin]=ADJUST (EEG,out)
  1144. function [art, horiz, vert, blink, disc,...
  1145. soglia_DV, diff_var, soglia_K, med2_K, meanK, soglia_SED, med2_SED, SED, soglia_SAD, med2_SAD, SAD, ...
  1146. soglia_GDSF, med2_GDSF, GDSF, soglia_V, med2_V, nuovaV, soglia_D, maxdin]=ADJUST (EEG)
  1147. %% Settings
  1148. % ----------------------------------------------------
  1149. % | Change experimental settings in this section |
  1150. % ----------------------------------------------------
  1151. % ----------------------------------------------------
  1152. % | Initial message to user: |
  1153. % ----------------------------------------------------
  1154. %
  1155. % disp(' ')
  1156. % disp('Detects Horizontal and Vertical eye movements,')
  1157. % disp('Blinks and Discontinuities in dataset:')
  1158. % disp([EEG.filename])
  1159. % disp(' ')
  1160. % ----------------------------------------------------
  1161. % | Collect useful data from EEG structure |
  1162. % ----------------------------------------------------
  1163. %number of ICs=size(EEG.icawinv,1);
  1164. %number of time points=size(EEG.data,2);
  1165. if length(size(EEG.data))==3
  1166. num_epoch=size(EEG.data,3);
  1167. else
  1168. num_epoch=1;
  1169. end
  1170. % Check the presence of ICA activations
  1171. EEG.icaact = eeg_getica(EEG);
  1172. topografie=EEG.icawinv'; %computes IC topographies
  1173. % Topographies and time courses normalization
  1174. %
  1175. % disp(' ');
  1176. % disp('Normalizing topographies...')
  1177. % disp('Scaling time courses...')
  1178. for i=1:size(EEG.icawinv,2) % number of ICs
  1179. ScalingFactor=norm(topografie(i,:));
  1180. topografie(i,:)=topografie(i,:)/ScalingFactor;
  1181. if length(size(EEG.data))==3
  1182. EEG.icaact(i,:,:)=ScalingFactor*EEG.icaact(i,:,:);
  1183. else
  1184. EEG.icaact(i,:)=ScalingFactor*EEG.icaact(i,:);
  1185. end
  1186. end
  1187. %
  1188. % disp('Done.')
  1189. % disp(' ')
  1190. % Variables memorizing artifacted ICs indexes
  1191. blink=[];
  1192. horiz=[];
  1193. vert=[];
  1194. disc=[];
  1195. %% Check EEG channel position information
  1196. nopos_channels=[];
  1197. if iscolumn(EEG.chanlocs)
  1198. EEG.chanlocs = EEG.chanlocs';
  1199. end
  1200. for el=1:length(EEG.chanlocs)
  1201. if(any((isempty(EEG.chanlocs(1,el).X)|isempty(EEG.chanlocs(1,el).Y)|isempty(EEG.chanlocs(1,el).Z))&(isempty(EEG.chanlocs(1,el).theta)|isempty(EEG.chanlocs(1,el).radius)))) % !!! changed by LUCA 7/30/15
  1202. nopos_channels=[nopos_channels el];
  1203. end;
  1204. end
  1205. if ~isempty(nopos_channels)
  1206. disp(['Warning : Channels ' num2str(nopos_channels) ' have incomplete location information. They will NOT be used to compute ADJUST spatial features']);
  1207. disp(' ');
  1208. end;
  1209. pos_channels=setdiff(1:length(EEG.chanlocs),nopos_channels);
  1210. pos_channels = intersect(pos_channels,EEG.icachansind); % Luca 10/15/15
  1211. %% Feature extraction
  1212. disp(' ')
  1213. disp('Features Extraction:')
  1214. %GDSF - General Discontinuity Spatial Feature
  1215. disp('GDSF - General Discontinuity Spatial Feature...')
  1216. GDSF = compute_GD_feat(topografie,EEG.chanlocs(1,pos_channels),size(EEG.icawinv,2));
  1217. %SED - Spatial Eye Difference
  1218. disp('SED - Spatial Eye Difference...')
  1219. [SED,medie_left,medie_right]=computeSED_NOnorm(topografie,EEG.chanlocs(1,pos_channels),size(EEG.icawinv,2));
  1220. %SAD - Spatial Average Difference
  1221. disp('SAD - Spatial Average Difference...')
  1222. try
  1223. [SAD,var_front,var_back,mean_front,mean_back]=computeSAD(topografie,EEG.chanlocs(1,pos_channels),size(EEG.icawinv,2));
  1224. catch
  1225. [SAD,var_front,var_back,mean_front,mean_back] = deal(nan(1, size(EEG.icawinv,2))); % Luca
  1226. end
  1227. %SVD - Spatial Variance Difference between front zone and back zone
  1228. diff_var=var_front-var_back;
  1229. %epoch dynamic range, variance and kurtosis
  1230. K=zeros(num_epoch,size(EEG.icawinv,2)); %kurtosis
  1231. Kloc=K;
  1232. Vmax=zeros(num_epoch,size(EEG.icawinv,2)); %variance
  1233. % disp('Computing variance and kurtosis of all epochs...')
  1234. for i=1:size(EEG.icawinv,2) % number of ICs
  1235. for j=1:num_epoch
  1236. Vmax(j,i)=var(EEG.icaact(i,:,j));
  1237. % Kloc(j,i)=kurtosis(EEG.icaact(i,:,j));
  1238. K(j,i)=kurt(EEG.icaact(i,:,j));
  1239. end
  1240. end
  1241. % check that kurt and kurtosis give the same values:
  1242. % [a,b]=max(abs(Kloc(:)-K(:)))
  1243. %TK - Temporal Kurtosis
  1244. disp('Temporal Kurtosis...')
  1245. meanK=zeros(1,size(EEG.icawinv,2));
  1246. for i=1:size(EEG.icawinv,2)
  1247. if num_epoch>100
  1248. meanK(1,i)=trim_and_mean(K(:,i));
  1249. else meanK(1,i)=mean(K(:,i));
  1250. end
  1251. end
  1252. %MEV - Maximum Epoch Variance
  1253. disp('Maximum epoch variance...')
  1254. maxvar=zeros(1,size(EEG.icawinv,2));
  1255. meanvar=zeros(1,size(EEG.icawinv,2));
  1256. for i=1:size(EEG.icawinv,2)
  1257. if num_epoch>100
  1258. maxvar(1,i)=trim_and_max(Vmax(:,i)');
  1259. meanvar(1,i)=trim_and_mean(Vmax(:,i)');
  1260. else
  1261. maxvar(1,i)=max(Vmax(:,i));
  1262. meanvar(1,i)=mean(Vmax(:,i));
  1263. end
  1264. end
  1265. % MEV in reviewed formulation:
  1266. nuovaV=maxvar./meanvar;
  1267. %% Thresholds computation
  1268. disp('Computing EM thresholds...')
  1269. % soglia_K=EM(meanK);
  1270. %
  1271. % soglia_SED=EM(SED);
  1272. %
  1273. % soglia_SAD=EM(SAD);
  1274. %
  1275. % soglia_GDSF=EM(GDSF);
  1276. %
  1277. % soglia_V=EM(nuovaV);
  1278. [soglia_K,med1_K,med2_K]=EM(meanK);
  1279. [soglia_SED,med1_SED,med2_SED]=EM(SED);
  1280. [soglia_SAD,med1_SAD,med2_SAD]=EM(SAD);
  1281. [soglia_GDSF,med1_GDSF,med2_GDSF]=EM(GDSF);
  1282. [soglia_V,med1_V,med2_V]=EM(nuovaV);
  1283. %% Horizontal eye movements (HEM)
  1284. horiz=intersect(intersect(find(SED>=soglia_SED),find(medie_left.*medie_right<0)),...
  1285. (find(nuovaV>=soglia_V)));
  1286. %% Vertical eye movements (VEM)
  1287. vert=intersect(intersect(find(SAD>=soglia_SAD),find(medie_left.*medie_right>0)),...
  1288. intersect(find(diff_var>0),find(nuovaV>=soglia_V)));
  1289. %% Eye Blink (EB)
  1290. blink=intersect ( intersect( find(SAD>=soglia_SAD),find(medie_left.*medie_right>0) ) ,...
  1291. intersect ( find(meanK>=soglia_K),find(diff_var>0) ));
  1292. %% Generic Discontinuities (GD)
  1293. disc=intersect(find(GDSF>=soglia_GDSF),find(nuovaV>=soglia_V));
  1294. %compute output variable
  1295. art = nonzeros( union (union(blink,horiz) , union(vert,disc)) )'; %artifact ICs
  1296. % these three are old outputs which are no more necessary in latest ADJUST version.
  1297. soglia_D=0;
  1298. soglia_DV=0;
  1299. maxdin=zeros(1,size(EEG.icawinv,2));
  1300. return
  1301. % compute_GD_feat() - Computes Generic Discontinuity spatial feature
  1302. %
  1303. % Usage:
  1304. % >> res = compute_GD_feat(topografie,canali,num_componenti);
  1305. %
  1306. % Inputs:
  1307. % topografie - topographies vector
  1308. % canali - EEG.chanlocs struct
  1309. % num_componenti - number of components
  1310. %
  1311. % Outputs:
  1312. % res - GDSF values
  1313. % Copyright (C) 2009-2014 Andrea Mognon (1) and Marco Buiatti (2),
  1314. % (1) Center for Mind/Brain Sciences, University of Trento, Italy
  1315. % (2) INSERM U992 - Cognitive Neuroimaging Unit, Gif sur Yvette, France
  1316. %
  1317. % This program is free software; you can redistribute it and/or modify
  1318. % it under the terms of the GNU General Public License as published by
  1319. % the Free Software Foundation; either version 2 of the License, or
  1320. % (at your option) any later version.
  1321. %
  1322. % This program is distributed in the hope that it will be useful,
  1323. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  1324. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1325. % GNU General Public License for more details.
  1326. %
  1327. % You should have received a copy of the GNU General Public License
  1328. % along with this program; if not, write to the Free Software
  1329. % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  1330. function res = compute_GD_feat(topografie,canali,num_componenti)
  1331. % Computes GDSF, discontinuity spatial feature
  1332. % topografie is the topography weights matrix
  1333. % canali is the structure EEG.chanlocs
  1334. % num_componenti is the number of ICs
  1335. % res is GDSF values
  1336. xpos=[canali.X];ypos=[canali.Y];zpos=[canali.Z];
  1337. pos=[xpos',ypos',zpos'];
  1338. res=zeros(1,num_componenti);
  1339. for ic=1:num_componenti
  1340. % consider the vector topografie(ic,:)
  1341. aux=[];
  1342. for el=1:length(canali)-1
  1343. P=pos(el,:); %position of current electrode
  1344. d=pos-repmat(P,length(canali),1);
  1345. %d=pos-repmat(P,62,1);
  1346. dist=sqrt(sum((d.*d),2));
  1347. [y,I]=sort(dist);
  1348. repchas=I(2:11); % list of 10 nearest channels to el
  1349. weightchas=exp(-y(2:11)); % respective weights, computed wrt distance
  1350. aux=[aux abs(topografie(ic,el)-mean(weightchas.*topografie(ic,repchas)'))];
  1351. % difference between el and the average of 10 neighbors
  1352. % weighted according to weightchas
  1353. end
  1354. res(ic)=max(aux);
  1355. end
  1356. % computeSAD() - Computes Spatial Average Difference feature
  1357. %
  1358. % Usage:
  1359. % >> [rapp,var_front,var_back,mean_front,mean_back]=computeSAD(topog,chanlocs,n);
  1360. %
  1361. % Inputs:
  1362. % topog - topographies vector
  1363. % chanlocs - EEG.chanlocs struct
  1364. % n - number of ICs
  1365. % nchannels - number of channels
  1366. %
  1367. % Outputs:
  1368. % rapp - SAD values
  1369. % var_front - Frontal Area variance values
  1370. % var_back - Posterior Area variance values
  1371. % mean_front - Frontal Area average values
  1372. % mean_back - Posterior Area average values
  1373. %
  1374. %
  1375. % Copyright (C) 2009-2014 Andrea Mognon (1) and Marco Buiatti (2),
  1376. % (1) Center for Mind/Brain Sciences, University of Trento, Italy
  1377. % (2) INSERM U992 - Cognitive Neuroimaging Unit, Gif sur Yvette, France
  1378. %
  1379. % This program is free software; you can redistribute it and/or modify
  1380. % it under the terms of the GNU General Public License as published by
  1381. % the Free Software Foundation; either version 2 of the License, or
  1382. % (at your option) any later version.
  1383. %
  1384. % This program is distributed in the hope that it will be useful,
  1385. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  1386. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1387. % GNU General Public License for more details.
  1388. %
  1389. % You should have received a copy of the GNU General Public License
  1390. % along with this program; if not, write to the Free Software
  1391. % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  1392. function [rapp,var_front,var_back,mean_front,mean_back]=computeSAD(topog,chanlocs,n)
  1393. nchannels=length(chanlocs);
  1394. %% Define scalp zones
  1395. % Find electrodes in Frontal Area (FA)
  1396. dimfront=0; %number of FA electrodes
  1397. index1=zeros(1,nchannels); %indexes of FA electrodes
  1398. for k=1:nchannels
  1399. if (abs(chanlocs(1,k).theta)<60) && (chanlocs(1,k).radius>0.40) %electrodes are in FA
  1400. dimfront=dimfront+1; %count electrodes
  1401. index1(1,dimfront)=k;
  1402. end
  1403. end
  1404. % Find electrodes in Posterior Area (PA)
  1405. dimback=0;
  1406. index3=zeros(1,nchannels);
  1407. for h=1:nchannels
  1408. if (abs(chanlocs(1,h).theta)>110)
  1409. dimback=dimback+1;
  1410. index3(1,dimback)=h;
  1411. end
  1412. end
  1413. if dimfront*dimback==0
  1414. disp('ERROR: no channels included in some scalp areas.')
  1415. disp('Check channels distribution and/or change scalp areas definitions in computeSAD.m and computeSED_NOnorm.m')
  1416. disp('ADJUST session aborted.')
  1417. return
  1418. end
  1419. %% Outputs
  1420. rapp=zeros(1,n); % SAD
  1421. mean_front=zeros(1,n); % FA electrodes mean value
  1422. mean_back=zeros(1,n); % PA electrodes mean value
  1423. var_front=zeros(1,n); % FA electrodes variance value
  1424. var_back=zeros(1,n); % PA electrodes variance value
  1425. %% Output computation
  1426. for i=1:n % for each topography
  1427. %create FA electrodes vector
  1428. front=zeros(1,dimfront);
  1429. for h=1:dimfront
  1430. front(1,h)=topog(i,index1(1,h));
  1431. end
  1432. %create PA electrodes vector
  1433. back=zeros(1,dimback);
  1434. for h=1:dimback
  1435. back(1,h)=topog(i,index3(1,h));
  1436. end
  1437. %compute features
  1438. rapp(1,i)=abs(mean(front))-abs(mean(back)); % SAD
  1439. mean_front(1,i)=mean(front);
  1440. mean_back(1,i)=mean(back);
  1441. var_back(1,i)=var(back);
  1442. var_front(1,i)=var(front);
  1443. end
  1444. % computeSED_NOnorm() - Computes Spatial Eye Difference feature
  1445. % without normalization
  1446. %
  1447. % Usage:
  1448. % >> [out,medie_left,medie_right]=computeSED_NOnorm(topog,chanlocs,n);
  1449. %
  1450. % Inputs:
  1451. % topog - topographies vector
  1452. % chanlocs - EEG.chanlocs struct
  1453. % n - number of ICs
  1454. % nchannels - number of channels
  1455. %
  1456. % Outputs:
  1457. % out - SED values
  1458. % medie_left - Left Eye area average values
  1459. % medie_right- Right Eye area average values
  1460. %
  1461. %
  1462. % Copyright (C) 2009-2014 Andrea Mognon (1) and Marco Buiatti (2),
  1463. % (1) Center for Mind/Brain Sciences, University of Trento, Italy
  1464. % (2) INSERM U992 - Cognitive Neuroimaging Unit, Gif sur Yvette, France
  1465. %
  1466. % This program is free software; you can redistribute it and/or modify
  1467. % it under the terms of the GNU General Public License as published by
  1468. % the Free Software Foundation; either version 2 of the License, or
  1469. % (at your option) any later version.
  1470. %
  1471. % This program is distributed in the hope that it will be useful,
  1472. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  1473. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1474. % GNU General Public License for more details.
  1475. %
  1476. % You should have received a copy of the GNU General Public License
  1477. % along with this program; if not, write to the Free Software
  1478. % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  1479. function [out,medie_left,medie_right]=computeSED_NOnorm(topog,chanlocs,n)
  1480. nchannels=length(chanlocs);
  1481. %% Define scalp zones
  1482. % Find electrodes in Left Eye area (LE)
  1483. dimleft=0; %number of LE electrodes
  1484. index1=zeros(1,nchannels); %indexes of LE electrodes
  1485. for k=1:nchannels
  1486. if (-61<chanlocs(1,k).theta) && (chanlocs(1,k).theta<-35) && (chanlocs(1,k).radius>0.30) %electrodes are in LE
  1487. dimleft=dimleft+1; %count electrodes
  1488. index1(1,dimleft)=k;
  1489. end
  1490. end
  1491. % Find electrodes in Right Eye area (RE)
  1492. dimright=0; %number of RE electrodes
  1493. index2=zeros(1,nchannels); %indexes of RE electrodes
  1494. for g=1:nchannels
  1495. if (34<chanlocs(1,g).theta) && (chanlocs(1,g).theta<61) && (chanlocs(1,g).radius>0.30) %electrodes are in RE
  1496. dimright=dimright+1; %count electrodes
  1497. index2(1,dimright)=g;
  1498. end
  1499. end
  1500. % Find electrodes in Posterior Area (PA)
  1501. dimback=0;
  1502. index3=zeros(1,nchannels);
  1503. for h=1:nchannels
  1504. if (abs(chanlocs(1,h).theta)>110)
  1505. dimback=dimback+1;
  1506. index3(1,dimback)=h;
  1507. end
  1508. end
  1509. if dimleft*dimright*dimback==0
  1510. disp('ERROR: no channels included in some scalp areas.')
  1511. disp('Check channels distribution and/or change scalp areas definitions in computeSAD.m and computeSED_NOnorm.m')
  1512. disp('ADJUST session aborted.')
  1513. return
  1514. end
  1515. %% Outputs
  1516. out=zeros(1,n); %memorizes SED
  1517. medie_left=zeros(1,n); %memorizes LE mean value
  1518. medie_right=zeros(1,n); %memorizes RE mean value
  1519. %% Output computation
  1520. for i=1:n % for each topography
  1521. %create LE electrodes vector
  1522. left=zeros(1,dimleft);
  1523. for h=1:dimleft
  1524. left(1,h)=topog(i,index1(1,h));
  1525. end
  1526. %create RE electrodes vector
  1527. right=zeros(1,dimright);
  1528. for h=1:dimright
  1529. right(1,h)=topog(i,index2(1,h));
  1530. end
  1531. %create PA electrodes vector
  1532. back=zeros(1,dimback);
  1533. for h=1:dimback
  1534. back(1,h)=topog(i,index3(1,h));
  1535. end
  1536. %compute features
  1537. out1=abs(mean(left)-mean(right));
  1538. out2=var(back);
  1539. out(1,i)=out1; % SED not notmalized
  1540. medie_left(1,i)=mean(left);
  1541. medie_right(1,i)=mean(right);
  1542. end
  1543. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1544. %
  1545. % EM - ADJUST package
  1546. %
  1547. % Performs automatic threshold on the digital numbers
  1548. % of the input vector 'vec'; based on Expectation - Maximization algorithm
  1549. % Reference paper:
  1550. % Bruzzone, L., Prieto, D.F., 2000. Automatic analysis of the difference image
  1551. % for unsupervised change detection.
  1552. % IEEE Trans. Geosci. Remote Sensing 38, 1171:1182
  1553. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1554. %
  1555. % Usage:
  1556. % >> [last,med1,med2,var1,var2,prior1,prior2]=EM(vec);
  1557. %
  1558. % Input: vec (row vector, to be thresholded)
  1559. %
  1560. % Outputs: last (threshold value)
  1561. % med1,med2 (mean values of the Gaussian-distributed classes 1,2)
  1562. % var1,var2 (variance of the Gaussian-distributed classes 1,2)
  1563. % prior1,prior2 (prior probabilities of the Gaussian-distributed classes 1,2)
  1564. %
  1565. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1566. % Copyright (C) 2009-2014 Andrea Mognon (1) and Marco Buiatti (2),
  1567. % (1) Center for Mind/Brain Sciences, University of Trento, Italy
  1568. % (2) INSERM U992 - Cognitive Neuroimaging Unit, Gif sur Yvette, France
  1569. %
  1570. % This program is free software; you can redistribute it and/or modify
  1571. % it under the terms of the GNU General Public License as published by
  1572. % the Free Software Foundation; either version 2 of the License, or
  1573. % (at your option) any later version.
  1574. %
  1575. % This program is distributed in the hope that it will be useful,
  1576. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  1577. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1578. % GNU General Public License for more details.
  1579. %
  1580. % You should have received a copy of the GNU General Public License
  1581. % along with this program; if not, write to the Free Software
  1582. % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  1583. function [last,med1,med2,var1,var2,prior1,prior2]=EM(vec)
  1584. if size(vec,2)>1
  1585. len=size(vec,2); %number of elements
  1586. else
  1587. vec=vec';
  1588. len=size(vec,2);
  1589. end
  1590. c_FA=1; % False Alarm cost
  1591. c_MA=1; % Missed Alarm cost
  1592. med=mean(vec);
  1593. standard=std(vec);
  1594. mediana=(max(vec)+min(vec))/2;
  1595. alpha1=0.01*(max(vec)-mediana); % initialization parameter/ righthand side
  1596. alpha2=0.01*(mediana-min(vec)); % initialization parameter/ lefthand side
  1597. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1598. % EXPECTATION
  1599. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1600. train1=[]; % Expectation of class 1
  1601. train2=[];
  1602. train=[]; % Expectation of 'unlabeled' samples
  1603. for i=1:(len)
  1604. if (vec(i)<(mediana-alpha2))
  1605. train2=[train2 vec(i)];
  1606. elseif (vec(i)>(mediana+alpha1))
  1607. train1=[train1 vec(i)];
  1608. else
  1609. train=[train vec(i)];
  1610. end
  1611. end
  1612. n1=length(train1);
  1613. n2=length(train2);
  1614. med1=mean(train1);
  1615. med2=mean(train2);
  1616. prior1=n1/(n1+n2);
  1617. prior2=n2/(n1+n2);
  1618. var1=var(train1);
  1619. var2=var(train2);
  1620. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1621. % MAXIMIZATION
  1622. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1623. count=0;
  1624. dif_med_1=1; % difference between current and previous mean
  1625. dif_med_2=1;
  1626. dif_var_1=1; % difference between current and previous variance
  1627. dif_var_2=1;
  1628. dif_prior_1=1; % difference between current and previous prior
  1629. dif_prior_2=1;
  1630. stop=0.0001;
  1631. while((dif_med_1>stop)&&(dif_med_2>stop)&&(dif_var_1>stop)&&(dif_var_2>stop)&&(dif_prior_1>stop)&&(dif_prior_2>stop))
  1632. count=count+1;
  1633. med1_old=med1;
  1634. med2_old=med2;
  1635. var1_old=var1;
  1636. var2_old=var2;
  1637. prior1_old=prior1;
  1638. prior2_old=prior2;
  1639. prior1_i=[];
  1640. prior2_i=[];
  1641. % FOLLOWING FORMULATION IS ACCORDING TO REFERENCE PAPER:
  1642. for i=1:len
  1643. prior1_i=[prior1_i prior1_old*Bayes(med1_old,var1_old,vec(i))/...
  1644. (prior1_old*Bayes(med1_old,var1_old,vec(i))+prior2_old*Bayes(med2_old,var2_old,vec(i)))];
  1645. prior2_i=[prior2_i prior2_old*Bayes(med2_old,var2_old,vec(i))/...
  1646. (prior1_old*Bayes(med1_old,var1_old,vec(i))+prior2_old*Bayes(med2_old,var2_old,vec(i)))];
  1647. end
  1648. prior1=sum(prior1_i)/len;
  1649. prior2=sum(prior2_i)/len;
  1650. med1=sum(prior1_i.*vec)/(prior1*len);
  1651. med2=sum(prior2_i.*vec)/(prior2*len);
  1652. var1=sum(prior1_i.*((vec-med1_old).^2))/(prior1*len);
  1653. var2=sum(prior2_i.*((vec-med2_old).^2))/(prior2*len);
  1654. dif_med_1=abs(med1-med1_old);
  1655. dif_med_2=abs(med2-med2_old);
  1656. dif_var_1=abs(var1-var1_old);
  1657. dif_var_2=abs(var2-var2_old);
  1658. dif_prior_1=abs(prior1-prior1_old);
  1659. dif_prior_2=abs(prior2-prior2_old);
  1660. end
  1661. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1662. % THRESHOLDING
  1663. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1664. k=c_MA/c_FA;
  1665. a=(var1-var2)/2;
  1666. b= ((var2*med1)-(var1*med2));
  1667. c=(log((k*prior1*sqrt(var2))/(prior2*sqrt(var1)))*(var2*var1))+(((((med2)^2)*var1)-(((med1)^2)*var2))/2);
  1668. rad=(b^2)-(4*a*c);
  1669. if rad<0
  1670. disp('Negative Discriminant!');
  1671. return;
  1672. end
  1673. soglia1=(-b+sqrt(rad))/(2*a);
  1674. soglia2=(-b-sqrt(rad))/(2*a);
  1675. if ((soglia1<med2)||(soglia1>med1))
  1676. last=soglia2;
  1677. else
  1678. last=soglia1;
  1679. end
  1680. if isnan(last) % TO PREVENT CRASHES
  1681. last=mediana;
  1682. end
  1683. return
  1684. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1685. function prob=Bayes(med,var,point)
  1686. if var==0
  1687. prob=1;
  1688. else
  1689. prob=((1/(sqrt(2*pi*var)))*exp((-1)*((point-med)^2)/(2*var)));
  1690. end
  1691. % trim_and_max() - Computes maximum value from vector 'vettore'
  1692. % after removing the top 1% of the values
  1693. % (to be outlier resistant)
  1694. %
  1695. % Usage:
  1696. % >> valore=trim_and_max(vettore);
  1697. %
  1698. % Inputs:
  1699. % vettore - row vector
  1700. %
  1701. % Outputs:
  1702. % valore - result
  1703. %
  1704. %
  1705. % Author: Andrea Mognon, Center for Mind/Brain Sciences, University of
  1706. % Trento, 2009
  1707. % Motivation taken from the following comment to our paper:
  1708. % "On page 11 the authors motivate the use of the max5 function when computing
  1709. % Maximum Epoch Variance because the simple maximum would be too sensitive
  1710. % to spurious outliers. This is a good concern, however the max5 function would
  1711. % still be sensitive to spurious outliers for very large data sets. In other words, if
  1712. % the data set is large enough, one will be very likely to record more than five
  1713. % outliers. The authors should use a trimmed max function that computes the
  1714. % simple maximum after the top say .1% of the values have been removed from
  1715. % consideration. This rejection criteria scales appropriately with the size of the data
  1716. % set."
  1717. % Copyright (C) 2009-2014 Andrea Mognon (1) and Marco Buiatti (2),
  1718. % (1) Center for Mind/Brain Sciences, University of Trento, Italy
  1719. % (2) INSERM U992 - Cognitive Neuroimaging Unit, Gif sur Yvette, France
  1720. %
  1721. % This program is free software; you can redistribute it and/or modify
  1722. % it under the terms of the GNU General Public License as published by
  1723. % the Free Software Foundation; either version 2 of the License, or
  1724. % (at your option) any later version.
  1725. %
  1726. % This program is distributed in the hope that it will be useful,
  1727. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  1728. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1729. % GNU General Public License for more details.
  1730. %
  1731. % You should have received a copy of the GNU General Public License
  1732. % along with this program; if not, write to the Free Software
  1733. % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  1734. function valore=trim_and_max(vettore)
  1735. dim=floor(.01*size(vettore,2)); % = 1% of vector length
  1736. tmp=sort(vettore);
  1737. valore= tmp(length(vettore)-dim);
  1738. % trim_and_mean() - Computes average value from vector 'vettore'
  1739. % after removing the top .1% of the values
  1740. % (to be outlier resistant)
  1741. %
  1742. % Usage:
  1743. % >> valore=trim_and_mean(vettore);
  1744. %
  1745. % Inputs:
  1746. % vettore - row vector
  1747. %
  1748. % Outputs:
  1749. % valore - result
  1750. %
  1751. % Copyright (C) 2009-2014 Andrea Mognon (1) and Marco Buiatti (2),
  1752. % (1) Center for Mind/Brain Sciences, University of Trento, Italy
  1753. % (2) INSERM U992 - Cognitive Neuroimaging Unit, Gif sur Yvette, France
  1754. %
  1755. % This program is free software; you can redistribute it and/or modify
  1756. % it under the terms of the GNU General Public License as published by
  1757. % the Free Software Foundation; either version 2 of the License, or
  1758. % (at your option) any later version.
  1759. %
  1760. % This program is distributed in the hope that it will be useful,
  1761. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  1762. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1763. % GNU General Public License for more details.
  1764. %
  1765. % You should have received a copy of the GNU General Public License
  1766. % along with this program; if not, write to the Free Software
  1767. % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  1768. function valore=trim_and_mean(vettore)
  1769. dim=floor(.01*size(vettore,2)); % = 1% of vector length
  1770. tmp=sort(vettore);
  1771. valore= mean (tmp(1:(length(vettore)-dim)));
  1772. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1773. %%%%%%%%%%%%%%%%%%%%%%% END ADJUST CODE%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1774. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1775. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1776. %%%%%%%%%%%%%%%%%%%%%%% BELOW IS FASTER CODE %%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1777. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1778. function list_properties = component_properties(EEG,blink_chans,lpf_band)
  1779. % Copyright (C) 2010 Hugh Nolan, Robert Whelan and Richard Reilly, Trinity College Dublin,
  1780. % Ireland
  1781. % nolanhu@tcd.ie, robert.whelan@tcd.ie
  1782. %
  1783. % This program is free software; you can redistribute it and/or modify
  1784. % it under the terms of the GNU General Public License as published by
  1785. % the Free Software Foundation; either version 2 of the License, or
  1786. % (at your option) any later version.
  1787. %
  1788. % This program is distributed in the hope that it will be useful,
  1789. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  1790. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1791. % GNU General Public License for more details.
  1792. %
  1793. % You should have received a copy of the GNU General Public License
  1794. % along with this program; if not, write to the Free Software
  1795. % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  1796. list_properties = [];
  1797. %
  1798. if isempty(EEG.icaweights)
  1799. fprintf('No ICA data.\n');
  1800. return;
  1801. end
  1802. if ~exist('lpf_band','var') || length(lpf_band)~=2 || ~any(lpf_band)
  1803. ignore_lpf=1;
  1804. else
  1805. ignore_lpf=0;
  1806. end
  1807. delete_activations_after=0;
  1808. if ~isfield(EEG,'icaact') || isempty(EEG.icaact)
  1809. delete_activations_after=1;
  1810. EEG.icaact = eeg_getica(EEG);
  1811. end
  1812. try
  1813. checkfunctionmatlab('pwelch', 'signal_toolbox')
  1814. end
  1815. for u = 1:size(EEG.icaact,1)
  1816. [spectra(u,:) freqs] = pwelch(EEG.icaact(u,:),[],[],(EEG.srate),EEG.srate);
  1817. end
  1818. list_properties = zeros(size(EEG.icaact,1),5); %This 5 corresponds to number of measurements made.
  1819. for u=1:size(EEG.icaact,1)
  1820. measure = 1;
  1821. % TEMPORAL PROPERTIES
  1822. % 1 Median gradient value, for high frequency stuff
  1823. list_properties(u,measure) = median(diff(EEG.icaact(u,:)));
  1824. measure = measure + 1;
  1825. % 2 Mean slope around the LPF band (spectral)
  1826. if ignore_lpf
  1827. list_properties(u,measure) = 0;
  1828. else
  1829. list_properties(u,measure) = mean(diff(10*log10(spectra(u,find(freqs>=lpf_band(1),1):find(freqs<=lpf_band(2),1,'last')))));
  1830. end
  1831. measure = measure + 1;
  1832. % SPATIAL PROPERTIES
  1833. % 3 Kurtosis of spatial map (if v peaky, i.e. one or two points high
  1834. % and everywhere else low, then it's probably noise on a single
  1835. % channel)
  1836. list_properties(u,measure) = kurt(EEG.icawinv(:,u));
  1837. measure = measure + 1;
  1838. % OTHER PROPERTIES
  1839. % 4 Hurst exponent
  1840. list_properties(u,measure) = hurst_exponent(EEG.icaact(u,:));
  1841. measure = measure + 1;
  1842. % 10 Eyeblink correlations
  1843. if (exist('blink_chans','var') && ~isempty(blink_chans))
  1844. for v = 1:length(blink_chans)
  1845. if ~(max(EEG.data(blink_chans(v),:))==0 && min(EEG.data(blink_chans(v),:))==0);
  1846. f = corrcoef(EEG.icaact(u,:),EEG.data(blink_chans(v),:));
  1847. x(v) = abs(f(1,2));
  1848. else
  1849. x(v) = v;
  1850. end
  1851. end
  1852. list_properties(u,measure) = max(x);
  1853. measure = measure + 1;
  1854. end
  1855. end
  1856. for u = 1:size(list_properties,2)
  1857. list_properties(isnan(list_properties(:,u)),u)=nanmean(list_properties(:,u));
  1858. list_properties(:,u) = list_properties(:,u) - median(list_properties(:,u));
  1859. end
  1860. if delete_activations_after
  1861. EEG.icaact=[];
  1862. end
  1863. % The Hurst exponent
  1864. %--------------------------------------------------------------------------
  1865. % This function does dispersional analysis on a data series, then does a
  1866. % Matlab polyfit to a log-log plot to estimate the Hurst exponent of the
  1867. % series.
  1868. %
  1869. % This algorithm is far faster than a full-blown implementation of Hurst's
  1870. % algorithm. I got the idea from a 2000 PhD dissertation by Hendrik J
  1871. % Blok, and I make no guarantees whatsoever about the rigor of this approach
  1872. % or the accuracy of results. Use it at your own risk.
  1873. %
  1874. % Bill Davidson
  1875. % 21 Oct 2003
  1876. function [hurst] = hurst_exponent(data0) % data set
  1877. data=data0; % make a local copy
  1878. [M,npoints]=size(data0);
  1879. yvals=zeros(1,npoints);
  1880. xvals=zeros(1,npoints);
  1881. data2=zeros(1,npoints);
  1882. index=0;
  1883. binsize=1;
  1884. while npoints>4
  1885. y=std(data);
  1886. index=index+1;
  1887. xvals(index)=binsize;
  1888. yvals(index)=binsize*y;
  1889. npoints=fix(npoints/2);
  1890. binsize=binsize*2;
  1891. for ipoints=1:npoints % average adjacent points in pairs
  1892. data2(ipoints)=(data(2*ipoints)+data((2*ipoints)-1))*0.5;
  1893. end
  1894. data=data2(1:npoints);
  1895. end % while
  1896. xvals=xvals(1:index);
  1897. yvals=yvals(1:index);
  1898. logx=log(xvals);
  1899. logy=log(yvals);
  1900. p2=polyfit(logx,logy,1);
  1901. hurst=p2(1); % Hurst exponent is the slope of the linear fit of log-log plot
  1902. return;
  1903. function [lengths] = min_z(list_properties, rejection_options)
  1904. if (~exist('rejection_options', 'var'))
  1905. rejection_options.measure = ones(1, size(list_properties, 2));
  1906. rejection_options.z = 3*ones(1, size(list_properties, 2));
  1907. end
  1908. rejection_options.measure = logical(rejection_options.measure);
  1909. zs = list_properties - repmat(mean(list_properties, 1), size(list_properties, 1), 1);
  1910. zs = zs./repmat(std(zs, [], 1), size(list_properties, 1), 1);
  1911. zs(isnan(zs)) = 0;
  1912. all_l = abs(zs) > repmat(rejection_options.z, size(list_properties, 1), 1);
  1913. lengths = any(all_l(:, rejection_options.measure), 2);
  1914. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1915. %%%%%%%%%%%%%%%%%%%%%%% END FASTER CODE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1916. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1917. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1918. %%%%%%%%%%%%%%%%%%%%%%% BEGIN MARA CODE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1919. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1920. % MARA() - Automatic classification of multiple artifact components
  1921. % Classies artifactual ICs based on 6 features from the time domain,
  1922. % the frequency domain, and the pattern
  1923. %
  1924. % Usage:
  1925. % >> [artcomps, info] = MARA(EEG);
  1926. %
  1927. % Inputs:
  1928. % EEG - input EEG structure
  1929. %
  1930. % Outputs:
  1931. % artcomps - array containing the numbers of the artifactual
  1932. % components
  1933. % info - struct containing more information about MARA classification
  1934. % .posterior_artefactprob : posterior probability for each
  1935. % IC of being an artefact
  1936. % .normfeats : <6 x nIC > features computed by MARA for each IC,
  1937. % normalized by the training data
  1938. % The features are: (1) Current Density Norm, (2) Range
  1939. % in Pattern, (3) Local Skewness of the Time Series,
  1940. % (4) Lambda, (5) 8-13 Hz, (6) FitError.
  1941. %
  1942. % For more information see:
  1943. % I. Winkler, S. Haufe, and M. Tangermann, Automatic classification of artifactual ICA-components
  1944. % for artifact removal in EEG signals, Behavioral and Brain Functions, 7, 2011.
  1945. %
  1946. % See also: processMARA()
  1947. % Copyright (C) 2013 Irene Winkler and Eric Waldburger
  1948. % Berlin Institute of Technology, Germany
  1949. %
  1950. % This program is free software; you can redistribute it and/or modify
  1951. % it under the terms of the GNU General Public License as published by
  1952. % the Free Software Foundation; either version 2 of the License, or
  1953. % (at your option) any later version.
  1954. %
  1955. % This program is distributed in the hope that it will be useful,
  1956. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  1957. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1958. % GNU General Public License for more details.
  1959. %
  1960. % You should have received a copy of the GNU General Public License
  1961. % along with this program; if not, write to the Free Software
  1962. % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  1963. function [artcomps, info] = MARA(EEG)
  1964. %%%%%%%%%%%%%%%%%%%%
  1965. %% Calculate features from the pattern (component map)
  1966. %%%%%%%%%%%%%%%%%%%%
  1967. % extract channel labels
  1968. clab = {};
  1969. for i=1:length(EEG.chanlocs)
  1970. clab{i} = EEG.chanlocs(i).labels;
  1971. end
  1972. % cut to channel labels common with training data
  1973. addpath SASICA-master
  1974. load('fv_training_MARA'); %load struct fv_tr
  1975. [clab_common i_te i_tr ] = intersect(upper(clab), upper(fv_tr.clab));
  1976. clab_common = fv_tr.clab(i_tr);
  1977. if length(clab_common) == 0
  1978. error(['There were no matching channeldescriptions found.' , ...
  1979. 'MARA needs channel labels of the form Cz, Oz, F3, F4, Fz, etc. Aborting.'])
  1980. end
  1981. patterns = (EEG.icawinv(i_te,:));
  1982. [M100 idx] = get_M100_ADE(clab_common); %needed for Current Density Norm
  1983. disp('MARA is computing features. Please wait');
  1984. %standardize patterns
  1985. patterns = patterns./repmat(std(patterns,0,1),length(patterns(:,1)),1);
  1986. %compute current density norm
  1987. feats(1,:) = log(sqrt(sum((M100*patterns(idx,:)).^2)));
  1988. %compute spatial range
  1989. feats(2,:) = log(max(patterns) - min(patterns));
  1990. %%%%%%%%%%%%%%%%%%%%
  1991. %% Calculate time and frequency features
  1992. %%%%%%%%%%%%%%%%%%%%
  1993. %compute time and frequency features (Current Density Norm, Range Within Pattern,
  1994. %Average Local Skewness, Band Power 8 - 13 Hz)
  1995. feats(3:6,:) = extract_time_freq_features(EEG);
  1996. disp('Features ready');
  1997. %%%%%%%%%%%%%%%%%%%%%%
  1998. %% Adapt train features to clab
  1999. %%%%%%%%%%%%%%%%%%%%
  2000. fv_tr.pattern = fv_tr.pattern(i_tr, :);
  2001. fv_tr.pattern = fv_tr.pattern./repmat(std(fv_tr.pattern,0,1),length(fv_tr.pattern(:,1)),1);
  2002. fv_tr.x(2,:) = log(max(fv_tr.pattern) - min(fv_tr.pattern));
  2003. fv_tr.x(1,:) = log(sqrt(sum((M100 * fv_tr.pattern).^2)));
  2004. %%%%%%%%%%%%%%%%%%%%
  2005. %% Classification
  2006. %%%%%%%%%%%%%%%%%%%%
  2007. [C, foo, posterior] = classify(feats',fv_tr.x',fv_tr.labels(1,:));
  2008. artcomps = find(C == 0)';
  2009. info.posterior_artefactprob = posterior(:, 1)';
  2010. info.normfeats = (feats - repmat(mean(fv_tr.x, 2), 1, size(feats, 2)))./ ...
  2011. repmat(std(fv_tr.x,0, 2), 1, size(feats, 2));
  2012. function features = extract_time_freq_features(EEG)
  2013. % - 1st row: Average Local Skewness
  2014. % - 2nd row: lambda
  2015. % - 3rd row: Band Power 8 - 13 Hz
  2016. % - 4rd row: Fit Error
  2017. %
  2018. data = EEG.data(EEG.icachansind,:,:);
  2019. fs = EEG.srate; %sampling frequency
  2020. % transform epoched data into continous data
  2021. data = data(:,:);
  2022. %downsample (to 100-200Hz)
  2023. factor = max(floor(EEG.srate/100),1);
  2024. data = data(:, 1:factor:end);
  2025. fs = round(fs/factor);
  2026. %compute icaactivation and standardise variance to 1
  2027. icacomps = (EEG.icaweights * EEG.icasphere * data)';
  2028. icacomps = icacomps./repmat(std(icacomps,0,1),length(icacomps(:,1)),1);
  2029. icacomps = icacomps';
  2030. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2031. % Calculate featues
  2032. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2033. for ic=1:size(icacomps,1) %for each component
  2034. fprintf('.');
  2035. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2036. % Proc Spectrum for Channel
  2037. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2038. [pxx, freq] = pwelch(icacomps(ic,:), ones(1, fs), [], fs, fs);
  2039. pxx = 10*log10(pxx * fs/2);
  2040. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2041. % The average log band power between 8 and 13 Hz
  2042. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2043. p = 0;
  2044. for i = 8:13
  2045. p = p + pxx(find(freq == i,1));
  2046. end
  2047. Hz8_13 = p / (13-8+1);
  2048. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2049. % lambda and FitError: deviation of a component's spectrum from
  2050. % a protoptypical 1/frequency curve
  2051. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2052. p1.x = 2; %first point: value at 2 Hz
  2053. p1.y = pxx(find(freq == p1.x,1));
  2054. p2.x = 3; %second point: value at 3 Hz
  2055. p2.y = pxx(find(freq == p2.x,1));
  2056. %third point: local minimum in the band 5-13 Hz
  2057. p3.y = min(pxx(find(freq == 5,1):find(freq == 13,1)));
  2058. p3.x = freq(find(pxx == p3.y,1));
  2059. %fourth point: min - 1 in band 5-13 Hz
  2060. p4.x = p3.x - 1;
  2061. p4.y = pxx(find(freq == p4.x,1));
  2062. %fifth point: local minimum in the band 33-39 Hz
  2063. p5.y = min(pxx(find(freq == 33,1):find(freq == 39,1)));
  2064. p5.x = freq(find(pxx == p5.y,1));
  2065. %sixth point: min + 1 in band 33-39 Hz
  2066. p6.x = p5.x + 1;
  2067. p6.y = pxx(find(freq == p6.x,1));
  2068. pX = [p1.x; p2.x; p3.x; p4.x; p5.x; p6.x];
  2069. pY = [p1.y; p2.y; p3.y; p4.y; p5.y; p6.y];
  2070. myfun = @(x,xdata)(exp(x(1))./ xdata.^exp(x(2))) - x(3);
  2071. xstart = [4, -2, 54];
  2072. fittedmodel = lsqcurvefit(myfun,xstart,double(pX),double(pY), [], [], optimset('Display', 'off'));
  2073. %FitError: mean squared error of the fit to the real spectrum in the band 2-40 Hz.
  2074. ts_8to15 = freq(find(freq == 8) : find(freq == 15));
  2075. fs_8to15 = pxx(find(freq == 8) : find(freq == 15));
  2076. fiterror = log(norm(myfun(fittedmodel, ts_8to15)-fs_8to15)^2);
  2077. %lambda: parameter of the fit
  2078. lambda = fittedmodel(2);
  2079. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2080. % Averaged local skewness 15s
  2081. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2082. interval = 15;
  2083. abs_local_scewness = [];
  2084. for i=1:interval:length(icacomps(ic,:))/fs-interval
  2085. abs_local_scewness = [abs_local_scewness, abs(skewness(icacomps(ic, i * fs:(i+interval) * fs)))];
  2086. end
  2087. if isempty(abs_local_scewness)
  2088. % error('MARA needs at least 15ms long ICs to compute its features.')
  2089. mean_abs_local_scewness_15 = nan;
  2090. else
  2091. mean_abs_local_scewness_15 = log(mean(abs_local_scewness));
  2092. end;
  2093. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2094. %Append Features
  2095. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2096. features(:,ic)= [mean_abs_local_scewness_15, lambda, Hz8_13, fiterror];
  2097. end
  2098. disp('.');
  2099. function [M100, idx_clab_desired] = get_M100_ADE(clab_desired)
  2100. % [M100, idx_clab_desired] = get_M100_ADEC(clab_desired)
  2101. %
  2102. % IN clab_desired - channel setup for which M100 should be calculated
  2103. % OUT M100
  2104. % idx_clab_desired
  2105. % M100 is the matrix such that feature = norm(M100*ica_pattern(idx_clab_desired), 'fro')
  2106. %
  2107. % (c) Stefan Haufe
  2108. lambda = 100;
  2109. load inv_matrix_icbm152; %L (forward matrix 115 x 2124 x 3), clab (channel labels)
  2110. [cl_ ia idx_clab_desired] = intersect(clab, clab_desired);
  2111. F = L(ia, :, :); %forward matrix for desired channels labels
  2112. [n_channels m foo] = size(F); %m = 2124, number of dipole locations
  2113. F = reshape(F, n_channels, 3*m);
  2114. %H - matrix that centralizes the pattern, i.e. mean(H*pattern) = 0
  2115. H = eye(n_channels) - ones(n_channels, n_channels)./ n_channels;
  2116. %W - inverse of the depth compensation matrix Lambda
  2117. W = sloreta_invweights(L);
  2118. L = H*F*W;
  2119. %We have inv(L'L +lambda eye(size(L'*L))* L' = L'*inv(L*L' + lambda
  2120. %eye(size(L*L')), which is easier to calculate as number of dimensions is
  2121. %much smaller
  2122. %calulate the inverse of L*L' + lambda * eye(size(L*L')
  2123. [U D] = eig(L*L');
  2124. d = diag(D);
  2125. di = d+lambda;
  2126. di = 1./di;
  2127. di(d < 1e-10) = 0;
  2128. inv1 = U*diag(di)*U'; %inv1 = inv(L*L' + lambda *eye(size(L*L'))
  2129. %get M100
  2130. M100 = L'*inv1*H;
  2131. function W = sloreta_invweights(LL)
  2132. % inverse sLORETA-based weighting
  2133. %
  2134. % Synopsis:
  2135. % W = sloreta_invweights(LL);
  2136. %
  2137. % Arguments:
  2138. % LL: [M N 3] leadfield tensor
  2139. %
  2140. % Returns:
  2141. % W: [3*N 3*N] block-diagonal matrix of weights
  2142. %
  2143. % Stefan Haufe, 2007, 2008
  2144. %
  2145. % License
  2146. %
  2147. % This program is free software: you can redistribute it and/or modify
  2148. % it under the terms of the GNU General Public License as published by
  2149. % the Free Software Foundation, either version 3 of the License, or
  2150. % (at your option) any later version.
  2151. %
  2152. % This program is distributed in the hope that it will be useful,
  2153. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  2154. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  2155. % GNU General Public License for more details.
  2156. %
  2157. % You should have received a copy of the GNU General Public License
  2158. % along with this program. If not, see http://www.gnu.org/licenses/.
  2159. [M N NDUM]=size(LL);
  2160. L=reshape(permute(LL, [1 3 2]), M, N*NDUM);
  2161. L = L - repmat(mean(L, 1), M, 1);
  2162. T = L'*pinv(L*L');
  2163. W = spalloc(N*NDUM, N*NDUM, N*NDUM*NDUM);
  2164. for ivox = 1:N
  2165. W(NDUM*(ivox-1)+(1:NDUM), NDUM*(ivox-1)+(1:NDUM)) = (T(NDUM*(ivox-1)+(1:NDUM), :)*L(:, NDUM*(ivox-1)+(1:NDUM)))^-.5;
  2166. end
  2167. ind = [];
  2168. for idum = 1:NDUM
  2169. ind = [ind idum:NDUM:N*NDUM];
  2170. end
  2171. W = W(ind, ind);
  2172. function [i_te, i_tr] = findconvertedlabels(pos_3d, chanlocs)
  2173. % IN pos_3d - 3d-positions of training channel labels
  2174. % chanlocs - EEG.chanlocs structure of data to be classified
  2175. %compute spherical coordinates theta and phi for the training channel
  2176. %label
  2177. [theta, phi, r] = cart2sph(pos_3d(1,:),pos_3d(2,:), pos_3d(3,:));
  2178. theta = theta - pi/2;
  2179. theta(theta < -pi) = theta(theta < -pi) + 2*pi;
  2180. theta = theta*180/pi;
  2181. phi = phi * 180/pi;
  2182. theta(find(pos_3d(1,:) == 0 & pos_3d(2,:) == 0)) = 0; %exception for Cz
  2183. clab_common = {};
  2184. i_te = [];
  2185. i_tr = [];
  2186. %For each channel in EEG.chanlocs, try to find matching channel in
  2187. %training data
  2188. for chan = 1:length(chanlocs)
  2189. if not(isempty(chanlocs(chan).sph_phi))
  2190. idx = find((theta <= chanlocs(chan).sph_theta + 6) ...
  2191. & (theta >= chanlocs(chan).sph_theta - 6) ...
  2192. & (phi <= chanlocs(chan).sph_phi + 6) ...
  2193. & (phi >= chanlocs(chan).sph_phi - 6));
  2194. if not(isempty(idx))
  2195. i_tr = [i_tr, idx(1)];
  2196. i_te = [i_te, chan];
  2197. end
  2198. end
  2199. end
  2200. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2201. %%%%%%%%%%%%%%%%%%%%%%% END MARA CODE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2202. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%