Empirical Mode Decomposition (EMD)

Initialize NeuroAnalyzer
using NeuroAnalyzer
using CairoMakie
CairoMakie.activate!(type = "png")
using FFTW
using Statistics
NeuroAnalyzer.progress_bar = false

# NEURO object with one channel from a signal vector
# (used in the examples below)
function signal2obj(s::Vector{Float64}, fs::Int64)
    obj = create_object(; data_type = "eeg")
    obj.header.recording[:sampling_rate] = fs
    add_channel!(obj;
                 data = reshape(s, 1, :, 1),
                 label = "s",
                 type = "eeg",
                 unit = "μV")
    create_time!(obj; fs = fs)
    return obj
end
# frequency of the largest peak of the amplitude spectrum
function peak_frq(x::AbstractVector, fs::Int64)
    a = abs.(rfft(x))
    return (argmax(a) - 1) * fs / length(x)
end
# Makie figure and axis settings matching NeuroAnalyzer plots
fig_size = (900, 450)
ax_style = (titlesize = 18,
            xlabelsize = 18,
            ylabelsize = 18,
            xticklabelsize = 12,
            yticklabelsize = 12,
            xticks = LinearTicks(10),
            xminorticksvisible = true,
            xminorticks = IntervalsBetween(10),
            xautolimitmargin = (0, 0),
            yautolimitmargin = (0.1, 0.1))

eeg = import_edf("files/eeg/eeg.edf")
keep_channel!(eeg; ch = "eeg")
# filtered 0.5-35 Hz
NeuroAnalyzer.filter!(eeg;
                      ch = "all",
                      fprototype = :butterworth,
                      ftype = :hp,
                      cutoff = 0.5,
                      order = 4)
NeuroAnalyzer.filter!(eeg;
                      ch = "all",
                      fprototype = :fir,
                      ftype = :lp,
                      cutoff = 35,
                      bw = 10)
# 2-second epochs
e2 = epoch(crop(eeg; seg = (100, 160)); ep_len = 2)

Empirical Mode Decomposition (EMD, Huang et al. 1998) splits a signal into a small number of oscillatory components, Intrinsic Mode Functions (IMFs), and a residual (trend):

\[ s(t) = \sum_{i=1}^{n} \mathrm{IMF}_i(t) + r(t) \]

Unlike the Fourier or wavelet transforms, EMD uses no predefined basis functions (sine waves, wavelets): the components are derived from the local maxima and minima of the signal itself (“empirical”). The IMFs need not be stationary or sinusoidal: their amplitude and frequency can change over time, which makes EMD suitable for non-stationary and non-linear signals such as EEG.

An IMF is an oscillation that:

  • has numbers of local extrema and zero crossings that are equal or differ by at most one,
  • is symmetric about zero: the mean of the envelope through its maxima and the envelope through its minima is (close to) zero.

Such a function has a well-defined instantaneous amplitude and frequency, which can be obtained with the Hilbert transform (the Hilbert-Huang transform).

Algorithm

The IMFs are extracted one by one, from the fastest to the slowest oscillation, by sifting:

  1. Find the local maxima and minima of the signal \(h\) (at the start, the signal itself).
  2. Interpolate the maxima and the minima with cubic splines: the upper and lower envelopes.
  3. Subtract the mean of the envelopes \(m\) from \(h\): \(h \leftarrow h - m\).
  4. Repeat steps 1-3 until the change is small: \(SD = \sum m^2 / \sum h^2 < \epsilon\). The result is an IMF.
  5. Subtract the IMF from the signal; the remainder (residual) is decomposed in the same way.

The decomposition stops when the residual has fewer than three maxima or three minima (too few for a reliable envelope: it is treated as the trend), or after max_imf IMFs. The IMFs and the residual sum exactly to the signal.

In emd(), epsilon (default 0.3) sets the stopping criterion of the sifting: smaller values give more sifting iterations and IMFs closer to the IMF definition (narrower-band), but too much sifting removes the amplitude variation of the IMFs. The ends of the signal are a problem for the spline envelopes (there are no extrema beyond them); to reduce end effects, the two outermost extrema at each end are mirrored about the ends of the signal.

Synthetic Signal

A 20 Hz and a 4 Hz sine wave with a linear trend:

fs = 256
t = collect(0:(1 / fs):(4 - 1 / fs))
s = 5 .* sin.(2π * 20 .* t) .+ 10 .* sin.(2π * 4 .* t) .+ 2 .* t
obj = signal2obj(s, fs)
imf = emd(obj; ch = "s", ep = 1)
plot_imf(imf; t = t)

emd() returns a matrix with the IMFs in rows and the residual in the last row; plot_imf() shows the IMFs, the residual and the reconstructed signal.

println("IMFs: ", size(imf, 1) - 1)
for idx in 1:(size(imf, 1) - 1)
    println("IMF $idx: peak at ", peak_frq(imf[idx, :], fs), " Hz, SD ",
            round(std(imf[idx, :]); digits = 2))
end
println("residual - trend: max ",
        round(maximum(abs.(imf[end, :] .- 2 .* t)); digits = 2))
println("reconstruction error: ",
        maximum(abs.(vec(sum(imf; dims = 1)) .- s)))
IMFs: 2
IMF 1: peak at 20.0 Hz, SD 3.52
IMF 2: peak at 4.0 Hz, SD 7.11
residual - trend: max 0.14
reconstruction error: 3.552713678800501e-15

The two sine waves (SD of a sine wave = amplitude / √2: 3.54 and 7.07) are separated into two IMFs and the trend is the residual.

Mode mixing

EMD separates oscillations by their local time scale, not by frequency bands. When a fast oscillation appears only intermittently, the first IMF contains the fast oscillation where it is present and the slower one elsewhere (mode mixing):

s2 = 10 .* sin.(2π * 4 .* t)
# 30 Hz bursts at 1-1.5 s and 2.5-3 s
burst = (1 .<= t .< 1.5) .| (2.5 .<= t .< 3)
s2[burst] .+= 3 .* sin.(2π * 30 .* t[burst])
imf2 = emd(signal2obj(s2, fs); ch = "s", ep = 1)
fig = Figure(size = fig_size)
ax = Axis(fig[1, 1];
          ax_style...,
          title = "IMF 1 of a 4 Hz wave with 30 Hz bursts",
          xlabel = "Time [s]",
          ylabel = "Amplitude [μV]")
lines!(ax, t, imf2[1, :]; color = :black)
fig

Between the bursts, IMF 1 contains the whole 4 Hz wave: the same IMF holds 30 Hz activity in some parts of the signal and 4 Hz activity in others. Variants of EMD reduce mode mixing by adding noise (Ensemble EMD, CEEMDAN); they are not available in NeuroAnalyzer.

EEG

Decomposition of one 2-s epoch of O1:

imf_eeg = emd(e2; ch = "O1", ep = 5)
plot_imf(imf_eeg; t = e2.epoch_time)
x = e2.data[get_channel(e2; ch = "O1")[1], :, 5]
println("signal: SD ", round(std(x); digits = 1), " μV")
for idx in 1:(size(imf_eeg, 1) - 1)
    println("IMF $idx: peak at ", peak_frq(imf_eeg[idx, :], sr(e2)),
            " Hz, SD ", round(std(imf_eeg[idx, :]); digits = 1), " μV")
end
println("residual: SD ", round(std(imf_eeg[end, :]); digits = 1), " μV")
signal: SD 5.9 μV
IMF 1: peak at 21.0 Hz, SD 1.8 μV
IMF 2: peak at 5.0 Hz, SD 2.3 μV
IMF 3: peak at 1.0 Hz, SD 4.6 μV
residual: SD 1.5 μV

The first IMFs contain the fastest activity, the last ones the slowest. Unlike Fourier components, IMFs are not orthogonal: neighboring IMFs can be partly correlated (or anti-correlated, e.g. when a large transient such as an eye blink is split between two IMFs), so their variances need not add up to the variance of the signal. In short epochs, slow oscillations have only a few cycles, so the number of IMFs is small and the slow IMFs are poorly defined (end effects); decompose longer segments when slow activity is of interest.

Tip: IMFs are not frequency bands: their frequency changes over time and differs between epochs and channels, so the “same” IMF number may contain different activity in different epochs. Compare IMFs by their frequency content, not by their number.

Hilbert-Huang Spectrum

The instantaneous frequency and amplitude of each IMF (from its analytic signal) give a time-frequency representation, the Hilbert-Huang spectrum: hhtspectrogram() (or spectrogram(obj; method = :hht)):

h = hhtspectrogram(imf_eeg[1:(end - 1), :], e2.epoch_time; fs = sr(e2))
fig = Figure(size = fig_size)
ax = Axis(fig[1, 1];
          ax_style...,
          title = "O1: Hilbert-Huang spectrum",
          xlabel = "Time [s]",
          ylabel = "Frequency [Hz]")
hm = heatmap!(ax, h.t, h.f, h.p'; colormap = :darktest)
ylims!(ax, 0, 40)
Colorbar(fig[1, 2], hm; label = "Power [dB]")
fig

Each IMF contributes one frequency at each time point, so the spectrum is sparse; see Time-frequency analysis for other methods.

Advantages and Limitations

Advantages Limitations
adaptive: no predefined basis functions mode mixing: one IMF can contain oscillations of different scales
non-stationary and non-linear signals end effects at the boundaries of the signal
exact reconstruction (IMFs + residual = signal) sensitive to noise and to the stopping criterion; IMFs not orthogonal
instantaneous frequency and amplitude (Hilbert-Huang) the number and content of IMFs differ between signals
removes trends without a model (the residual) no theoretical basis like the Fourier transform; results are empirical