Skip to content

Commit 804e2fb

Browse files
committed
Remove SPES artifacts with process_cutstim
1 parent 919d281 commit 804e2fb

2 files changed

Lines changed: 47 additions & 139 deletions

File tree

toolbox/process/functions/process_cutstim.m

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
% =============================================================================@
2121
%
2222
% Authors: Francois Tadel, 2010-2017
23+
% Raymundo Cassani, 2026
2324

2425
eval(macro_method);
2526
end
@@ -28,7 +29,7 @@
2829
%% ===== GET DESCRIPTION =====
2930
function sProcess = GetDescription() %#ok<DEFNU>
3031
% Description the process
31-
sProcess.Comment = 'Cut stimulation artifact';
32+
sProcess.Comment = 'Remove stimulation artifact';
3233
sProcess.FileTag = 'cutstim';
3334
sProcess.Category = 'Filter';
3435
sProcess.SubGroup = 'Artifacts';
@@ -65,6 +66,12 @@
6566
sProcess.options.method.Type = 'combobox_label';
6667
sProcess.options.method.Value = {'linear', {'linear', 'spline', 'pchip', 'v5cubic', 'makima'; ...
6768
'linear', 'spline', 'pchip', 'v5cubic', 'makima'}};
69+
sProcess.options.method.Controller = struct('spline', 'Spline');
70+
% === Spline: time buffer taken on each side of the artifact window for interpolation
71+
sProcess.options.splinebuffer.Comment = 'Spline interpolation buffer around artifact time window: <BR> (Buffer = 0, entire signal is used)';
72+
sProcess.options.splinebuffer.Type = 'value';
73+
sProcess.options.splinebuffer.Value = {0.000, 'ms', 0};
74+
sProcess.options.splinebuffer.Class = 'Spline';
6875
end
6976

7077

@@ -105,6 +112,11 @@
105112
else
106113
Method = 'linear';
107114
end
115+
if strcmpi(Method, 'spline') && isfield(sProcess.options, 'splinebuffer') && isfield(sProcess.options.splinebuffer, 'Value') && iscell(sProcess.options.splinebuffer.Value) && ~isempty(sProcess.options.splinebuffer.Value) && ~isempty(sProcess.options.splinebuffer.Value{1})
116+
SplineBuffer = sProcess.options.splinebuffer.Value{1};
117+
else
118+
SplineBuffer = [];
119+
end
108120

109121
% Check inputs
110122
if isempty(TimeBounds)
@@ -131,19 +143,38 @@
131143
sInput = [];
132144
return;
133145
end
134-
% Find event in the list
135-
iEvt = find(strcmpi({sFile.events.label}, EvtName));
136-
if isempty(iEvt) || (size(sFile.events(iEvt).times,2) == 0)
146+
% Find input event in file
147+
iEvts = find(strcmpi({sFile.events.label}, EvtName));
148+
% If not found with exact names, try searching interpreting strings as regular expressions
149+
if isempty(iEvts)
150+
iEvts = find(~cellfun(@isempty, regexp({sFile.events.label}, EvtName)));
151+
end
152+
% Error if no events are found, or none of the found events have occurrences
153+
if isempty(iEvts) || all(arrayfun(@(x) size(x.times,2), sFile.events(iEvts)) == 0)
137154
bst_report('Error', sProcess, [], ['Event not found:' EvtName]);
138155
sInput = [];
139156
return;
140157
end
141-
% Extended events: Use as is
142-
if (size(sFile.events(iEvt).times, 1) == 2)
143-
cutSegments = sFile.events(iEvt).times';
144-
% Simple events: Use the time window definition
145-
else
146-
cutSegments = bst_bsxfun(@plus, [sFile.events(iEvt).times', sFile.events(iEvt).times'], TimeBounds);
158+
warningMsg = '';
159+
cutSegmentsAll = [];
160+
for ix = 1 : length(iEvts)
161+
iEvt = iEvts(ix);
162+
% Report found event with zero occurences
163+
if (size(sFile.events(iEvt).times,2) == 0) && (~isRaw || (sInput.iBlockCol == 1 && sInput.iBlockRow == 1))
164+
warningMsg = [warningMsg, sprintf('Event "%s" has zero occurrences', sFile.events(iEvt).label), 10];
165+
end
166+
% Extended events: Use as is
167+
if (size(sFile.events(iEvt).times, 1) == 2)
168+
cutSegments = sFile.events(iEvt).times';
169+
% Simple events: Use the time window definition
170+
else
171+
cutSegments = bst_bsxfun(@plus, [sFile.events(iEvt).times', sFile.events(iEvt).times'], TimeBounds);
172+
end
173+
cutSegmentsAll = [cutSegmentsAll; cutSegments];
174+
end
175+
cutSegments = cutSegmentsAll;
176+
if ~isempty(warningMsg)
177+
bst_report('Warning', sProcess, [], warningMsg);
147178
end
148179
end
149180
% Default segment: around zero
@@ -162,15 +193,18 @@
162193
end
163194
% Get all the indices except but the removed time window
164195
iValid = setdiff(1:Ntime, iTime);
196+
if ~isempty(SplineBuffer) && SplineBuffer ~= 0
197+
% Use artifact-time-window +- SplineBuffer for interpolation
198+
splineBufferSmp = round(SplineBuffer./diff(sInput.TimeVector(1:2)));
199+
iValid = intersect(iValid, (iTime(1) - splineBufferSmp) : (iTime(end) + splineBufferSmp));
200+
end
165201
if isempty(iValid)
166202
bst_report('Error', sProcess, [], 'No valid time segment left in the file.');
167203
sInput = [];
168204
return;
169205
end
170206
% Reinterpolate values for the removed time window
171-
for iChan = 1:Nchan
172-
sInput.A(iChan,iTime) = interp1(iValid, sInput.A(iChan,iValid), iTime, Method);
173-
end
207+
sInput.A(:,iTime) = interp1(iValid, sInput.A(:,iValid)', iTime, Method)';
174208
end
175209

176210
% Do not keep the Std field in the output

toolbox/process/functions/process_remove_spes_artifacts.m

Lines changed: 0 additions & 126 deletions
This file was deleted.

0 commit comments

Comments
 (0)