├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitmodules ├── ICL_feature_extractor.m ├── ICLabel_menu.png ├── README.md ├── Viewprops_eye.png ├── activate_matconvnet.m ├── eeg_autocorr.m ├── eeg_autocorr_fftw.m ├── eeg_autocorr_welch.m ├── eeg_icalabelstat.m ├── eeg_rpsd.m ├── eegplugin_iclabel.m ├── iclabel.m ├── netICL.mat ├── netICL_beta.mat ├── netICL_lite.mat ├── pop_icflag.m ├── pop_iclabel.m ├── run_ICL.m ├── run_tests.m ├── tests ├── eeglab_data.fdt └── eeglab_data.set ├── topoplotFast.m └── zipper.m /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Please, do not submit bugs here. 11 | 12 | For faster processing, please submit bugs at https://github.com/sccn/eeglab/issues 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "matconvnet"] 2 | path = matconvnet 3 | url = https://github.com/lucapton/matconvnet.git 4 | [submodule "viewprops"] 5 | path = viewprops 6 | url = https://github.com/lucapton/viewprops.git 7 | -------------------------------------------------------------------------------- /ICL_feature_extractor.m: -------------------------------------------------------------------------------- 1 | % extract features for the ICLabel Classifier 2 | % if there are any issues, report them to lpionton@ucsd.edu 3 | 4 | function features = ICL_feature_extractor(EEG, flag_autocorr) 5 | %% check inputs 6 | if ~exist('flag_autocorr', 'var') || isempty(flag_autocorr) 7 | flag_autocorr = false; 8 | end 9 | ncomp = size(EEG.icawinv, 2); 10 | 11 | % check for ica 12 | assert(isfield(EEG, 'icawinv'), 'You must have an ICA decomposition to use ICLabel') 13 | 14 | % assuming chanlocs are correct 15 | if ~isequal(EEG.ref, 'average') && ~isequal(EEG.ref, 'averef') 16 | [~, EEG] = evalc('pop_reref(EEG, [], ''exclude'', setdiff(1:EEG.nbchan, EEG.icachansind));'); 17 | end 18 | 19 | % calculate ica activations if missing and cast to double 20 | if isempty(EEG.icaact) 21 | EEG.icaact = eeg_getica(EEG); 22 | end 23 | EEG.icaact = double(EEG.icaact); 24 | 25 | % check ica is real 26 | assert(isreal(EEG.icaact), 'Your ICA decomposition must be real to use ICLabel') 27 | 28 | %% calc topo 29 | topo = zeros(32, 32, 1, ncomp); 30 | for it = 1:ncomp 31 | if ~exist('OCTAVE_VERSION', 'builtin') 32 | [~, temp_topo] = ... 33 | topoplotFast(EEG.icawinv(:, it), EEG.chanlocs(EEG.icachansind), ... 34 | 'noplot', 'on'); 35 | else 36 | [~, temp_topo] = ... 37 | topoplot(EEG.icawinv(:, it), EEG.chanlocs(EEG.icachansind), ... 38 | 'noplot', 'on', 'gridscale', 32); 39 | end 40 | temp_topo(isnan(temp_topo)) = 0; 41 | topo(:, :, 1, it) = temp_topo / max(abs(temp_topo(:))); 42 | end 43 | 44 | % cast 45 | topo = single(topo); 46 | 47 | %% calc psd 48 | psd = eeg_rpsd(EEG, 100); 49 | 50 | % extrapolate or prune as needed 51 | nfreq = size(psd, 2); 52 | if nfreq < 100 53 | psd = [psd, repmat(psd(:, end), 1, 100 - nfreq)]; 54 | end 55 | 56 | % undo notch filter 57 | for linenoise_ind = [50, 60] 58 | linenoise_around = [linenoise_ind - 1, linenoise_ind + 1]; 59 | difference = bsxfun(@minus, psd(:, linenoise_around), ... 60 | psd(:, linenoise_ind)); 61 | notch_ind = all(difference > 5, 2); 62 | if any(notch_ind) 63 | psd(notch_ind, linenoise_ind) = mean(psd(notch_ind, linenoise_around), 2); 64 | end 65 | end 66 | 67 | % normalize 68 | psd = bsxfun(@rdivide, psd, max(abs(psd), [], 2)); 69 | 70 | % reshape and cast 71 | psd = single(permute(psd, [3 2 4 1])); 72 | 73 | %% calc autocorrelation? 74 | if flag_autocorr 75 | if EEG.trials == 1 76 | if EEG.pnts / EEG.srate > 5 77 | autocorr = eeg_autocorr_welch(EEG); 78 | else 79 | autocorr = eeg_autocorr(EEG); 80 | end 81 | else 82 | autocorr = eeg_autocorr_fftw(EEG); 83 | end 84 | 85 | % reshape and cast 86 | autocorr = single(permute(autocorr, [3 2 4 1])); 87 | end 88 | 89 | %% format outputs 90 | if flag_autocorr 91 | features = {0.99 * topo, 0.99 * psd, 0.99 * autocorr}; 92 | else 93 | features = {0.99 * topo, 0.99 * psd}; 94 | end 95 | -------------------------------------------------------------------------------- /ICLabel_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sccn/ICLabel/9fdcb3b1428fb8753d32ce45da7c206955b379e0/ICLabel_menu.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ICLabel 2 | An automatic EEG independent component classifer plugin for EEGLAB. 3 | For more information, see [the ICLabel website tutorial](https://labeling.ucsd.edu/tutorial/about). 4 | 5 | ## Installation 6 | The easiest way to get the ICLabel plugin is through the EEGLAB plugin manager. 7 | 8 | If you plan to install the plugin through GitHub rather than the EEGLAB plugin manager, be aware that matconvnet is included as submodule. This means that it will no be included in the zip-file download. You will have to download [my fork (version) of matconvnet](https://github.com/lucapton/matconvnet) and extract that zip into the ICLabel folder. Alternatively, if you are cloning this repository through the command line, be sure to include the "--recusive" flag to clone submodules as well. Once you are in the desired directory, the correct command is: 9 | 10 | git clone --recursive https://github.com/lucapton/ICLabel.git 11 | 12 | ## Usage 13 | ### Graphical Usage 14 | ![menu](ICLabel_menu.png) 15 | Once you finish installing ICLabel, of if you already have it installed, you need to load your EEG dataset. To run ICLabel, your dataset must already have been decomposed using independent component analysis. 16 | 17 | With your dataset loaded, start ICLabel using the EEGLAB window by clicking on "Tools"->"ICLabel". You will see progress notes displayed on MATLAB's command window as ICLabel's pipeline progresses. When ICLabel finishes, it will display "Done" on MATLAB's command window and the Viewprops plug-in will open if available. If you do not have "Viewprops" installed, then nothing else will appear on the screen. 18 | 19 | ### Command-line Usage 20 | Assuming you have stored your ICA-decomposed EEG dataset in the variable EEG, you can use ICLabel by entering the following into MATLAB's command window: 21 | ``` 22 | EEG = iclabel(EEG) 23 | ``` 24 | ### Finding Results 25 | Either way you use ICLabel, from the EEGLAB window or MATLAB's command window, the IC classification information is saved to the EEG structure in the matrix: 26 | ``` 27 | EEG.etc.ic_classification.ICLabel.classifications 28 | ``` 29 | The labels are stored as a matrix in which each row is a label vector for the corresponding IC. A label vector is a row of seven numbers, summing to one, which represent the probabilities that an IC being in any of the seven ICLabel IC categories. For example, to find the label vector for the fifth IC, reference the fifth row of the matrix: 30 | ``` 31 | EEG.etc.ic_classification.ICLabel.classifications(5, :) 32 | ``` 33 | You can also find the class labels in the cell array of strings: 34 | ``` 35 | EEG.etc.ic_classification.ICLabel.classes 36 | ``` 37 | Each element of the cell array of strings indicates the category of the corresponding element in the label vectors. For example, to find the category of the third element in the label vector: 38 | ``` 39 | EEG.etc.ic_classification.ICLabel.classes{3} 40 | ``` 41 | You will find the category is "eye." 42 | ## Viewprops plug-in 43 | ![](Viewprops_eye.png) 44 | The ICLabel plugin offers no built-in plotting or visualization; therefore, it is highly suggested that you also install the [Viewprops plug-in](https://sccn.ucsd.edu/wiki/Viewprops). It will produce figures like the one shown at the top of this article. See the [Installation](https://sccn.ucsd.edu/wiki/ICLabel#Installation) section for directions on how to acquire the [Viewprops plug-in](https://sccn.ucsd.edu/wiki/Viewprops) and see [its wiki page](https://sccn.ucsd.edu/wiki/Viewprops) for information on how to use it. 45 | 46 | ### Run ICLabel in Fieldtrip 47 | 48 | First install EEGLAB (it includes the ICLabel plugin by default). Then use the script below. 49 | 50 | ```matlab 51 | cfg = []; 52 | cfg.datafile = '~/data/matlab/eeglab/sample_data/eeglab_data.set'; 53 | cfg.headerfile = '~/data/matlab/eeglab/sample_data/eeglab_data.set'; 54 | my_ft_data = ft_preprocessing(cfg); 55 | 56 | eeglab; close; % add paths to EEGLAB 57 | EEG = fieldtrip2eeglab(my_ft_data, my_ft_data.trial); 58 | EEG = eeg_checkset(EEG); 59 | EEG = pop_runica(EEG, 'icatype', 'runica'); 60 | EEG = pop_icflag(EEG, [0 0;0 0; 0.001 1; 0 0; 0 0; 0 0; 0 0]); % see function help message 61 | rejected_comps = find(EEG.reject.gcompreject > 0); 62 | EEG = pop_subcomp(EEG, rejected_comps); 63 | EEG = eeg_checkset(EEG); 64 | 65 | curPath = pwd; 66 | p = fileparts(which('ft_read_header')); 67 | cd(fullfile(p, 'private')); 68 | hdr = read_eeglabheader( EEG ); 69 | data = read_eeglabdata( EEG, 'header', hdr ); 70 | event = read_eeglabevent( EEG, 'header', hdr ); 71 | cd(curPath); 72 | ``` 73 | 74 | ## Version history 75 | Pending - new message if signal processing toolbox is absent (we judged a new release was not necessary for such a small change) 76 | 77 | 1.6 - fix issue with electrode orientation introduced in version 1.5 one month prior 78 | 79 | 1.5 - minor tweaks and better stability (see commit history) 80 | 81 | 1.4 - adding 'average' to the list of possible reference for EEGLAB compatibility 82 | 83 | 1.3 - make sure the classification probabilities are identical when processing multiple datasets with the same ICA decompositions 84 | 85 | 1.2.6 - fix issue in pop_iclabel.m for Matlab prior to 2016, fix rare path issue and issue with autocorrelation length 86 | 87 | 1.2.5 - fix issue when pressing cancel in pop_iclabel.m 88 | 89 | 1.2.4 - Forgot to include some dependencies in 1.2.3, adding them back and fix issue to view properties 90 | 91 | 1.2.3 - Fix bug for single dataset 92 | 93 | 1.2.2 - Fix STUDY calling format and add new function eeg_icalabelstat 94 | -------------------------------------------------------------------------------- /Viewprops_eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sccn/ICLabel/9fdcb3b1428fb8753d32ce45da7c206955b379e0/Viewprops_eye.png -------------------------------------------------------------------------------- /activate_matconvnet.m: -------------------------------------------------------------------------------- 1 | function pluginpath = activate_matconvnet() 2 | 3 | % get path information 4 | pluginpath = fileparts(which('pop_iclabel')); 5 | 6 | % activate matconvnet 7 | if ~exist(['vl_nnconv.', mexext()], 'file') 8 | addpath(fullfile(pluginpath, 'matconvnet', 'matlab')); 9 | vl_setupnn(); 10 | end 11 | -------------------------------------------------------------------------------- /eeg_autocorr.m: -------------------------------------------------------------------------------- 1 | function resamp = eeg_autocorr(EEG, pct_data) 2 | 3 | if ~exist('pct_data', 'var') || isempty(pct_data) 4 | pct_data = 100; 5 | end 6 | 7 | ncomp = size(EEG.icaact, 1); 8 | nfft = 2^nextpow2(2*EEG.pnts-1); 9 | 10 | % calc autocorrelation 11 | c = zeros(ncomp, nfft); 12 | for it = 1:ncomp 13 | X = mean(abs(fft(EEG.icaact(it, :), nfft, 2)).^2, 3); % Code not used here, since there is never a 3rd dim the mean has no effect 14 | c(it, :) = ifft(X, [], 2); 15 | end 16 | % X = fft(EEG.icaact, 2^nextpow2(2*EEG.pnts-1), 2); 17 | % c = ifft(mean(abs(X).^2, 3), [], 2); 18 | if EEG.pnts < EEG.srate 19 | ac = [c(:, 1:EEG.pnts, :) zeros(size(c, 1), EEG.srate - EEG.pnts + 1)]; 20 | else 21 | ac = c(:, 1:EEG.srate + 1, :); 22 | end 23 | 24 | % normalize by 0-tap autocorrelation 25 | ac = bsxfun(@rdivide, ac, ac(:, 1)); 26 | 27 | % resample to 1 second at 100 samples/sec 28 | resamp = resample(ac', 100, EEG.srate)'; 29 | resamp(:, 1) = []; -------------------------------------------------------------------------------- /eeg_autocorr_fftw.m: -------------------------------------------------------------------------------- 1 | function resamp = eeg_autocorr_fftw(EEG, pct_data) 2 | 3 | if exist('resample') ~= 2 4 | error('Signal Processing Toolbox absent: You need the signal processing toolbox to run ICLabel'); 5 | end 6 | 7 | if ~exist('pct_data', 'var') || isempty(pct_data) 8 | pct_data = 100; 9 | end 10 | 11 | nfft = 2^nextpow2(2*EEG.pnts-1); 12 | 13 | % calc autocorrelation 14 | fftw('planner', 'hybrid'); 15 | ac = zeros(size(EEG.icaact, 1), nfft); 16 | for it = 1:size(EEG.icaact, 1) 17 | X = fft(EEG.icaact(it, :, :), nfft, 2); 18 | ac(it, :) = mean(abs(X).^2, 3); 19 | end 20 | ac = ifft(ac, [], 2); 21 | if EEG.pnts < EEG.srate 22 | ac = [ac(:, 1:EEG.pnts, :) zeros(size(ac, 1), EEG.srate - EEG.pnts + 1)]; 23 | else 24 | ac = ac(:, 1:EEG.srate + 1, :); 25 | end 26 | 27 | % normalize by 0-tap autocorrelation 28 | ac = bsxfun(@rdivide, ac(:, 1:EEG.srate + 1, :), ac(:, 1)); 29 | 30 | % resample to 1 second at 100 samples/sec 31 | resamp = resample(ac', 100, EEG.srate)'; 32 | resamp(:, 1) = []; -------------------------------------------------------------------------------- /eeg_autocorr_welch.m: -------------------------------------------------------------------------------- 1 | function ac = eeg_autocorr_welch(EEG, pct_data) 2 | 3 | % clean input cutoff freq 4 | if ~exist('pct_data', 'var') || isempty(pct_data) 5 | pct_data = 100; 6 | end 7 | 8 | % setup constants 9 | ncomp = size(EEG.icaweights, 1); 10 | n_points = min(EEG.pnts, EEG.srate * 3); 11 | nfft = 2^nextpow2(n_points * 2 - 1); 12 | cutoff = floor(EEG.pnts / n_points) * n_points; 13 | index = bsxfun(@plus, ceil(0:n_points / 2:cutoff - n_points), (1:n_points)'); 14 | 15 | % separate data segments 16 | if pct_data ~=100 17 | rng(0) 18 | n_seg = size(index, 2) * EEG.trials; 19 | subset = randperm(n_seg, ceil(n_seg * pct_data / 100)); % need to find a better way to take subset 20 | rng('shuffle') % remove duplicate data first (from 50% overlap) 21 | temp = reshape(EEG.icaact(:, index, :), [ncomp size(index) .* [1 EEG.trials]]); 22 | segments = temp(:, :, subset); 23 | else 24 | segments = reshape(EEG.icaact(:, index, :), [ncomp size(index) .* [1 EEG.trials]]); 25 | end 26 | 27 | %% calc autocorrelation 28 | ac = zeros(ncomp, nfft); 29 | for it = 1:ncomp 30 | fftpow = mean(abs(fft(segments(it, :, :), nfft, 2)).^2, 3); 31 | ac(it, :) = ifft(fftpow, [], 2); 32 | end 33 | % fftpow = abs(fft(segments, nfft, 2)).^2; 34 | % ac = ifft(mean(fftpow, 3), [], 2); 35 | 36 | % normalize 37 | if EEG.pnts < EEG.srate 38 | ac = [ac(:, 1:EEG.pnts, :) ./ (ac(:, 1) * (n_points:-1:1) / (n_points)) ... 39 | zeros(ncomp, EEG.srate - n_points + 1)]; 40 | else 41 | ac = ac(:, 1:EEG.srate + 1, :) ./ ... 42 | (ac(:, 1) * [(n_points:-1:n_points - EEG.srate + 1) ... 43 | max(1, n_points - EEG.srate)] / (n_points)); 44 | end 45 | 46 | % resample to 1 second at 100 samples/sec 47 | if ~exist('OCTAVE_VERSION', 'builtin') && exist('resample') 48 | ac = resample(double(ac'), 100, EEG.srate)'; 49 | else 50 | ac = myresample(double(ac'), 100, EEG.srate)'; 51 | end 52 | ac = ac(:, 2:101); 53 | 54 | % resample if resample is not present 55 | % ----------------------------------- 56 | function tmpeeglab = myresample(data, p, q) 57 | 58 | % Default cutoff frequency (pi rad / smp) 59 | fc = 0.9; 60 | 61 | % Default transition band width (pi rad / smp) 62 | df = 0.2; 63 | 64 | % anti-alias filter 65 | % ----------------- 66 | % data = eegfiltfft(data', 256, 0, 128*pnts/new_pnts); % Downsample from 256 to 128 times the ratio of freq. 67 | % % Code was verified by Andreas Widdman March 2014 68 | 69 | % % No! Only cutoff frequency for downsampling was confirmed. 70 | % % Upsampling doesn't work and FFT filtering introduces artifacts. 71 | % % Also see bug 1757. Replaced May 05, 2015, AW 72 | 73 | if p < q, nyq = p / q; else nyq = q / p; end 74 | fc = fc * nyq; % Anti-aliasing filter cutoff frequency 75 | df = df * nyq; % Anti-aliasing filter transition band width 76 | m = pop_firwsord('kaiser', 2, df, 0.002); % Anti-aliasing filter kernel 77 | b = firws(m, fc, windows('kaiser', m + 1, 5)); % Anti-aliasing filter kernel 78 | % figure; freqz(b, 1, 2^14, 1000) % Debugging only! Sampling rate hardcoded as it is unknown in this context. Manually adjust for debugging! 79 | 80 | if p < q % Downsampling, anti-aliasing filter 81 | data = fir_filterdcpadded(b, 1, data, 0); 82 | end 83 | 84 | % spline interpolation 85 | % -------------------- 86 | % X = [1:length(data)]; 87 | % nbnewpoints = length(data)*p/q; 88 | % nbnewpoints2 = ceil(nbnewpoints); 89 | % lastpointval = length(data)/nbnewpoints*nbnewpoints2; 90 | % XX = linspace( 1, lastpointval, nbnewpoints2); 91 | 92 | % New time axis scaling, May 06, 2015, AW 93 | X = 0:length(data) - 1; 94 | newpnts = ceil(length(data) * p / q); 95 | XX = (0:newpnts - 1) / (p / q); 96 | 97 | cs = spline( X, data'); 98 | tmpeeglab = ppval(cs, XX)'; 99 | 100 | if p > q % Upsampling, anti-imaging filter 101 | tmpeeglab = fir_filterdcpadded(b, 1, tmpeeglab, 0); 102 | end 103 | -------------------------------------------------------------------------------- /eeg_icalabelstat.m: -------------------------------------------------------------------------------- 1 | % eeg_icalabelstat - show some stats about IC label 2 | % 3 | % Input: 4 | % EEG - EEGLAB dataset 5 | % threshold - threshold for belonging to a category (default 0.9 6 | % corresponding to 90% 7 | % 8 | % Author: A. Delorme 9 | 10 | function eeg_icalabelstat(EEG, threshold) 11 | 12 | if nargin < 1 13 | help eeg_icalabelstat; 14 | return; 15 | end 16 | if nargin < 2 17 | threshold = 0.9; 18 | end 19 | 20 | ics = EEG.etc.ic_classification.ICLabel; 21 | if length(threshold) == 1, threshold(1:length(ics.classes)) = threshold; end 22 | for iIC = 1:length(ics.classes) 23 | ICfound = find(ics.classifications(:,iIC) > threshold(iIC)); 24 | fprintf('%30s: %d/%d components at %d%% threshold\n', sprintf('IClabel class "%s"',ics.classes{iIC}), length(ICfound), size(ics.classifications,1), round(threshold(iIC)*100)); 25 | end -------------------------------------------------------------------------------- /eeg_rpsd.m: -------------------------------------------------------------------------------- 1 | function psdmed = eeg_rpsd(EEG, nfreqs, pct_data) 2 | 3 | % clean input cutoff freq 4 | nyquist = floor(EEG.srate / 2); 5 | if ~exist('nfreqs', 'var') || isempty(nfreqs) 6 | nfreqs = nyquist; 7 | elseif nfreqs > nyquist 8 | nfreqs = nyquist; 9 | end 10 | if ~exist('pct_data', 'var') || isempty(pct_data) 11 | pct_data = 100; 12 | end 13 | 14 | % setup constants 15 | ncomp = size(EEG.icaweights, 1); 16 | n_points = floor(min(EEG.pnts, EEG.srate)); 17 | try 18 | window = windows('hamming', n_points, 0.54)'; 19 | catch 20 | lasterr 21 | error('The windows function is in the firfilt plugin, make sure it is in the path') 22 | end 23 | cutoff = floor(EEG.pnts / n_points) * n_points; 24 | index = bsxfun(@plus, ceil(0:n_points / 2:cutoff - n_points), (1:n_points)'); 25 | if ~exist('OCTAVE_VERSION', 'builtin') 26 | rng('default') 27 | else 28 | rand('state', 0); 29 | end 30 | n_seg = size(index, 2) * EEG.trials; 31 | subset = randperm(n_seg, ceil(n_seg * pct_data / 100)); % need to improve this 32 | if exist('OCTAVE_VERSION', 'builtin') == 0 33 | rng('shuffle') 34 | end 35 | 36 | % calculate windowed spectrums 37 | psdmed = zeros(ncomp, nfreqs); 38 | for it = 1:ncomp 39 | temp = reshape(EEG.icaact(it, index, :), [1 size(index) .* [1 EEG.trials]]); 40 | temp = bsxfun(@times, temp(:, :, subset), window); 41 | temp = fft(temp, n_points, 2); 42 | temp = temp .* conj(temp); 43 | temp = temp(:, 2:nfreqs + 1, :) * 2 / (EEG.srate*sum(window.^2)); 44 | if nfreqs == nyquist 45 | temp(:, end, :) = temp(:, end, :) / 2; end 46 | 47 | psdmed(it, :) = 20 * log10(median(temp, 3)); 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /eegplugin_iclabel.m: -------------------------------------------------------------------------------- 1 | function vers = eegplugin_iclabel( fig, try_strings, catch_strings ) 2 | %EEGLABPLUGIN_ICLABEL EEGLAB plugin for EEG IC labeling 3 | % Label independent components using ICLabel. Go to 4 | % https://sccn.ucsd.edu/wiki/ICLabel for a tutorial on this plug-in. Go 5 | % to labeling.ucsd.edu/tutorial/about for more information. To report a 6 | % bug or issue, please create an "Issue" post on the GitHub page at 7 | % https://github.com/sccn/ICLabel/issues or send an email to 8 | % eeglab@sccn.ucsd.edu. 9 | % 10 | % Results are stored in EEG.etc.ic_classifications.ICLabel. The matrix of 11 | % label vectors is stored under "classifications" and the cell array of 12 | % class names are stored under "classes". The matrix stored under 13 | % "classifications" is organized with each column matching to the 14 | % equivalent element in "classes" and each row matching to the equivalent 15 | % IC. For example, if you want to see what percent ICLabel attributes IC 16 | % 7 to the class "eye", you would look at: 17 | % EEG.etc.ic_classifications.ICLabel.classifications(7, 3) 18 | % since EEG.etc.ic_classifications.ICLabel.classes{3} is "eye". 19 | 20 | % version 21 | vers = 'ICLabel1.6'; 22 | 23 | % input check 24 | if nargin < 3 25 | error('eegplugin_iclabel requires 3 arguments'); 26 | end 27 | 28 | % add items to EEGLAB tools menu 29 | plotmenu = findobj(fig, 'tag', 'tools'); 30 | 31 | iclabelmenu = uimenu( plotmenu, 'label', 'Classify components using ICLabel','userdata', 'startup:off;study:on;roi:off'); 32 | lightMenuFlag = isempty(findobj(fig, 'Label', 'Reject data epochs')); 33 | if lightMenuFlag, set(iclabelmenu, 'position', 9); else set(iclabelmenu, 'position', 12); end 34 | 35 | viewpropcom = [ 'try, pop_viewprops(EEG, 0); catch,' ... 36 | 'try, vp_path = fullfile(fileparts(which(''iclabel'')), ''viewprops'');' ... 37 | 'addpath(vp_path); if length(EEG) == 1, LASTCOM = pop_viewprops(EEG, 0); end;' ... 38 | 'disp(''ICLabel: Install the viewprops eeglab plugin to create IC visualizations like these elsewhere.''),' ... 39 | 'catch, end; end;']; 40 | 41 | if ~isfield(try_strings, 'check_ica_chanlocs') 42 | try_strings.check_ica_chanlocs = try_strings.check_ica; 43 | end 44 | uimenu( iclabelmenu, 'label', 'Label components', ... 45 | 'callback', [try_strings.check_ica_chanlocs ... 46 | '[EEG, LASTCOM] = pop_iclabel(EEG);' ... 47 | catch_strings.store_and_hist ... 48 | 'if ~isempty(LASTCOM),' ... 49 | viewpropcom ' end' ], 'userdata', 'startup:off;study:on'); 50 | 51 | uimenu( iclabelmenu, 'label', 'Flag components as artifacts', ... 52 | 'callback', [try_strings.check_ica_chanlocs '[EEG, LASTCOM] = pop_icflag(EEG);' catch_strings.store_and_hist ], 'userdata', 'startup:off;study:on'); 53 | 54 | uimenu( iclabelmenu, 'label', 'View extended component properties', ... 55 | 'callback', viewpropcom ); 56 | 57 | % activate matconvnet 58 | activate_matconvnet(); 59 | end 60 | 61 | -------------------------------------------------------------------------------- /iclabel.m: -------------------------------------------------------------------------------- 1 | function EEG = iclabel(EEG, version) 2 | %ICLABEL Function for EEG IC labeling 3 | % Label independent components using ICLabel. Go to 4 | % https://sccn.ucsd.edu/wiki/ICLabel for a tutorial on this plug-in. Go 5 | % to labeling.ucsd.edu/tutorial/about for more information. To report a 6 | % bug or issue, please create an "Issue" post on the GitHub page at 7 | % https://github.com/sccn/ICLabel/issues or send an email to 8 | % eeglab@sccn.ucsd.edu. 9 | % 10 | % Inputs: 11 | % EEG: EEGLAB EEG structure. Must have an attached ICA decomposition. 12 | % version (optional): Version of ICLabel to use. Default 13 | % (recommended) version is used if passed 'default', '', or left 14 | % empty. Pass 'lite' to use ICLabelLite or 'beta' to use the original 15 | % beta version of ICLabel (only recommended for replicating old 16 | % results). 17 | % 18 | % Results are stored in EEG.etc.ic_classifications.ICLabel. The matrix of 19 | % label vectors is stored under "classifications" and the cell array of 20 | % class names are stored under "classes". The version if ICLabel used is 21 | % stored under The matrix stored under "version". "classifications" is 22 | % organized with each column matching to the equivalent element in 23 | % "classes" and each row matching to the equivalent IC. For example, if 24 | % you want to see what percent ICLabel attributes IC 7 to the class 25 | % "eye", you would look at: 26 | % EEG.etc.ic_classifications.ICLabel.classifications(7, 3) 27 | % since EEG.etc.ic_classifications.ICLabel.classes{3} is "eye". 28 | 29 | 30 | % check inputs 31 | if ~exist('version', 'var') || isempty(version) 32 | version = 'default'; 33 | else 34 | version = lower(version); 35 | end 36 | assert(any(strcmp(version, {'default', 'lite', 'beta'})), ... 37 | ['Invalid network version choice. ' ... 38 | 'Version must be one of the following: ' ... 39 | '''default'', ''lite'', or ''beta''.']) 40 | if any(strcmpi(version, {'', 'default'})) 41 | flag_autocorr = true; 42 | else 43 | flag_autocorr = false; 44 | end 45 | 46 | % check for ica 47 | assert(isfield(EEG, 'icawinv') && ~isempty(EEG.icawinv), ... 48 | 'You must have an ICA decomposition to use ICLabel') 49 | 50 | if EEG.srate < 100 51 | error('ICLabel requires a sampling frequency of at least 100 Hz') 52 | end 53 | 54 | % extract features 55 | disp 'ICLabel: extracting features...' 56 | features = ICL_feature_extractor(EEG, flag_autocorr); 57 | 58 | % run ICL 59 | disp 'ICLabel: calculating labels...' 60 | labels = run_ICL(version, features{:}); 61 | 62 | % save into EEG 63 | disp 'ICLabel: saving results...' 64 | EEG.etc.ic_classification.ICLabel.classes = ... 65 | {'Brain', 'Muscle', 'Eye', 'Heart', ... 66 | 'Line Noise', 'Channel Noise', 'Other'}; 67 | EEG.etc.ic_classification.ICLabel.classifications = labels; 68 | EEG.etc.ic_classification.ICLabel.version = version; 69 | 70 | -------------------------------------------------------------------------------- /netICL.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sccn/ICLabel/9fdcb3b1428fb8753d32ce45da7c206955b379e0/netICL.mat -------------------------------------------------------------------------------- /netICL_beta.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sccn/ICLabel/9fdcb3b1428fb8753d32ce45da7c206955b379e0/netICL_beta.mat -------------------------------------------------------------------------------- /netICL_lite.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sccn/ICLabel/9fdcb3b1428fb8753d32ce45da7c206955b379e0/netICL_lite.mat -------------------------------------------------------------------------------- /pop_icflag.m: -------------------------------------------------------------------------------- 1 | % pop_icflag - Flag components as atifacts 2 | % 3 | % Usage: 4 | % EEG = pop_icflag(EEG, thresh) 5 | % 6 | % Input 7 | % EEG - EEGLAB dataset 8 | % thresh - [7x2 float] array with threshold values with limits to include 9 | % for selection as artifacts. The 6 categories are (in order) Brain, Muscle, 10 | % Eye, Heart, Line Noise, Channel Noise, Other. 11 | % 12 | % Example: 13 | % % the threshold below will only select component if they are in the 14 | % % eye category with at least 90% confidence 15 | % threshold = [0 0;0 0; 0.9 1; 0 0; 0 0; 0 0; 0 0]; 16 | % EEG = pop_icflag(EEG, threshold) 17 | % 18 | % % the threshold below will select component if they are in the brain 19 | % % category with less than 20% confidence 20 | % threshold = [0 0.2;0 0; 0 0; 0 0; 0 0; 0 0; 0 0]; 21 | % EEG = pop_icflag(EEG, threshold) 22 | % 23 | % Author: Arnaud Delorme 24 | 25 | function [EEG,com] = pop_icflag(EEG, thresh) 26 | 27 | com = ''; 28 | if nargin < 1 29 | help pop_icflag 30 | return 31 | end 32 | 33 | % check IC label has been done 34 | try 35 | classification = EEG(1).etc.ic_classification.ICLabel.classifications; 36 | if isempty(classification), err; end 37 | catch 38 | disp('No labeling found, use Tools > Classify components using ICLabel > Label components first') 39 | return; 40 | end 41 | 42 | if nargin < 2 43 | if length(EEG) == 1 44 | eeg_icalabelstat(EEG); 45 | end 46 | 47 | cat = { 'Brain' 'Muscle' 'Eye' 'Heart' 'Line Noise' 'Channel Noise' 'Other' }; 48 | defaultMin = { '' '0.9' '0.9' '' '' '' '' }; 49 | defaultMax = { '' '1' '1' '' '' '' '' }; 50 | 51 | geom = [2 0.4 0.4]; 52 | row = { { 'style' 'text' 'string' 'Probability range for ' } { 'style' 'edit' 'string' '' } { 'style' 'edit' 'string' '' } }; 53 | allRows = { { 'style' 'text' 'string' 'Select range for flagging component for rejection' 'fontweight' 'bold' } {} { 'style' 'text' 'string' 'Min' } { 'style' 'text' 'string' 'Max' } }; 54 | allGeom = { 1 geom }; 55 | for iCat = 1:length(cat) 56 | tmpRow = row; 57 | tmpRow{1}{end} = [ tmpRow{1}{end} '"' cat{iCat} '"' ]; 58 | tmpRow{2}{end} = defaultMin{iCat}; 59 | tmpRow{3}{end} = defaultMax{iCat}; 60 | allRows = { allRows{:} tmpRow{:} }; 61 | allGeom{end+1} = geom; 62 | end 63 | 64 | res = inputgui(allGeom, allRows); 65 | if isempty(res) 66 | com = ''; 67 | return 68 | end 69 | 70 | thresh = cellfun(@str2double, res, 'uniformoutput', false); 71 | thresh(cellfun(@isempty, thresh)) = { NaN }; 72 | thresh = [ thresh{:} ]; 73 | thresh = reshape(thresh, 2, 7)'; 74 | end 75 | 76 | if length(EEG) > 1 77 | [ EEG, com ] = eeg_eval( 'pop_icflag', EEG, 'params', { thresh } ); 78 | else 79 | % perform rejection 80 | flagReject = zeros(1,size(EEG.icaweights,1))'; 81 | for iCat = 1:7 82 | tmpReject = EEG.etc.ic_classification.ICLabel.classifications(:,iCat) > thresh(iCat,1) & EEG.etc.ic_classification.ICLabel.classifications(:,iCat) < thresh(iCat,2); 83 | flagReject = flagReject | tmpReject; 84 | end 85 | EEG.reject.gcompreject = flagReject; 86 | fprintf('%d components flagged for rejection, to reject them use Tools > Remove components from data\n', sum(flagReject)); 87 | com = sprintf('EEG = pop_icflag(EEG, %s);',vararg2str(thresh)); 88 | end 89 | 90 | -------------------------------------------------------------------------------- /pop_iclabel.m: -------------------------------------------------------------------------------- 1 | function [EEG, varargout] = pop_iclabel(EEG, icversion) 2 | %POP_ICLABEL Function for EEG IC labeling 3 | % Label independent components using ICLabel. Go to 4 | % https://sccn.ucsd.edu/wiki/ICLabel for a tutorial on this plug-in. Go 5 | % to labeling.ucsd.edu/tutorial/about for more information. This is a 6 | % beta version and results may change in the near future. For direct 7 | % usage of ICLabel in scripts and functions, the function "iclabel" is 8 | % suggested. To report a bug or issue, please create an "Issue" post on 9 | % the GitHub page at https://github.com/sccn/ICLabel/issues or send an 10 | % email to eeglab@sccn.ucsd.edu. 11 | % 12 | % Inputs 13 | % EEG: EEGLAB EEG structure. Must have an attached ICA decomposition. 14 | % version: Which version of the ICLabel classifier to use. If not 15 | % provided, 'Default' is used. The three ` choices are: 16 | % 'Default' - the full classifier validated in the accompanying 17 | % publications 18 | % 'Lite' - the lite version of the classifier which excludes 19 | % autocorrelation as a feature for performance reasons. 20 | % 'Beta' - included only to maintain the repoducibility of any 21 | % studies which may previously have used it. 22 | % 23 | % Results are stored in EEG.etc.ic_classifications.ICLabel. The matrix of 24 | % label vectors is stored under "classifications" and the cell array of 25 | % class names are stored under "classes". The matrix stored under 26 | % "classifications" is organized with each column matching to the 27 | % equivalent element in "classes" and each row matching to the equivalent 28 | % IC. For example, if you want to see what percent ICLabel attributes IC 29 | % 7 to the class "eye", you would look at: 30 | % EEG.etc.ic_classifications.ICLabel.classifications(7, 3) 31 | % since EEG.etc.ic_classifications.ICLabel.classes{3} is "eye" 32 | 33 | if ~exist('icversion', 'var') 34 | try 35 | [~, icversion] = evalc(['inputdlg3(' ... 36 | '''prompt'', {''Select which icversion of ICLabel to use:'', ''Default (recommended)|Lite|Beta''},' ... 37 | '''style'', {''text'', ''popupmenu''},' ... 38 | '''default'', {[], 1},' ... 39 | '''tag'', {'''', [''"Default" and "Lite" are validated in the ''' ... 40 | '''ICLabel publication. Beta is included ''' ... 41 | '''only to maintain the repoducibility of any studies ''' ... 42 | '''which may have used it'']},' ... 43 | '''title'', ''ICLabel'');']); 44 | % The above code is equivalent to the block below except it successfully 45 | % supresses an uneccessary warning from supergui. 46 | % icversion = inputdlg3( ... 47 | % 'prompt', {'Select which icversion of ICLabel to use:', 'Default (recommended)|Lite|Beta'}, ... 48 | % 'style', {'text', 'popupmenu'}, ... 49 | % 'default', {[], 1}, ... 50 | % 'tag', {'', ['"Default" and "Lite" are validated in the ' ... 51 | % 'ICLabel publication. Beta is included ' ... 52 | % 'only to maintain the repoducibility of any studies ' ... 53 | % 'which may have used it']}, ... 54 | % 'title', 'ICLabel'); 55 | icversion = icversion{2}; 56 | catch 57 | icversion = 0; 58 | end 59 | 60 | switch icversion 61 | case 0 62 | varargout = { [] }; 63 | return 64 | case 1 65 | icversion = 'default'; 66 | case 2 67 | icversion = 'lite'; 68 | case 3 69 | icversion = 'beta'; 70 | end 71 | 72 | end 73 | 74 | if length(EEG) > 1 75 | [EEG,com] = eeg_eval( 'iclabel', EEG, 'params', { icversion } ); 76 | 77 | disp('*****************************************************'); 78 | disp('Scanning datasets which have common ICA decomposition'); 79 | disp('and averaging component classification probabilities'); 80 | disp('*****************************************************'); 81 | if ~isempty(com) 82 | sameICA = std_findsameica(EEG); 83 | if any(cellfun(@length, sameICA) > 1) 84 | for iSame = 1:length(sameICA) 85 | if ~isempty(sameICA{iSame}) 86 | % average matrix 87 | icMatrix = zeros(size(EEG(sameICA{iSame}(1)).etc.ic_classification.ICLabel.classifications), 'single'); 88 | for iDat = 1:length(sameICA{iSame}) 89 | icMatrix = icMatrix + EEG(sameICA{iSame}(iDat)).etc.ic_classification.ICLabel.classifications/length(sameICA{iSame}); 90 | end 91 | % copy average matrix to datasets 92 | for iDat = 1:length(sameICA{iSame}) 93 | EEG(sameICA{iSame}(iDat)).etc.ic_classification.ICLabel.classifications = icMatrix; 94 | end 95 | end 96 | end 97 | 98 | % resave datasets if needed 99 | for iDat = 1:length(EEG) 100 | EEG(iDat).saved = 'no'; 101 | end 102 | eeglab_options; 103 | if option_storedisk 104 | EEG = pop_saveset(EEG, 'savemode', 'resave'); 105 | end 106 | end 107 | end 108 | else 109 | EEG = iclabel(EEG, icversion); 110 | end 111 | varargout = {['EEG = pop_iclabel(EEG, ''' icversion ''');']}; 112 | 113 | % % visualize with viewprops 114 | % try 115 | % pop_viewprops(EEG, 0); 116 | % catch 117 | % try 118 | % addpath(fullfile(fileparts(which('iclabel')), 'viewprops')) 119 | % vp_com = pop_viewprops(EEG, 0); 120 | % catch 121 | % disp('ICLabel: Install the viewprops eeglab plugin to see IC label visualizations.') 122 | % end 123 | % end 124 | % 125 | 126 | % inputdlg3() - A comprehensive gui automatic builder. This function takes 127 | % text, type of GUI and default value and builds 128 | % automatically a simple graphic interface. 129 | % 130 | % Usage: 131 | % >> [outparam outstruct] = inputdlg3( 'key1', 'val1', 'key2', 'val2', ... ); 132 | % 133 | % Inputs: 134 | % 'prompt' - cell array of text 135 | % 'style' - cell array of style for each GUI. Default is edit. 136 | % 'default' - cell array of default values. Default is empty. 137 | % 'tags' - cell array of tag text. Default is no tags. 138 | % 'tooltip' - cell array of tooltip texts. Default is no tooltip. 139 | % 140 | % Output: 141 | % outparam - list of outputs. The function scans all lines and 142 | % add up an output for each interactive uicontrol, i.e 143 | % edit box, radio button, checkbox and listbox. 144 | % userdat - 'userdata' value of the figure. 145 | % strhalt - the function returns when the 'userdata' field of the 146 | % button with the tag 'ok' is modified. This returns the 147 | % new value of this field. 148 | % outstruct - returns outputs as a structure (only tagged ui controls 149 | % are considered). The field name of the structure is 150 | % the tag of the ui and contain the ui value or string. 151 | % 152 | % Note: the function also adds three buttons at the bottom of each 153 | % interactive windows: 'CANCEL', 'HELP' (if callback command 154 | % is provided) and 'OK'. 155 | % 156 | % Example: 157 | % res = inputdlg3('prompt', { 'What is your name' 'What is your age' } ); 158 | % res = inputdlg3('prompt', { 'Chose a value below' 'Value1|value2|value3' ... 159 | % 'uncheck the box' }, ... 160 | % 'style', { 'text' 'popupmenu' 'checkbox' }, ... 161 | % 'default',{ 0 2 1 }); 162 | % 163 | % Author: Arnaud Delorme, Tim Mullen, Christian Kothe, SCCN, INC, UCSD 164 | % 165 | % See also: supergui(), eeglab() 166 | 167 | % Copyright (C) Arnaud Delorme, SCCN, INC, UCSD, 2010, arno@ucsd.edu 168 | % 169 | % This program is free software; you can redistribute it and/or modify 170 | % it under the terms of the GNU General Public License as published by 171 | % the Free Software Foundation; either icversion 2 of the License, or 172 | % (at your option) any later icversion. 173 | % 174 | % This program is distributed in the hope that it will be useful, 175 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 176 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 177 | % GNU General Public License for more details. 178 | % 179 | % You should have received a copy of the GNU General Public License 180 | % along with this program; if not, write to the Free Software 181 | % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 182 | 183 | function [result, userdat, strhalt, resstruct] = inputdlg3( varargin) 184 | 185 | if nargin < 2 186 | help inputdlg3; 187 | return; 188 | end; 189 | 190 | % check input values 191 | % ------------------ 192 | [opt addopts] = finputcheck(varargin, { 'prompt' 'cell' [] {}; 193 | 'style' 'cell' [] {}; 194 | 'default' 'cell' [] {}; 195 | 'tag' 'cell' [] {}; 196 | 'tooltip','cell' [] {}}, 'inputdlg3', 'ignore'); 197 | if isempty(opt.prompt), error('The ''prompt'' parameter must be non empty'); end; 198 | if isempty(opt.style), opt.style = cell(1,length(opt.prompt)); opt.style(:) = {'edit'}; end; 199 | if isempty(opt.default), opt.default = cell(1,length(opt.prompt)); opt.default(:) = {0}; end; 200 | if isempty(opt.tag), opt.tag = cell(1,length(opt.prompt)); opt.tag(:) = {''}; end; 201 | 202 | % creating GUI list input 203 | % ----------------------- 204 | uilist = {}; 205 | uigeometry = {}; 206 | outputind = ones(1,length(opt.prompt)); 207 | for index = 1:length(opt.prompt) 208 | if strcmpi(opt.style{index}, 'edit') 209 | uilist{end+1} = { 'style' 'text' 'string' opt.prompt{index} }; 210 | uilist{end+1} = { 'style' 'edit' 'string' opt.default{index} 'tag' opt.tag{index} 'tooltip' opt.tag{index}}; 211 | uigeometry{index} = [2 1]; 212 | else 213 | uilist{end+1} = { 'style' opt.style{index} 'string' opt.prompt{index} 'value' opt.default{index} 'tag' opt.tag{index} 'tooltip' opt.tag{index}}; 214 | uigeometry{index} = [1]; 215 | end; 216 | if strcmpi(opt.style{index}, 'text') 217 | outputind(index) = 0; 218 | end; 219 | end; 220 | 221 | w = warning('off', 'MATLAB:namelengthmaxexceeded'); 222 | [tmpresult, userdat, strhalt, resstruct] = inputgui('uilist', uilist,'geometry', uigeometry, addopts{:}); 223 | warning(w.state, 'MATLAB:namelengthmaxexceeded') % warning suppression added by luca 224 | result = cell(1,length(opt.prompt)); 225 | result(find(outputind)) = tmpresult; 226 | -------------------------------------------------------------------------------- /run_ICL.m: -------------------------------------------------------------------------------- 1 | function labels = run_ICL(version, images, psds, autocorrs) 2 | %% check inputs 3 | if any(strcmpi(version, {[], 'default'})) 4 | version = ''; 5 | elseif any(strcmpi(version, 'lite')) 6 | version = '_lite'; 7 | elseif any(strcmpi(version, 'beta')) 8 | version = '_beta'; 9 | else 10 | error(['Invalid network version choice. '... 11 | 'Must be one of the following: ' ... 12 | '''default'' (alternatively ''[] or ''''), '... 13 | '''lite'', or ''beta''.']) 14 | end 15 | 16 | if ~exist('autocorrs', 'var') || isempty(autocorrs) 17 | flag_autocorr = false; 18 | else 19 | flag_autocorr = true; 20 | end 21 | 22 | %% get path information and activate matconvnet 23 | pluginpath = activate_matconvnet(); 24 | 25 | %% load network 26 | netStruct = load(fullfile(pluginpath, ['netICL' version])); 27 | try 28 | net = dagnn.DagNN.loadobj(netStruct); 29 | catch 30 | net = dagnn_bc.DagNN.loadobj(netStruct); 31 | end 32 | clear netStruct; 33 | 34 | %% format network inputs 35 | images = cat(4, images, -images, images(:, end:-1:1, :, :), -images(:, end:-1:1, :, :)); 36 | psds = repmat(psds, [1 1 1 4]); 37 | input = { 38 | 'in_image', single(images), ... 39 | 'in_psdmed', single(psds) 40 | }; 41 | if flag_autocorr 42 | autocorrs = repmat(autocorrs, [1 1 1 4]); 43 | input = [input {'in_autocorr', single(autocorrs)}]; 44 | end 45 | 46 | % check path (sometimes mex file not first which create a problem) 47 | path2vl_nnconv = which('-all', 'vl_nnconv'); 48 | if ~ischar(path2vl_nnconv) 49 | if ~isempty(path2vl_nnconv) && isempty(findstr('mex', path2vl_nnconv{1})) && length(path2vl_nnconv) > 1 50 | addpath(fileparts(path2vl_nnconv{2})); 51 | end 52 | end 53 | 54 | %% inference 55 | try 56 | % run with mex-files 57 | net.eval(input); 58 | catch 59 | % failed, try to recompile mex-files 60 | disp 'Failed to run ICLabel. Trying to compile MEX-files.' 61 | curr_path = pwd; 62 | cd(fileparts(which('vl_compilenn'))); 63 | try 64 | vl_compilenn 65 | cd(curr_path) 66 | disp(['MEX-files successfully compiled. Attempting to run ICLabel again. ' ... 67 | 'Please consider emailing Luca Pion-Tonachini at lpionton@ucsd.edu to ' ... 68 | 'share the compiled MEX-files. They will likely help other EEGLAB users ' ... 69 | 'with similar computers as yourself.']) 70 | net.eval(input); 71 | catch 72 | % could not recompile. running natively 73 | % ~80x slower than using mex-files 74 | cd(curr_path) 75 | disp(['MEX-file compilation failed. Further instructions on compiling ' ... 76 | 'the MEX-files can be found at http://www.vlfeat.org/matconvnet/install/. ' ... 77 | 'Further, you may contact Luca Pion-Tonachini at lpionton@ucsd.edu for help. ' ... 78 | 'If you solve this issue without help, please consider emailing Luca as the ' ... 79 | 'compiled files will likely be useful to other EEGLAB users with similar ' ... 80 | 'computers as yourself.']) 81 | warning('ICLabel: defaulting to uncompiled matlab code (about 80x slower)') 82 | net = uncompiled_network_evaluation(net, input); 83 | end 84 | end 85 | 86 | %% extract result 87 | labels = squeeze(net.getVar(net.getOutputs()).value)'; 88 | labels = reshape(mean(reshape(labels', [], 4), 2), 7, [])'; 89 | -------------------------------------------------------------------------------- /run_tests.m: -------------------------------------------------------------------------------- 1 | % test the ICLabel EEGLAB plugin 2 | function run_tests() 3 | 4 | %% set up 5 | % load test data 6 | EEG = pop_loadset('tests/eeglab_data.set'); 7 | 8 | % calculate sphereing matrix 9 | try 10 | icasphere = pca(EEG.data')'; 11 | catch 12 | % BCILAB (unfortunately) shadows pca from the stats toolbox. 13 | flist = which('pca.m', '-all'); 14 | fpath = flist{find(strncmp(flist, matlabroot, length(matlabroot)), 1)}; 15 | cwd = pwd; 16 | cd(fileparts(fpath)) 17 | icasphere = pca(EEG.data')'; 18 | cd(cwd) 19 | end 20 | 21 | % make sample ica matrix and test legacy rng 22 | w = warning; 23 | warning('off', 'MATLAB:RandStream:ActivatingLegacyGenerators') 24 | rand('state', 11) 25 | warning(w); 26 | icaweights = randn(size(EEG.data, 1)); 27 | icaweights = bsxfun(@rdivide, icaweights, sqrt(sum(icaweights.^2))); 28 | 29 | %% begin testing: first unepoched then epoched 30 | 31 | for it = 1:2 32 | 33 | % epoch on second run through 34 | if it == 2 35 | EEG = pop_epoch(EEG, {'square'}, [-1 1]); 36 | end 37 | 38 | %% test complete ICA with data as singles 39 | 40 | EEG_temp = setup(EEG, icasphere, icaweights, 1:32); 41 | EEG_temp.data = single(EEG_temp.data); 42 | EEG_temp.icaact = single(EEG_temp.icaact); 43 | 44 | test_iclabel(EEG_temp) 45 | 46 | 47 | %% test ICA with channels removed (A) 48 | 49 | remove = [5 10 15 20 25 30]; 50 | icachansind = setdiff(1:32, remove); 51 | icasphere_temp = icasphere(icachansind, icachansind); 52 | icaweights_temp = icaweights(icachansind, icachansind); 53 | EEG_temp = setup(EEG, icasphere_temp, icaweights_temp, icachansind); 54 | 55 | test_iclabel(EEG_temp) 56 | 57 | 58 | %% test ICA with components removed (B) 59 | 60 | EEG_temp = setup(EEG, icasphere, icaweights, 1:32); 61 | remove = [5 10 15 20 25 30]; 62 | EEG_temp = pop_subcomp(EEG_temp, remove); 63 | EEG_temp.icaact = eeg_getica(EEG_temp); 64 | 65 | test_iclabel(EEG_temp) 66 | 67 | 68 | %% test PCA-reduced ICA (C) 69 | 70 | EEG_temp = setup(EEG, icasphere(1:20, :), icaweights(1:20, 1:20), 1:32); 71 | 72 | test_iclabel(EEG_temp) 73 | 74 | 75 | %% test ABC 76 | 77 | remove = [5 10 15 20 25 30]; 78 | icachansind = setdiff(1:32, remove); 79 | pca_subset = 1:20; 80 | icasphere_temp = icasphere(pca_subset, icachansind); 81 | icaweights_temp = icaweights(pca_subset, pca_subset); 82 | EEG_temp = setup(EEG, icasphere_temp, icaweights_temp, icachansind); 83 | remove = [5 10 15]; 84 | EEG_temp = pop_subcomp(EEG_temp, remove); 85 | EEG_temp.icaact = eeg_getica(EEG_temp); 86 | 87 | test_iclabel(EEG_temp) 88 | 89 | 90 | %% test ABC also missing IC activations (D) 91 | 92 | EEG_temp.icaact = []; 93 | 94 | test_iclabel(EEG_temp) 95 | 96 | 97 | end 98 | 99 | 100 | % testing for a single run of iclabel 101 | function test_iclabel(EEG) 102 | 103 | nic = size(EEG.icaweights, 1); 104 | 105 | % test all three versions 106 | for version = {'default', 'lite', 'beta'} 107 | out = pop_iclabel(EEG, version); 108 | assert(all(size(out.etc.ic_classification.ICLabel.classifications) == [nic, 7])) 109 | assert(strcmp(out.etc.ic_classification.ICLabel.version, version)) 110 | end 111 | 112 | 113 | % prepare a dataset for testing 114 | function EEG = setup(EEG, icasphere, icaweights, icachansind) 115 | EEG.icasphere = icasphere; 116 | EEG.icaweights = icaweights; 117 | EEG.icawinv = pinv(EEG.icaweights * EEG.icasphere); 118 | EEG.icachansind = icachansind; 119 | EEG.icaact = eeg_getica(EEG); 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /tests/eeglab_data.fdt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sccn/ICLabel/9fdcb3b1428fb8753d32ce45da7c206955b379e0/tests/eeglab_data.fdt -------------------------------------------------------------------------------- /tests/eeglab_data.set: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sccn/ICLabel/9fdcb3b1428fb8753d32ce45da7c206955b379e0/tests/eeglab_data.set -------------------------------------------------------------------------------- /topoplotFast.m: -------------------------------------------------------------------------------- 1 | % topoplot() - plot a topographic map of a scalp data field in a 2-D circular view 2 | % (looking down at the top of the head) using interpolation on a fine 3 | % cartesian grid. Can also show specified channnel location(s), or return 4 | % an interpolated value at an arbitrary scalp location (see 'noplot'). 5 | % By default, channel locations below head center (arc_length 0.5) are 6 | % shown in a 'skirt' outside the cartoon head (see 'plotrad' and 'headrad' 7 | % options below). Nose is at top of plot; left is left; right is right. 8 | % Using option 'plotgrid', the plot may be one or more rectangular grids. 9 | % Usage: 10 | % >> topoplot(datavector, EEG.chanlocs); % plot a map using an EEG chanlocs structure 11 | % >> topoplot(datavector, 'my_chan.locs'); % read a channel locations file and plot a map 12 | % >> topoplot('example'); % give an example of an electrode location file 13 | % >> [h grid_or_val plotrad_or_grid, xmesh, ymesh]= ... 14 | % topoplot(datavector, chan_locs, 'Input1','Value1', ...); 15 | % Required Inputs: 16 | % datavector - single vector of channel values. Else, if a vector of selected subset 17 | % (int) channel numbers -> mark their location(s) using 'style' 'blank'. 18 | % chan_locs - name of an EEG electrode position file (>> topoplot example). 19 | % Else, an EEG.chanlocs structure (>> help readlocs or >> topoplot example) 20 | % Optional inputs: 21 | % 'maplimits' - 'absmax' -> scale map colors to +/- the absolute-max (makes green 0); 22 | % 'maxmin' -> scale colors to the data range (makes green mid-range); 23 | % [lo.hi] -> use user-definined lo/hi limits 24 | % {default: 'absmax'} 25 | % 'style' - 'map' -> plot colored map only 26 | % 'contour' -> plot contour lines only 27 | % 'both' -> plot both colored map and contour lines 28 | % 'fill' -> plot constant color between contour lines 29 | % 'blank' -> plot electrode locations only {default: 'both'} 30 | % 'electrodes' - 'on','off','labels','numbers','ptslabels','ptsnumbers'. To set the 'pts' 31 | % marker,,see 'Plot detail options' below. {default: 'on' -> mark electrode 32 | % locations with points ('.') unless more than 64 channels, then 'off'}. 33 | % 'plotchans' - [vector] channel numbers (indices) to use in making the head plot. 34 | % {default: [] -> plot all chans} 35 | % 'chantype' - cell array of channel type(s) to plot. Will also accept a single quoted 36 | % string type. Channel type for channel k is field EEG.chanlocs(k).type. 37 | % If present, overrides 'plotchans' and also 'chaninfo' with field 38 | % 'chantype'. Ex. 'EEG' or {'EEG','EOG'} {default: all, or 'plotchans' arg} 39 | % 'plotgrid' - [channels] Plot channel data in one or more rectangular grids, as 40 | % specified by [channels], a position matrix of channel numbers defining 41 | % the topographic locations of the channels in the grid. Zero values are 42 | % given the figure background color; negative integers, the color of the 43 | % polarity-reversed channel values. Ex: >> figure; ... 44 | % >> topoplot(values,'chanlocs','plotgrid',[11 12 0; 13 14 15]); 45 | % % Plot a (2,3) grid of data values from channels 11-15 with one empty 46 | % grid cell (top right) {default: no grid plot} 47 | % 'nosedir' - ['+X'|'-X'|'+Y'|'-Y'] direction of nose {default: '+X'} 48 | % 'chaninfo' - [struct] optional structure containing fields 'nosedir', 'plotrad' 49 | % and/or 'chantype'. See these (separate) field definitions above, below. 50 | % {default: nosedir +X, plotrad 0.5, all channels} 51 | % 'plotrad' - [0.15<=float<=1.0] plotting radius = max channel arc_length to plot. 52 | % See >> topoplot example. If plotrad > 0.5, chans with arc_length > 0.5 53 | % (i.e. below ears-eyes) are plotted in a circular 'skirt' outside the 54 | % cartoon head. See 'intrad' below. {default: max(max(chanlocs.radius),0.5); 55 | % If the chanlocs structure includes a field chanlocs.plotrad, its value 56 | % is used by default}. 57 | % 'headrad' - [0.15<=float<=1.0] drawing radius (arc_length) for the cartoon head. 58 | % NOTE: Only headrad = 0.5 is anatomically correct! 0 -> don't draw head; 59 | % 'rim' -> show cartoon head at outer edge of the plot {default: 0.5} 60 | % 'intrad' - [0.15<=float<=1.0] radius of the scalp map interpolation area (square or 61 | % disk, see 'intsquare' below). Interpolate electrodes in this area and use 62 | % this limit to define boundaries of the scalp map interpolated data matrix 63 | % {default: max channel location radius} 64 | % 'intsquare' - ['on'|'off'] 'on' -> Interpolate values at electrodes located in the whole 65 | % square containing the (radius intrad) interpolation disk; 'off' -> Interpolate 66 | % values from electrodes shown in the interpolation disk only {default: 'on'}. 67 | % 'conv' - ['on'|'off'] Show map interpolation only out to the convext hull of 68 | % the electrode locations to minimize extrapolation. {default: 'off'} 69 | % 'noplot' - ['on'|'off'|[rad theta]] do not plot (but return interpolated data). 70 | % Else, if [rad theta] are coordinates of a (possibly missing) channel, 71 | % returns interpolated value for channel location. For more info, 72 | % see >> topoplot 'example' {default: 'off'} 73 | % 'verbose' - ['on'|'off'] comment on operations on command line {default: 'on'}. 74 | % 75 | % Plot detail options: 76 | % 'drawaxis' - ['on'|'off'] draw axis on the top left corner. 77 | % 'emarker' - Matlab marker char | {markerchar color size linewidth} char, else cell array 78 | % specifying the electrode 'pts' marker. Ex: {'s','r',32,1} -> 32-point solid 79 | % red square. {default: {'.','k',[],1} where marker size ([]) depends on the number 80 | % of channels plotted}. 81 | % 'emarker2' - {markchans}|{markchans marker color size linewidth} cell array specifying 82 | % an alternate marker for specified 'plotchans'. Ex: {[3 17],'s','g'} 83 | % {default: none, or if {markchans} only are specified, then {markchans,'o','r',10,1}} 84 | % 'hcolor' - color of the cartoon head. Use 'hcolor','none' to plot no head. {default: 'k' = black} 85 | % 'shading' - 'flat','interp' {default: 'flat'} 86 | % 'numcontour' - number of contour lines {default: 6} 87 | % 'contourvals' - values for contour {default: same as input values} 88 | % 'pmask' - values for masking topoplot. Array of zeros and 1 of the same size as the input 89 | % value array {default: []} 90 | % 'color' - color of the contours {default: dark grey} 91 | % 'whitebk ' - ('on'|'off') make the background color white (e.g., to print empty plotgrid channels) 92 | % {default: 'off'} 93 | % 'gridscale' - [int > 32] size (nrows) of interpolated scalp map data matrix {default: 67} 94 | % 'colormap' - (n,3) any size colormap {default: existing colormap} 95 | % 'circgrid' - [int > 100] number of elements (angles) in head and border circles {201} 96 | % 97 | % Dipole plotting options: 98 | % 'dipole' - [xi yi xe ye ze] plot dipole on the top of the scalp map 99 | % from coordinate (xi,yi) to coordinates (xe,ye,ze) (dipole head 100 | % model has radius 1). If several rows, plot one dipole per row. 101 | % Coordinates returned by dipplot() may be used. Can accept 102 | % an EEG.dipfit.model structure (See >> help dipplot). 103 | % Ex: ,'dipole',EEG.dipfit.model(17) % Plot dipole(s) for comp. 17. 104 | % 'dipnorm' - ['on'|'off'] normalize dipole length {default: 'on'}. 105 | % 'diporient' - [-1|1] invert dipole orientation {default: 1}. 106 | % 'diplen' - [real] scale dipole length {default: 1}. 107 | % 'dipscale' - [real] scale dipole size {default: 1}. 108 | % 'dipsphere' - [real] size of the dipole sphere. {default: 85 mm}. 109 | % 'dipcolor' - [color] dipole color as Matlab code code or [r g b] vector 110 | % {default: 'k' = black}. 111 | % Outputs: 112 | % h - handle of the colored surface. If no surface is plotted, 113 | % return "gca", the handle of the current plot. 114 | % grid_or_val - [matrix] the interpolated data image (with off-head points = NaN). 115 | % Else, single interpolated value at the specified 'noplot' arg channel 116 | % location ([rad theta]), if any. 117 | % plotrad_or_grid - IF grid image returned above, then the 'plotrad' radius of the grid. 118 | % Else, the grid image 119 | % xmesh, ymesh - x and y values of the returned grid (above) 120 | % 121 | % Chan_locs format: 122 | % See >> topoplot 'example' 123 | % 124 | % Examples: 125 | % 126 | % To plot channel locations only: 127 | % >> figure; topoplot([],EEG.chanlocs,'style','blank','electrodes','labelpoint','chaninfo',EEG.chaninfo); 128 | % 129 | % Notes: - To change the plot map masking ring to a new figure background color, 130 | % >> set(findobj(gca,'type','patch'),'facecolor',get(gcf,'color')) 131 | % - Topoplots may be rotated. From the commandline >> view([deg 90]) {default: [0 90]) 132 | % 133 | % Authors: Andy Spydell, Colin Humphries, Arnaud Delorme & Scott Makeig 134 | % CNL / Salk Institute, 8/1996-/10/2001; SCCN/INC/UCSD, Nov. 2001 - 135 | % 136 | % See also: timtopo(), envtopo() 137 | 138 | % Deprecated options: 139 | % 'shrink' - ['on'|'off'|'force'|factor] Deprecated. 'on' -> If max channel arc_length 140 | % > 0.5, shrink electrode coordinates towards vertex to plot all channels 141 | % by making max arc_length 0.5. 'force' -> Normalize arc_length 142 | % so the channel max is 0.5. factor -> Apply a specified shrink 143 | % factor (range (0,1) = shrink fraction). {default: 'off'} 144 | % 'electcolor' {'k'} ... electrode marking details and their {defaults}. 145 | % 'emarker' {'.'}|'emarkersize' {14}|'emarkersizemark' {40}|'efontsize' {var} - 146 | % electrode marking details and their {defaults}. 147 | % 'ecolor' - color of the electrode markers {default: 'k' = black} 148 | % 'interplimits' - ['electrodes'|'head'] 'electrodes'-> interpolate the electrode grid; 149 | % 'head'-> interpolate the whole disk {default: 'head'}. 150 | 151 | % Unimplemented future options: 152 | 153 | % Copyright (C) Colin Humphries & Scott Makeig, CNL / Salk Institute, Aug, 1996 154 | % 155 | % This program is free software; you can redistribute it and/or modify 156 | % it under the terms of the GNU General Public License as published by 157 | % the Free Software Foundation; either version 2 of the License, or 158 | % (at your option) any later version. 159 | % 160 | % This program is distributed in the hope that it will be useful, 161 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 162 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 163 | % GNU General Public License for more details. 164 | % 165 | % You should have received a copy of the GNU General Public License 166 | % along with this program; if not, write to the Free Software 167 | % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 168 | 169 | % Topoplot Version 2.1 170 | % Early development history: 171 | % Begun by Andy Spydell and Scott Makeig, NHRC, 7-23-96 172 | % 8-96 Revised by Colin Humphries, CNL / Salk Institute, La Jolla CA 173 | % -changed surf command to imagesc (faster) 174 | % -can now handle arbitrary scaling of electrode distances 175 | % -can now handle non integer angles in chan_locs 176 | % 4-4-97 Revised again by Colin Humphries, reformatted by SM 177 | % -added parameters 178 | % -changed chan_locs format 179 | % 2-26-98 Revised by Colin 180 | % -changed image back to surface command 181 | % -added fill and blank styles 182 | % -removed extra background colormap entry (now use any colormap) 183 | % -added parameters for electrode colors and labels 184 | % -now each topoplot axes use the caxis command again. 185 | % -removed OUTPUT parameter 186 | % 3-11-98 changed default emarkersize, improve help msg -sm 187 | % 5-24-01 made default emarkersize vary with number of channels -sm 188 | % 01-25-02 reformated help & license, added link -ad 189 | % 03-15-02 added readlocs and the use of eloc input structure -ad 190 | % 03-25-02 added 'labelpoint' options and allow Values=[] -ad &sm 191 | % 03-25-02 added details to "Unknown parameter" warning -sm & ad 192 | 193 | function [handle,Zi,grid,Xi,Yi] = topoplotFast(Values,loc_file,varargin) 194 | 195 | % 196 | %%%%%%%%%%%%%%%%%%%%%%%% Set defaults %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 197 | % 198 | icadefs % read defaults MAXTOPOPLOTCHANS and DEFAULT_ELOC and BACKCOLOR 199 | if ~exist('BACKCOLOR') % if icadefs.m does not define BACKCOLOR 200 | BACKCOLOR = [.93 .96 1]; % EEGLAB standard 201 | end 202 | whitebk = 'off'; % by default, make gridplot background color = EEGLAB screen background color 203 | 204 | plotgrid = 'off'; 205 | plotchans = []; 206 | noplot = 'off'; 207 | handle = []; 208 | Zi = []; 209 | chanval = NaN; 210 | rmax = 0.5; % actual head radius - Don't change this! 211 | INTERPLIMITS = 'head'; % head, electrodes 212 | INTSQUARE = 'on'; % default, interpolate electrodes located though the whole square containing 213 | % the plotting disk 214 | default_intrad = 1; % indicator for (no) specified intrad 215 | MAPLIMITS = 'absmax'; % absmax, maxmin, [values] 216 | GRID_SCALE = 67; % plot map on a 67X67 grid 217 | CIRCGRID = 201; % number of angles to use in drawing circles 218 | AXHEADFAC = 1.3; % head to axes scaling factor 219 | CONTOURNUM = 6; % number of contour levels to plot 220 | STYLE = 'both'; % default 'style': both,straight,fill,contour,blank 221 | HEADCOLOR = [0 0 0]; % default head color (black) 222 | CCOLOR = [0.2 0.2 0.2]; % default contour color 223 | ELECTRODES = []; % default 'electrodes': on|off|label - set below 224 | MAXDEFAULTSHOWLOCS = 64;% if more channels than this, don't show electrode locations by default 225 | EMARKER = '.'; % mark electrode locations with small disks 226 | ECOLOR = [0 0 0]; % default electrode color = black 227 | EMARKERSIZE = []; % default depends on number of electrodes, set in code 228 | EMARKERLINEWIDTH = 1; % default edge linewidth for emarkers 229 | EMARKERSIZE1CHAN = 20; % default selected channel location marker size 230 | EMARKERCOLOR1CHAN = 'red'; % selected channel location marker color 231 | EMARKER2CHANS = []; % mark subset of electrode locations with small disks 232 | EMARKER2 = 'o'; % mark subset of electrode locations with small disks 233 | EMARKER2COLOR = 'r'; % mark subset of electrode locations with small disks 234 | EMARKERSIZE2 = 10; % default selected channel location marker size 235 | EMARKER2LINEWIDTH = 1; 236 | EFSIZE = get(0,'DefaultAxesFontSize'); % use current default fontsize for electrode labels 237 | HLINEWIDTH = 1.7; % default linewidth for head, nose, ears 238 | BLANKINGRINGWIDTH = .035;% width of the blanking ring 239 | HEADRINGWIDTH = .007;% width of the cartoon head ring 240 | SHADING = 'flat'; % default 'shading': flat|interp 241 | shrinkfactor = []; % shrink mode (dprecated) 242 | intrad = []; % default interpolation square is to outermost electrode (<=1.0) 243 | plotrad = []; % plotting radius ([] = auto, based on outermost channel location) 244 | headrad = []; % default plotting radius for cartoon head is 0.5 245 | squeezefac = 1.0; 246 | MINPLOTRAD = 0.15; % can't make a topoplot with smaller plotrad (contours fail) 247 | VERBOSE = 'off'; 248 | MASKSURF = 'off'; 249 | CONVHULL = 'off'; % dont mask outside the electrodes convex hull 250 | DRAWAXIS = 'off'; 251 | CHOOSECHANTYPE = 0; 252 | ContourVals = Values; 253 | PMASKFLAG = 0; 254 | 255 | %%%%%% Dipole defaults %%%%%%%%%%%% 256 | DIPOLE = []; 257 | DIPNORM = 'on'; 258 | DIPSPHERE = 85; 259 | DIPLEN = 1; 260 | DIPSCALE = 1; 261 | DIPORIENT = 1; 262 | DIPCOLOR = [0 0 0]; 263 | NOSEDIR = '+X'; 264 | CHANINFO = []; 265 | 266 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 267 | 268 | % 269 | %%%%%%%%%%%%%%%%%%%%%%% Handle arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 270 | % 271 | if nargin< 1 272 | help topoplot; 273 | return 274 | end 275 | 276 | % calling topoplot from Fieldtrip 277 | % ------------------------------- 278 | fieldtrip = 0; 279 | if nargin < 2, loc_file = []; end; 280 | if isstruct(Values) | ~isstruct(loc_file), fieldtrip == 1; end; 281 | if isstr(loc_file), if exist(loc_file) ~= 2, fieldtrip == 1; end; end; 282 | if fieldtrip 283 | disp('Calling topoplot from Fieldtrip'); 284 | dir1 = which('topoplot'); dir1 = fileparts(dir1); 285 | dir2 = which('electrodenormalize'); dir2 = fileparts(dir2); 286 | addpath(dir2); 287 | try, 288 | topoplot(Values, loc_file, varargin{:}); 289 | catch, 290 | end; 291 | addpath(dir1); 292 | return; 293 | end; 294 | 295 | nargs = nargin; 296 | if nargs == 1 297 | if isstr(Values) 298 | if any(strcmp(lower(Values),{'example','demo'})) 299 | fprintf(['This is an example of an electrode location file,\n',... 300 | 'an ascii file consisting of the following four columns:\n',... 301 | ' channel_number degrees arc_length channel_name\n\n',... 302 | 'Example:\n',... 303 | ' 1 -18 .352 Fp1 \n',... 304 | ' 2 18 .352 Fp2 \n',... 305 | ' 5 -90 .181 C3 \n',... 306 | ' 6 90 .181 C4 \n',... 307 | ' 7 -90 .500 A1 \n',... 308 | ' 8 90 .500 A2 \n',... 309 | ' 9 -142 .231 P3 \n',... 310 | '10 142 .231 P4 \n',... 311 | '11 0 .181 Fz \n',... 312 | '12 0 0 Cz \n',... 313 | '13 180 .181 Pz \n\n',... 314 | ... 315 | 'In topoplot() coordinates, 0 deg. points to the nose, positive\n',... 316 | 'angles point to the right hemisphere, and negative to the left.\n',... 317 | 'The model head sphere has a circumference of 2; the vertex\n',... 318 | '(Cz) has arc_length 0. Locations with arc_length > 0.5 are below\n',... 319 | 'head center and are plotted outside the head cartoon.\n'.... 320 | 'Option plotrad controls how much of this lower-head "skirt" is shown.\n',... 321 | 'Option headrad controls if and where the cartoon head will be drawn.\n',... 322 | 'Option intrad controls how many channels will be included in the interpolation.\n',... 323 | ]) 324 | return 325 | end 326 | end 327 | end 328 | if nargs < 2 329 | loc_file = DEFAULT_ELOC; 330 | if ~exist(loc_file) 331 | fprintf('default locations file "%s" not found - specify chan_locs in topoplot() call.\n',loc_file) 332 | error(' ') 333 | end 334 | end 335 | if isempty(loc_file) 336 | loc_file = 0; 337 | end 338 | if isnumeric(loc_file) & loc_file == 0 339 | loc_file = DEFAULT_ELOC; 340 | end 341 | 342 | if nargs > 2 343 | if ~(round(nargs/2) == nargs/2) 344 | error('Odd number of input arguments??') 345 | end 346 | for i = 1:2:length(varargin) 347 | Param = varargin{i}; 348 | Value = varargin{i+1}; 349 | if ~isstr(Param) 350 | error('Flag arguments must be strings') 351 | end 352 | Param = lower(Param); 353 | switch Param 354 | case 'conv' 355 | CONVHULL = lower(Value); 356 | if ~strcmp(CONVHULL,'on') & ~strcmp(CONVHULL,'off') 357 | error('Value of ''conv'' must be ''on'' or ''off''.'); 358 | end 359 | case 'colormap' 360 | if size(Value,2)~=3 361 | error('Colormap must be a n x 3 matrix') 362 | end 363 | colormap(Value) 364 | case 'intsquare' 365 | INTSQUARE = lower(Value); 366 | if ~strcmp(INTSQUARE,'on') & ~strcmp(INTSQUARE,'off') 367 | error('Value of ''intsquare'' must be ''on'' or ''off''.'); 368 | end 369 | case {'interplimits','headlimits'} 370 | if ~isstr(Value) 371 | error('''interplimits'' value must be a string') 372 | end 373 | Value = lower(Value); 374 | if ~strcmp(Value,'electrodes') & ~strcmp(Value,'head') 375 | error('Incorrect value for interplimits') 376 | end 377 | INTERPLIMITS = Value; 378 | case 'verbose' 379 | VERBOSE = Value; 380 | case 'nosedir' 381 | NOSEDIR = Value; 382 | if isempty(strmatch(lower(NOSEDIR), { '+x', '-x', '+y', '-y' })) 383 | error('Invalid nose direction'); 384 | end; 385 | case 'chaninfo' 386 | CHANINFO = Value; 387 | if isfield(CHANINFO, 'nosedir'), NOSEDIR = CHANINFO.nosedir; end; 388 | if isfield(CHANINFO, 'shrink' ), shrinkfactor = CHANINFO.shrink; end; 389 | if isfield(CHANINFO, 'plotrad') & isempty(plotrad), plotrad = CHANINFO.plotrad; end; 390 | if isfield(CHANINFO, 'chantype') 391 | chantype = CHANINFO.chantype; 392 | if ischar(chantype), chantype = cellstr(chantype); end 393 | CHOOSECHANTYPE = 1; 394 | end 395 | case 'chantype' 396 | chantype = Value; 397 | CHOOSECHANTYPE = 1; 398 | if ischar(chantype), chantype = cellstr(chantype); end 399 | if ~iscell(chantype), error('chantype must be cell array. e.g. {''EEG'', ''EOG''}'); end 400 | case 'drawaxis' 401 | DRAWAXIS = Value; 402 | case 'maplimits' 403 | MAPLIMITS = Value; 404 | case 'masksurf' 405 | MASKSURF = Value; 406 | case 'circgrid' 407 | CIRCGRID = Value; 408 | if isstr(CIRCGRID) | CIRCGRID<100 409 | error('''circgrid'' value must be an int > 100'); 410 | end 411 | case 'style' 412 | STYLE = lower(Value); 413 | case 'numcontour' 414 | CONTOURNUM = Value; 415 | case 'electrodes' 416 | ELECTRODES = lower(Value); 417 | if strcmpi(ELECTRODES,'pointlabels') | strcmpi(ELECTRODES,'ptslabels') ... 418 | | strcmpi(ELECTRODES,'labelspts') | strcmpi(ELECTRODES,'ptlabels') ... 419 | | strcmpi(ELECTRODES,'labelpts') 420 | ELECTRODES = 'labelpoint'; % backwards compatability 421 | elseif strcmpi(ELECTRODES,'pointnumbers') | strcmpi(ELECTRODES,'ptsnumbers') ... 422 | | strcmpi(ELECTRODES,'numberspts') | strcmpi(ELECTRODES,'ptnumbers') ... 423 | | strcmpi(ELECTRODES,'numberpts') | strcmpi(ELECTRODES,'ptsnums') ... 424 | | strcmpi(ELECTRODES,'numspts') 425 | ELECTRODES = 'numpoint'; % backwards compatability 426 | elseif strcmpi(ELECTRODES,'nums') 427 | ELECTRODES = 'numbers'; % backwards compatability 428 | elseif strcmpi(ELECTRODES,'pts') 429 | ELECTRODES = 'on'; % backwards compatability 430 | elseif ~strcmp(ELECTRODES,'off') ... 431 | & ~strcmpi(ELECTRODES,'on') ... 432 | & ~strcmp(ELECTRODES,'labels') ... 433 | & ~strcmpi(ELECTRODES,'numbers') ... 434 | & ~strcmpi(ELECTRODES,'labelpoint') ... 435 | & ~strcmpi(ELECTRODES,'numpoint') 436 | error('Unknown value for keyword ''electrodes'''); 437 | end 438 | case 'dipole' 439 | DIPOLE = Value; 440 | case 'dipsphere' 441 | DIPSPHERE = Value; 442 | case 'dipnorm' 443 | DIPNORM = Value; 444 | case 'diplen' 445 | DIPLEN = Value; 446 | case 'dipscale' 447 | DIPSCALE = Value; 448 | case 'contourvals' 449 | ContourVals = Value; 450 | case 'pmask' 451 | ContourVals = Value; 452 | PMASKFLAG = 1; 453 | case 'diporient' 454 | DIPORIENT = Value; 455 | case 'dipcolor' 456 | DIPCOLOR = Value; 457 | case 'emarker' 458 | if ischar(Value) 459 | EMARKER = Value; 460 | elseif ~iscell(Value) | length(Value) > 4 461 | error('''emarker'' argument must be a cell array {marker color size linewidth}') 462 | else 463 | EMARKER = Value{1}; 464 | end 465 | if length(Value) > 1 466 | ECOLOR = Value{2}; 467 | end 468 | if length(Value) > 2 469 | EMARKERSIZE2 = Value{3}; 470 | end 471 | if length(Value) > 3 472 | EMARKERLINEWIDTH = Value{4}; 473 | end 474 | case 'emarker2' 475 | if ~iscell(Value) | length(Value) > 5 476 | error('''emarker2'' argument must be a cell array {chans marker color size linewidth}') 477 | end 478 | EMARKER2CHANS = abs(Value{1}); % ignore channels < 0 479 | if length(Value) > 1 480 | EMARKER2 = Value{2}; 481 | end 482 | if length(Value) > 2 483 | EMARKER2COLOR = Value{3}; 484 | end 485 | if length(Value) > 3 486 | EMARKERSIZE2 = Value{4}; 487 | end 488 | if length(Value) > 4 489 | EMARKER2LINEWIDTH = Value{5}; 490 | end 491 | case 'shrink' 492 | shrinkfactor = Value; 493 | case 'intrad' 494 | intrad = Value; 495 | if isstr(intrad) | (intrad < MINPLOTRAD | intrad > 1) 496 | error('intrad argument should be a number between 0.15 and 1.0'); 497 | end 498 | case 'plotrad' 499 | plotrad = Value; 500 | if isstr(plotrad) | (plotrad < MINPLOTRAD | plotrad > 1) 501 | error('plotrad argument should be a number between 0.15 and 1.0'); 502 | end 503 | case 'headrad' 504 | headrad = Value; 505 | if isstr(headrad) & ( strcmpi(headrad,'off') | strcmpi(headrad,'none') ) 506 | headrad = 0; % undocumented 'no head' alternatives 507 | end 508 | if isempty(headrad) % [] -> none also 509 | headrad = 0; 510 | end 511 | if ~isstr(headrad) 512 | if ~(headrad==0) & (headrad < MINPLOTRAD | headrad>1) 513 | error('bad value for headrad'); 514 | end 515 | elseif ~strcmpi(headrad,'rim') 516 | error('bad value for headrad'); 517 | end 518 | case {'headcolor','hcolor'} 519 | HEADCOLOR = Value; 520 | case {'contourcolor','ccolor'} 521 | CCOLOR = Value; 522 | case {'electcolor','ecolor'} 523 | ECOLOR = Value; 524 | case {'emarkersize','emsize'} 525 | EMARKERSIZE = Value; 526 | case {'emarkersize1chan','emarkersizemark'} 527 | EMARKERSIZE1CHAN= Value; 528 | case {'efontsize','efsize'} 529 | EFSIZE = Value; 530 | case 'shading' 531 | SHADING = lower(Value); 532 | if ~any(strcmp(SHADING,{'flat','interp'})) 533 | error('Invalid shading parameter') 534 | end 535 | case 'noplot' 536 | noplot = Value; 537 | if ~isstr(noplot) 538 | if length(noplot) ~= 2 539 | error('''noplot'' location should be [radius, angle]') 540 | else 541 | chanrad = noplot(1); 542 | chantheta = noplot(2); 543 | noplot = 'on'; 544 | end 545 | end 546 | case 'gridscale' 547 | GRID_SCALE = Value; 548 | if isstr(GRID_SCALE) | GRID_SCALE ~= round(GRID_SCALE) | GRID_SCALE < 32 549 | error('''gridscale'' value must be integer > 32.'); 550 | end 551 | case {'plotgrid','gridplot'} 552 | plotgrid = 'on'; 553 | gridchans = Value; 554 | case 'plotchans' 555 | plotchans = Value(:); 556 | if find(plotchans<=0) 557 | error('''plotchans'' values must be > 0'); 558 | end 559 | % if max(abs(plotchans))>max(Values) | max(abs(plotchans))>length(Values) -sm ??? 560 | case {'whitebk','whiteback','forprint'} 561 | whitebk = Value; 562 | otherwise 563 | error(['Unknown input parameter ''' Param ''' ???']) 564 | end 565 | end 566 | end 567 | if strcmpi(whitebk, 'on') 568 | BACKCOLOR = [ 1 1 1 ]; 569 | end; 570 | 571 | cmap = jet(64); 572 | cmaplen = size(cmap,1); 573 | 574 | %ELECTRODES = 'off'; 575 | GRID_SCALE = 32; 576 | STYLE='map'; 577 | 578 | if strcmp(STYLE,'blank') % else if Values holds numbers of channels to mark 579 | if length(Values) < length(loc_file) 580 | ContourVals = zeros(1,length(loc_file)); 581 | ContourVals(Values) = 1; 582 | Values = ContourVals; 583 | end; 584 | end; 585 | 586 | % 587 | %%%%%%%%%%%%%%%%%%%%%%%%%%% test args for plotting an electrode grid %%%%%%%%%%%%%%%%%%%%%% 588 | % 589 | if strcmp(plotgrid,'on') 590 | STYLE = 'grid'; 591 | gchans = sort(find(abs(gridchans(:))>0)); 592 | 593 | % if setdiff(gchans,unique(gchans)) 594 | % fprintf('topoplot() warning: ''plotgrid'' channel matrix has duplicate channels\n'); 595 | % end 596 | 597 | if ~isempty(plotchans) 598 | if intersect(gchans,abs(plotchans)) 599 | fprintf('topoplot() warning: ''plotgrid'' and ''plotchans'' have channels in common\n'); 600 | end 601 | end 602 | end 603 | 604 | % 605 | %%%%%%%%%%%%%%%%%%%%%%%%%%% misc arg tests %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 606 | % 607 | if isempty(ELECTRODES) % if electrode labeling not specified 608 | if length(Values) > MAXDEFAULTSHOWLOCS % if more channels than default max 609 | ELECTRODES = 'off'; % don't show electrodes 610 | else % else if fewer chans, 611 | ELECTRODES = 'on'; % do 612 | end 613 | end 614 | 615 | if isempty(Values) 616 | STYLE = 'blank'; 617 | end 618 | [r,c] = size(Values); 619 | if r>1 & c>1, 620 | error('input data must be a single vector'); 621 | end 622 | Values = Values(:); % make Values a column vector 623 | ContourVals = ContourVals(:); % values for contour 624 | 625 | if ~isempty(intrad) & ~isempty(plotrad) & intrad < plotrad 626 | error('intrad must be >= plotrad'); 627 | end 628 | 629 | if ~strcmpi(STYLE,'grid') % if not plot grid only 630 | 631 | % 632 | %%%%%%%%%%%%%%%%%%%% Read the channel location information %%%%%%%%%%%%%%%%%%%%%%%% 633 | % 634 | if isstr(loc_file) 635 | [tmpeloc labels Th Rd indices] = readlocs( loc_file); 636 | elseif isstruct(loc_file) % a locs struct 637 | [tmpeloc labels Th Rd indices] = readlocs( loc_file ); 638 | % Note: Th and Rd correspond to indices channels-with-coordinates only 639 | else 640 | error('loc_file must be a EEG.locs struct or locs filename'); 641 | end 642 | Th = pi/180*Th; % convert degrees to radians 643 | allchansind = 1:length(Th); 644 | 645 | 646 | if ~isempty(plotchans) 647 | if max(plotchans) > length(Th) 648 | error('''plotchans'' values must be <= max channel index'); 649 | end 650 | end 651 | 652 | % 653 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% channels to plot %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 654 | % 655 | if ~isempty(plotchans) 656 | plotchans = intersect(plotchans, indices); 657 | end; 658 | if ~isempty(Values) & ~strcmpi( STYLE, 'blank') & isempty(plotchans) 659 | plotchans = indices; 660 | end 661 | if isempty(plotchans) & strcmpi( STYLE, 'blank') 662 | plotchans = indices; 663 | end 664 | 665 | % 666 | %%%%%%%%%%%%%%%%%%%%%%%%%%% filter for channel type(s), if specified %%%%%%%%%%%%%%%%%%%%% 667 | % 668 | 669 | if CHOOSECHANTYPE, 670 | newplotchans = eeg_chantype(loc_file,chantype); 671 | plotchans = intersect(newplotchans, plotchans); 672 | end 673 | 674 | % 675 | %%%%%%%%%%%%%%%%%%%%%%%%%%% filter channels used for components %%%%%%%%%%%%%%%%%%%%% 676 | % 677 | if isfield(CHANINFO, 'icachansind') & ~isempty(Values) & length(Values) ~= length(tmpeloc) 678 | 679 | % test if ICA component 680 | % --------------------- 681 | if length(CHANINFO.icachansind) == length(Values) 682 | 683 | % if only a subset of channels are to be plotted 684 | % and ICA components also use a subject of channel 685 | % we must find the new indices for these channels 686 | 687 | plotchans = intersect(CHANINFO.icachansind, plotchans); 688 | tmpvals = zeros(1, length(tmpeloc)); 689 | tmpvals(CHANINFO.icachansind) = Values; 690 | Values = tmpvals; 691 | tmpvals = zeros(1, length(tmpeloc)); 692 | tmpvals(CHANINFO.icachansind) = ContourVals; 693 | ContourVals = tmpvals; 694 | 695 | end; 696 | end; 697 | 698 | % 699 | %%%%%%%%%%%%%%%%%%% last channel is reference? %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 700 | % 701 | if length(tmpeloc) == length(Values) + 1 % remove last channel if necessary 702 | % (common reference channel) 703 | if plotchans(end) == length(tmpeloc) 704 | plotchans(end) = []; 705 | end; 706 | 707 | end; 708 | 709 | % 710 | %%%%%%%%%%%%%%%%%%% remove infinite and NaN values %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 711 | % 712 | if length(Values) > 1 713 | inds = union(find(isnan(Values)), find(isinf(Values))); % NaN and Inf values 714 | plotchans = setdiff(plotchans, inds); 715 | end; 716 | if strcmp(plotgrid,'on') 717 | plotchans = setxor(plotchans,gchans); % remove grid chans from head plotchans 718 | end 719 | 720 | [x,y] = pol2cart(Th,Rd); % transform electrode locations from polar to cartesian coordinates 721 | plotchans = abs(plotchans); % reverse indicated channel polarities 722 | allchansind = allchansind(plotchans); 723 | Th = Th(plotchans); 724 | Rd = Rd(plotchans); 725 | x = x(plotchans); 726 | y = y(plotchans); 727 | labels = labels(plotchans); % remove labels for electrodes without locations 728 | labels = strvcat(labels); % make a label string matrix 729 | if ~isempty(Values) & length(Values) > 1 730 | Values = Values(plotchans); 731 | ContourVals = ContourVals(plotchans); 732 | end; 733 | 734 | % 735 | %%%%%%%%%%%%%%%%%% Read plotting radius from chanlocs %%%%%%%%%%%%%%%%%%%%%%%%%%%%% 736 | % 737 | if isempty(plotrad) & isfield(tmpeloc, 'plotrad'), 738 | plotrad = tmpeloc(1).plotrad; 739 | if isstr(plotrad) % plotrad shouldn't be a string 740 | plotrad = str2num(plotrad) % just checking 741 | end 742 | if plotrad < MINPLOTRAD | plotrad > 1.0 743 | fprintf('Bad value (%g) for plotrad.\n',plotrad); 744 | error(' '); 745 | end 746 | if strcmpi(VERBOSE,'on') & ~isempty(plotrad) 747 | fprintf('Plotting radius plotrad (%g) set from EEG.chanlocs.\n',plotrad); 748 | end 749 | end; 750 | if isempty(plotrad) 751 | plotrad = min(1.0,max(Rd)*1.02); % default: just outside the outermost electrode location 752 | plotrad = max(plotrad,0.5); % default: plot out to the 0.5 head boundary 753 | end % don't plot channels with Rd > 1 (below head) 754 | 755 | if isempty(intrad) 756 | default_intrad = 1; % indicator for (no) specified intrad 757 | intrad = min(1.0,max(Rd)*1.02); % default: just outside the outermost electrode location 758 | else 759 | default_intrad = 0; % indicator for (no) specified intrad 760 | if plotrad > intrad 761 | plotrad = intrad; 762 | end 763 | end % don't interpolate channels with Rd > 1 (below head) 764 | if isstr(plotrad) | plotrad < MINPLOTRAD | plotrad > 1.0 765 | error('plotrad must be between 0.15 and 1.0'); 766 | end 767 | 768 | % 769 | %%%%%%%%%%%%%%%%%%%%%%% Set radius of head cartoon %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 770 | % 771 | if isempty(headrad) % never set -> defaults 772 | if plotrad >= rmax 773 | headrad = rmax; % (anatomically correct) 774 | else % if plotrad < rmax 775 | headrad = 0; % don't plot head 776 | if strcmpi(VERBOSE, 'on') 777 | fprintf('topoplot(): not plotting cartoon head since plotrad (%5.4g) < 0.5\n',... 778 | plotrad); 779 | end 780 | end 781 | elseif strcmpi(headrad,'rim') % force plotting at rim of map 782 | headrad = plotrad; 783 | end 784 | 785 | % 786 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Shrink mode %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 787 | % 788 | if ~isempty(shrinkfactor) | isfield(tmpeloc, 'shrink'), 789 | if isempty(shrinkfactor) & isfield(tmpeloc, 'shrink'), 790 | shrinkfactor = tmpeloc(1).shrink; 791 | if strcmpi(VERBOSE,'on') 792 | if isstr(shrinkfactor) 793 | fprintf('Automatically shrinking coordinates to lie above the head perimter.\n'); 794 | else 795 | fprintf('Automatically shrinking coordinates by %3.2f\n', shrinkfactor); 796 | end; 797 | end 798 | end; 799 | 800 | if isstr(shrinkfactor) 801 | if strcmpi(shrinkfactor, 'on') | strcmpi(shrinkfactor, 'force') | strcmpi(shrinkfactor, 'auto') 802 | if abs(headrad-rmax) > 1e-2 803 | fprintf(' NOTE -> the head cartoon will NOT accurately indicate the actual electrode locations\n'); 804 | end 805 | if strcmpi(VERBOSE,'on') 806 | fprintf(' Shrink flag -> plotting cartoon head at plotrad\n'); 807 | end 808 | headrad = plotrad; % plot head around outer electrodes, no matter if 0.5 or not 809 | end 810 | else % apply shrinkfactor 811 | plotrad = rmax/(1-shrinkfactor); 812 | headrad = plotrad; % make deprecated 'shrink' mode plot 813 | if strcmpi(VERBOSE,'on') 814 | fprintf(' %g%% shrink applied.'); 815 | if abs(headrad-rmax) > 1e-2 816 | fprintf(' Warning: With this "shrink" setting, the cartoon head will NOT be anatomically correct.\n'); 817 | else 818 | fprintf('\n'); 819 | end 820 | end 821 | end 822 | end; % if shrink 823 | 824 | % 825 | %%%%%%%%%%%%%%%%% Issue warning if headrad ~= rmax %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 826 | % 827 | 828 | if headrad ~= 0.5 & strcmpi(VERBOSE, 'on') 829 | fprintf(' NB: Plotting map using ''plotrad'' %-4.3g,',plotrad); 830 | fprintf( ' ''headrad'' %-4.3g\n',headrad); 831 | fprintf('Warning: The plotting radius of the cartoon head is NOT anatomically correct (0.5).\n') 832 | end 833 | % 834 | %%%%%%%%%%%%%%%%%%%%% Find plotting channels %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 835 | % 836 | 837 | pltchans = find(Rd <= plotrad); % plot channels inside plotting circle 838 | 839 | if strcmpi(INTSQUARE,'on') % interpolate channels in the radius intrad square 840 | intchans = find(x <= intrad & y <= intrad); % interpolate and plot channels inside interpolation square 841 | else 842 | intchans = find(Rd <= intrad); % interpolate channels in the radius intrad circle only 843 | end 844 | 845 | % 846 | %%%%%%%%%%%%%%%%%%%%% Eliminate channels not plotted %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 847 | % 848 | 849 | allx = x; 850 | ally = y; 851 | intchans; % interpolate using only the 'intchans' channels 852 | pltchans; % plot using only indicated 'plotchans' channels 853 | 854 | if length(pltchans) < length(Rd) & strcmpi(VERBOSE, 'on') 855 | fprintf('Interpolating %d and plotting %d of the %d scalp electrodes.\n', ... 856 | length(intchans),length(pltchans),length(Rd)); 857 | end; 858 | 859 | 860 | % fprintf('topoplot(): plotting %d channels\n',length(pltchans)); 861 | if ~isempty(EMARKER2CHANS) 862 | if strcmpi(STYLE,'blank') 863 | error('emarker2 not defined for style ''blank'' - use marking channel numbers in place of data'); 864 | else % mark1chans and mark2chans are subsets of pltchans for markers 1 and 2 865 | [tmp1 mark1chans tmp2] = setxor(pltchans,EMARKER2CHANS); 866 | [tmp3 tmp4 mark2chans] = intersect(EMARKER2CHANS,pltchans); 867 | end 868 | end 869 | 870 | if ~isempty(Values) 871 | if length(Values) == length(Th) % if as many map Values as channel locs 872 | intValues = Values(intchans); 873 | intContourVals = ContourVals(intchans); 874 | Values = Values(pltchans); 875 | ContourVals = ContourVals(pltchans); 876 | end; 877 | end; % now channel parameters and values all refer to plotting channels only 878 | 879 | allchansind = allchansind(pltchans); 880 | intTh = Th(intchans); % eliminate channels outside the interpolation area 881 | intRd = Rd(intchans); 882 | intx = x(intchans); 883 | inty = y(intchans); 884 | Th = Th(pltchans); % eliminate channels outside the plotting area 885 | Rd = Rd(pltchans); 886 | x = x(pltchans); 887 | y = y(pltchans); 888 | 889 | labels= labels(pltchans,:); 890 | % 891 | %%%%%%%%%%%%%%% Squeeze channel locations to <= rmax %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 892 | % 893 | 894 | squeezefac = rmax/plotrad; 895 | intRd = intRd*squeezefac; % squeeze electrode arc_lengths towards the vertex 896 | Rd = Rd*squeezefac; % squeeze electrode arc_lengths towards the vertex 897 | % to plot all inside the head cartoon 898 | intx = intx*squeezefac; 899 | inty = inty*squeezefac; 900 | x = x*squeezefac; 901 | y = y*squeezefac; 902 | allx = allx*squeezefac; 903 | ally = ally*squeezefac; 904 | % Note: Now outermost channel will be plotted just inside rmax 905 | 906 | else % if strcmpi(STYLE,'grid') 907 | intx = rmax; inty=rmax; 908 | end % if ~strcmpi(STYLE,'grid') 909 | 910 | % 911 | %%%%%%%%%%%%%%%% rotate channels based on chaninfo %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 912 | % 913 | if strcmpi(lower(NOSEDIR), '+x') 914 | rotate = 0; 915 | else 916 | if strcmpi(lower(NOSEDIR), '+y') 917 | rotate = 3*pi/2; 918 | elseif strcmpi(lower(NOSEDIR), '-x') 919 | rotate = pi; 920 | else rotate = pi/2; 921 | end; 922 | allcoords = (inty + intx*sqrt(-1))*exp(sqrt(-1)*rotate); 923 | intx = imag(allcoords); 924 | inty = real(allcoords); 925 | allcoords = (ally + allx*sqrt(-1))*exp(sqrt(-1)*rotate); 926 | allx = imag(allcoords); 927 | ally = real(allcoords); 928 | allcoords = (y + x*sqrt(-1))*exp(sqrt(-1)*rotate); 929 | x = imag(allcoords); 930 | y = real(allcoords); 931 | end; 932 | 933 | % 934 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Make the plot %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 935 | % 936 | if ~strcmpi(STYLE,'blank') % if draw interpolated scalp map 937 | if ~strcmpi(STYLE,'grid') % not a rectangular channel grid 938 | % 939 | %%%%%%%%%%%%%%%% Find limits for interpolation %%%%%%%%%%%%%%%%%%%%%%%%%%%% 940 | % 941 | if default_intrad % if no specified intrad 942 | if strcmpi(INTERPLIMITS,'head') % intrad is 'head' 943 | xmin = min(-rmax,min(intx)); xmax = max(rmax,max(intx)); 944 | ymin = min(-rmax,min(inty)); ymax = max(rmax,max(inty)); 945 | 946 | else % INTERPLIMITS = rectangle containing electrodes -- DEPRECATED OPTION! 947 | xmin = max(-rmax,min(intx)); xmax = min(rmax,max(intx)); 948 | ymin = max(-rmax,min(inty)); ymax = min(rmax,max(inty)); 949 | end 950 | else % some other intrad specified 951 | xmin = -intrad*squeezefac; xmax = intrad*squeezefac; % use the specified intrad value 952 | ymin = -intrad*squeezefac; ymax = intrad*squeezefac; 953 | end 954 | % 955 | %%%%%%%%%%%%%%%%%%%%%%% Interpolate scalp map data %%%%%%%%%%%%%%%%%%%%%%%% 956 | % 957 | xi = linspace(xmin,xmax,GRID_SCALE); % x-axis description (row vector) 958 | yi = linspace(ymin,ymax,GRID_SCALE); % y-axis description (row vector) 959 | [yi,xi] = meshgrid(yi,xi); 960 | [Xi,Yi,Zi] = griddata(double(inty),double(intx),double(intValues),double(yi),double(xi),'v4'); % interpolate data 961 | %[Xi,Yi,Zi] = griddata(double(inty),double(intx),double(intContourVals),double(yi)',double(xi),'v4'); % interpolate data 962 | 963 | % 964 | %%%%%%%%%%%%%%%%%%%%%%% Mask out data outside the head %%%%%%%%%%%%%%%%%%%%% 965 | % 966 | mask = (sqrt(Xi.^2 + Yi.^2) <= rmax); % mask outside the plotting circle 967 | ii = find(mask == 0); 968 | Zi(ii) = NaN; % mask non-plotting voxels with NaNs 969 | % ZiC(ii) = NaN; % mask non-plotting voxels with NaNs 970 | grid = plotrad; % unless 'noplot', then 3rd output arg is plotrad 971 | % 972 | %%%%%%%%%% Return interpolated value at designated scalp location %%%%%%%%%% 973 | % 974 | if exist('chanrad') % optional first argument to 'noplot' 975 | chantheta = (chantheta/360)*2*pi; 976 | chancoords = round(ceil(GRID_SCALE/2)+GRID_SCALE/2*2*chanrad*[cos(-chantheta),... 977 | -sin(-chantheta)]); 978 | if chancoords(1)<1 ... 979 | | chancoords(1) > GRID_SCALE ... 980 | | chancoords(2)<1 ... 981 | | chancoords(2)>GRID_SCALE 982 | error('designated ''noplot'' channel out of bounds') 983 | else 984 | chanval = Zi(chancoords(1),chancoords(2)); 985 | grid = Zi; 986 | Zi = chanval; % return interpolated value instead of Zi 987 | end 988 | end 989 | % 990 | %%%%%%%%%%%%%%%%%%%%%%%%%% Return interpolated image only %%%%%%%%%%%%%%%%% 991 | % 992 | if strcmpi(noplot, 'on') 993 | if strcmpi(VERBOSE,'on') 994 | fprintf('topoplot(): no plot requested.\n') 995 | end 996 | return; 997 | end 998 | % 999 | %%%%%%%%%%%%%%%%%%%%%%% Calculate colormap limits %%%%%%%%%%%%%%%%%%%%%%%%%% 1000 | % 1001 | if isstr(MAPLIMITS) 1002 | if strcmp(MAPLIMITS,'absmax') 1003 | amax = max(max(abs(Zi))); 1004 | amin = -amax; 1005 | elseif strcmp(MAPLIMITS,'maxmin') | strcmp(MAPLIMITS,'minmax') 1006 | amin = min(min(Zi)); 1007 | amax = max(max(Zi)); 1008 | else 1009 | error('unknown ''maplimits'' value.'); 1010 | end 1011 | elseif length(MAPLIMITS) == 2 1012 | amin = MAPLIMITS(1); 1013 | amax = MAPLIMITS(2); 1014 | else 1015 | error('unknown ''maplimits'' value'); 1016 | end 1017 | delta = xi(2)-xi(1); % length of grid entry 1018 | 1019 | end % if ~strcmpi(STYLE,'grid') 1020 | % 1021 | %%%%%%%%%%%%%%%%%%%%%%%%%% Scale the axes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1022 | % 1023 | %cla % clear current axis 1024 | hold on 1025 | h = gca; % uses current axes 1026 | 1027 | % instead of default larger AXHEADFAC 1028 | if squeezefac<0.92 & plotrad-headrad > 0.05 % (size of head in axes) 1029 | AXHEADFAC = 1.05; % do not leave room for external ears if head cartoon 1030 | % shrunk enough by the 'skirt' option 1031 | end 1032 | 1033 | set(gca,'Xlim',[-rmax rmax]*AXHEADFAC,'Ylim',[-rmax rmax]*AXHEADFAC); 1034 | % specify size of head axes in gca 1035 | 1036 | unsh = (GRID_SCALE+1)/GRID_SCALE; % un-shrink the effects of 'interp' SHADING 1037 | 1038 | % 1039 | %%%%%%%%%%%%%%%%%%%%%%%% Plot grid only %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1040 | % 1041 | if strcmpi(STYLE,'grid') % plot grid only 1042 | 1043 | % 1044 | % The goal below is to make the grid cells square - not yet achieved in all cases? -sm 1045 | % 1046 | g1 = size(gridchans,1); 1047 | g2 = size(gridchans,2); 1048 | gmax = max([g1 g2]); 1049 | Xi = linspace(-rmax*g2/gmax,rmax*g2/gmax,g1+1); 1050 | Xi = Xi+rmax/g1; Xi = Xi(1:end-1); 1051 | Yi = linspace(-rmax*g1/gmax,rmax*g1/gmax,g2+1); 1052 | Yi = Yi+rmax/g2; Yi = Yi(1:end-1); Yi = Yi(end:-1:1); % by trial and error! 1053 | % 1054 | %%%%%%%%%%% collect the gridchans values %%%%%%%%%%%%%%%%%%%%%%%%%%% 1055 | % 1056 | gridvalues = zeros(size(gridchans)); 1057 | for j=1:size(gridchans,1) 1058 | for k=1:size(gridchans,2) 1059 | gc = gridchans(j,k); 1060 | if gc > 0 1061 | gridvalues(j,k) = Values(gc); 1062 | elseif gc < 0 1063 | gridvalues(j,k) = -Values(gc); 1064 | else 1065 | gridvalues(j,k) = nan; % not-a-number = no value 1066 | end 1067 | end 1068 | end 1069 | % 1070 | %%%%%%%%%%% reset color limits for grid plot %%%%%%%%%%%%%%%%%%%%%%%%% 1071 | % 1072 | if isstr(MAPLIMITS) 1073 | if strcmp(MAPLIMITS,'maxmin') | strcmp(MAPLIMITS,'minmax') 1074 | amin = min(min(gridvalues(~isnan(gridvalues)))); 1075 | amax = max(max(gridvalues(~isnan(gridvalues)))); 1076 | elseif strcmp(MAPLIMITS,'absmax') 1077 | % 11/21/2005 Toby edit 1078 | % This should now work as specified. Before it only crashed (using 1079 | % "plotgrid" and "maplimits>absmax" options). 1080 | amax = max(max(abs(gridvalues(~isnan(gridvalues))))); 1081 | amin = -amax; 1082 | %amin = -max(max(abs([amin amax]))); 1083 | %amax = max(max(abs([amin amax]))); 1084 | else 1085 | error('unknown ''maplimits'' value'); 1086 | end 1087 | elseif length(MAPLIMITS) == 2 1088 | amin = MAPLIMITS(1); 1089 | amax = MAPLIMITS(2); 1090 | else 1091 | error('unknown ''maplimits'' value'); 1092 | end 1093 | % 1094 | %%%%%%%%%% explicitly compute grid colors, allowing BACKCOLOR %%%%%% 1095 | % 1096 | gridvalues = 1+floor(cmaplen*(gridvalues-amin)/(amax-amin)); 1097 | gridvalues(find(gridvalues == cmaplen+1)) = cmaplen; 1098 | gridcolors = zeros([size(gridvalues),3]); 1099 | for j=1:size(gridchans,1) 1100 | for k=1:size(gridchans,2) 1101 | if ~isnan(gridvalues(j,k)) 1102 | gridcolors(j,k,:) = cmap(gridvalues(j,k),:); 1103 | else 1104 | if strcmpi(whitebk,'off') 1105 | gridcolors(j,k,:) = BACKCOLOR; % gridchans == 0 -> background color 1106 | % This allows the plot to show 'space' between separate sub-grids or strips 1107 | else % 'on' 1108 | gridcolors(j,k,:) = [1 1 1]; BACKCOLOR; % gridchans == 0 -> white for printing 1109 | end 1110 | end 1111 | end 1112 | end 1113 | 1114 | % 1115 | %%%%%%%%%% draw the gridplot image %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1116 | % 1117 | handle=imagesc(Xi,Yi,gridcolors); % plot grid with explicit colors 1118 | axis square 1119 | 1120 | % 1121 | %%%%%%%%%%%%%%%%%%%%%%%% Plot map contours only %%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1122 | % 1123 | % elseif strcmp(STYLE,'contour') % plot surface contours only 1124 | % [cls chs] = contour(Xi,Yi,ZiC,CONTOURNUM,'k'); 1125 | % % for h=chs, set(h,'color',CCOLOR); end 1126 | % % 1127 | % %%%%%%%%%%%%%%%%%%%%%%%% Else plot map and contours %%%%%%%%%%%%%%%%%%%%%%%%% 1128 | % % 1129 | % elseif strcmp(STYLE,'both') % plot interpolated surface and surface contours 1130 | % if strcmp(SHADING,'interp') 1131 | % tmph = surface(Xi*unsh,Yi*unsh,zeros(size(Zi))-0.1,Zi,... 1132 | % 'EdgeColor','none','FaceColor',SHADING); 1133 | % else % SHADING == 'flat' 1134 | % tmph = surface(Xi-delta/2,Yi-delta/2,zeros(size(Zi))-0.1,Zi,... 1135 | % 'EdgeColor','none','FaceColor',SHADING); 1136 | % end 1137 | % if strcmpi(MASKSURF, 'on') 1138 | % set(tmph, 'visible', 'off'); 1139 | % handle = tmph; 1140 | % end; 1141 | % 1142 | % warning off; 1143 | % if ~PMASKFLAG 1144 | % [cls chs] = contour(Xi,Yi,ZiC,CONTOURNUM,'k'); 1145 | % else 1146 | % ZiC(find(ZiC > 0.5 )) = NaN; 1147 | % [cls chs] = contourf(Xi,Yi,ZiC,0,'k'); 1148 | % subh = get(chs, 'children'); 1149 | % for indsubh = 1:length(subh) 1150 | % numfaces = size(get(subh(indsubh), 'XData'),1); 1151 | % set(subh(indsubh), 'FaceVertexCData', ones(numfaces,3), 'Cdatamapping', 'direct', 'facealpha', 0.5, 'linewidth', 2); 1152 | % end; 1153 | % end; 1154 | % for h=chs, set(h,'color',CCOLOR); end 1155 | % warning on; 1156 | % 1157 | %%%%%%%%%%%%%%%%%%%%%%%% Else plot map only %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1158 | % 1159 | elseif strcmp(STYLE,'straight') | strcmp(STYLE,'map') % 'straight' was former arg 1160 | 1161 | if strcmp(SHADING,'interp') % 'interp' mode is shifted somehow... but how? 1162 | tmph = surface(Xi*unsh,Yi*unsh,zeros(size(Zi)),Zi,'EdgeColor','none',... 1163 | 'FaceColor',SHADING); 1164 | else 1165 | tmph = surface(Xi-delta/2,Yi-delta/2,zeros(size(Zi)),Zi,'EdgeColor','none',... 1166 | 'FaceColor',SHADING); 1167 | end 1168 | if strcmpi(MASKSURF, 'on') 1169 | set(tmph, 'visible', 'off'); 1170 | handle = tmph; 1171 | end; 1172 | % 1173 | %%%%%%%%%%%%%%%%%% Else fill contours with uniform colors %%%%%%%%%%%%%%%%%% 1174 | % 1175 | elseif strcmp(STYLE,'fill') 1176 | [cls chs] = contourf(Xi,Yi,Zi,CONTOURNUM,'k'); 1177 | 1178 | % for h=chs, set(h,'color',CCOLOR); end 1179 | % <- 'not line objects.' Why does 'both' work above??? 1180 | 1181 | else 1182 | error('Invalid style') 1183 | end 1184 | % 1185 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Set color axis %%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1186 | % 1187 | caxis([amin amax]); % set coloraxis 1188 | colormap(jet(100)) 1189 | 1190 | else % if STYLE 'blank' 1191 | % 1192 | %%%%%%%%%%%%%%%%%%%%%%% Draw blank head %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1193 | % 1194 | if strcmpi(noplot, 'on') 1195 | if strcmpi(VERBOSE,'on') 1196 | fprintf('topoplot(): no plot requested.\n') 1197 | end 1198 | return; 1199 | end 1200 | %cla 1201 | hold on 1202 | 1203 | set(gca,'Xlim',[-rmax rmax]*AXHEADFAC,'Ylim',[-rmax rmax]*AXHEADFAC) 1204 | % pos = get(gca,'position'); 1205 | % fprintf('Current axes size %g,%g\n',pos(3),pos(4)); 1206 | 1207 | if strcmp(ELECTRODES,'labelpoint') | strcmp(ELECTRODES,'numpoint') 1208 | text(-0.6,-0.6, ... 1209 | [ int2str(length(Rd)) ' of ' int2str(length(tmpeloc)) ' electrode locations shown']); 1210 | text(-0.6,-0.7, [ 'Click on electrodes to toggle name/number']); 1211 | tl = title('Channel locations'); 1212 | set(tl, 'fontweight', 'bold'); 1213 | end; 1214 | end % STYLE 'blank' 1215 | 1216 | if exist('handle') ~= 1 1217 | handle = gca; 1218 | end; 1219 | 1220 | if ~strcmpi(STYLE,'grid') % if not plot grid only 1221 | 1222 | % 1223 | %%%%%%%%%%%%%%%%%%% Plot filled ring to mask jagged grid boundary %%%%%%%%%%%%%%%%%%%%%%%%%%% 1224 | % 1225 | hwidth = HEADRINGWIDTH; % width of head ring 1226 | hin = squeezefac*headrad*(1- hwidth/2); % inner head ring radius 1227 | 1228 | if strcmp(SHADING,'interp') 1229 | rwidth = BLANKINGRINGWIDTH*1.3; % width of blanking outer ring 1230 | else 1231 | rwidth = BLANKINGRINGWIDTH; % width of blanking outer ring 1232 | end 1233 | rin = rmax*(1-rwidth/2); % inner ring radius 1234 | if hin>rin 1235 | rin = hin; % dont blank inside the head ring 1236 | end 1237 | 1238 | if strcmp(CONVHULL,'on') %%%%%%%%% mask outside the convex hull of the electrodes %%%%%%%%% 1239 | cnv = convhull(allx,ally); 1240 | cnvfac = round(CIRCGRID/length(cnv)); % spline interpolate the convex hull 1241 | if cnvfac < 1, cnvfac=1; end; 1242 | CIRCGRID = cnvfac*length(cnv); 1243 | 1244 | startangle = atan2(allx(cnv(1)),ally(cnv(1))); 1245 | circ = linspace(0+startangle,2*pi+startangle,CIRCGRID); 1246 | rx = sin(circ); 1247 | ry = cos(circ); 1248 | 1249 | allx = allx(:)'; % make x (elec locations; + to nose) a row vector 1250 | ally = ally(:)'; % make y (elec locations, + to r? ear) a row vector 1251 | erad = sqrt(allx(cnv).^2+ally(cnv).^2); % convert to polar coordinates 1252 | eang = atan2(allx(cnv),ally(cnv)); 1253 | eang = unwrap(eang); 1254 | eradi =spline(linspace(0,1,3*length(cnv)), [erad erad erad], ... 1255 | linspace(0,1,3*length(cnv)*cnvfac)); 1256 | eangi =spline(linspace(0,1,3*length(cnv)), [eang+2*pi eang eang-2*pi], ... 1257 | linspace(0,1,3*length(cnv)*cnvfac)); 1258 | xx = eradi.*sin(eangi); % convert back to rect coordinates 1259 | yy = eradi.*cos(eangi); 1260 | yy = yy(CIRCGRID+1:2*CIRCGRID); 1261 | xx = xx(CIRCGRID+1:2*CIRCGRID); 1262 | eangi = eangi(CIRCGRID+1:2*CIRCGRID); 1263 | eradi = eradi(CIRCGRID+1:2*CIRCGRID); 1264 | xx = xx*1.02; yy = yy*1.02; % extend spline outside electrode marks 1265 | 1266 | splrad = sqrt(xx.^2+yy.^2); % arc radius of spline points (yy,xx) 1267 | oob = find(splrad >= rin); % enforce an upper bound on xx,yy 1268 | xx(oob) = rin*xx(oob)./splrad(oob); % max radius = rin 1269 | yy(oob) = rin*yy(oob)./splrad(oob); % max radius = rin 1270 | 1271 | splrad = sqrt(xx.^2+yy.^2); % arc radius of spline points (yy,xx) 1272 | oob = find(splrad < hin); % don't let splrad be inside the head cartoon 1273 | xx(oob) = hin*xx(oob)./splrad(oob); % min radius = hin 1274 | yy(oob) = hin*yy(oob)./splrad(oob); % min radius = hin 1275 | 1276 | ringy = [[ry(:)' ry(1) ]*(rin+rwidth) yy yy(1)]; 1277 | ringx = [[rx(:)' rx(1) ]*(rin+rwidth) xx xx(1)]; 1278 | 1279 | ringh2= patch(ringy,ringx,ones(size(ringy)),BACKCOLOR,'edgecolor','none'); hold on 1280 | 1281 | % plot(ry*rmax,rx*rmax,'b') % debugging line 1282 | 1283 | else %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% mask the jagged border around rmax %%%%%%%%%%%%%%%5%%%%%% 1284 | 1285 | circ = linspace(0,2*pi,CIRCGRID); 1286 | rx = sin(circ); 1287 | ry = cos(circ); 1288 | ringx = [[rx(:)' rx(1) ]*(rin+rwidth) [rx(:)' rx(1)]*rin]; 1289 | ringy = [[ry(:)' ry(1) ]*(rin+rwidth) [ry(:)' ry(1)]*rin]; 1290 | 1291 | if ~strcmpi(STYLE,'blank') 1292 | ringh= patch(ringx,ringy,0.01*ones(size(ringx)),BACKCOLOR,'edgecolor','none'); hold on 1293 | end 1294 | % plot(ry*rmax,rx*rmax,'b') % debugging line 1295 | end 1296 | 1297 | %f1= fill(rin*[rx rX],rin*[ry rY],BACKCOLOR,'edgecolor',BACKCOLOR); hold on 1298 | %f2= fill(rin*[rx rX*(1+rwidth)],rin*[ry rY*(1+rwidth)],BACKCOLOR,'edgecolor',BACKCOLOR); 1299 | 1300 | % Former line-style border smoothing - width did not scale with plot 1301 | % brdr=plot(1.015*cos(circ).*rmax,1.015*sin(circ).*rmax,... % old line-based method 1302 | % 'color',HEADCOLOR,'Linestyle','-','LineWidth',HLINEWIDTH); % plot skirt outline 1303 | % set(brdr,'color',BACKCOLOR,'linewidth',HLINEWIDTH + 4); % hide the disk edge jaggies 1304 | 1305 | % 1306 | %%%%%%%%%%%%%%%%%%%%%%%%% Plot cartoon head, ears, nose %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1307 | % 1308 | if headrad > 0 % if cartoon head to be plotted 1309 | % 1310 | %%%%%%%%%%%%%%%%%%% Plot head outline %%%%%%%%%%%%%%%%%%%%%%%%%%% 1311 | % 1312 | headx = [[rx(:)' rx(1) ]*(hin+hwidth) [rx(:)' rx(1)]*hin]; 1313 | heady = [[ry(:)' ry(1) ]*(hin+hwidth) [ry(:)' ry(1)]*hin]; 1314 | 1315 | if ~isstr(HEADCOLOR) | ~strcmpi(HEADCOLOR,'none') 1316 | ringh= patch(headx,heady,ones(size(headx)),HEADCOLOR,'edgecolor',HEADCOLOR); hold on 1317 | end 1318 | 1319 | % rx = sin(circ); rX = rx(end:-1:1); 1320 | % ry = cos(circ); rY = ry(end:-1:1); 1321 | % for k=2:2:CIRCGRID 1322 | % rx(k) = rx(k)*(1+hwidth); 1323 | % ry(k) = ry(k)*(1+hwidth); 1324 | % end 1325 | % f3= fill(hin*[rx rX],hin*[ry rY],HEADCOLOR,'edgecolor',HEADCOLOR); hold on 1326 | % f4= fill(hin*[rx rX*(1+hwidth)],hin*[ry rY*(1+hwidth)],HEADCOLOR,'edgecolor',HEADCOLOR); 1327 | 1328 | % Former line-style head 1329 | % plot(cos(circ).*squeezefac*headrad,sin(circ).*squeezefac*headrad,... 1330 | % 'color',HEADCOLOR,'Linestyle','-','LineWidth',HLINEWIDTH); % plot head outline 1331 | 1332 | % 1333 | %%%%%%%%%%%%%%%%%%% Plot ears and nose %%%%%%%%%%%%%%%%%%%%%%%%%%% 1334 | % 1335 | base = rmax-.0046; 1336 | basex = 0.18*rmax; % nose width 1337 | tip = 1.15*rmax; 1338 | tiphw = .04*rmax; % nose tip half width 1339 | tipr = .01*rmax; % nose tip rounding 1340 | q = .04; % ear lengthening 1341 | EarX = [.497-.005 .510 .518 .5299 .5419 .54 .547 .532 .510 .489-.005]; % rmax = 0.5 1342 | EarY = [q+.0555 q+.0775 q+.0783 q+.0746 q+.0555 -.0055 -.0932 -.1313 -.1384 -.1199]; 1343 | sf = headrad/plotrad; % squeeze the model ears and nose 1344 | % by this factor 1345 | if ~isstr(HEADCOLOR) | ~strcmpi(HEADCOLOR,'none') 1346 | plot3([basex;tiphw;0;-tiphw;-basex]*sf,[base;tip-tipr;tip;tip-tipr;base]*sf,... 1347 | 2*ones(size([basex;tiphw;0;-tiphw;-basex])),... 1348 | 'Color',HEADCOLOR,'LineWidth',HLINEWIDTH); % plot nose 1349 | plot3(EarX*sf,EarY*sf,2*ones(size(EarX)),'color',HEADCOLOR,'LineWidth',HLINEWIDTH) % plot left ear 1350 | plot3(-EarX*sf,EarY*sf,2*ones(size(EarY)),'color',HEADCOLOR,'LineWidth',HLINEWIDTH) % plot right ear 1351 | end 1352 | end 1353 | 1354 | % 1355 | % %%%%%%%%%%%%%%%%%%% Show electrode information %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1356 | % 1357 | plotax = gca; 1358 | axis square % make plotax square 1359 | axis off 1360 | 1361 | pos = get(gca,'position'); 1362 | xlm = get(gca,'xlim'); 1363 | ylm = get(gca,'ylim'); 1364 | % textax = axes('position',pos,'xlim',xlm,'ylim',ylm); % make new axes so clicking numbers <-> labels 1365 | % will work inside head cartoon patch 1366 | % axes(textax); 1367 | axis square % make textax square 1368 | 1369 | pos = get(gca,'position'); 1370 | set(plotax,'position',pos); 1371 | 1372 | xlm = get(gca,'xlim'); 1373 | set(plotax,'xlim',xlm); 1374 | 1375 | ylm = get(gca,'ylim'); 1376 | set(plotax,'ylim',ylm); % copy position and axis limits again 1377 | 1378 | axis equal; 1379 | set(gca, 'xlim', [-0.525 0.525]); set(plotax, 'xlim', [-0.525 0.525]); 1380 | set(gca, 'ylim', [-0.525 0.525]); set(plotax, 'ylim', [-0.525 0.525]); 1381 | 1382 | %get(textax,'pos') % test if equal! 1383 | %get(plotax,'pos') 1384 | %get(textax,'xlim') 1385 | %get(plotax,'xlim') 1386 | %get(textax,'ylim') 1387 | %get(plotax,'ylim') 1388 | 1389 | if isempty(EMARKERSIZE) 1390 | EMARKERSIZE = 10; 1391 | if length(y)>=160 1392 | EMARKERSIZE = 3; 1393 | elseif length(y)>=128 1394 | EMARKERSIZE = 3; 1395 | elseif length(y)>=100 1396 | EMARKERSIZE = 3; 1397 | elseif length(y)>=80 1398 | EMARKERSIZE = 4; 1399 | elseif length(y)>=64 1400 | EMARKERSIZE = 5; 1401 | elseif length(y)>=48 1402 | EMARKERSIZE = 6; 1403 | elseif length(y)>=32 1404 | EMARKERSIZE = 8; 1405 | end 1406 | end 1407 | % 1408 | %%%%%%%%%%%%%%%%%%%%%%%% Mark electrode locations only %%%%%%%%%%%%%%%%%%%%%%%%%% 1409 | % 1410 | ELECTRODE_HEIGHT = 2.1; % z value for plotting electrode information (above the surf) 1411 | 1412 | if strcmp(ELECTRODES,'on') % plot electrodes as spots 1413 | if isempty(EMARKER2CHANS) 1414 | hp2 = plot3(y,x,ones(size(x))*ELECTRODE_HEIGHT,... 1415 | EMARKER,'Color',ECOLOR,'markersize',EMARKERSIZE,'linewidth',EMARKERLINEWIDTH); 1416 | else % plot markers for normal chans and EMARKER2CHANS separately 1417 | hp2 = plot3(y(mark1chans),x(mark1chans),ones(size((mark1chans)))*ELECTRODE_HEIGHT,... 1418 | EMARKER,'Color',ECOLOR,'markersize',EMARKERSIZE,'linewidth',EMARKERLINEWIDTH); 1419 | hp2b = plot3(y(mark2chans),x(mark2chans),ones(size((mark2chans)))*ELECTRODE_HEIGHT,... 1420 | EMARKER2,'Color',EMARKER2COLOR,'markerfacecolor',EMARKER2COLOR,'linewidth',EMARKER2LINEWIDTH,'markersize',EMARKERSIZE2); 1421 | end 1422 | % 1423 | %%%%%%%%%%%%%%%%%%%%%%%% Print electrode labels only %%%%%%%%%%%%%%%%%%%%%%%%%%%% 1424 | % 1425 | elseif strcmp(ELECTRODES,'labels') % print electrode names (labels) 1426 | for i = 1:size(labels,1) 1427 | text(double(y(i)),double(x(i)),... 1428 | ELECTRODE_HEIGHT,labels(i,:),'HorizontalAlignment','center',... 1429 | 'VerticalAlignment','middle','Color',ECOLOR,... 1430 | 'FontSize',EFSIZE) 1431 | end 1432 | % 1433 | %%%%%%%%%%%%%%%%%%%%%%%% Mark electrode locations plus labels %%%%%%%%%%%%%%%%%%% 1434 | % 1435 | elseif strcmp(ELECTRODES,'labelpoint') 1436 | if isempty(EMARKER2CHANS) 1437 | hp2 = plot3(y,x,ones(size(x))*ELECTRODE_HEIGHT,... 1438 | EMARKER,'Color',ECOLOR,'markersize',EMARKERSIZE,'linewidth',EMARKERLINEWIDTH); 1439 | else 1440 | hp2 = plot3(y(mark1chans),x(mark1chans),ones(size((mark1chans)))*ELECTRODE_HEIGHT,... 1441 | EMARKER,'Color',ECOLOR,'markersize',EMARKERSIZE,'linewidth',EMARKERLINEWIDTH); 1442 | hp2b = plot3(y(mark2chans),x(mark2chans),ones(size((mark2chans)))*ELECTRODE_HEIGHT,... 1443 | EMARKER2,'Color',EMARKER2COLOR,'markerfacecolor',EMARKER2COLOR,'linewidth',EMARKER2LINEWIDTH,'markersize',EMARKERSIZE2); 1444 | end 1445 | for i = 1:size(labels,1) 1446 | hh(i) = text(double(y(i)+0.01),double(x(i)),... 1447 | ELECTRODE_HEIGHT,labels(i,:),'HorizontalAlignment','left',... 1448 | 'VerticalAlignment','middle','Color', ECOLOR,'userdata', num2str(allchansind(i)), ... 1449 | 'FontSize',EFSIZE, 'buttondownfcn', ... 1450 | ['tmpstr = get(gco, ''userdata'');'... 1451 | 'set(gco, ''userdata'', get(gco, ''string''));' ... 1452 | 'set(gco, ''string'', tmpstr); clear tmpstr;'] ); 1453 | end 1454 | % 1455 | %%%%%%%%%%%%%%%%%%%%%%% Mark electrode locations plus numbers %%%%%%%%%%%%%%%%%%% 1456 | % 1457 | elseif strcmp(ELECTRODES,'numpoint') 1458 | if isempty(EMARKER2CHANS) 1459 | hp2 = plot3(y,x,ones(size(x))*ELECTRODE_HEIGHT,... 1460 | EMARKER,'Color',ECOLOR,'markersize',EMARKERSIZE,'linewidth',EMARKERLINEWIDTH); 1461 | else 1462 | hp2 = plot3(y(mark1chans),x(mark1chans),ones(size((mark1chans)))*ELECTRODE_HEIGHT,... 1463 | EMARKER,'Color',ECOLOR,'markersize',EMARKERSIZE,'linewidth',EMARKERLINEWIDTH); 1464 | hp2b = plot3(y(mark2chans),x(mark2chans),ones(size((mark2chans)))*ELECTRODE_HEIGHT,... 1465 | EMARKER2,'Color',EMARKER2COLOR,'markerfacecolor',EMARKER2COLOR,'linewidth',EMARKER2LINEWIDTH,'markersize',EMARKERSIZE2); 1466 | end 1467 | for i = 1:size(labels,1) 1468 | hh(i) = text(double(y(i)+0.01),double(x(i)),... 1469 | ELECTRODE_HEIGHT,num2str(allchansind(i)),'HorizontalAlignment','left',... 1470 | 'VerticalAlignment','middle','Color', ECOLOR,'userdata', labels(i,:) , ... 1471 | 'FontSize',EFSIZE, 'buttondownfcn', ... 1472 | ['tmpstr = get(gco, ''userdata'');'... 1473 | 'set(gco, ''userdata'', get(gco, ''string''));' ... 1474 | 'set(gco, ''string'', tmpstr); clear tmpstr;'] ); 1475 | end 1476 | % 1477 | %%%%%%%%%%%%%%%%%%%%%% Print electrode numbers only %%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1478 | % 1479 | elseif strcmp(ELECTRODES,'numbers') 1480 | for i = 1:size(labels,1) 1481 | text(double(y(i)),double(x(i)),... 1482 | ELECTRODE_HEIGHT,int2str(allchansind(i)),'HorizontalAlignment','center',... 1483 | 'VerticalAlignment','middle','Color',ECOLOR,... 1484 | 'FontSize',EFSIZE) 1485 | end 1486 | % 1487 | %%%%%%%%%%%%%%%%%%%%%% Mark emarker2 electrodes only %%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1488 | % 1489 | elseif strcmp(ELECTRODES,'off') & ~isempty(EMARKER2CHANS) 1490 | hp2b = plot3(y(mark2chans),x(mark2chans),ones(size((mark2chans)))*ELECTRODE_HEIGHT,... 1491 | EMARKER2,'Color',EMARKER2COLOR,'markerfacecolor',EMARKER2COLOR,'linewidth',EMARKER2LINEWIDTH,'markersize',EMARKERSIZE2); 1492 | end 1493 | % 1494 | %%%%%%%% Mark specified electrode locations with red filled disks %%%%%%%%%%%%%%%%%%%%%% 1495 | % 1496 | try, 1497 | if strcmpi(STYLE,'blank') % if mark-selected-channel-locations mode 1498 | for kk = 1:length(1:length(x)) 1499 | if Values(kk) == 3 1500 | hp2 = plot3(y(kk),x(kk),ELECTRODE_HEIGHT,EMARKER,'Color', [0 0 0], 'markersize', EMARKERSIZE1CHAN); 1501 | elseif Values(kk) == 2 1502 | hp2 = plot3(y(kk),x(kk),ELECTRODE_HEIGHT,EMARKER,'Color', [0.5 0 0], 'markersize', EMARKERSIZE1CHAN); 1503 | elseif Values(kk) == 1 1504 | hp2 = plot3(y(kk),x(kk),ELECTRODE_HEIGHT,EMARKER,'Color', [1 0 0], 'markersize', EMARKERSIZE1CHAN); 1505 | elseif strcmpi(ELECTRODES,'on') 1506 | hp2 = plot3(y(kk),x(kk),ELECTRODE_HEIGHT,EMARKER,'Color', ECOLOR, 'markersize', EMARKERSIZE); 1507 | end 1508 | end 1509 | end 1510 | catch, end; 1511 | % 1512 | %%%%%%%%%%%%%%%%%%%%%%%%%%% Plot dipole(s) on the scalp map %%%%%%%%%%%%%%%%%%%%%%%%%%%% 1513 | % 1514 | if ~isempty(DIPOLE) 1515 | hold on; 1516 | tmp = DIPOLE; 1517 | if isstruct(DIPOLE) 1518 | if ~isfield(tmp,'posxyz') 1519 | error('dipole structure is not an EEG.dipfit.model') 1520 | end 1521 | DIPOLE = []; % Note: invert x and y from dipplot usage 1522 | DIPOLE(:,1) = -tmp.posxyz(:,2)/DIPSPHERE; % -y -> x 1523 | DIPOLE(:,2) = tmp.posxyz(:,1)/DIPSPHERE; % x -> y 1524 | DIPOLE(:,3) = -tmp.momxyz(:,2); 1525 | DIPOLE(:,4) = tmp.momxyz(:,1); 1526 | else 1527 | DIPOLE(:,1) = -tmp(:,2); % same for vector input 1528 | DIPOLE(:,2) = tmp(:,1); 1529 | DIPOLE(:,3) = -tmp(:,4); 1530 | DIPOLE(:,4) = tmp(:,3); 1531 | end; 1532 | for index = 1:size(DIPOLE,1) 1533 | if ~any(DIPOLE(index,:)) 1534 | DIPOLE(index,:) = []; 1535 | end 1536 | end; 1537 | DIPOLE(:,1:4) = DIPOLE(:,1:4)*rmax*(rmax/plotrad); % scale radius from 1 -> rmax (0.5) 1538 | DIPOLE(:,3:end) = (DIPOLE(:,3:end))*rmax/100000*(rmax/plotrad); 1539 | if strcmpi(DIPNORM, 'on') 1540 | for index = 1:size(DIPOLE,1) 1541 | DIPOLE(index,3:4) = DIPOLE(index,3:4)/norm(DIPOLE(index,3:end))*0.2; 1542 | end; 1543 | end; 1544 | DIPOLE(:, 3:4) = DIPORIENT*DIPOLE(:, 3:4)*DIPLEN; 1545 | 1546 | PLOT_DIPOLE=1; 1547 | if sum(DIPOLE(1,3:4).^2) <= 0.00001 1548 | if strcmpi(VERBOSE,'on') 1549 | fprintf('Note: dipole is length 0 - not plotted\n') 1550 | end 1551 | PLOT_DIPOLE = 0; 1552 | end 1553 | if 0 % sum(DIPOLE(1,1:2).^2) > plotrad 1554 | if strcmpi(VERBOSE,'on') 1555 | fprintf('Note: dipole is outside plotting area - not plotted\n') 1556 | end 1557 | PLOT_DIPOLE = 0; 1558 | end 1559 | if PLOT_DIPOLE 1560 | for index = 1:size(DIPOLE,1) 1561 | hh = plot( DIPOLE(index, 1), DIPOLE(index, 2), '.'); 1562 | set(hh, 'color', DIPCOLOR, 'markersize', DIPSCALE*30); 1563 | hh = line( [DIPOLE(index, 1) DIPOLE(index, 1)+DIPOLE(index, 3)]', ... 1564 | [DIPOLE(index, 2) DIPOLE(index, 2)+DIPOLE(index, 4)]',[10 10]); 1565 | set(hh, 'color', DIPCOLOR, 'linewidth', DIPSCALE*30/7); 1566 | end; 1567 | end; 1568 | end; 1569 | 1570 | end % if ~ 'gridplot' 1571 | 1572 | % 1573 | %%%%%%%%%%%%% Plot axis orientation %%%%%%%%%%%%%%%%%%%% 1574 | % 1575 | if strcmpi(DRAWAXIS, 'on') 1576 | axes('position', [0 0.85 0.08 0.1]); 1577 | axis off; 1578 | coordend1 = sqrt(-1)*3; 1579 | coordend2 = -3; 1580 | coordend1 = coordend1*exp(sqrt(-1)*rotate); 1581 | coordend2 = coordend2*exp(sqrt(-1)*rotate); 1582 | 1583 | line([5 5+round(real(coordend1))]', [5 5+round(imag(coordend1))]', 'color', 'k'); 1584 | line([5 5+round(real(coordend2))]', [5 5+round(imag(coordend2))]', 'color', 'k'); 1585 | if round(real(coordend2))<0 1586 | text( 5+round(real(coordend2))*1.2, 5+round(imag(coordend2))*1.2-2, '+Y'); 1587 | else text( 5+round(real(coordend2))*1.2, 5+round(imag(coordend2))*1.2, '+Y'); 1588 | end; 1589 | if round(real(coordend1))<0 1590 | text( 5+round(real(coordend1))*1.2, 5+round(imag(coordend1))*1.2+1.5, '+X'); 1591 | else text( 5+round(real(coordend1))*1.2, 5+round(imag(coordend1))*1.2, '+X'); 1592 | end; 1593 | set(gca, 'xlim', [0 10], 'ylim', [0 10]); 1594 | end; 1595 | 1596 | % 1597 | %%%%%%%%%%%%% Set EEGLAB background color to match head border %%%%%%%%%%%%%%%%%%%%%%%% 1598 | % 1599 | try, 1600 | icadefs; 1601 | set(gcf, 'color', BACKCOLOR); 1602 | catch, 1603 | end; 1604 | 1605 | hold off 1606 | axis off 1607 | % set(get(h,'children'),'hittest','off') 1608 | set(get(h,'children'),'buttonDownFcn',get(h,'buttonDownFcn')) 1609 | return 1610 | 1611 | -------------------------------------------------------------------------------- /zipper.m: -------------------------------------------------------------------------------- 1 | % info 2 | name = 'ICLabel'; 3 | filetext = fileread('eegplugin_iclabel.m'); 4 | version = regexp(filetext, 'vers = ''ICLabel(\d+\.\d+\.?\d*)'';', 'tokens'); 5 | version = version{1}{1}; 6 | 7 | % enumerate files/directories 8 | files = dir(); 9 | file_names = {files.name}'; 10 | file_names = file_names(cellfun(@isempty, ... 11 | regexp(file_names,['^\.|\.md$|~$|^' name '.*\.zip$|zipper.m|tests']))); 12 | 13 | % create zip file 14 | zip([name version '.zip'], file_names) --------------------------------------------------------------------------------