A typical EEG channel in my files (ant neuro edf) looks like this:
I.e., there is the magic number 32767uV which is, I suppose, the device max sensitivity.
I want to annotate this channel to exclude all the time slots with values close to this border (this is the “flatline” step).
It is relatively easy to do it in Python, but mne.preprocessing.annotate_amplitude does not seem to be able to do it because it looks at peak-to-peak signal amplitude (PTP).
I wonder if I will have to create them myself, or if there is some existing functionality I am missing.
One more piece of information: 32 767 uV is probably your amplifier clipping its maximum acquisition range. It’s not supposed to happen, and I would suggest you contact your amplifier manufacturer for additional information and improvements on your recording setup.
Some amplifiers support different signal ranges, one solution can be to increase the acquisition range (at the cost of resolution).
To give you an example, we used ANT Neuro amplifiers with touch-proof connectors and intracranial electrodes. The manufacturer cap uses Silver/Silver Chloride electrodes while the intracranial electrodes were stainless steel and platinum electrodes which have a higher DC offset potential. The signal was clipping as well, but not if we increased the signal acquisition range to its maximum (1V).
I believe by default the channel is marked as bad if the rejection criterion is met for at least 5% of the recording duration. You can adjust this setting to 100% and you should only get annotations and no rejected channels. The parameter is called bad_percent.
Please take the time to read all the parameters on the function description page. I suspect you are setting the peak argument and not the flat argument.
import numpy as np
from mne import create_info
from mne.io import RawArray
from mne.preprocessing import annotate_amplitude
data = np.random.randn(1, 4096) # 8 seconds @ 512 Hz
# make some flat segments
data[0, 100:801] = 0.
data[0, 2000:3001] = 0.
# create a raw object
info = create_info(ch_names=["EEG 01"], sfreq=512, ch_types="eeg")
raw = RawArray(data, info)
# find flat
annotations, bads = annotate_amplitude(raw, flat=0., bad_percent=100)
raw.set_annotations(annotations)
# plot
raw.plot(scalings="auto")
Also, make sure that the data is actually exactly flat, or flat=0. will not work and you will have to set flat to a PTP value that suits your data, e.g. flat=1e-8 if the variation within the flat segments do not exceed 1e-8.
actually, flat=0 does produce (<Annotations | 505 segments: BAD_flat (505)>, [])!
why didn’t 0.1 work?!
and how do I merge those annotations? I suspect that shorter “good” segments are worthless, so I would rather merge adjacent annotations separated by less than, say, 1-5 secs of good signal.
Apologies if it read like that, it was not my intention. I’m glad I was wrong and you were using the correct arguments.
I don’t know, the code snippet below works. Note that I multiplied the data by 10 since randn returns values within (0, 1). Without the multiplication, additional segments annotated with BAD_flat are created where the random value respects the 0.1 threshold for more than min_duration (5 ms by default). The multiplication makes this way more unlikely and improves the example.
import numpy as np
from mne import create_info
from mne.io import RawArray
from mne.preprocessing import annotate_amplitude
data = np.random.randn(1, 4096) * 10 # 8 seconds @ 512 Hz
# make some flat segments
data[0, 100:801] = 0.
data[0, 2000:3001] = 0.
# create a raw object
info = create_info(ch_names=["EEG 01"], sfreq=512, ch_types="eeg")
raw = RawArray(data, info)
# find flat
annotations, bads = annotate_amplitude(raw, flat=0.1, bad_percent=100)
raw.set_annotations(annotations)
# plot
raw.plot(scalings="auto")
And finally, as @richard mentions, for very short good segments, you’ll have to find them yourself by looking at the returned annotations onsets and durations and figuring out which ones are closer than a threshold and should be merged.