The problem with spectral analysis and brain waves.

Hello, colleagues!

  • MNE version: e.g. 0.24.0
  • operating system: e.g. macOS 12 / Windows 10 / Ubuntu 18.04

I am trying to analyze EEG data in real time, namely, I want to determine the type of wave of brain activity (gamma, beta, alpha, theta, delta). To do this, I use Ebneuro BE Plus LTM 4 for 20 channels. I use a sliding window to accumulate enough data for filtering and analysis, and then work with it.

Waves(Hz):

waves = {'delta': (0.5, 4),
         'theta': (4, 8),
         'alpha': (8, 12),
         'beta': (12, 30),
         'gamma': (30, 50)}

I used different methods to calculate the spectrum: FFT, Welch, multitaper. But they all had the same problem… The delta wave always prevails by one or more values compared to other waves, although at this time the EEG is on me and there can be no other wave.

FFT:

# FFT
fft_values = np.fft.fft(data, axis=1)
freqs = np.fft.fftfreq(data.shape[1], d=1 / raw.info['sfreq'])
# frequencies
psd = np.abs(fft_values) ** 2

Welch:

# Welch
freqs, psds = welch(data, fs=raw.info['sfreq'], nperseg=4 * raw.info['sfreq'])

Multitaper:

# multitaper
psds, freqs = psd_array_multitaper(data, sfreq=raw.info['sfreq'], fmin=0.5, fmax=50, bandwidth=4.0, verbose=False)
# The average PSD value over time
psds = psds.mean(axis=0)

Search for the maximum value of the spectrum:

for band, (low, high) in waves.items():
     band_ix = np.logical_and(freqs >= low, freqs < high)
     power = psds[band_ix].mean(axis=0)
     power_values[band] = power
     if power > max_power:
          max_power = power
          max_power_band = band

And it’s interesting that when I used another device (OPENBCI) for the same purpose, on the contrary, gamma and beta waves prevailed there (I tried to relax and fall asleep to check the correctness of the work and see low-frequency waves, but I couldn’t)

The question is, how do I avoid the predominance of delta waves?

Maybe use some other filters?
My Filters:

    buffer_raw.notch_filter(freqs=50, notch_widths=1)
    # buffer_raw.filter(l_freq=0.5, h_freq=None)
    buffer_raw.filter(l_freq=0.5, h_freq=30)
    # buffer_raw.set_eeg_reference(ref_channels='average', projection=True)
    # buffer_raw.apply_proj()  # the average reference projection

I wanted to try ICA filters, but they need channels for EOG or ECG, which I don’t seem to have…

P.S.
An example of what each output looks like:

New data: (20, 64)
Add new data in buffer
Buffer size after adding new data: (20, 3648)
Buffer size limit.
Current buffer size: (20, 3584)
-------------------------------
delta: 347.0794858876983
theta: 14.873777333872232
alpha: 16.30381922891555
beta: 2.394770935893433
gamma: 0.7051439715417689
-------------------------------
$$$$$$$$
delta
$$$$$$$$

EEG inherently exhibits a 1/f-like behavior, indicating that in the measurements lower frequencies are higher in power than higher frequencies. From the measured powers you see that this is the case for this dataset. If you would like to compensate for that, you could for example use the FOOOF toolbox (FOOOF - fitting oscillations & one over f — fooof 1.1.0 documentation). I do not know if this would work in real-time, but for real-time, you could do some more frequency compensation.

1 Like