Making bad channel detection portable
Showing
4 changed files
with
104 additions
and
61 deletions
@@ -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); |
toolbox/ImaGIN_trainBaseFeatures.mat
0 → 100644
No preview for this file type
-
Please register or login to post a comment