Please note that this page is outdated!
If you are interested in the spike detection code, please refer to my new page and follow the "Software" link, or go directly to
the updated page.
Previous.
The problem of detecting transient signals given a collection of noisy
observations has been studied for decades. The presence of a useful signal
in a background noise is normally cast as a hypothesis testing, where under
the null hypothesis no signal is present.
If the signal to be detected is
not perfectly known, usually no uniformly most powerful (UMP) test
exists, and the performance of a detector depends on
signal representation. In general, a signal representation can be model
based and expansion based. When no apropriate model for the signal can be
found,
one usually resorts to a "canonical set" of basis function where the signal
is
projected, giving rise to expansion coefficients. We can think of these
coefficients as of signal representation in a new coordinate system.
Depending on the signal representation the
detection problem can be formulated in different domains such as time domain,
frequency domain and time-frequency domain. In time-frequency domain, a
signal is projected onto a basis of waveforms that are well localized
(subject to Hiesenberg uncertainty principle) in both
time and frequency, yielding a two-dimensional signal
representation Tx(w,t) of a one-dimensional signal
x(t).
An example of this
representation is a windowed Fourier
transform introduced by
Gabor. A
breakthrough in the theory of wavelets offered
a powerful alternative to windowed Fourier transform, where a one-dimensional
signal
x(t) is represented in time-scale domain by virtue of a wavelet
transform Tx(a,b).
Software
All files are MATLABŪ M-files or MAT-files. You will need Wavelet Toolbox
installed in order to execute the main function.
detect_spikes_wavelet.m (6.6 KB)
get_score.m (1.4 KB)
lag_ts.m (0.7 KB)
lead_ts.m (0.7 KB)
clean.mat (160 KB)
corrupted.mat (160 KB)
Tutorial
detect_spikes_wavelet
is a stand-alone function for detection of
neural transients in noisy data. The other files are for tutorial purposes
only.
>> load clean.mat
>> who
Your variables are:
Signal
Your workspace now contains a variable Signal
,
which is a row vector of neural
data to be analyzed. This data is artififially created, and contains 10 equally
spaced spikes (transients). The attribute "clean" indicates that the data
does not contain any noise. You can visualize the signal by
>> plot(Signal)
>> set(gca,'XLim',[1 length(Signal)],'YLim',[-3 3])
The produced figure is shown below (click on the figure for full size image)
The sampling rate of the generated signal is 20 [kHz], so the figure
corresponds to 1 second of data.
Close inspection of individual spikes shows that they consist of two sinusoid
semicycles of different amplitudes. The "width" of the spikes is set to 0.6
[msec]. Running the detection algorithm on Signal
is not particularly
challenging, but reveals the true times of spike occurrences.
>> TT=detect_spikes_wavelet(Signal,20,[0.5 1.0],'reset',0.1,'bior1.5',1,1)
wavelet family: bior1.5
10 spikes found
elapsed time: 1.7704
TT =
  Columns 1 through 6
     1003   3003   5003   7003   9003    11003
   Columns 7 through 10
     13003   15003   17003   19003
The last two arguments in the function are plot and comment flags. To supress
comments and plots, you should pass a number different from 1, for example
>> TT=detect_spikes_wavelet(Signal,20,[0.5 1.0],'reset',0.1,'bior1.5',0,1);
produces comments but not plots, and
>> TT=detect_spikes_wavelet(Signal,20,[0.5 1.0],'reset',0.1,'bior1.5',1,0);
produces plots but not comments. If you opt for plots, the program generates
two figures which are shown below.
The upper plot shows the time-scale representation of the signal, where the
absolute values of wavelet coefficients are color coded. At the bottom of
the same plot are the locations of the detected spikes. By looking at the full
size version of this figure, it is obvious that the spikes correspond to
"large" coefficients across different scales. The bottom figure shows the
wavelet coefficients at
different scales that cross a detection threshold. The coefficients at
different scales are represented by different colors, e.g red, blue, green
etc. The coefficients that do not cross the detection threshold are shrunk to
0.
We now take the time series from above and corrupt it by an additive white
noise, with a standard deviation determined from desired signal-to-noise
ratio (SNR).
>> load corrupted.mat
>> plot(Signal)
>> set(gca,'XLim',[1 length(Signal)],'YLim',[-3 3])
In the outcome of the code above, we obtain the figure of the noisy spike
train (see below). Black squares indicate the positions of the spikes.
Clearly, a
simple detection based on amplitude thresholding could not detect "short"
spikes (spikes 1, 5 and 7) without accepting a number of false positives.
Then, we detect the spikes in the noisy data by
>> TE=detect_spikes_wavelet(Signal,20,[0.5 1.0],'reset',0.1,'bior1.5',1,0);
which results in the following figures:
As can be seen from the figure above, one of the spikes (spike 7) was not detected. This can be verfied by running the function get_score.m
which
returns the number of correctly detected spikes, the number of omissions as well as the number of false positives. Indeed,
>> get_score(TT,TE,20)
ans =
      9     1     0
indicating that there are 9 correctly detected spikes, 1 omission and 0 false positives. You can trade-off omissions and false positives by varying the parameter L (type help detect_spikes_wavelet
to learn more about this
parameter), i.e.
>> TE=detect_spikes_wavelet(Signal,20,[0.5 1.0],'reset',0.0,'bior1.5',0,0);
>> get_score(TT,TE,20)
ans =
      9     1     3
>> TE=detect_spikes_wavelet(Signal,20,[0.5 1.0],'reset',-0.03,'bior1.5',0,0);
>> get_score(TT,TE,20)
ans =
      10     0     4
You can now play with different choice of wavelet functions, such as
'bior1.3' 'db2' 'haar'
etc., and compare their performances.