Authored by Francois Tadel

Making bad channel detection portable

@@ -172,12 +172,12 @@ uicontrol(Fmenu,'Style','Text','String',SPMc,... @@ -172,12 +172,12 @@ uicontrol(Fmenu,'Style','Text','String',SPMc,...
172 %-Data 172 %-Data
173 %----------------------------------------------------------------------- 173 %-----------------------------------------------------------------------
174 uicontrol(Fmenu,'Style','PopUp',... 174 uicontrol(Fmenu,'Style','PopUp',...
175 - 'String','Import| Convert| Electrodes positions| Bipolar montage| Bad channels| Events| Set time origin',... 175 + 'String','Import| Convert| Electrodes positions| Bipolar montage| Set bad channels| Detect bad channels| Events| Set time origin',...
176 'Position',[035 379 100 030].*WS,... 176 'Position',[035 379 100 030].*WS,...
177 'FontSize', FS(9), ... 177 'FontSize', FS(9), ...
178 'ToolTipString', 'Convert data in SPM EEG format',... 178 'ToolTipString', 'Convert data in SPM EEG format',...
179 'CallBack','spm(''PopUpCB'',gcbo)',... 179 'CallBack','spm(''PopUpCB'',gcbo)',...
180 - 'UserData',{'ImaGIN_spm_eeg_converteeg2mat;','ImaGIN_Electrode;','ImaGIN_BipolarMontage;','ImaGIN_BadChannelSet;','ImaGIN_Events;','ImaGIN_TimeZero;'},... 180 + 'UserData',{'ImaGIN_spm_eeg_converteeg2mat;','ImaGIN_Electrode;','ImaGIN_BipolarMontage;','ImaGIN_BadChannelSet;','ImaGIN_BadChannel;','ImaGIN_Events;','ImaGIN_TimeZero;'},...
181 'Tag', 'EEG'); 181 'Tag', 'EEG');
182 182
183 uicontrol(Fmenu,'Style','PopUp',... 183 uicontrol(Fmenu,'Style','PopUp',...
1 function D = ImaGIN_BadChannel(S) 1 function D = ImaGIN_BadChannel(S)
2 -% Extract badchannels indices 2 +% Detect bad channels indices with a trained classifier.
  3 +%
3 % -============================================================================= 4 % -=============================================================================
4 % This function is part of the ImaGIN software: 5 % This function is part of the ImaGIN software:
5 % https://f-tract.eu/ 6 % https://f-tract.eu/
@@ -16,62 +17,96 @@ function D = ImaGIN_BadChannel(S) @@ -16,62 +17,96 @@ function D = ImaGIN_BadChannel(S)
16 % 17 %
17 % Authors: Viateur Tuyisenge & Olivier David 18 % Authors: Viateur Tuyisenge & Olivier David
18 19
19 -try  
20 - FileIn = S.dataset; % name of cropped iEEG file .dat/.mat  
21 - %badDir = S.DirFileOut; % directory where iEEG file .dat/.mat with badchannels indices will be stored  
22 - trainDir = S.trainBase; % directory where training set is stored  
23 - try  
24 - D = spm_eeg_load(FileIn); % Load the cropped meeg object  
25 - catch  
26 - FileIn = spm_select(1, '\.mat$', 'Select data file');  
27 - D=spm_eeg_load(FileIn); 20 +% Name of input iEEG file .dat/.mat (cropped)
  21 +if (nargin >= 1) && isfield(S, 'dataset') && ~isempty(S.dataset)
  22 + FileIn = S.dataset;
  23 +else
  24 + FileIn = spm_select(1, '\.mat$', 'Select EEG mat file');
  25 + if isempty(FileIn)
  26 + return;
28 end 27 end
  28 +end
  29 +
  30 +% Directory where iEEG file .dat/.mat with badchannels indices will be stored
  31 +if (nargin >= 1) && isfield(S, 'FileOut') && ~isempty(S.FileOut)
  32 + [badDir, FileOut] = fileparts(S.FileOut);
  33 + isNewFile = 1;
  34 +else
  35 + [badDir, FileOut] = fileparts(FileIn);
  36 + isNewFile = 0;
  37 +end
  38 +
  39 +% Directory where training set is stored (if not defined, use the toolbox folder)
  40 +if (nargin >= 1) && isfield(S, 'trainBase') && ~isempty(S.trainBase)
  41 + trainDir = S.trainBase;
  42 +else
  43 + trainDir = fileparts(mfilename('fullpath'));
  44 +end
  45 +
  46 +
  47 +% Detect bad channels and save summary images
  48 +try
  49 + % Load the cropped meeg object
  50 + D = spm_eeg_load(FileIn);
29 51
30 - [badDir,FileOut,~] = fileparts(S.FileOut);  
31 - bPrefix = strcat(badDir,'/',FileOut);  
32 - 52 + % Compute signal features (apply artifact correction only for stims)
33 clear S2; 53 clear S2;
34 S2.FileName = FileIn; 54 S2.FileName = FileIn;
35 - 55 + if ~isempty(D.events) && ~isempty(D.events.type) && ismember('Stim', {D.events.type})
  56 + S2.InterpolationFilter = 1;
  57 + else
  58 + S2.InterpolationFilter = 0;
  59 + end
  60 + T = ImaGIN_FeatureSEEG(S2);
36 61
37 -% T = ImaGIN_FeatureSEEG(S2); % function returns a table T of features 62 + % If the trained classifier is not available: compute it
  63 + trainedFile = fullfile(trainDir, 'ImaGIN_trainedClassifier.mat');
  64 + if ~exist(trainedFile, 'file')
  65 + % Get train base file
  66 + trainBaseFile = fullfile(trainDir, 'ImaGIN_trainBaseFeatures.mat');
  67 + if ~exist(trainBaseFile, 'file')
  68 + error(['File ' trainBaseFile ' was not found.']);
  69 + end
  70 + % Load the train base
  71 + trainBase = load(trainBaseFile);
  72 + % Train the classifier
  73 + trainedClassifier = ImaGIN_trainClassifier(trainBase.predictors, trainBase.response);
  74 + % Otherwise, load the trained classifier
  75 + else
  76 + trainedClassifier = load(trainedFile);
  77 + end
38 78
39 -% Tbase = readtable(strcat(trainDir, '/trainBaseFeatures.csv')); % load training base  
40 -% trainingData = Tbase(:,2:9);  
41 -% [trainedClassifier, ~] = ImaGIN_trainClassifier(trainingData); % train the model  
42 - %Uncomment the following line to use the trained model  
43 -  
44 -% load(strcat(trainDir, '/ImaGIN_trainedClassifier.mat')) % load the trained classifier  
45 -% channelClass = trainedClassifier.predictFcn(T(:,2:8)); % predict new dataset  
46 -% bd = strcmp(channelClass','Bad);  
47 -  
48 - bd = strcmp('channelClass','Bad'); 79 + % Predict new dataset
  80 + channelClass = trainedClassifier.predictFcn(T(:,2:8));
  81 + % Get list of detected bad channels
  82 + bIdx = find(strcmp(channelClass, 'Bad'));
49 83
50 - bIdx = find(bd);  
51 - badFile = fopen(strcat(bPrefix,'_bIdx.txt'),'w'); % Save badchannel indices in .txt file  
52 - fprintf(badFile,'%d\n',bIdx(:)); 84 + % Save bad channel indices in .txt file
  85 + badFile = fopen(fullfile(badDir, [FileOut, '_bIdx.txt']), 'w');
  86 + fprintf(badFile, '%d\n', bIdx(:));
53 fclose(badFile); 87 fclose(badFile);
54 88
55 -% Tnew = [T channelClass];  
56 -% Tnew.Properties.VariableNames{'Var9'} = 'Note';  
57 -% csvfilename = strcat(bPrefix,'.csv'); % Save feature table & badchannels indices  
58 -% writetable(Tnew,csvfilename,'Delimiter',',');  
59 - 89 + % Add badchannel index in meeg object
60 if ~isempty(bIdx) 90 if ~isempty(bIdx)
61 - D = badchannels(D,bIdx,1); %Add badchannel index in meeg object 91 + D = badchannels(D,bIdx,1);
  92 + end
  93 + % Create new file in output (otherwise, simply update the input file)
  94 + if isNewFile
  95 + Dbad = clone(D, fullfile(badDir, FileOut), [D.nchannels D.nsamples D.ntrials]); % save meeg with badchannel indices in Badchannel directory
  96 + Dbad(:,:,:) = D(:,:,:);
62 end 97 end
63 - Dbad = clone(D, bPrefix, [D.nchannels D.nsamples D.ntrials]); % save meeg with badchannel indices in Badchannel directory  
64 - Dbad(:,:,:) = D(:,:,:); 98 + % Save modified list of bad channels
65 save(Dbad); 99 save(Dbad);
66 - %Channel plots and ScreenShots  
67 - figDir = strcat(badDir, '/ScreenShot'); 100 +
  101 + % Channel plots and ScreenShots
  102 + figDir = fullfile(badDir, 'ScreenShot');
68 if ~exist(figDir, 'dir') 103 if ~exist(figDir, 'dir')
69 mkdir(figDir); 104 mkdir(figDir);
70 end 105 end
71 106
72 close all; 107 close all;
73 108
74 - Size = 8; %Number of channels per screenshot 109 + Size = 8; % Number of channels per screenshot
75 n_c = size(D,1); 110 n_c = size(D,1);
76 if n_c >= Size 111 if n_c >= Size
77 tmp = floor(n_c/Size); 112 tmp = floor(n_c/Size);
@@ -148,8 +183,9 @@ try @@ -148,8 +183,9 @@ try
148 close 183 close
149 end 184 end
150 set_final_status('OK') 185 set_final_status('OK')
  186 +
151 catch ME 187 catch ME
152 - fprintf('%s \n',ME.message) 188 + fprintf('%s \n', ME.message)
153 set_final_status('NOK') 189 set_final_status('NOK')
154 end 190 end
155 191
@@ -12,24 +12,29 @@ function T = ImaGIN_FeatureSEEG(S) @@ -12,24 +12,29 @@ function T = ImaGIN_FeatureSEEG(S)
12 % 12 %
13 % Copyright (c) 2000-2018 Inserm U1216 13 % Copyright (c) 2000-2018 Inserm U1216
14 % =============================================================================- 14 % =============================================================================-
15 -% 15 +%
16 % Authors: Viateur Tuyisenge & Olivier David 16 % Authors: Viateur Tuyisenge & Olivier David
17 17
18 sFile = S.FileName; 18 sFile = S.FileName;
19 -[pth, fName, ~] = fileparts(sFile);  
20 -clear S  
21 -S.Fname = sFile;  
22 -S.EventType = 'Stim';  
23 -%S.StartInterpolation= -0.015;  
24 -%S.EndInterpolation = 0.015;  
25 -S.StartInterpolation = -0.008;  
26 -S.EndInterpolation = 0.008;  
27 -  
28 -D = ImaGIN_InterpolationFilter(S);  
29 -clear S  
30 -  
31 -iFile = [pth '/i' fName];  
32 -nFile = [pth '/ni' fName]; 19 +[pth, fName] = fileparts(sFile);
  20 +
  21 +% Apply interpolation filter to remove artifact (unless disabled)
  22 +if ~isfield(S, 'InterpolationFilter') || isempty(S.InterpolationFilter) || S.InterpolationFilter
  23 + clear S
  24 + S.Fname = sFile;
  25 + S.EventType = 'Stim';
  26 + %S.StartInterpolation= -0.015;
  27 + %S.EndInterpolation = 0.015;
  28 + S.StartInterpolation = -0.008;
  29 + S.EndInterpolation = 0.008;
  30 + D = ImaGIN_InterpolationFilter(S);
  31 + strInterp = 'i';
  32 +else
  33 + strInterp = '';
  34 +end
  35 +
  36 +iFile = [pth '/' strInterp fName];
  37 +nFile = [pth '/n' strInterp fName];
33 P.Fname = iFile; 38 P.Fname = iFile;
34 P.Freq = 50; 39 P.Freq = 50;
35 ImaGIN_NotchFilter(P) % notch filter 50Hz 40 ImaGIN_NotchFilter(P) % notch filter 50Hz
@@ -38,19 +43,21 @@ P.Fname = nFile; @@ -38,19 +43,21 @@ P.Fname = nFile;
38 P.Freq = 100; 43 P.Freq = 100;
39 ImaGIN_NotchFilter(P) % notch filter 100Hz 44 ImaGIN_NotchFilter(P) % notch filter 100Hz
40 clear P.Freq; 45 clear P.Freq;
  46 +if ~isempty(strInterp)
  47 + delete([iFile '.*']);
  48 +end
41 delete([nFile '.*']); 49 delete([nFile '.*']);
42 -delete([iFile '.*']);  
43 -nnFile = [pth '/nni' fName]; 50 +nnFile = [pth '/nn' strInterp fName];
44 P.Fname = nnFile; 51 P.Fname = nnFile;
45 P.Freq = 150; 52 P.Freq = 150;
46 ImaGIN_NotchFilter(P) % notch filter 150Hz 53 ImaGIN_NotchFilter(P) % notch filter 150Hz
47 clear P.Freq; 54 clear P.Freq;
48 delete([nnFile,'.*']); 55 delete([nnFile,'.*']);
49 56
50 -nnnFile = [pth '/nnni' fName]; 57 +nnnFile = [pth '/nnn' strInterp fName];
51 P.LFname = nnnFile; 58 P.LFname = nnnFile;
52 ImaGIN_LowPassFilter(P) % lowpass filter 0.2Hz 59 ImaGIN_LowPassFilter(P) % lowpass filter 0.2Hz
53 -lpf_nFile = [pth '/lpf_nnni' fName]; 60 +lpf_nFile = [pth '/lpf_nnn' strInterp fName];
54 delete([nnnFile,'.*']) 61 delete([nnnFile,'.*'])
55 62
56 D = spm_eeg_load(lpf_nFile); 63 D = spm_eeg_load(lpf_nFile);
No preview for this file type