Environment for design and debugging of EEG analysis pipelines
Integrated Development Environment for EEG
IDE4EEG reads raw EEG recordings in several formats, applies a configurable preprocessing pipeline (filtering, artifact rejection, ICA, Matching Pursuit), and produces time-domain, frequency-domain, time-frequency, connectivity, and source-localisation analyses with publication-ready plots. The two main analysis paths are event-locked (stimulus-related epochs) and rest (continuous timed windows); the choice is made on the Preprocess tab’s Segmentation setup panel and propagates through every subsequent step.
The intended workflow is interactive: you load a recording, configure preprocessing and analysis steps in the GUI, run the pipeline, browse the results, and iterate. The same configuration can also be saved as a config.toml file and run from the command line for batch processing or cluster jobs, or exported as a self-contained Python script — all three paths share the same code, so results are bit-identical.
This manual mirrors the GUI tab layout. Chapters 1–7 each cover one
tab in the order you would encounter it on first use:
Config, Input,
Preprocess, Analysis,
Run, Output, Help.
Chapter 8 documents the bundled helper applications (Svarog,
Connectivis, empi, MP Book Viewer). The appendices cover installation,
supported file formats, the complete config.toml reference,
an FAQ, and advanced/hidden parameters.
IDE4EEG is built on top of MNE-Python: every loaded recording lives as
an mne.io.Raw object, every cut signal as
mne.Epochs, and the canonical save format is MNE-FIF
(*-raw.fif for continuous data, *-epo.fif for
epochs). Channel types use MNE’s vocabulary (eeg,
eog, emg, ecg, bio,
stim, misc, …); montages are taken from MNE’s
built-in library (standard_1020,
standard_1005, biosemi64, …). When in doubt
about a parameter’s meaning, the corresponding MNE function’s docstring
is the authoritative reference — IDE4EEG forwards most parameters
unchanged.
MNE stores voltages in volts (V) — not microvolts. Times are in seconds, frequencies in hertz. IDE4EEG follows this convention wherever a parameter is forwarded to MNE:
[epochs] reject_dict = {eeg = 500e-6} means 500
µV peak-to-peak (5 × 10⁻⁴ V).amplitude_max_threshold = 150e-6 means 150
µV.Legacy IDE4EEG-developed code paths — predating the move onto MNE — kept their thresholds in µV directly for human readability:
reject_uv = 500 (the _uv suffix flags
the unit explicitly).SlopesAbsThr, OutliersAbsThr in µV;
MusclesAbsThr in µV²).reject_threshold / flat_threshold in
µV.[choosing_channels.badchs_params] thresholds in
µV².Every parameter table in this manual names the unit it expects; the GUI tooltips also show the friendly unit (µV, ms) regardless of how the value is stored on disk.
IDE4EEG uses . (period) as the decimal separator
everywhere, regardless of the system locale. This matches:
config.toml floats always use
.),float() (the codebase parses every text field
with float(text)).A German / Polish / French OS may format numbers with ,
(comma) elsewhere — that comma is not accepted by
IDE4EEG’s input fields. Type 0.5, not 0,5.
Numeric input fields that semantically can’t be negative (frequency
bounds, amplitudes, scales, iteration counts) are clamped at typing time
on the GUI side: the - key is rejected outright by the
field’s validator. The pre-launch consistency rules also flag negative
values that arrive via TOML edits or clipboard paste, so a typo never
reaches the pipeline silently.
Preprocessing. The following steps wrap MNE transparently — IDE4EEG supplies the parameters, MNE does the work:
raw.resample(sfreq).raw.filter(...). IIR mode uses
scipy.signal.iirdesign + sosfiltfilt.raw.set_montage(...),
mne.set_eeg_reference(...).mne.preprocessing.ICA (Picard backend), with
component labelling through mne_icalabel (ICLabel) or
find_bads_eog / find_bads_ecg /
find_bads_muscle.mne.find_events /
mne.events_from_annotations for segment boundaries,
mne.Epochs for cutting, and FIF I/O for
-epo.fif saves.Analysis. Chapter 4 is split on purpose. §4.1 UW-developed analyses (Matching Pursuit decomposition, MP→dipole, MVAR / DTF / PDC connectivity, FASP EEG profiles) is original IDE4EEG code. §4.2 MNE-wrapped analyses — ERP butterfly / joint / topomap / GFP, cluster permutation, PSD multitaper / Welch / per-channel, TFR Morlet/multitaper, ERD/S topomap, evoked comparison, drop-log, channel locations, source estimation — are thin wrappers over the corresponding MNE functions, organised on the Analysis tab into six collapsible category panels.
Source estimation. All four source-estimation
entries on the Analysis tab call MNE inverse-solution routines:
classical ERP dipole fit (mne.fit_dipole), minimum-norm
family (mne.minimum_norm.apply_inverse with method ∈ MNE /
dSPM / sLORETA / eLORETA), LCMV beamformer
(mne.beamformer.make_lcmv + apply_lcmv), and
MxNE / iRMxNE (mne.inverse_sparse.mixed_norm). This
includes the MP→dipole pipeline in §4.1.1: the
time-frequency MP decomposition is IDE4EEG-native (empi),
but the inverse problem that turns each MP atom into a current source is
solved by MNE against an MNE-derived BEM and forward solution.
Independent of MNE. Matching Pursuit decomposition
(empi binary), the MVAR / DTF / PDC connectivity solver,
the FASP EEG-profile algorithm, and the L2CS-Net + InsightFace gaze
pipeline are IDE4EEG-native code — no MNE dependency on the algorithmic
side, although their outputs are still wrapped into MNE-compatible
structures where useful.
The Config tab holds settings that apply to the whole pipeline rather than to any individual step. From here you point IDE4EEG at the bundled helper applications, control how many CPU cores it uses, switch a few global behaviours on or off, and load/save TOML configuration files.
A new install needs nothing here to run — the only mandatory inputs are on the Input tab. Visit the Config tab once to download the helper apps, then leave it alone.
The bottom row of the tab has four buttons (also reachable via the File menu):
.toml
file into the GUI. Every editable widget across all tabs is
repopulated..toml. The format is documented in Appendix C..py
contains only the non-default parameters and runs independently of the
GUI — useful for batch processing, automation, cluster submission, or
sharing reproducible analyses. Scripts run non-interactively by default;
pass rest={"check_rest": True} or
epochs={"check_epochs": True} to run_file() to
enable interactive segment review. See Appendix F for a
side-by-side comparison with config.toml.If a config.toml exists in the working directory when
IDE4EEG launches it is loaded automatically. Otherwise the GUI starts
with sensible defaults — no config file is required.
These paths are auto-detected at startup. On first
launch of a fresh install, IDE4EEG
auto-downloads the missing helpers (SVAROG with bundled
empi, ConnectiVIS, and Adoptium Temurin JRE 17) — they are treated as
crucial parts of the package, not optional add-ons. A modal progress
dialog appears while the ~80–150 MB total downloads in the background;
click Cancel to defer and use the Config tab’s per-row
Download recent button later. CLI / batch mode
(ide4eeg --run config.toml) auto-installs silently to
stdout.
When the auto-install completes (or if you already had a helper
installed) the corresponding path field on the Config tab is filled in
automatically. Only the missing tools are fetched — a Svarog version
you’ve pinned by hand at ~/.obci/svarog/ will NOT be
overwritten by the first-launch flow.
The per-row Download recent button on the Config tab forces a refresh — it downloads the latest GitLab CI artifact and overwrites the existing install. Use this when you want to update to a newer build; the prompt explicitly warns when other helpers will also be re-fetched.
See Chapter 8 for what each helper does.
| Parameter | Auto-install path | Description |
|---|---|---|
svarog_jar |
~/.obci/svarog/svarog-standalone-*.jar |
Svarog standalone JAR (signal viewing, MP book display, interactive review). |
connectivis_jar |
~/.obci/connectivis/connectivis.jar |
Connectivis JAR (3D viewer for dipole sources and connectivity arrows). |
empi_path |
~/.obci/svarog/empi-*-<platform> |
empi binary for Matching Pursuit decomposition. |
Download recent drops files at the paths above (the
* is a version stamp; <platform> is
mac-x86_64 / mac-arm64 /
linux-x86_64 / windows-x86_64). When
auto-detection at startup finds a matching file, the corresponding
Parameter field is filled in automatically. Configuring an
explicit path in the field is only needed when you want a custom build
outside ~/.obci/.
Video backends for Svarog video sync. Svarog plays video frames lockstep with EEG signal scrolling using one of three backends, in priority order:
Depends: in the
.deb. The Download recent button extracts
portable mpv into ~/.obci/svarog/mpv/ on Linux and Windows
from the official mpv release archives. Plays MKV / MP4 / AVI / WebM
directly via libavcodec.On macOS, portable mpv is impractical for the
pip-installed app (it would require bundling ~30 dylibs from
/opt/homebrew/lib and rewriting their rpaths). Instead,
IDE4EEG checks whether Homebrew is
installed; if so, the Download recent button asks for
explicit consent and runs brew install mpv on your behalf.
If you decline or Homebrew is missing, the button still installs
Svarog/empi/Connectivis successfully and reports that mpv is unavailable
— install manually with brew install mpv to enable video
sync. Note that without mpv on macOS, JavaFX becomes the only fallback,
which means video sync works only for MP4/H.264
recordings; MKV/WebM/AVI files won’t play. For non-MP4 study
recordings, install either mpv or VLC before launching Svarog.
The macOS full .dmg sidesteps all of this: it
bundles a self-contained arm64 mpv (from the stolendata.net
portable build) and a static arm64 ffmpeg (from osxexperts.net) inside
IDE4EEG-full.app/Contents/Resources/, and a PATH prepend in
ide4eeg/__init__.py makes them discoverable to SVAROG’s
Runtime.exec("mpv") and Runtime.exec("ffmpeg")
calls. No Homebrew required.
Planned improvement. A future release will add a one-click
VLC.app download as an explicit macOS fallback for users
without Homebrew, populating Svarog’s <vlcPath>
config key automatically. VLC.app is self-contained (no dylib bundling
needed), plays the same broad container set as mpv, and would close the
“macOS without Homebrew + MKV recordings” gap. mpv stays primary on
every platform; VLC is added only as a fallback path, not a
replacement.
L2CS-Net (default eye-gaze backend). A separate
Download button at the bottom of the Tool Paths group
installs PyTorch + the l2cs Python package + the Gaze360
weights (~96 MB checkpoint, MIT-mirrored from Ahmednull/L2CS-Net) in one
shot. Total install ~500 MB on macOS / Linux-CPU, up to ~2 GB on Linux
with CUDA wheels. L2CS provides true per-eye gaze direction; without it,
the only available backend is the InsightFace head-pose fallback, which
measures where the head is pointing rather than where
the eyes are looking (see §3.2.12 Gaze for the
full comparison).
Video processing panel. A persistent panel under Tool Paths reports the live status of the five core Python packages the facetag pipeline needs: OpenCV, PyAV, imageio, InsightFace, ONNX Runtime. (PyTorch + L2CS-Net live in the dedicated L2CS-Net row above — they have their own one-shot installer that also fetches the Gaze360 weights file.) Each package row shows one of:
[ok] (green) — installed and importable.[--] (amber) — missing, but a clean
pip install is expected to succeed on this platform.[!!] (red) — missing and
platform-blocked: the wheel-availability or build-tooling situation on
this (Python, OS, arch) cell prevents a clean install. The blocker
reason is shown inline below the affected row. Two cells are seeded
today:
onnxruntime
has no cp314 wheel for darwin x86_64. Recommended: install
Python 3.13 via Homebrew (brew install python@3.13) and
recreate the venv. Apple Silicon Macs have working wheels and don’t need
this workaround.insightface (and
the transitive stringzilla via albumentations) ship no
cp314 wheels yet, so pip falls back to building from sdist, which needs
a C/C++ compiler. Install with
sudo apt install build-essential (Debian / Ubuntu) or
sudo dnf groupinstall "Development Tools" (Fedora), or use
Python 3.13.The panel refreshes automatically when the Config tab gets focus, so
packages installed in another terminal flip from [--] to
[ok] without restarting IDE4EEG. When any package is
missing, an Install missing button appears next to the
summary; click it to confirm + run pip install for the core
packages with progress streamed live into a monospace log dialog. After
install, packages requiring a process restart (cv2,
av, insightface, onnxruntime —
anything with a C extension) trigger a “Restart IDE4EEG?” prompt that
re-execs the Python process so the new modules load cleanly.
The same diagnostic is available without launching the GUI:
python -m ide4eeg.install_diagnostics # print status only
python -m ide4eeg.install_runner -y # install everything missing
python -m ide4eeg.install_runner -y --no-l2cs # skip L2CS extrasThe first invocation only prints status (read-only). The second runs
the same pip install flow as the GUI’s Install missing
button, with progress streamed to stdout and a final per-package
summary. Useful on headless machines, in CI, and for scripted setup.
The CLI runner (ide4eeg --run config.toml) runs the same
preflight automatically — if a config has
prepare_video_artifacts = true and any required package is
missing or platform-blocked, the run aborts with the diagnostic text
before any preprocessing starts, so the user sees actionable remediation
up front instead of a Python traceback ten minutes into a long
pipeline.
First-time download troubleshooting (TLS certificate errors). If Download recent fails with a TLS error, IDE4EEG handles it automatically in two steps:
certifi is in
requirements.txt.If both fail, the error dialog shows platform-specific remediation:
/Applications/Python 3.XX/Install Certificates.command
once.ca-certificates
package, then pip install --upgrade certifi.pip install --upgrade certifi.SSL_CERT_FILE /
REQUESTS_CA_BUNDLE to a corporate bundle.Three top-level filesystem locations cover everything IDE4EEG installs, downloads, or saves at runtime:
| Path | Contents | Typical size |
|---|---|---|
~/.obci/svarog/svarog-standalone-*.jar |
Svarog standalone JAR (signal + book + tag review). | ~50 MB |
~/.obci/svarog/empi-*-<platform> |
empi binary (MP decomposition). | ~2 MB |
~/.obci/svarog/mpv/mpv-*-<platform>/ |
Portable mpv (Linux / Windows only — macOS uses
brew install mpv). |
~30 MB |
~/.obci/connectivis/connectivis.jar |
ConnectiVIS JAR (3D dipole / connectivity viewer). | ~35 MB |
~/.obci/ide4eeg/models/L2CSNet_gaze360.pkl |
L2CS-Net gaze checkpoint (downloaded on first L2CS use). | 91 MB |
~/.obci/ide4eeg/insightface/models/buffalo_l/ |
InsightFace face detection / identity models (downloaded on first use). | ~200 MB |
~/.obci/ide4eeg/stage5_dismissed.json |
Per-rule “don’t show again” dismissals (created on first dismissal). | < 1 KB |
~/IDE4EEG_examples/<name>/ |
Example datasets + their pipeline outputs (each example is self-contained). | ~50–200 MB per example |
<venv>/lib/python3.X/site-packages/{cv2,av,imageio,insightface,onnxruntime,torch,torchvision,l2cs}/ |
Python video stack — installed via pip into the active
venv. |
~250 MB core; up to ~2 GB with PyTorch CUDA |
Three principles drive the layout:
~/.obci/ is the OBCI ecosystem
convention shared with Svarog and other OBCI tools. External binaries
(Svarog, empi, mpv, ConnectiVIS) live in subdirectories chosen by Svarog
itself — Svarog’s resolver probes ~/.obci/svarog/mpv/...,
so we install matching that path. We do not move these.~/.obci/ide4eeg/ holds runtime caches
IDE4EEG manages directly: model weights, dismissal state, and per-user
data the user shouldn’t have to back up. Python packages can’t live here
— pip places them in the active venv’s site-packages/,
which is the only third location.~/IDE4EEG_examples/ is a plain
user-visible folder under $HOME, deliberately
outside ~/.obci/. Reason: when you run the
pipeline on
~/IDE4EEG_examples/dipole_spindles/dipoles_example.raw and
don’t override output_path, IDE4EEG writes results into the
parent of the input file
(~/IDE4EEG_examples/dipole_spindles/IDE4EEG_OUT_*/).
Keeping that under your home dir means analysis output stays where you’d
naturally look — not buried in an opaque app-data tree.Migration of legacy paths. Existing installs may have files in three old locations:
<package>/preprocessing/facetag/models/L2CSNet_gaze360.pkl
(early L2CS layout, inside the IDE4EEG package tree)~/.insightface/models/buffalo_l/ (InsightFace’s
upstream default)~/.obci/ide4eeg/examples/ (the previous examples
location — output-path pollution prompted the move to
~/IDE4EEG_examples/)On first launch after upgrading, IDE4EEG runs
migrate_legacy_paths() from
ide4eeg/__init__.py which performs same-disk renames into
the new tree. The migration is idempotent and fast (single rename per
file, no re-download). Cross-disk or permission failures are logged as
warnings and the legacy path stays in place; on next use, InsightFace
re-downloads to the new location while L2CS retries reading from the
legacy file as a fallback.
To remove IDE4EEG’s runtime data:
rm -rf ~/.obci/ide4eeg/ frees up to ~300 MB.pip uninstall -y for
opencv-contrib-python-headless, av,
imageio, insightface,
onnxruntime, torch, torchvision,
l2cs, gdown (worker thread, streamed log) and
then deletes the L2CS weights file + the InsightFace
buffalo_l/ cache. mpv is preserved (Svarog uses it
independently). Frees up to ~700 MB. Equivalent CLI form:
pip uninstall <list> from inside the venv plus
rm -rf ~/.obci/ide4eeg/{models,insightface}/.~/.obci/svarog/ and ~/.obci/connectivis/
unless you’ve confirmed no other OBCI tool uses them.The Parallel jobs field controls how many parallel worker processes (via https://joblib.readthedocs.io/) IDE4EEG uses for CPU-heavy parts of the pipeline. This is the same knob MNE and scikit-learn already expose internally, unified behind one entry point.
GUI default — auto-fallback. GUI pre-fills the field with the auto-resolved value min(physical_cores - 2, available_ram_gb // 2), floored at 1 — i.e. use all cores except two, capped by RAM at roughly 2 GB per worker. On an 8-physical-core / 16 GB machine the field starts showing 6. The system-info readout below the field shows the same number plus the hardware breakdown (live-updated as you edit), with warnings (yellow) for risky values like “more than half of physical cores” or “RAM footprint exceeds one third of detected RAM.”
Headless / CLI default — 1 (sequential). “Headless” means running
without the GUI — typically ide4eeg --run config.toml
over SSH on a remote server, a cluster job, or a CI pipeline. When
parallelism.n_jobs is not set in the TOML, these runs default to 1
(sequential), following MNE’s own convention: the library does not take
cores from you unless you explicitly ask.
Every parallelism-aware step uses the same n_jobs value: MNE preprocessing (filter, notch_filter, resample), MNE catalog analyses (PSD, TFR, cluster permutation tests), connectivity bootstraps, and MP decomposition. MP decomposition has its own override field (matching_pursuit.cpu_workers on the Preprocess tab) for power users on RAM-constrained machines, since empi’s C++ workers share memory and cost less RAM than joblib’s full-Python workers — but by default the MP field is empty and the inherited value is shown as grey italic placeholder text. For practical guidance on choosing a value and BLAS thread pinning details see Appendix E.7.
Results are written to a folder named
IDE4EEG_OUT_<input_filename>/ with
preprocessing/ and analysis/ subdirectories.
By default the folder lives next to the input signal; the
output_path field on the Input tab
overrides this. Each run creates a timestamped subfolder unless
Overwrite output is checked.
Full table of output subfolders, file naming conventions, and what triggers each artefact: see Chapter 6 (Output tab).
| Option | Default | Description |
|---|---|---|
| Overwrite output | off | Overwrite previous results instead of creating timestamped folders. |
| Verbose console logging | off | Print startup progress and Svarog launch commands to the terminal. |
| Allow changing the order of preprocessing steps and adding new filters | off | Reveal ↑/↓ arrows on every reorderable step and the Constraints editor (see §3.2.0). Off by default — the standard pipeline order is correct for most workflows. |
| Use MNE viewer instead of Svarog | off | Force every interactive review window (eye buttons, Output-tab double-click) to use MNE’s plot rather than Svarog. Useful if you don’t have Svarog installed or prefer MNE’s interactive plot. Independent of the explicit “Open in MNE” / “Open in SVAROG” buttons on the Input tab. |
| Display functions and config names in panels | off | Show a small grey row at the top of each preprocessing panel listing the actual Python function it calls and the TOML config sections it reads. Useful for debugging and matching GUI steps to CLI equivalents. |
Visible only when Allow changing the order… is checked. Lets you edit the step-order constraints used by the reorderable preprocessing steps. See §3.2.0.
The Input tab is where you tell IDE4EEG which recording to analyse and inspect the signal you just loaded. Every preprocessing and analysis decision in the rest of the GUI assumes a valid recording is loaded here. The segmentation mode (event-locked epochs vs timed windows) is set later on the Preprocess tab — see §3.1 Segmentation setup.
Type or browse for the path in the Input field. It can point to a single EEG file or a directory; when given a directory, IDE4EEG walks it recursively and treats every supported file as a batch member. The supported file formats and their companion-file requirements are listed in Appendix B.
Recent files. Click Recent files to pick from the last few inputs. Use Clear to wipe the list.
Output root path. The Output field
controls where IDE4EEG_OUT_<filename>/ is created.
Empty = save next to the input file.
Video path. For §3.2.12 Gaze the video
is auto-detected from the input filename (recording.raw →
recording.mp4). Override with the Video
field if needed.
MNE sample dataset. The Download MNE example (1.5 GB) button downloads the MNE sample dataset — a single-subject auditory/visual EEG+MEG recording (59 EEG channels, 600 Hz, 278 s, 4 conditions) with full FreeSurfer reconstruction. It auto-configures the classical ERP analysis: filter 0.1–40 Hz → epoch -0.2 to 0.5 s → baseline → reject 150 µV → ERP butterfly / joint / topomap / GFP / cluster permutation / ERP dipole fit with subject-specific 3D visualisation.
Once a file is selected, IDE4EEG reads its header and shows duration, sampling rate, channel count, channel-type histogram, and any event markers it found. A collapsible Detail view lists every channel name and event tag.
View buttons. Two buttons launch a viewer on the raw input (no preprocessing): Open in SVAROG and Open in MNE. These are explicit file-viewer buttons — they bypass the Use MNE viewer instead of Svarog option in §1.5. Disabled until a file is loaded.
Every channel in a loaded recording is tagged with an MNE channel
type (eeg, eog, emg,
ecg, bio, stim,
misc, …). The Input tab is where this typing is reviewed
and, when needed, overridden — every later panel on the Preprocess and
Analysis tabs reads these type assignments to populate the adaptive
EEG only / EOG only / … filter buttons and to power
MNE’s pick_types(eeg=True) calls.
Channel selection (which channels to actually keep, drop, or hand to bad-channel detection) is configured later, on the Preprocess tab — see §3.2.3 Bad channels and §3.2.8 Drop Channels.
Every channel is tagged with an MNE channel type (eeg,
eog, emg, ecg, bio,
stim, misc, …). IDE4EEG relies on this type
for:
mne.pick_types(eeg=True) calls throughout the analysis
pipeline;Where types come from, in order of authority:
File-format metadata. For FIF, EEGLAB, and BrainVision files, channel types are read directly from the native file metadata (explicit per-channel type tags, chanlocs structures, header fields). Authoritative — IDE4EEG respects whatever the reader assigned.
Readmanager heuristic. For BrainTech
.raw/.xml recordings, IDE4EEG calls
readmanager.get_mne_raw() which internally runs
chtype_heuristic(name) on every channel name. The heuristic
recognises (in priority order):
| Rule | Example names | Type |
|---|---|---|
Substring eog |
EOG Left Horiz, VEOG,
EOG Fp1-M2 |
eog |
Substring emg |
EMG Chin1, EMG Ant Tibia-0 |
emg |
Substring ecg / ekg |
ECG ECGI, EKG_lead1 |
ecg |
Substring resp / sao2 /
spo2 |
Resp Thermistor, SaO2 SaO2 |
bio |
Substring stim / trig /
marker / status / sync, prefix
sti |
STIM, Trigger, STI 014 |
stim |
| Tokenised 10-05 position lookup | Fp1, C3, EEG F3-CLE,
Fp1-M2 |
eeg |
| Fall-through | Aux1, Photo, Channel_42 |
misc |
Non-EEG substring rules take priority over the position lookup, so
EOG Fp1-M2 (an EOG reference channel using Fp1/M2
references) is correctly typed as eog rather than
eeg. The 10-05 position check tokenises on
whitespace/punctuation before matching, so short position names
(A1, C3, O1) don’t accidentally
match inside unrelated words (audio1, Data1,
misc3). Requires readmanager ≥
1.4.0.
IDE4EEG overlay. For EDF/BDF files, MNE’s reader
defaults every channel to "eeg" regardless of the name —
the EDF format has no standard channel-type field. IDE4EEG applies a
final overlay (ide4eeg.input.input._refine_channel_types)
that delegates default-eeg channels to readmanager’s
chtype_heuristic, so EDF polysomnography files get the same
refinement as BrainTech recordings. Channels already assigned a specific
type by any reader (including the rare case of a FIF file author
deliberately typing a channel as misc) are respected and
never second-guessed.
User overrides — see §2.3.2.
When the automatic heuristic gets a channel wrong — typically for
lab-specific names the substring rules don’t recognise
(Heart_sensor, Chest_strap,
Channel_42) — you can override it via the GUI or the TOML
config.
In the GUI: expand the Review channel types collapsible section in the Signal Info area. This is an inline panel that lists every channel with its current MNE type. Right-click any channel → Set type → submenu to change it. Changes apply live: the override dict updates, every channel panel on Preprocessing/Analysis tabs refreshes, the per-type filter buttons (EEG only, EOG only, …) rebuild to reflect the new counts, and the Signal Info type-count summary updates. Picking auto removes an override and restores the auto-inferred type.
In TOML:
[choosing_channels.type_overrides]
"Heart_sensor" = "ecg"
"Chest_strap" = "bio"
"Aux_L" = "eog"
"BadChannel" = "misc" # exclude from EEG analysis by nameKeys are exact channel names (as they appear in the loaded file);
values are any valid MNE channel type (eeg,
eog, emg, ecg, bio,
stim, misc, ref_meg,
seeg, ecog, dbs,
fnirs). Overrides always win — they take effect after both
the file reader and the name-based heuristic have run. Entries whose
channel name isn’t in the loaded file are logged and silently
skipped.
The Review panel is the single entry point for editing channel types. It lives on the Input tab because channel typing is a file-level concern (a property of “what’s in this recording?”) rather than a preprocessing step. Per-tab channel panels are read-only for types — they USE the type assignments (to power the adaptive filter buttons) but don’t let you edit them. This keeps the editing surface in one place and prevents GUI↔︎pipeline disagreements by construction.
Every channel panel with eeg_filter=True on the
Preprocessing and Analysis tabs shows a row of per-type filter buttons
next to All and None. The buttons are
adaptive: only types actually present in the loaded signal get a button,
with the current count shown in parentheses. For a MASS PSG example:
[All] [None] [EEG only (18)] [EOG only (2)] [EMG only (5)] [ECG only (1)] [BIO only (5)]
Clicking ECG only (1) unchecks everything except the
single ECG channel — useful for configuring ICA EOG correlation
references, channel-subset spectral analyses, or any pipeline step that
needs a specific modality. If you right-click a channel in the Review
panel and change its type, the filter button row rebuilds
automatically.
Channels whose current type isn’t eeg are dimmed in the
channel panels and get a trailing type badge
(e.g. EOG Left Horiz [eog]).
Override precedence:
choosing_channels.type_overrides
(authoritative)chtype_heuristic (BrainTech; also applied
to EDF/BDF channels defaulted to eeg by MNE)eeg for any channel not matched by the
aboveIf you need channel-level selection rather than retyping, use
dropped_channels or selected_channels — those
act after typing and exclude channels entirely regardless of
their type.
The Preprocess tab is where you configure the signal-cleaning pipeline. Each step has its own collapsible panel with an enable checkbox, parameter fields, and a 🗄️ Save toggle controlling whether the step’s intermediate output is written to disk.
The pipeline has three sections:
The reorder controls are hidden by default. Tick Allow changing the order of preprocessing steps in the Config tab (see §1.5) to reveal them. The default order is correct for most workflows.
GUI panel: Segmentation setup (always visible, non-checkable) Pipeline phase: Preamble (always runs)
The preamble prepares the segmentation coordinate system before the main preprocessing chain runs. It does two things:
[epochs.tags] selected = [...] in TOML) to integer IDs,
consumed later by mne.Epochs /
_cut_segments.REST annotations are added to mark
synthetic window onsets via signal.set_annotations(...).
The original STIM channel is never
touched.Non-destruction invariant. Every preprocessing step
preserves the event markers the file reader produced. Rest-mode analyses
do not overwrite the original STIM channel — synthetic
window markers go to REST annotations instead, leaving
file-derived events intact. A recording with hardware triggers analysed
in timed-windows mode keeps both coordinate systems available in the
saved -epo.fif outputs.
The rule is enforced by construction: no preprocessing step writes to
STIM. Rest-marker generation is purely metadata
(set_annotations is lazy — no load_data() is
forced) and downstream consumers pick which event source to read based
on the active segmentation mode (annotations in rest mode via
events_from_annotations, the STIM channel in event-locked
mode via find_events). Stim channels are filtered by type
(mne.channel_type(info, i) == "stim"), not by exact
name.
Used when segmentation.mode = "rest".
| Parameter | Default | Description |
|---|---|---|
rest_duration |
[0, ""] |
[start, end] time interval (seconds) of the continuous
signal to extract for rest analysis. Empty-string or omitted end = full
signal. |
window_length |
20 |
Length (seconds) of each analysis window. The rest segment is divided into non-overlapping windows of this duration. |
check_rest |
true |
Interactively review rest segments after automatic rejection. |
Rest windows are computed in the trimmed signal’s
coordinate system — rest.rest_duration means seconds
relative to t=0 of the post-trim data, matching MNE’s
convention that raw.times restarts at 0 after
crop.
Used when segmentation.mode = "epochs".
| Parameter | Default | Description |
|---|---|---|
start_offset |
-0.3 |
Epoch start time relative to the event trigger (seconds). Negative = before the event. |
stop_offset |
0.7 |
Epoch end time relative to the event trigger (seconds). |
epochs_baseline |
"None" |
Baseline correction: "None" to skip,
[start, end] in seconds (e.g. [-0.3, 0]), or
["None", "None"] for baseline across the entire epoch.
Inside the list, individual "None" entries mean “use the
epoch boundary” (so ["None", 0] = from epoch start to 0
s). |
check_epochs |
true |
Interactively review epochs after automatic rejection. |
reject_dict |
{eeg = 500e-6} |
Peak-to-peak amplitude maximum (V) for epoch rejection. Set to
"None" to disable. See Appendix E.5. |
flat_dict |
{eeg = 1e-6} |
Peak-to-peak amplitude minimum (V) for flat-signal rejection. Set to
"None" to disable. |
Example with custom thresholds:
[epochs]
start_offset = -0.2
stop_offset = 0.8
epochs_baseline = [-0.2, 0]
reject_dict = {eeg = 300e-6}
flat_dict = {eeg = 0.5e-6}Tags — selecting which events to include. In the GUI, events discovered from the file appear as checkboxes. In TOML:
[epochs.tags]
selected = ["picture_1", "picture_2", "picture_A"]Legacy formats (still supported):
# Auto-discover target / nontarget by keyword:
[epochs.tags]
AUTO = true
# Manual tag groups:
[epochs.tags]
AUTO = false
visual = ["picture_1", "picture_2"]
auditory = ["beep"]The Segmentation setup panel header carries a 👁
segments button that opens Svarog with the raw input signal
overlaid by .tag markers for each segment the current
config would produce — a preview of where segments will fall, before
running the pipeline.
window_length seconds each, from
rest_duration[0] to [1] (or the end of the
signal if whole_signal is checked). Positions are offset by
trim_start so the windows land correctly on the raw signal
even when trim is configured. A point marker is also emitted at each
segment start.STIM channel and filters by
events_desc_id.selected), spanning
[start_offset, stop_offset] around each event. Point marker
at each event onset.Both point markers (segment starts / event anchors) and duration blocks (full windows) are written, so Svarog shows both the anchor positions and the actual signal ranges that will become segments. The button requires an input signal to be loaded on the Input tab; it opens the raw signal (no preprocessing applied yet).
Each reorderable step has a checkbox in its panel header. When the
order controls are visible (Config tab → Allow changing the
order…), each step also gets ↑/↓
arrows for repositioning. Two kinds of constraints govern valid
orderings:
[a, b] means
a must come before b. Violations are errors
and block the run.Default constraints:
[preprocessing]
hard_constraints = [
["bad_channels", "montage"],
["montage", "filtering"],
["montage", "ica"],
["resample", "filtering"],
["mp_decomposition", "mp_filter"],
]
soft_constraints = [
["filtering", "ica"],
["ica", "mp_decomposition"],
]The constraints editor on the Config tab lets you add or remove
rules. The current step order is stored as
preprocessing.step_order = [...]. If you omit
step_order in TOML, the canonical default order is
used.
GUI panel: Trim signal (checkable, per-step Save)
Crops the recording to a region of interest, removing irrelevant segments far from any event. Reduces memory usage and prevents boundary artifacts from contaminating later processing.
Algorithm.
trim_start /
trim_end from config, or auto-crop to
first_tag - 5 s through last_tag + 5 s when
unset.[0, signal_duration].signal.crop(tmin=trim_start, tmax=trim_end).| Parameter | Default | Description |
|---|---|---|
trim_start |
0.0 |
Start time in seconds. 0 = beginning. |
trim_end |
None |
End time in seconds. None = end of file. |
Memory-preserving (lazy) cropping.
signal.copy().crop(...) works on MNE’s file-backed
Raw object — .copy() is a metadata-only
shallow copy and .crop() just adjusts the internal
sample-range markers. The full recording is never materialised
in RAM. Only the kept sub-range is read from disk on demand,
when a downstream step that actually needs data (filtering, ICA fit,
etc.) consumes it. The same lazy property holds for
set_montage, set_eeg_reference, and
set_annotations, which are all pure metadata. As a result,
recordings substantially larger than available RAM — multi-hour
overnight polysomnography, long resting-state sessions, gigabyte-scale
BrainTech .raw files — can be trimmed and processed without
ever loading the original full file. When
[trim] save = true, only the cropped sub-range is loaded
for the FIF write.
Save default: [trim] save = true (the
snapshot is small and serves as the canonical
<base>-trimmed-raw.fif for every Svarog launch from
the Preprocessing tab — see below).
Eye button. Opens the Preview &
Trim window directly (full-recording overview with the trim
region shaded; click on the canvas to set start/end, or type values;
+/- amplitude controls and a DC-removal checkbox). It does
not open a Svarog split view because Svarog’s
split-sync aligns by sample index, which left the cropped FIF’s time
axis labelled 00:00 and the original at
trim_start — visually misleading when the alignment was
actually correct.
Trim is the source of truth for “what the pipeline
sees.” Every “open in Svarog” button on the Preprocessing tab —
Drop Channels → Mark in Svarog, Bad Channels → Run and check, the manual
ICA / bad-channels / segments review hooks driven from inside the
running pipeline — feeds Svarog the cropped segment,
not the full input. If [trim] save = true and the on-disk
snapshot’s [cfg:<hash>] matches the live config, the
snapshot is reused; otherwise a tempfile-named temp FIF is
written for the launch and cleaned up afterwards. Channel names are
preserved by crop, so any .tag file Svarog
writes back translates unchanged.
Hidden defaults for
resample_freq,cover_time,threshold_time(the last two deprecated): see Appendix E.1.
GUI panel: Filtering and Resampling (checkbox “Resample to:”)
Downsamples the signal to a lower sampling frequency, reducing data
size and computation time without losing information below the target
Nyquist frequency. Uses MNE’s
signal.resample(sfreq=target), which internally applies a
low-pass anti-aliasing FIR filter at the new Nyquist frequency, then
decimates via FFT-based polyphase resampling. Signals already at or
below the target rate are left unchanged.
| Parameter | Default | Description |
|---|---|---|
resample_freq |
512 |
Target sampling frequency (Hz). 0 = keep original rate. |
Reference: Gramfort A et al. (2014) “MNE software for processing MEG and EEG data.” NeuroImage 86:446–460.
GUI panel: Bad Channels (checkable, per-step Save)
Automatically identifies noisy, flat, or uncorrelated EEG channels that would degrade later processing (filtering, ICA, epoch averaging). Detected channels are excluded from all subsequent steps.
Algorithm. Two complementary methods are applied
(independently or together, depending on
choose_bad_channels).
A. Band-power noise detection. For each frequency
band defined in badchs_params (default: slow oscillations
2–10 Hz, noise power 52–98 Hz):
median(scores) + 4 * std(scores_below_threshold), clamped
to [20%, 60%] of total windows.B. Correlation-based detection.
correlation_window (default
[0.75, 0.975]).correlation_badch_threshold (default 0.95).C. Manual review. When
choose_bad_channels = "manual" or "both", an
interactive signal browser opens so the user can mark additional bad
channels:
--select-mode bad_channels flag is installed, IDE4EEG
launches Svarog as a subprocess with the signal + a pre-selection tag
file carrying any auto-detected bad channels. The user ticks/unticks
channels in Svarog’s side panel and closes the window; on exit, Svarog
writes a tag file which IDE4EEG reads to produce the final bad-channel
list. If the user cancels without saving, the auto-detected list is kept
unchanged.--select-mode, or the
subprocess fails, IDE4EEG opens MNE’s interactive signal browser on the
GUI main thread.| Parameter | Default | Description |
|---|---|---|
choose_bad_channels |
"auto" |
Detection mode: "auto" (algorithmic),
"manual" (interactive Svarog/MNE picker), or
"both". |
Output. When save = true,
bad_channels_detection/ folder containing band-power and
correlation plots per channel. Config updated with the
bad_channels list; signal info["bads"] set
accordingly.
Reference: Klekowicz H et al. (2009) “On the Robust Parametric Detection of EEG Artifacts in Polysomnographic Recordings.” Neuroinformatics 7:147–160.
Advanced parameters (
badchs_params,correlation_window,correlation_badch_threshold): see Appendix E.3.
GUI panel: Montage (checkable, reorderable)
Step key: montage ·
Backend: _set_montage
(channels_and_signal.py)
Assigns physical electrode positions to every EEG channel. Coordinates are required downstream for topographic plots, source estimation, and any spatial filtering.
Algorithm. Loads a standard montage
(e.g. standard_1020, standard_1005,
biosemi64) from MNE’s built-in library and applies it via
signal.set_montage(montage, on_missing="warn"). Channel
names are matched to montage positions; unmatched channels keep their
existing position (or none) and a warning is logged.
Native-position keep. When the loaded file already
provides 3D digitised positions for every EEG channel (e.g. an MNE
sample FIF with EEG 001… naming + digitised coords), the
standard-montage step is skipped to preserve the file’s
own coordinates — applying standard_1020 would silently
blank them out by name-matching. The config records this with the
sentinel value electrodes_layout = "_native_".
Phase 1 split (2026-04). Re-referencing used to live
inside this step; it now has its own panel — see §3.2.5 Reference. The split lets you reorder
them independently (e.g. apply CAR before ICA but set the
montage after). Legacy step-orders that list
montage but not reference are auto-migrated by
the pipeline driver.
| Parameter | Default | Description |
|---|---|---|
electrodes_layout |
"standard_1020" |
MNE montage name ("standard_1020",
"standard_1005", "10-20",
"biosemi64", …) or "_native_" to keep the
file’s own positions. See the MNE
montage docs. |
This step has no Save toggle: set_montage writes only
metadata into signal.info, no signal data is altered. The 👁
button on the panel header opens the montage in 3D / topographic
view.
Reference: Gramfort A et al. (2014) — MNE software (NeuroImage 86:446–460).
GUI panel: Reference (checkable, reorderable,
per-step Save) Step key: reference ·
Backend: _set_reference
(channels_and_signal.py)
Re-references the signal — i.e. subtracts a reference signal from
every channel — using signal.set_eeg_reference(...).
Independent of §3.2.4 Montage; since the
Phase 1 (2026-04) split, montage and reference are separate reorderable
steps.
Six options:
| Preset | Formula | Description |
|---|---|---|
| Raw signal (no re-referencing) | x_i(t) unchanged |
Keep the recording’s native reference. Default. |
| Common Average (CAR) | x_i'(t) = x_i(t) − (1/N) · Σ x_j(t) |
Subtract the mean across all good EEG channels. |
| Linked mastoids | x_i'(t) = x_i(t) − mean(M1, M2) |
Average of mastoid channels. |
| Linked ears | x_i'(t) = x_i(t) − mean(A1, A2) |
Average of ear channels. |
| REST (infinity) | Yao (2001) standardisation | Reconstruct a reference-free potential via the lead-field matrix. |
| Custom | x_i'(t) = x_i(t) − mean(ref_channels) |
Comma-separated list of channels — their mean becomes the reference,
and they are added to info["bads"] so subsequent steps and
channel filters ignore them. |
| Parameter | Default | Description |
|---|---|---|
re_reference |
"" |
"" / "None" (raw), "average"
/ "CAR" (common average), "REST" (infinity),
or a list / comma-string of channel names (custom). |
The default changed in Phase 1 from ['M1', 'M2']
(legacy) to "" (raw): silently picking M1/M2 was misleading
on recordings that don’t have those channels. Re-referencing is now an
opt-in decision.
When save = true, the rereferenced signal is written to
<base>-rereferenced-raw.fif. The 👁 button shows the
before/after comparison.
Reference: Yao D (2001) “A method to standardize a reference of scalp EEG recordings to a point at infinity.” Physiol. Meas. 22:693.
GUI panel: Filtering and Resampling (checkable, per-step Save)
Removes unwanted frequency components: slow electrode drift (highpass), high-frequency noise and EMG (lowpass), and power-line interference (notch). Only EEG channels are filtered; STIM, EOG, and other auxiliary channels are bypassed.
IIR (Butterworth / Chebyshev II) — default. Filters
are designed automatically from the cutoff frequency and the signal’s
sampling rate using scipy.signal.iirdesign. Stored in
second-order sections (SOS) format for numerical stability and applied
zero-phase via sosfiltfilt (bidirectional, no phase
distortion). Note that the forward+backward pass squares the filter’s
magnitude response — a -3 dB design becomes
-6 dB at cutoff — and doubles the effective filter order.
The filter-response plots produced by plot_filt = true show
the single-pass design, not the realised doubled
response.
| Filter | Passband edge | Stopband edge | Design |
|---|---|---|---|
| Highpass | f_hp Hz |
f_hp / 2 Hz |
Butterworth, gstop=10 dB, gpass=3 dB |
| Lowpass | f_lp Hz |
min(2*f_lp, 0.95*Nyquist) Hz |
Butterworth, gstop=12 dB, gpass=3 dB |
| Notch | f_notch ± 0.1 Hz |
f_notch ± 2.5 Hz |
Chebyshev II, gstop=25 dB |
FIR (MNE windowed sinc). Uses
mne.filter.filter_data() with a Hamming-windowed sinc
design and automatic filter length. Always stable, linear phase, zero
group delay. Applied via FFT overlap-add. Slower than IIR but has no
stability concerns.
SOS vs ba format. SOS decomposes the filter into
cascaded biquad stages, numerically stable for high-order filters. The
older ba (numerator/denominator polynomial) format can
produce unstable filters due to floating-point errors, especially at
high sampling rates.
| Parameter | Default | Description |
|---|---|---|
show_filt |
false |
Display filter response plots interactively. |
plot_filt |
false |
Save filter response plots to disk. |
method |
"iir" |
"iir" (Butterworth/Chebyshev) or "fir"
(MNE). |
highpass_freq |
0.5 |
Highpass cutoff Hz (0 = off). Common: 0.1, 0.5, 1.0 Hz. |
lowpass_freq |
30 |
Lowpass cutoff Hz (0 = off). Common: 30, 40, 100 Hz. |
notch_freq |
50 |
Notch frequency: 50 (Europe/Asia) or 60 (Americas). 0 = off. |
Output. When plot_filt = true,
filters_plot/ folder with frequency response PNGs.
GUI panel: ICA (checkable, per-step Save, 👁 View
Step Result) Backend:
ide4eeg/preprocessing/ica.py::fit_and_apply_ica
Independent Component Analysis separates the EEG into statistically independent sources. Components flagged by the selector as artifacts (eye blinks, eye movements, muscle, cardiac, line noise, channel noise) are projected out, preserving the brain activity in the remaining subspace.
The TOML section is still named [ICA_EOG] for
config-file compatibility with older configs; the historical “EOG”
naming is misleading now that the default selector is ICLabel (which
flags eye, muscle, heart, line noise, channel noise, and “other” in
addition to EOG).
Algorithm.
Fit copy. A raw.copy() is taken; if
fit_highpass_hz > 0 it’s high-passed at that cutoff
(Winkler 2015, default 1 Hz). The original signal is untouched — the
filter is decoupled from the analysis band.
Reject gate. Segments whose peak-to-peak
amplitude exceeds reject_uv (default 500 µV) are dropped
from the fit via ica.fit(..., reject=dict(eeg=...)). Rare
big transients (electrode pops, subject coughs) don’t dominate the
decomposition.
Rank-aware dimensionality.
n_components defaults to "rank", auto-derived
from mne.compute_rank(fit_copy) — reads rank from the data
via SVD, so average-reference rank loss (n − 1) and interpolated
channels are accounted for automatically. Integer or float-fraction
overrides available.
Fit. method defaults to
"picard" with
fit_params=dict(ortho=False, extended=True) — same
extended-Infomax objective, 3–10× faster via preconditioning (Ablin,
Cardoso & Gramfort 2018). infomax and
fastica remain selectable. BLAS threads are temporarily
uncapped via threadpoolctl.threadpool_limits(limits=None)
for the duration of the fit so Picard’s inner loop can use all
cores.
Component labelling. Four selector modes:
selector="iclabel"):
MNE-ICALabel’s CNN classifies every component as brain,
muscle, eye, heart,
line_noise, channel_noise, or
other. Classify copy gets average reference + 1–100 Hz
bandpass first (ICLabel’s preconditions). Components whose top-1 class
is not in iclabel_keep (default
["brain", "other"]) are removed. Needs a
backend — mne_icalabel requires
onnxruntime (~30 MB) or pytorch (~1 GB);
neither is a core dependency. Enable ICLabel with
pip install onnxruntime (or
pip install ide4eeg[iclabel]). When no backend is
installed, the GUI grays out the iclabel /
both choices with an orange note, and a pre-launch check
blocks a config that still requests them.ica.find_bads_eog,
find_bads_ecg, find_bads_muscle against the
reference channels named in find_bads_eog_ch.--select-mode ica_components, it launches Svarog
as a subprocess showing the component sources + pre-rendered topomap
PNGs (one per component). Otherwise it falls back to MNE’s
ica.plot_sources(...) opened on the GUI main thread.Apply.
ica.apply(signal, exclude=bads) with full reconstruction.
The step returns the cleaned signal directly.
Why “other” is in the default keep-list. The
other class covers low-confidence components the classifier
isn’t sure about. Removing them by default would strip real brain
activity the classifier happened to miss. Conservative default: keep,
let the user opt into aggressive cleanup.
| Parameter | Default | Description |
|---|---|---|
method |
"picard" |
Algorithm: "picard", "infomax", or
"fastica". |
selector |
backend-aware¹ | Component labelling: "iclabel",
"find_bads", "manual",
"both". |
fit_highpass_hz |
1.0 |
Fit-time high-pass Hz (Winkler 2015). 0 = off. |
reject_uv |
500 |
PTP µV — segments above this dropped from fit. 0 = off. |
n_components |
"rank" |
"rank" (auto), int, or float fraction of variance. |
iclabel_keep |
["brain", "other"] |
Classes to keep when selector="iclabel". |
iclabel_min_prob |
0.0 |
Min classifier confidence for top-1 label; below threshold falls
into other. |
find_bads_eog_ch |
["Fp1","Fp2","Fpz"] |
EOG reference channels for selector="find_bads". |
find_bads_ecg |
true |
Also run find_bads_ecg. |
find_bads_muscle |
true |
Also run find_bads_muscle. |
decim |
1 |
Subsampling factor for the fit. |
random_state |
42 |
RNG seed. |
save |
false |
🗄️ drawer — writes
<base>-ica-cleaned-raw.fif. |
save_components_audit |
false |
“Save components plot and table” — writes audit tree under
artifacts_detection/ICA_EOG/. |
¹ Backend-aware default. The out-of-box
selector is "iclabel" when an ICLabel backend
(onnxruntime/pytorch) is importable, else
"find_bads" — so a vanilla (backend-free) install runs
without crashing. Install a backend
(pip install onnxruntime) to get ICLabel as the
default.
Failure modes.
reject drops every segment → warning
logged, step continues with the unchanged signal.ICAFailureError (GUI: modal error; CLI/batch: logged ERROR,
current file aborts, batch continues).find_bads detector raises (wrong
reference channel, version drift) → that detector’s components are
not removed; a prominent end-of-run WARNING names which
artifact types (EOG/ECG/muscle) were skipped, so the “cleaned” label
doesn’t hide a retained artifact.selector="iclabel"/"both" → caught at
pre-launch (hard.tools.iclabel_requires_backend) with the
pip install onnxruntime hint; the runtime
ICAFailureError is the final net for programmatic
run_file calls that bypass the GUI gray-out + pre-launch
check.Output (when save = true). Under
<preproc_dir>/saved_steps/:
<base>-ica-cleaned-raw.fif — cleaned continuous
signal. Use the 👁 button to view before/after.When save_components_audit = true, under
<preproc_dir>/artifacts_detection/ICA_EOG/:
<base>-ica.fif — serialised
mne.preprocessing.ICA object.<base>_ica_classification.csv — per-component
table (index, class, probability, kept/removed).<base>_ICA_topomap[_<n>].png — component
topomaps (ica.plot_components).<base>_ICA_properties_<i>.png —
per-component property plots (ica.plot_properties).<base>_ica_report.html — bundled MNE Report.References: Ablin, Cardoso & Gramfort (2018) Picard (arXiv:1706.08171) · Winkler et al. (2015) 1 Hz high-pass before ICA (PubMed 26737196) · Pion-Tonachini et al. (2019) ICLabel (NeuroImage 198:181–197) · Gramfort et al. (2014) MNE software.
GUI panel: Drop Channels (checkable, per-step Save)
Selects or excludes channels by name. Non-EEG technical channels (e.g. Photo, Audio, Sample_Counter) are dropped, and you can restrict analysis to a specific channel subset.
Algorithm.
selected_channels ("all" = keep
everything, or a list of names).dropped_channels (names to exclude).selected_channels \ dropped_channels.| Parameter | Default | Description |
|---|---|---|
dropped_channels |
"None" |
Channels to exclude entirely (e.g. non-EEG technical channels). |
selected_channels |
"all" |
Channels to keep. "all" keeps everything except those
in dropped_channels. |
Channel types
(eeg/eog/ecg/…) are reviewed and
overridden separately on the Input tab — see §2.3 Channel types. The selection here
acts after typing.
GUI panel: MP Decomposition (checkable, save forced on, has [Run])
Decomposes the EEG signal into a sum of Gabor atoms —
Gaussian-windowed sinusoids that are optimally matched to the signal’s
time-frequency content. The result is an adaptive, parametric
representation stored in a persistent SQLite book file
(.db).
Requires the empi binary. If Svarog is installed, empi
is auto-detected from the mp/ subdirectory.
Mathematical foundation. Matching Pursuit (Mallat & Zhang 1993) is a greedy iterative algorithm:
R_0(t) = x(t).n, find the Gabor atom g_n
that maximises the inner product with the residual:
g_n = argmax_g |<R_n, g>|.R_{n+1}(t) = R_n(t) - <R_n, g_n> * g_n(t).(1 - explained_energy) of the original, or
iterations atoms are extracted.Each Gabor atom is parameterised as:
g(t) = A * exp(-pi * (t - t0)^2 / s^2) * cos(2*pi*f*t + phi)
where A = amplitude, t0 = centre time,
s = scale (temporal width), f = frequency,
phi = phase.
Multivariate MP (MMP) extends the algorithm to multiple channels simultaneously: at each iteration, the atom parameters (t0, s, f) are shared across all channels, while amplitude and phase are fitted per channel. This produces “macroatoms” that describe coherent multi-channel activity.
empi (Różański 2024) is a GPU-accelerated implementation that uses continuous parameter optimisation over an epsilon-dense dictionary, achieving faster convergence and more precise atom placement than discrete-dictionary approaches.
Algorithm types.
| Algorithm | Key | Description |
|---|---|---|
| SMP | smp |
Single-channel MP: each channel decomposed independently. |
| MMP1 | mmp1 |
Multivariate MP, constant phase across derivations. |
| MMP3 | mmp3 |
Multivariate MP, variable phase. |
Parameters exposed in the GUI (Preprocess tab → MP Decomposition panel):
| Parameter | TOML key | Default | GUI control | Description |
|---|---|---|---|---|
| Algorithm | matching_pursuit.algorithm |
"smp" |
dropdown | SMP / MMP1 / MMP3 — see the table above. |
| Channels | matching_pursuit.channels |
all EEG | channel-pick panel | Subset of channels to decompose; empty = every EEG channel. |
| Empi binary path | matching_pursuit.empi_path |
auto | file picker | Path to the empi executable. Auto-detected from Svarog’s
mp/ subdir if Svarog is installed. |
| Iterations | matching_pursuit.iterations |
30 |
spin | Maximum atoms to extract. 0 = no cap (then
explained_energy is the only stop condition). |
| Explained energy | matching_pursuit.explained_energy |
0.99 |
percent edit | Stop when this fraction of total energy is explained.
1.0 = stop only at the iteration cap. |
| CPU workers | matching_pursuit.cpu_workers |
inherit | spin (0 = inherit) |
Override for empi worker count — see the CPU workers note below. |
Fixed defaults (round-tripped through
config.toml but not editable from the GUI):
| TOML key | Value | Why fixed |
|---|---|---|
matching_pursuit.optimization |
"global" |
Continuous-dictionary refinement gives the best atom localisation
accuracy; the "local" and "none" paths exist
in empi for benchmarking against legacy implementations. Change in the
TOML only if you know why. |
matching_pursuit.include_delta |
true |
Dirac-delta atoms are needed to represent sharp transients (sample-scale spikes) that a Gabor dictionary alone misses. |
matching_pursuit.energy_error |
0.01 |
Dictionary density (ε²). Smaller = denser dictionary = more precise
but quadratically slower. 0.01 is tuned for the accuracy /
runtime balance of typical EEG. |
matching_pursuit.full_atoms_in_signal |
false |
When true, restricts atoms to those that fully overlap
with the segment — MPTK-compatible. Off by default because the empi
continuous-parameter formulation handles edge atoms more cleanly. |
CPU workers — why empi has its own override, separate from
parallelism.n_jobs.
Empi’s parallelism uses a fundamentally different cost model than the
joblib/loky workers that drive every other parallelisable step (MNE
filter / resample, TFR, FOOOF, …). The pipeline-level
parallelism.n_jobs heuristic (see §1.3) caps worker count by
ram_gb // 2 because each loky worker spawns a fresh Python
interpreter — typically 200–500 MB of resident memory per worker once
MNE + NumPy are loaded. On a 16 GB box parallelism.n_jobs
would top out around 8.
Empi runs as a single C++ subprocess; its workers share the atom
dictionary and signal buffer through one address space. Total RSS is
roughly 100 MB regardless of how many workers you configure — there is
no per-worker Python interpreter to pay for. So a machine that’s
appropriately set to parallelism.n_jobs = 4 for the rest of
the pipeline can comfortably run
matching_pursuit.cpu_workers = 8 (or as many as the CPU has
physical cores). The override exists precisely to break out of the
RAM-bound heuristic when the underlying workload is RAM-cheap.
Resolution order at empi invocation:
matching_pursuit.cpu_workers > 0 — explicit user
override (takes precedence).parallelism.n_jobs from
[parallelism] (the unified pipeline knob).1 as the final fallback for tests and callers that
didn’t inject anything.Each empi worker runs single-threaded (--cpu-threads 1);
Gabor-dictionary matching is not BLAS-heavy, so adding BLAS threads per
worker hurts more than it helps.
Output. SQLite .db book file containing
all atoms with parameters: segment, channel, iteration, frequency,
scale, amplitude, energy, phase. Reused by MP filter, EEG profiles, and
dipole fitting.
MP book reuse — hash-controlled. MP decomposition is
one of the most expensive preprocessing steps (minutes to hours). To
avoid recomputing when nothing relevant has changed, MP follows a
hash-controlled reuse policy. The freshness check runs whether or not
mp_decomposition is in step_order. See §3.4.5 MP book reuse for the full policy
table.
References:
Mallat SG, Zhang Z (1993) “Matching Pursuits with Time-Frequency Dictionaries.” IEEE Trans Signal Process 41(12):3397–3415. DOI: 10.1109/78.258082
Kuś R, Różański PT, Durka PJ (2013) “Multivariate matching pursuit in optimal Gabor dictionaries.” BioMed Eng OnLine 12:94. DOI: 10.1186/1475-925X-12-94
Różański PT (2024) “empi: GPU-Accelerated Matching Pursuit with Continuous Dictionaries.” ACM Trans Math Softw 50(3):17. DOI: 10.1145/3674832
GUI panel: MP Filter (checkable, per-step Save) Prerequisite: MP Decomposition
Reconstructs the signal from a selected subset of Matching Pursuit atoms, implementing a nonlinear filter. Unlike conventional linear filters, which operate in the frequency domain, MP filtering can select atoms based on combined time-frequency-energy criteria, removing or keeping specific signal components with atomic precision.
Algorithm.
[freq_min, freq_max][time_min, time_max]min_energymax_iterationsmode:
"keep": reconstruct from matching
atoms only."remove": reconstruct from
non-matching atoms (subtract artefact atoms).x_recon(t) = sum_i A_i * g_i(t) over
selected Gabor atoms.| Parameter | Default | Description |
|---|---|---|
mode |
"remove" |
"keep" or "remove" matching atoms. |
freq_min |
0 |
Atoms with frequency ≥ this (Hz). 0 = no constraint. |
freq_max |
0 |
Atoms with frequency ≤ this (Hz). 0 = no constraint. |
time_min |
0 |
Atoms centered at time ≥ this (s). |
time_max |
0 |
Atoms centered at time ≤ this (s). |
min_energy |
0 |
Atoms with energy ≥ this. |
max_iterations |
0 |
Only first N atoms (by iteration). 0 = all. |
Editing MP filter parameters does not trigger an empi rerun (the filter operates on already-decomposed atoms; its sub-block is excluded from the book hash — see §3.4.5).
GUI panel: EEG Artifacts (checkable, per-step Save)
Detects and marks time intervals contaminated by EEG artifacts. Three complementary detection methods target different artifact types: transient peaks (outliers), rapid voltage changes (slopes), and high-frequency muscle activity (muscles). Additionally, a broadband amplitude check catches large, widespread disturbances.
The artifact detection algorithms implement Klekowicz et al. (2009), originally developed for robust artifact detection in polysomnographic (sleep) recordings at the University of Warsaw.
Common robust thresholding. All three methods share an approach: for each channel, three thresholds are computed (absolute, std-based, median-based), and the strictest (lowest) is used. The standard deviation is computed iteratively — outliers are removed until the estimate stabilises — making the threshold robust to non-Gaussian amplitude distributions.
A. Outlier detection. Detects samples where instantaneous amplitude exceeds an adaptive threshold.
s (remove samples > k*s until convergence).OutliersAbsThr, OutliersMedThr,
OutliersStdThr).|signal| > threshold.OutliersMergeWin
(default 0.1 s).B. Slope detection (peak-to-peak). Detects rapid voltage changes using sliding-window p2p amplitude.
SlopeWindow (default 0.0704 s); run twice (normal +
half-window-shifted) and concatenate.C. Muscle artifact detection. Detects high-frequency EMG contamination via spectral power in the 40–90 Hz band.
MusclesWindow
(default 0.5 s) with Hann taper.MusclesFreqRange (default
[40, 90] Hz).D. Amplitude thresholding. Broadband check applied before the per-channel detectors.
amplitude_l_freq–amplitude_h_freq Hz (default
1–40).|amplitude| > amplitude_max_threshold (default 150
µV).amplitude_nchan_threshold
(default 0.33) of channels exceed the threshold.amplitude_time_threshold);
expand bad regions by amplitude_time_threshold on each
side.Segment-level rejection. All artifact masks are
OR-combined. For each segment, contamination fraction is computed per
channel; if any channel exceeds artifact_threshold (default
0.3 = 30%), the entire segment is marked for rejection.
| Parameter | Default | Description |
|---|---|---|
artifact_threshold |
0.3 |
Max acceptable fraction of artifact-contaminated samples in an epoch (lower = stricter). |
| Type | Method | Window | Default thresholds |
|---|---|---|---|
| Slopes | Peak-to-peak in window | 0.0704 s | Abs 150 µV, Std ×7, Median ×6 |
| Outliers | Amplitude exceeds threshold | per sample | Abs 150 µV, Std ×7, Median ×13 |
| Muscles | Spectral power in 40–90 Hz band | 0.5 s | Abs 0.15 µV², Std ×7, Median ×5 |
Reference: Klekowicz H, Malinowska U, Piotrowska AJ, Wołyńczyk-Gmaj D, Niemcewicz S, Durka PJ (2009) “On the Robust Parametric Detection of EEG Artifacts in Polysomnographic Recordings.” Neuroinformatics 7:147–160. DOI: 10.1007/s12021-009-9045-2
Advanced detection / rejection thresholds: see Appendix E.4.
GUI panel: Gaze (checkable, per-step Save, has [Detect])
Identifies time intervals where the subject is not looking at the
screen, using a synchronised video recording. These intervals are marked
as not_looking artifacts and excluded from EEG analysis.
Particularly useful for infant EEG studies where attention monitoring is
essential.
The pipeline processes each video frame through two stages.
Stage 1: Face detection and identity (InsightFace).
InsightFace is
the only supported backend
(facetag_backend = "insightface").
| Component | Model | What it does |
|---|---|---|
| Detection | RetinaFace | Finds all face bounding boxes in each frame |
| Identity | ArcFace | Computes a 512-d embedding per face, compared to reference photos |
| Tracking | Custom | Adaptive encoding + spatial/size/switch tracking across frames |
InsightFace also produces 5 facial keypoints (left eye, right eye, nose tip, left and right mouth corners) as a byproduct of detection. These are reused by the InsightFace gaze backend.
Requirements: pip install insightface onnxruntime.
Models (~200 MB) are downloaded automatically on first use to
~/.obci/ide4eeg/insightface/models/buffalo_l/. (IDE4EEG
redirects InsightFace away from its upstream default
~/.insightface/ so all of our runtime data lives under one
tree; existing users with ~/.insightface/ get migrated once
on first launch.)
Stage 2: Gaze estimation. Two backends, selected via
facetag_gaze_method:
| Backend | Config value | What it measures | Extra deps | Speed |
|---|---|---|---|---|
| L2CS-Net (default) | "l2cs" |
Eye-gaze direction (where the eyes are actually looking; ResNet50, 3.92° MAE on MPIIGaze) | l2cs + PyTorch (~2 GB) |
~20 ms / frame |
| InsightFace | "insightface" |
Head pose only (where the head is pointing; reuses 5 keypoints) | None (reuses Stage 1) | ~0 ms extra |
The two backends are categorically different. L2CS-Net answers “where are the eyes looking?”. InsightFace answers “where is the head pointing?”. A subject whose head faces the camera but whose eyes are darting to a side monitor is “looking at the screen” by InsightFace’s measure but not by L2CS-Net’s. Pick L2CS-Net unless you have a specific reason to fall back.
Choosing a backend. Default "l2cs" —
true eye-gaze direction is what most experimental designs want. Use
"insightface" only when PyTorch can’t be installed
(e.g. Intel Mac on a Python version without torch wheels) or when
L2CS-Net fails on a particular video (rare; e.g. very low-resolution
face crops). For maximum throughput combine either backend with
facetag_frame_skip = 3–5.
Parameters. All parameters are top-level (not in a TOML section).
| Parameter | Default | Description |
|---|---|---|
prepare_video_artifacts |
false |
Enable video artifact detection. |
video_path |
"" |
Path to .mp4 video file. Auto-detected from EEG
filename if empty. |
video_reference_dir |
"" |
Directory with 3–5 reference photos of the subject looking at the
camera. Empty = the GUI’s Reference faces picker
creates
<output>/IDE4EEG_OUT_<base>/preprocessing/reference_faces/. |
video_exclude_dir |
"" |
Directory with photos of faces to exclude (e.g. mother). Empty = disabled. |
facetag_backend |
"insightface" |
Face detection/identity backend. Only "insightface" is
supported. |
facetag_gaze_method |
"l2cs" |
Gaze backend: "insightface" or
"l2cs". |
facetag_max_angle |
20.0 |
Maximum angle (degrees) between current and reference gaze vectors to classify as “looking”. |
facetag_gaze_smoothing |
0.0 |
Temporal smoothing (EMA alpha, 0–1). 0 = off; 0.3 = moderate; 0.1 = heavy. |
facetag_frame_skip |
3 |
Process every Nth frame (1 = every frame). |
facetag_downscale |
1.0 |
Resize factor before face detection (0.5 = half resolution). |
facetag_det_size |
0 |
InsightFace detection resolution. 0 = full frame; 640 = fast default. |
facetag_face_tolerance |
0.6 |
Maximum encoding distance for face match. |
facetag_min_interval |
0.0 |
Minimum duration (seconds) for a not-looking interval. |
facetag_no_face_is_artifact |
true |
Treat frames with no detected face as “not looking”. |
facetag_ref_use_roi |
false |
Use face ROI (not full image) for reference photo gaze vectors. |
facetag_tracking_adapt_rate |
0.5 |
Rate of adaptive encoding update (0 = static, 1 = instant). |
facetag_position_weight |
0.4 |
Weight of spatial continuity in face tracking score. |
facetag_size_weight |
0.2 |
Weight of face size consistency in tracking score. |
facetag_switch_resistance |
0.25 |
Extra cost for switching to a different face. |
Outputs. When
prepare_video_artifacts = true:
<output>/artifacts_detection/video_artifacts/<file>___video_artifacts.tag
— BrainTech XML tag file with one entry per not-looking interval.<output>/artifacts_detection/video_artifacts/<file>___video_summary.txt
— human-readable summary (frame counts, durations, interval list).<output>/artifacts_detection/video_artifacts/<file>___video_timeline.png
— timeline plot.<video_stem>_facetag_<gaze_method>_review.json
— placed next to the video file so the Review
Results button on the Gaze panel auto-loads detected
intervals.BAD_not_looking MNE annotations on the artifacts-marked
signal snapshot — Svarog and MNE viewers render them as colored
overlays. Visible via the eye on the EEG-artifacts or Gaze panel (both
panels drive the same artifacts step).The Run-tab Stop button cancels the video frame loop
cooperatively — cancel_event.is_set() is checked once per
frame inside facetag_video, so a multi-hour video doesn’t
keep running after Stop.
Speed tips.
facetag_frame_skip = 3–5):
~3–5× speedup at 30 fps with minimal accuracy loss.facetag_downscale = 0.5):
~2× speedup per frame. Avoid below 0.3 for small faces.facetag_det_size = 640): faster than full-frame (0) but
may miss distant faces.These options combine multiplicatively.
The postamble runs after all reorderable steps complete. It cuts the continuous signal into segments, drops bad ones, optionally lets you review them manually, saves the final epochs, and computes per-segment statistics.
Three rejection paths. A segment can be dropped for
any of three independent reasons: (a) at cut time, by MNE’s
reject_dict / flat_dict peak-to-peak limits
(set in §3.1.3 for epochs
mode); (b) in §3.3.2, by
the auto-percentile or fixed-µV [segment_rejection]
thresholds; (c) by the artifact step (if enabled in §3.2.11) — segments where the
artifact-contamination fraction on any channel exceeds
artifact_threshold are rejected here.
GUI panel: Cut Segments (always enabled, non-checkable)
Slices the continuous preprocessed signal into discrete segments (MNE Epochs objects) for analysis.
[start_offset, stop_offset] around each event marker in the
STIM channel. Apply optional baseline correction
(epochs_baseline).window_length seconds from the rest period.Uses MNE’s mne.Epochs() constructor with
reject and flat dicts for immediate p2p
rejection.
GUI panel: Drop Segments (checkable, per-step Save)
Provides a simple, data-driven rejection mechanism based on p2p amplitude. Segments with excessively large or suspiciously flat signals are dropped.
Algorithm.
PTP(seg, ch) = max(signal) - min(signal).reject_threshold > 0: use it directly (in
µV).reject_threshold = 0 (auto): estimate from data —
threshold = percentile(all_PTP_values, auto_percentile)
(default 95th percentile).flat_threshold (detecting dead channels).| Parameter | Default | Description |
|---|---|---|
enabled |
false |
Enable automatic segment rejection. |
reject_threshold |
0 |
p2p amplitude threshold in µV. 0 = auto-detect via
auto_percentile. |
flat_threshold |
0 |
Minimum p2p (µV). 0 = disabled. |
auto_percentile |
95.0 |
When reject_threshold = 0, percentile of all-segment
p2p amplitudes used as the threshold. |
The TOML section is [segment_rejection] (historical
name).
When Review segments manually is checked in the Drop
Segments panel (or rest.check_rest /
epochs.check_epochs is true in TOML), an
interactive epoch browser opens after Drop. Same Svarog → MNE dispatch
as bad-channels and ICA: if the installed jar advertises
--select-mode bad_epochs, Svarog opens the bad-epoch panel;
otherwise MNE’s epochs.plot(...) runs on the GUI main
thread.
The Drop Segments panel also has a Review now button that triggers a one-off preprocessing-only run (analyses skipped) terminating at this review — useful when you want to inspect rejections without committing to a full Run.
The pipeline always writes the final cleaned epochs as
-epo.fif to preprocessing/saved_signals/. This
is the primary pipeline output and is never suppressed
by save toggles — every analysis depends on it.
Computes per-epoch per-channel statistics (tag, channel, time, bad
flags, artifact fractions) and stores them in a pandas DataFrame for
downstream analyses. Written as CSV in the stats/
subfolder.
Every preprocessing step has a 🗄️ file-cabinet toggle in its header row, sitting between the step’s enable checkbox and its title. Opacity encodes state — bright when save is on, dimmed to ~25% when off. The toggle auto-syncs to the step’s enable state: enabling a step (checking its main checkbox) automatically turns its save on; disabling a step turns save off. You can still uncheck save manually after enabling — the override sticks until you toggle the step off and back on.
What gets saved per step:
<base>-<suffix>-raw.fif in
preprocessing/saved_steps/..db
book is the step’s output).saved_signals/) is the primary pipeline
product and is always saved.The toggle is always visible, even when the step is collapsed. Clicking the 👁 eye button (below) auto-flips the relevant save toggles on so the snapshots persist — useful if you wanted to see the before/after and keep it for later too.
The 🗄️ drawer represents signal snapshots only. Auxiliary outputs (filter plots, ICA component reports, gaze video plots) get their own widgets — typically separate inline checkboxes labelled “Save filter plots”, “Save components plot and table”, and so on. This separation lets you keep an ICA snapshot for the eye-button while skipping the heavy MNE Report HTML build, or vice versa.
Every preprocessing step has an eye button (👁) in its row header, next to the up/down reorder arrows (when visible). Click it to open a synchronised before/after view of the signal around that step — IDE4EEG’s answer to “I just changed a parameter, show me what that actually did.”
Clicking 👁 auto-flips the 🗄️ save toggles on for this step and its preceding step, so the snapshots persist across future runs.
Viewer dispatch.
--split-signal
flag (requires a Svarog jar that supports it).Freshness / staleness detection. Each saved snapshot
FIF carries a hash of the preprocessing config that produced it,
embedded in info["description"] as
"after <step> [cfg:<hash>]". When you click 👁,
IDE4EEG compares that hash to the hash of your current GUI config. If
they match, the viewer opens directly. If you’ve changed any
preprocessing parameter since the snapshot was written, the hash differs
and the snapshot is treated as stale — IDE4EEG auto-runs the pipeline to
regenerate it before opening the viewer.
The same hash drives a write-skip optimisation: when
the pipeline reaches a step whose target snapshot already exists on disk
with a matching [cfg:<hash>], the save is skipped
(the bytes would be identical anyway). Saves hundreds of MB of FIF I/O
per run when only downstream parameters changed.
Run-to-step — the eye button can drive the pipeline.
If the saved files don’t exist yet (or are stale), clicking the eye
button runs preprocessing up to that step instead of
asking you to click Run manually. A truncated full pipeline launches via
the Run tab: your config is deepcopied, step_order is
sliced at the target step, every analysis flag is forced off, and
save=True is forced on every enabled saveable step up to
and including the target — so both the before and after snapshots land
on disk, along with intermediate snapshots you can view later without
re-running. Your GUI state is untouched. The viewer opens automatically
when the pipeline finishes.
The eye button is available on every saveable preprocessing step:
Trim, Drop Channels, Bad Channels, Montage, Filtering, ICA, EEG
Artifacts, Gaze, MP Filter. The Gaze eye opens the same
artifacts-marked snapshot as the EEG-artifacts eye
(both panels drive the same artifacts pipeline step). The
Trim eye is a special case: it opens the
Preview & Trim window directly rather than a Svarog
split view, which would mislabel the trimmed segment’s time axis as
00:00. MP Decomposition has no eye — it
produces a .db book, not a -raw.fif. The
before/after walk skips steps without a Raw FIF snapshot, so on a
trim → mp_decomposition → mp_filter pipeline the MP Filter
eye correctly pairs with the trim snapshot, not the MP book.
A second 👁 segments button on the Segmentation setup header previews the segment windows themselves — see §3.1.4.
If a pipeline run is already in progress when you click, the button refuses with a “Pipeline already running” dialog — wait for it to finish or click Stop on the Run tab first.
Each preprocessing step’s header row carries up to two compact status badges (small monochrome icons) next to its title:
"manual" / "both" mode, ICA in
"manual" / "both" mode, and Drop Segments with
Review segments manually all surface this badge.The badges let you see at a glance which steps will produce extra output or pause for user input, without expanding every panel.
IDE4EEG uses a Merkle-style hash chain to identify the exact
configuration that produced each step’s signal snapshot. The
[cfg:<hash>] marker stamped into every saved FIF lets
the GUI detect staleness when you change a parameter, drives the
eye-button’s freshness check (§3.4.2), and powers the write-skip
optimisation when re-running a pipeline whose downstream parameters
changed but whose upstream snapshots are still valid.
For internals — the per-step fingerprint, what’s included in / excluded from each step’s hash, and the three invariants that keep the chain stable across re-runs — see Appendix E.8.
MP decomposition follows a hash-controlled reuse policy: the
freshness check runs whether or not mp_decomposition is in
step_order.
| Preprocessing tab → “MP Decomposition” checkbox | Book on disk | Hash | What happens |
|---|---|---|---|
| Checked | none | — | Run empi, write book_*.db, stamp the current config’s
hash. |
| Checked | present | matches | Skip empi, reuse the existing book. Log:
MP Decomposition: reusing existing book (hash match). |
| Checked | present | differs / legacy | Run empi, overwrite the book with a fresh hash. |
| Unchecked | none | — | Skip MP; downstream MP-consuming analyses fall back to ephemeral per-ERP decomposition. |
| Unchecked | present | matches | Reuse the book silently. |
| Unchecked | present | differs | Halt with an error explaining how to recover (re-check the box, or restore the upstream parameters that produced the book). |
| Unchecked | present | legacy (no hash) | Reuse with a one-time soft warning. |
This means iterating on mp_filter parameters (the
[matching_pursuit.filter] sub-block) does
not trigger an empi rerun — the filter operates on
already-decomposed atoms, so its sub-block is stripped from the book
hash. Same for runtime-only fields (empi_path,
cpu_workers, outputs).
The hash covers everything that determines what data MP sees: the
source EEG file’s identity (name + size + mtime),
preprocessing.step_order (so reordering invalidates), and
every config section read by a step that runs before MP — including
filters, ICA_EOG, montage,
bad_channels, choosing_channels, and the
decomposition-relevant fields of matching_pursuit
(algorithm, channels, iterations, explained_energy,
optimisation, …). Postamble sections (epochs,
mp_filter) are excluded — they can’t influence MP
input.
Consumer-channel check on reuse. Even when the hash
matches, the on-disk book’s channel_names list may not
include a channel that an enabled downstream consumer needs
(eeg_profiles.channel). When that happens the
auto-discovery preamble halts with a structured error pointing at two
recovery paths: set the analysis channel to one in the book, or check MP
Decomposition AND update matching_pursuit.channels to
include the required channel (or "all") — the latter
rewrites the book.
The same rule applies to ide4eeg --run config.toml and
to scripts exported via api.generate_script. MP enable
state lives in preprocessing.step_order, not in any
top-level flag. Both Run Pipeline and per-analysis
Run buttons respect the MP checkbox identically —
checking it forces a recompute, unchecking it lets auto-discovery
decide.
Migrating from older configs: the legacy
prepare_mp_decompositionTOML key was removed. To enable MP, add"mp_decomposition"topreprocessing.step_order. Configs that still use the old key log an error and run without MP.
The Analysis tab groups all post-preprocessing computations: ERPs, time-frequency analyses, statistical tests, connectivity, source localisation, and the external MNE-Python catalog. Each analysis has its own checkbox, parameter group, and per-analysis [Run] button. Three IDE4EEG-native analyses appear at the top, then a separator, then a series of MNE-Python wrappers grouped by category (ERP / Spectra / Time-frequency / Spatial / Comparison / Source estimation).
Most analysis sections (PSD, Connectivity, Statistics, EEG Profiles,
MMP Dipole Fitting, …) have their own [Run] button.
Clicking it narrows the run to just that analysis — every other
prepare_* flag is forced off — and otherwise behaves like
Run Pipeline. The current GUI state is collected into a config dict at
the moment you click; a config.toml reflecting the
per-analysis narrowing is saved to the output directory so you can see
exactly what was executed.
What a per-analysis [Run] saves depends on whether the analysis is book-only (reads the MP book directly) or not.
| Analysis | Per-analysis [Run] behaviour |
|---|---|
| Book-only: EEG Profiles, MMP Dipole Fitting | If a matching MP book is on disk, skips signal loading and preprocessing entirely — reads atoms straight from the book and runs only the analysis. Fast path. If no matching book exists, falls through to the full pipeline (preprocessing + the analysis). |
| Non-book-only: Connectivity, MNE-catalog ERP / Spectra / Time-frequency / Spatial / Comparison / Source-estimation panels | Runs full preprocessing (same as Run Pipeline), then only this analysis. Saves the cost of other enabled analyses; no faster than Run Pipeline if this is your only enabled analysis. |
Notes that apply to both cases:
mp_decomposition to preprocessing.step_order
(when an empi binary is configured) or refuses with a clear
message.Some analyses require event markers (≥ 2 event types are needed to compute differences); others work on continuous data. The Analysis tab marks each analysis with which modes it supports:
| Analysis | Available for |
|---|---|
| MMP → dipole sources | Both modes |
| EEG profiles | Both modes |
| Connectivity | Both modes |
| MNE catalog (ERP / Comparison / Time-frequency) | Needs events |
| MNE catalog (Spectra / Spatial) | Both modes |
| MNE catalog (Source estimation) | Needs events |
When the loaded recording’s segmentation mode doesn’t supply what an analysis needs, the GUI greys out the unavailable entries and shows a tooltip explaining why.
GUI panel: MMP → dipole sources (checkable, has
[Run]) Config section: [dipole_fitting]
Prerequisite: MMP1 book (multivariate MP
decomposition). SMP and MMP3 are rejected.
Fits equivalent current dipoles (ECDs) to Multivariate Matching Pursuit macroatoms, localising the brain sources of coherent multi-channel EEG activity on a template brain (fsaverage) or a subject-specific FreeSurfer reconstruction.
Mathematical foundation. An equivalent current
dipole (Scherg 1990) is a point source of neural current characterised
by position (x, y, z), orientation
(o_x, o_y, o_z), and moment q (nAm). The scalp
potential it produces at electrode i is:
V_i = L_i(x, y, z) * q * (o_x, o_y, o_z)
where L_i is the lead-field vector from the BEM forward
model.
Fitting procedure.
EvokedArray from the macroatom’s
spatial pattern.mne.fit_dipole() with the BEM
model.Structure classification: match fitted dipole parameters to predefined EEG structures (K-complex, sleep spindle, alpha wave, etc.) via a lookup table based on the atom’s frequency and scale.
| Parameter | Default | Description |
|---|---|---|
ref_channel |
"average" |
Reference channel for dipole fitting. |
montage |
"standard_1020" |
Electrode montage name (MNE-compatible). |
max_iterations |
0 |
Maximum number of dipoles to fit. 0 = all atoms. |
min_gof |
0 |
Minimum goodness-of-fit (%). 0 = no constraint. |
cortical_distance |
false |
Compute cortical distances (requires nibabel). |
fsaverage_dir |
"" |
FreeSurfer subject directory. Empty = fsaverage (auto-downloaded).
For subject-specific, set to the subject’s recon-all
output. |
freq_min/freq_max |
0 |
Atom frequency filter (Hz). 0 = no constraint. |
scale_min/scale_max |
0 |
Atom duration filter (s). 0 = no constraint. |
Output files (in
<output>/analysis/dipole_analysis/):
<base>___dipoles.csv — per-dipole parameters:
position (HEAD metres + MNI mm), orientation, GOF, amplitude, frequency,
scale, cortical distance. Connectivis reads this directly for 3D
rendering — the mni_x/y/z_mm columns place dipoles on the
cortex PLY, and amplitude_nAm / gof_pct /
frequency_Hz drive size/colour modes.<base>___electrodes.dat — electrode positions in
MNI mm for Connectivis.<base>___dipole_brain_*.png — MNE 2D scatter
plots (axial / coronal / sagittal) coloured by amplitude, frequency,
GOF.<base>___dipole_gof.png — goodness-of-fit
histogram.Template mode (fsaverage — default).
fsaverage is a template brain — the average of 40
healthy adults’ MRI scans, created by FreeSurfer at the Montreal
Neurological Institute. It is the standard reference brain used across
neuroscience when a subject’s own MRI is not available. MNE downloads it
automatically (~30 MB) the first time dipole fitting runs; it lives at
~/mne_data/MNE-fsaverage-data/fsaverage/.
When fsaverage_dir is empty or points to the
MNE-downloaded fsaverage, dipole fitting uses this template brain. The
brain cortex PLY (with Desikan-Killiany atlas colours) is cached once in
~/.obci/connectivis/fsaverage_brain_aparc.ply (327k
vertices, 13 MB) and shared across all runs.
Connectivis renders the brain PLY inside its built-in head model (a stylised mesh with nose, ears, neck). The head is cosmetic — not registered to MNI space, so electrodes may hover slightly above or sink into the visible scalp. The electrode and dipole positions themselves are geometrically correct in MNI coordinates.
Limitation. Because fsaverage is an average of 40 people, individual facial features (nose, ears, chin) are literally averaged out. The exported head surface is a featureless smooth ellipsoid. That is why Connectivis uses its built-in stylised head for visualisation. For a real face, use subject-specific mode.
Subject-specific mode. When
fsaverage_dir points to a subject’s own FreeSurfer
recon-all output (not fsaverage), the pipeline uses the
subject’s individual anatomy. Requires a full FreeSurfer reconstruction
(recon-all -all -s SUBJ01).
Per-run outputs (in the output directory, not cached):
<base>___brain.ply — the subject’s own pial
cortex with Desikan-Killiany atlas colours. Shows individual gyral
folding.<base>___head.ply — the subject’s own
surf/lh.seghead — head surface from the subject’s MRI with
real nose, ears, chin, and eye sockets.<base>___dipoles.csv — same as template mode, in
the subject’s MRI frame.<base>___electrodes.dat — electrode positions in
the subject’s MRI frame.All files share the subject’s FreeSurfer surface RAS frame, so brain, head, dipoles, and electrodes align perfectly in Connectivis — no cosmetic AC3D head needed.
# Subject-specific dipole fitting
[dipole_fitting]
fsaverage_dir = "/path/to/subjects/SUBJ01"
ref_channel = "average"
montage = "standard_1020"Prerequisites for subject-specific mode:
recon-all must have
completed for the subject.*-bem-sol.fif) —
mne.make_bem_model + mne.make_bem_solution (or
mne.make_forward_solution).*-trans.fif)
mapping HEAD coordinates to the subject’s MRI. Created by
mne.gui.coregistration or mne coreg.lh.aparc.annot + rh.aparc.annot) in the
subject’s label/ directory.Example (MP-based). See
examples/dipole_spindles/ — methodology from Durka et
al. (2024) Sensors 24(3):842 (DOI). 24-channel sleep EEG,
MMP1 decomposition (200 iterations), dipole fit with spindle selection
(11–16 Hz, scale ≥ 0.5 s). A separate, non-standard approach using
multivariate Matching Pursuit. Uses fsaverage (template mode).
References:
Scherg M (1990) “Fundamentals of dipole source potential analysis.” Adv Audiol 6:40–69.
Durka PJ (2018) “Matching Pursuit Dipole Fitting for EEG source localization.” University of Warsaw.
Kuś R, Różański PT, Durka PJ (2013) “Multivariate matching pursuit in optimal Gabor dictionaries.” BioMed Eng OnLine 12:94.
GUI panel: EEG Profiles (checkable, has [Run])
Config section: [eeg_profiles]
Prerequisite: MP book (decomposition)
Detects and counts EEG graphoelements — stereotyped waveforms such as sleep spindles, alpha waves, delta waves, K-complexes — by filtering MP atoms on their Frequency, Amplitude, Scale, and Phase (FASP) parameters. Originally developed for differentiating disorders of consciousness (Malinowska et al. 2013).
Use this for continuous (rest-mode) signals. EEG profiles are designed around a continuous time axis — graphoelement counts are binned over absolute time. The GUI flags this when you pair Profiles with event-locked mode.
FASP filtering. Each atom from the MP book is
characterised by frequency f, amplitude A,
scale s, phase phi. A structure template
defines acceptable ranges for each parameter. An atom matches if
all criteria are satisfied:
match = (freq_min <= f <= freq_max) AND
(amp_min <= |A| <= amp_max) AND
(scale_min <= s <= scale_max) AND
(phase_min <= phi <= phase_max)
where 0 in any field means “no constraint” (always passes).
Preset structure templates.
| Structure | Frequency [Hz] | Amplitude [µV] | Scale [s] | Phase |
|---|---|---|---|---|
| Sleep spindles | 11–16 | ≥ 12 | 0.5–2.5 | — |
| Alpha waves | 8–12 | ≥ 5 | — | — |
| Delta waves | 0.2–4 | ≥ 65 | 0.5–6 | — |
| Beta waves | 15–25 | ≥ 5 | 0–0.5 | — |
| Theta waves | 4–8 | ≥ 15 | ≥ 1.0 | — |
Time-evolution metrics. Detections are binned into intervals equal to the MP segment length (read from book metadata). Three display modes:
| Mode | Metric | Description |
|---|---|---|
count |
Detections per bin | Total number of matching atoms per interval. |
time_percentage |
sum(scale_s) / interval * 100 |
Fraction of time occupied by the structure. |
single_occurrence |
Binary markers | Marks at detection times with height = amplitude. |
| Parameter | Default | Description |
|---|---|---|
structures |
(built-in) | List of structure definitions (FASP). |
mode |
"count" |
Display mode. |
channel |
0 |
Channel index for analysis (0-based). |
Bin width is always equal to the MP segment length. It is not
user-configurable — the legacy interval_sec config key is
ignored if present.
👁 Open in Svarog. The EEG Profiles panel header
carries a 👁 button that opens Svarog with the cleaned signal + the MP
book + the per-structure .tag files in one synchronised
view, so you can scrub the recording and see detected structures
inline.
Outputs (in eeg_profiles/):
.tag file with colour-coded structure
detections.Reference: Malinowska U, Chatelle C, Bruno MA, Noirhomme Q, Laureys S, Durka PJ (2013) “Electroencephalographic profiles for differentiation of disorders of consciousness.” BioMedical Engineering OnLine 12:109. DOI: 10.1186/1475-925X-12-109
GUI panel: Connectivity (checkable, has [Run])
Config section: [connectivity]
Estimates directed and undirected information flow between EEG channels, revealing how brain regions communicate during cognitive tasks or rest. The implementation is based on ConnectiviPy (Krzemiński & Kamiński, University of Warsaw), developed during Google Summer of Code 2015 under INCF sponsorship.
Multivariate Autoregressive (MVAR) model. The k-channel EEG signal is modelled as:
X(t) = A_1 * X(t-1) + A_2 * X(t-2) + ... + A_p * X(t-p) + E(t)
where A_m are (k × k) coefficient matrices,
p is the model order, and E(t) is Gaussian
noise with covariance V.
Model order is selected via Akaike Information Criterion:
AIC(p) = N * log(det(V(p))) + 2 * p * k²,
p_optimal = argmin AIC(p).
MVAR fitting methods.
| Method | Key | Description |
|---|---|---|
| Yule-Walker | yw |
Solves the matrix Yule-Walker equations. Fast, default. |
| Nuttall-Strand | ns |
Forward-backward lattice. Better for short data. |
| Vieira-Morf | vm |
Harmonic-mean normalised lattice. Most robust. |
Spectral decomposition. From the fitted MVAR coefficients:
A(f) = I - sum_{m=1}^{p} A_m * exp(-j*2*pi*f*m/fs)
H(f) = A(f)^(-1) -- transfer function
S(f) = H(f) * V * H(f)^H -- cross-spectral density
Connectivity measures.
| Method | Type | Formula | Interpretation |
|---|---|---|---|
| DTF | AR | DTF(f)_{i->j} = |H_ij| / sqrt(sum_k |H_ik|^2) |
Directed transfer from j to i, normalised by total outflow. |
| gDTF | AR | DTF weighted by noise covariance | Accounts for differing noise levels across channels. |
| ffDTF | AR | |H_ij| / sqrt(sum_f sum_k |H_ik(f)|^2) |
Full-frequency normalisation. |
| dDTF | AR | DTF * Partial_Coherence |
Direct DTF: removes indirect (mediated) paths. |
| iDTF | AR | Instantaneous DTF | Captures contemporaneous interactions. |
| PDC | AR | PDC(f)_{i->j} = |A_ij| / sqrt(sum_k |A_kj|^2) |
Partial directed coherence: direct influence at the input side. |
| gPDC | AR | PDC weighted by inverse noise covariance | Generalised PDC. |
| iPDC | AR | Instantaneous PDC | Zero-lag PDC variant. |
| PCoh | AR | Partial coherence | Undirected, controls for volume conduction. |
| Coh | Signal | Standard coherency from FFT | Undirected frequency-domain correlation. |
| PSI | Signal | Phase Slope Index | Directed, based on the slope of the phase spectrum. |
| GCI | Signal | Granger Causality Index | Directed, based on prediction error reduction. |
| AEC | Signal | Amplitude Envelope Correlation | Undirected, correlation of Hilbert envelopes. |
Short-time connectivity is optionally computed in a sliding window to track dynamics over time.
| Parameter | Default | Description |
|---|---|---|
methods |
["dtf", "coh"] |
List of connectivity measures. |
mvar_method |
"yw" |
MVAR fitting method. |
mvar_order |
0 |
AR model order. 0 = automatic via Akaike. |
resolution |
100 |
Frequency resolution for spectral estimation. |
channels |
"all" |
Channels to include. |
short_time |
false |
Enable sliding-window connectivity. |
st_window |
0 |
Short-time window length (s). 0 = auto. |
st_overlap |
0 |
Short-time window overlap (s). |
significance |
false |
Compute bootstrap significance thresholds. |
sig_reps |
100 |
Bootstrap repetitions. |
sig_alpha |
0.05 |
Significance level. |
Parallelism. The bootstrap / surrogate / short-time
inner loops in connectivity/conn.py are dispatched via
joblib.Parallel, inheriting n_jobs from
[parallelism]. MVAR-based methods (DTF, PDC, iDTF, iPDC,
gDTF, gPDC, dDTF, ffDTF) typically see 3–5× speedup on 8 cores when
significance or short-time is on. Coherency/PSI/GCI per-rep work is
small enough that joblib spawn overhead competes — those paths show flat
or marginally-slower wall time for small inputs but break even on longer
recordings.
AEC notes. AEC bandpass-filters the signal in five default bands (theta [6, 7], alpha [8, 13], beta [15, 25], low-gamma [30, 45], high-gamma [55, 70] Hz). Bands whose upper edge would exceed the Nyquist frequency are skipped with a one-shot warning rather than aborting (e.g. high-gamma drops out at fs ≤ 140 Hz, including standard 128 Hz BrainTech).
Connectivis .dat export. For every
successfully-computed method, the connectivity step writes one
.dat file per frequency band so the user can scrub bands ×
methods in 3D.
| Band | Range (Hz) |
|---|---|
fullband |
0 → Nyquist |
delta |
1–4 |
theta |
6–7 |
alpha |
8–13 |
beta |
15–25 |
low-gamma |
30–45 |
high-gamma |
55–70 |
Bands are defined locally in
connectivity_analysis.py::_CONNECTIVIS_BANDS (separate from
the AEC FQ_BANDS registry — adding delta does
not affect AEC). fullband averages all bins from DC to
Nyquist. Bands whose upper bound exceeds Nyquist are skipped with an
info-level log line.
Filenames follow
<base>___<tag>_<data_label>_<method>_<band>.dat.
A run with DTF + PDC selected produces 14 files (2 methods × 7 bands).
Per-file headers include ;title and ;band so
each file self-describes. Significance and short-time results are
intentionally not exported to .dat — their shapes (CI
matrices, k×k×T tensors) need a different scheme. They remain available
as PNG plots.
Output. Per-method directional plots, channel ×
channel heatmaps, PSD plots, frequency-resolved connectivity, optional
short-time spectrograms, and the .dat files for
Connectivis.
References:
Kamiński M, Blinowska KJ (1991) “A new method of the description of the information flow in the brain structures.” Biol Cybern 65:203–210.
Baccalá LA, Sameshima K (2001) “Partial directed coherence.” Biol Cybern 84:463–474.
Blinowska KJ, Kuś R, Kamiński M (2004) “Granger causality and information flow in multivariate processes.” Phys Rev E 70:050902.
Below the Analyses from MNE: separator, the Analysis tab exposes the MNE-Python catalog as six collapsible category panels — ERP, Spectra, Time-frequency, Spatial, Comparison, and Source estimation. Each panel has its own enable checkbox, a category-level [?] help button, an inline [Run] button, and a list of individual entries with per-entry [?] buttons that link straight to the MNE documentation. A few entries have inline parameter fields (frequency range, number of time points, …); the rest run with MNE’s defaults.
Config key: mne_catalog (list of
analysis IDs). Outputs land in the MNE/ subdirectory of
IDE4EEG_OUT_*.
Four of the six category panels — ERP, Spectra, Time-frequency, Comparison — each carry a single Channels checkbox panel at the top of the section (same widget used by Connectivity and the MMP→dipole analysis). All channel-aware entries within that category share the panel’s selection at run time. (“Channel-aware” = an entry that plots or analyses per-channel data — a butterfly trace, a PSD overlay — as opposed to a whole-scalp topography.) The remaining categories (Spatial, Source estimation) have no panel — channel selection would distort topographic maps or break the inverse problem.
The 11 channel-aware entries are: erp_butterfly,
erp_image, gfp,
cluster_permutation, evoked_image,
compare_per_channel (ERP) · psd_multitaper,
psd_welch (Spectra) · tfr_morlet,
tfr_multitaper (Time-frequency) ·
compare_evokeds (Comparison). erp_joint is
intentionally excluded — plot_joint needs the full montage
to draw the topomap inserts at GFP peaks; a small subset crashes the
underlying MNE call.
Default = all EEG channels. Untick channels to restrict the whole category’s analyses to a region of interest (e.g. uncheck everything except motor-cortex electrodes in the Time-frequency panel while ERP butterfly elsewhere still shows whole-scalp). Filter buttons on the panel (All / None / EEG only) speed up bulk picks.
Refactored 2026-05-20 — replaces the per-entry
free-text comma-separated channels field. Old TOML configs
with per-entry mne_params.<aid>.channels = "Fz, Cz"
continue to work at runtime (the resolver accepts both list and
comma-string forms); save the config from the new GUI to migrate to the
per-category panel.
Validation: unknown channel names log a warning and are dropped from the subset; if NONE of the requested names exist in the data, that analysis raises an error (visible in the Run-tab log) and the rest of the catalog continues.
TOML keys: - GUI persistence:
mne_channels.<Category> — list[str]
(subset) or "all". Per-category panel state, restored after
the signal loads. - Runtime (auto-injected):
mne_params.<analysis_id>.channels —
list[str]. Set by the GUI from the per-category panel
selection right before pipeline launch (omitted when the panel is
“all”); headless callers may still write either form.
Panel: ERP · Available for: event-locked epochs (some entries need 2+ event types)
The ERP — Event-Related Potential — is the trial-averaged waveform per condition:
ERP(t) = (1/N) · Σᵢ epochᵢ(t)
Phase-locked neural responses sum constructively while non-phase-locked noise cancels out.
| ID | Name | Requires | Parameters |
|---|---|---|---|
erp_butterfly |
ERP butterfly (one line per channel) | — | — |
erp_image |
ERP image (heatmap of single-trial ERPs) | — | — |
erp_joint |
ERP joint (topos at peaks) | montage | — |
erp_topomap |
ERP topomap series | montage | n_times |
gfp |
Global field power (std across channels) | — | — |
cluster_permutation |
Spatio-temporal cluster permutation test | 2+ tags | step_down_p, n_permutations |
evoked_image |
Evoked image (channels × time) | — | — |
compare_per_channel |
Compare evokeds (per-channel grid) | 2+ tags | — |
Spatio-temporal cluster permutation test (Maris &
Oostenveld 2007). Non-parametric method controlling the
family-wise error rate when testing for differences across many time
points and channels, without overly conservative corrections like
Bonferroni. Per time point and channel, compute a t-statistic comparing
two conditions; threshold to identify above-significance samples; group
adjacent above-threshold samples (in time and space) into clusters; sum
statistics within each cluster; randomly reassign condition labels and
repeat (default n_permutations = 1024); report the fraction
of permutations where the maximum cluster statistic exceeds the observed
cluster statistic. Wraps
mne.stats.spatio_temporal_cluster_test with channel
adjacency from the electrode montage.
| Parameter | Default | Description |
|---|---|---|
step_down_p |
0.05 |
P-value for step-down-in-jumps test. |
n_permutations |
1024 |
Number of permutations (higher = more precise). |
Reference: Maris E, Oostenveld R (2007) “Nonparametric statistical testing of EEG- and MEG-data.” J Neurosci Methods 164(1):177–190.
Panel: Spectra · Available for: rest or epochs
| ID | Name | Requires | Parameters |
|---|---|---|---|
psd_topomap |
PSD topomap (default 5 standard bands) | montage | bands, scale
("dB"/"power") |
psd_multitaper |
PSD via multitaper (Spectrum.plot) |
— | fmin, fmax, scale
("dB"/"power"/"amplitude"),
bandwidth, tmin, tmax,
xscale, average, ci |
psd_welch |
PSD via Welch’s method | — | fmin, fmax, scale (same three
choices), n_per_seg, n_overlap,
average, xscale |
psd_topo |
Per-channel PSD laid out at sensor positions | montage | fmin, fmax, scale
("dB"/"power") |
psd_topomap covers the legacy
psd_bands_topomap use case via its bands
parameter; the two were merged in late 2026. Line-plot entries
(psd_multitaper, psd_welch) expose three
scale choices (dB / power /
amplitude); topographic entries only the first two (no
amplitude).
Panel: Time-frequency · Available for: rest mode OR event-locked epochs (ERD/S Topomap is event-locked-only — it needs real per-condition baselines)
All three TFR entries call
Epochs.compute_tfr(method=...) (the current MNE ≥ 1.4 API;
the legacy mne.time_frequency.tfr_morlet /
tfr_multitaper functions are deprecated upstream).
| ID | Name | Requires | Parameters |
|---|---|---|---|
tfr_morlet |
TFR via Morlet wavelet | — | freq_min, freq_max (0 = Nyquist),
n_cycles_mode
("adaptive"/"constant"),
n_cycles |
tfr_multitaper |
TFR via multitaper | — | freq_min, freq_max,
n_cycles_mode, n_cycles,
time_bandwidth (default 4.0, advanced) |
erds_topomap |
ERD/S topographic maps — baseline-corrected | montage | freq_min, freq_max,
n_cycles_mode, n_cycles |
n_cycles_mode picks the
time-frequency-resolution trade-off: - "adaptive"
(default): the number of cycles grows with frequency
(n_cycles_i = freqs_i / value), so low frequencies use a
wider — longer — time window (MNE-tutorial convention). Default
value = 2 gives 5 cycles at 10 Hz, 25 cycles at 50 Hz. -
"constant": n_cycles cycles at every frequency
(Tallon-Baudry convention; commonly 7).
Wavelet length must fit the epoch. MNE’s Morlet
kernel is truncated to ±5σ where σ = n_cycles / (2π·f) (σ
is the width of the wavelet’s Gaussian envelope; ±5σ captures
essentially all its energy), giving an effective duration of
≈ 1.59·n_cycles/f seconds. At fmin this is the longest
kernel in the bank — and the formula depends on which
n_cycles_mode is active:
n_cycles[i] = freqs[i] / value, so the kernel at fmin is
≈ 1.59 / value seconds — independent of
fmin. With default value=2, that’s ~0.8 s,
comfortably inside typical event-locked epochs. This warning rarely
fires here.n_cycles = value at
every frequency, so the kernel at fmin is
≈ 1.59·value / fmin seconds. With value=7
(Tallon-Baudry convention) and fmin=1 Hz that’s ~11 s —
needs an 11+ second epoch. This mode is where the wavelet-too-long
warning typically bites.When the kernel exceeds the epoch length, the low-frequency rows of
the TFR are essentially noise: MNE itself warns (“at least one of the
wavelets is longer than the signal”). IDE4EEG pre-empts MNE’s terse
message with an actionable line naming the entry, the kernel duration,
and the epoch length — visible on the Run-tab log. Fix any of: raise
freq_min, lower n_cycles, switch to adaptive
mode, or use longer epochs. Multitaper uses DPSS windows with comparable
nominal duration; the same check applies.
ERD/S Topomap behaviour: the catalog entry first
computes a Morlet TFR per condition, then calls
power.apply_baseline(baseline=(None, 0), mode="percent").
The baseline window is fixed to the entire pre-event
interval (epoch start → event onset at t=0 s, relative time).
The colorbar reads in %: negative values = ERD
(event-related desynchronization, i.e. power decrease), positive values
= ERS (synchronization, power increase). The cmap is diverging
(RdBu_r) centred at 0.
Panel: Spatial · Available for: rest or epochs
| ID | Name | Requires | Parameters |
|---|---|---|---|
evoked_topomap_anim |
Evoked topomap animation (sequence of frames) | montage | n_times |
channel_locations |
2D plot of electrode positions on the head | montage | — |
Panel: Comparison · Available for: event-locked epochs
| ID | Name | Requires | Parameters |
|---|---|---|---|
compare_evokeds |
Overlay grand-average ERPs for all conditions with confidence intervals | 2+ tags | ci |
drop_log |
Bar chart of why each epoch was rejected (amplitude, flat, user) | — | — |
Panel: Source estimation · Subtitle: MNE + connectivis
Four source-estimation entries that share
[dipole_fitting] paths / BEM / trans configuration but
expose their own per-entry parameters. The output of every entry is also
wrapped into Connectivis scene companion files (brain PLY, head PLY,
electrodes DAT) so the 3D view button on the Output tab
works identically across them.
erp_dipole_fit)Standard MNE dipole-fit tutorial: average epochs per condition →
compute noise covariance from baseline →
mne.fit_dipole(evoked, cov, bem, trans) at each time point.
No MP decomposition needed — the classical approach (Scherg 1990).
| Parameter | Default | Description |
|---|---|---|
time_min |
0.0 |
Start of time window to fit (s). 0 = event onset. |
time_max |
0.0 |
End of time window. 0 = full epoch. |
min_gof |
50 |
Minimum goodness-of-fit (%). |
min_dist (advanced) |
5.0 |
Minimum distance (mm) the dipole keeps from the inner skull. |
pos (advanced) |
"" |
Optional "x,y,z" in head-coordinate millimetres to fix
location (orientation + amplitude only). |
n_jobs is read from [parallelism] and
passed straight to mne.fit_dipole. Outputs: cross-section
plots, ___erp_dipoles.csv, and Connectivis companion files.
Reference: MNE
dipole fit tutorial.
mne_inverse)Minimum-norm distributed-source family. Builds a forward solution +
inverse operator from the epochs and applies it to the per-condition
Evoked. Output is a cortical activation per vertex (~2.5k–8k vertices ×
time, depending on spacing), saved as *-stc.h5
(replayable in any MNE session) plus a static PNG screenshot per
condition rendered via PyVista.
| Parameter | Default | Description |
|---|---|---|
method |
"dSPM" |
"MNE" / "dSPM" / "sLORETA" /
"eLORETA". |
snr |
3.0 |
Signal-to-noise ratio (λ² = 1/SNR²). |
loose (advanced) |
0.2 |
Source orientation constraint (0 = fixed, 1 = free). |
depth (advanced) |
0.8 |
Depth-weighting exponent. |
spacing (advanced) |
"ico4" |
Source-space subdivision. |
n_peaks (advanced) |
0 |
If > 0, extract N strongest cortical vertices and export as
___mne_inverse_<method>_dipoles.csv for
Connectivis. |
Reference: MNE inverse tutorial.
lcmv_beamformer)Linearly Constrained Minimum-Variance beamformer
(mne.beamformer.make_lcmv + apply_lcmv). Same
output shape as mne_inverse.
| Parameter | Default | Description |
|---|---|---|
reg |
0.05 |
Tikhonov regularisation of the data covariance. |
pick_ori (advanced) |
"max-power" |
"max-power" / "normal" /
"vector". |
weight_norm (advanced) |
"unit-noise-gain" |
"unit-noise-gain" / "nai" /
"none". |
spacing (advanced) |
"ico4" |
Source-space subdivision. |
n_peaks (advanced) |
0 |
Same opt-in as mne_inverse. |
Reference: MNE LCMV tutorial.
mxne)mne.inverse_sparse.mixed_norm. Returns a handful of
focal sources (~5–20 vertices) instead of a smooth distribution — feeds
Connectivis automatically via ___mxne_dipoles.csv, no
n_peaks opt-in needed.
| Parameter | Default | Description |
|---|---|---|
alpha |
80.0 |
Sparsity / regularisation (% of α_max). 50–90 typical for ERP. |
depth (advanced) |
0.9 |
Depth-weighting exponent. |
loose (advanced) |
0.2 |
Source orientation constraint. |
n_mxne_iter (advanced) |
1 |
1 = vanilla MxNE. ≥ 2 enables iterative re-weighted MxNE (iRMxNE). |
spacing (advanced) |
"ico4" |
Source-space subdivision. |
Reference: MNE mixed-norm example.
| Method | Output shape | Connectivis path |
|---|---|---|
erp_dipole_fit |
one ECD per time sample | direct (___erp_dipoles.csv) |
mxne |
~5–20 sparse vertices | direct (___mxne_dipoles.csv) |
mne_inverse (MNE/dSPM/sLORETA/eLORETA) |
per-vertex cortical map | opt-in via n_peaks > 0 |
lcmv_beamformer |
per-vertex cortical map | opt-in via n_peaks > 0 |
The discrete-source methods feed Connectivis natively; distributed
methods write *-stc.h5 plus a static PNG, with
n_peaks > 0 collapsing the cortical map to N
pseudo-dipoles for 3D inspection.
References: all MNE-wrapper analyses cite Gramfort A et al. (2014) “MNE software for processing MEG and EEG data.” NeuroImage 86:446–460. Each entry’s [?] button also links to the relevant MNE tutorial.
The Run tab is where you actually execute the pipeline. It shows the
live log, a per-stage checklist with progress bars, and a Stop button to
cancel an in-progress run. The same code path is used for the GUI’s
Run Pipeline button, the per-analysis
[Run] buttons on the Analysis tab, and the command-line
ide4eeg --run myconfig.toml.
A typical session: configure the steps you want on Preprocess and Analysis, then switch to the Run tab and click Run Pipeline.
Executes the complete pipeline: all checked preprocessing steps
followed by all checked analyses, for every input file. The current GUI
state is collected into a config dict and saved as
config.toml in the output directory before execution
starts.
.fif epochs) are
written to the IDE4EEG_OUT_* directory.IDE4EEG validates all numeric configuration parameters before running the pipeline. Invalid values are caught at two levels:
GUI (live feedback). Key numeric fields turn red as you type when the value is out of range:
When you click Run Pipeline, a dialog warns about invalid TF frequency ranges and lets you abort.
Beyond the numeric range checks above, IDE4EEG ships a registry of consistency rules covering things that the pipeline would otherwise discover too late (typos, stale configs reused across recordings, ordering constraints between preprocessing steps, dipole/EEG-profile dependencies on Matching Pursuit, etc.). Rules fire at five points:
| Fire-point | When it runs |
|---|---|
config_load |
When a TOML config is loaded into the GUI/CLI |
signal_load |
When a recording is selected / loaded |
field_edit |
Live, after every edit to a rule-triggered field |
pre_launch |
When you click Run Pipeline (or --run
in CLI) |
step_entry |
Just before each preprocessing step runs |
The same rule set runs in both the GUI and the CLI, so a config that passes preflight in one passes in the other.
Field-validation badges (live feedback). Some fields paint a small inline glyph the moment their value becomes inconsistent with the loaded signal or the rest of the config:
Hover the glyph for the full rule message and the rule-id (useful for filing bug reports). Badges currently appear next to:
When the issue is resolved the badge disappears automatically.
Step-entry rules (during a running pipeline). A few
rules also fire just before each preprocessing step runs, catching
last-moment state divergence – e.g. the pinned MP book file was deleted
between clicking Run and mp_filter actually starting. These
rules abort the step with a clean error message rather than letting the
underlying tool fail mid-execution with a cryptic message.
Panel-header status surface. Some rules emit on a field that doesn’t have its own widget (e.g. “Dipole fitting requires MMP1 to be in the pipeline”). Those messages render inline on the panel’s header label, alongside the existing “needs MMP1 book” text. The two surfaces don’t fight: the Stage 5 status only clears when the rule that set it stops emitting.
Help → Pipeline invariants. The bottom of the Help tab lists every registered rule, grouped by module (Reference, Dipole, Channels, Modes, Step order, …). Each entry shows the rule-id, the fire-points it runs at, and a short rationale. Auto-updates as rules are added.
Pre-flight dialog (clicking Run Pipeline). Any rule
that emitted at pre_launch shows up in the dialog: errors
block execution (no Run anyway button), warnings just confirm.
The CLI’s parity is _preflight_check_cli: errors raise
ValueError, warnings log via logging.warning
and the run continues.
“Don’t show this again” per-rule dismissal.
Right-click any badge to dismiss the rule that fired it. Dismissals
persist across sessions in
~/.obci/ide4eeg/stage5_dismissed.json. A dismissal only
silences the inline live-feedback (badge + panel-header
status); the pre-launch preflight dialog still surfaces every rule, so
you can’t accidentally bypass a run-time error.
To restore a dismissed rule: open Help → Pipeline invariants, scroll to the “Dismissed rules” list, and click Restore next to the rule-id.
CLI (auto-correction). When running from the command line, invalid values are automatically clamped to safe defaults and a warning is logged. The pipeline continues rather than crashing. Validated parameters and their ranges:
| Parameter | Valid range | On invalid (CLI) |
|---|---|---|
amplitude_max_threshold |
[0, 0.01] V | clamped |
amplitude_nchan_threshold |
[0, 1] | clamped |
amplitude_time_threshold |
[0, 10] s | clamped |
amplitude_l_freq <
amplitude_h_freq |
— | reset to [1, 40] Hz |
artifacts.artifact_threshold |
[0, 1] | clamped |
filters.highpass_freq, lowpass_freq |
≥ 0; hp < lp | set to 0 (disabled) |
trim_start < trim_end |
— | trim_start reset to 0 |
rest.window_length |
> 0 | set to 5 |
epochs.start_offset < stop_offset |
— | reset to [-0.3, 0.7] |
TF f_lim_start / f_lim_end |
≤ Nyquist | clamped to Nyquist |
Below the progress bar, a scrolling log panel shows the same colored output that goes to the terminal in CLI runs (INFO / WARNING / ERROR levels). The panel is always read-only; you can select and copy text from it normally. Clicking inside the log no longer disturbs the running stream — IDE4EEG uses a dedicated end-of-document write cursor so user selections and pipeline appends are independent.
The log is capped at 50 000 blocks (~a few MB of text) so long pipelines emitting tens of thousands of log lines (ICLabel, empi) don’t grow QTextEdit’s document into the multi-GB range.
The Auto-scroll checkbox at the top right controls whether new lines automatically scroll into view; Clear wipes the panel.
The Stop button (next to Run Pipeline) is enabled only while a pipeline is running. Clicking it sets a cancel flag that all long-running loops in the pipeline check cooperatively — most stages bail within a second, with the notable exception of MP decomposition, which finishes its current empi invocation before the cancel takes effect.
When a run is cancelled, partial outputs already on disk are kept
(per-step snapshots, MP books). Subsequent runs see them and reuse them
via the hash-based caching machinery if their
[cfg:<hash>] markers still match.
When the pipeline runs (via Run Pipeline or
per-analysis [Run]), output files land in
IDE4EEG_OUT_<filename>/:
-epo.fif
in saved_signals/; the MP atom book in
matching_pursuit/ (if MP decomposition ran); the exact
config as config.toml for reproducibility.saved_steps/, ICA
components in artifacts_detection/ICA_EOG/, bad channel
plots in bad_channels_detection/, artifact detection plots
in artifacts_detection/found_artifacts/, gaze plots in
artifacts_detection/video_artifacts/, filter response plots
in filters_plot/.connectivity/, stats/, etc.) for
every analysis that ran.See Chapter 6 for the full output table.
On a failed file (e.g. ICLabel raises ICAFailureError), the
pipeline aborts that file and continues with the next in a multi-file
batch — partial outputs already written stay on disk.
In all cases, the GUI collects the current state of all widgets into a config dict at the moment you click Run. This means:
config.toml in the
output directory, so you can inspect exactly what was executed.For batch / headless / scripted runs:
ide4eeg # open GUI, blank state (default)
ide4eeg myconfig.toml # GUI with config preloaded
ide4eeg --run myconfig.toml # batch run, no GUI
ide4eeg --run myconfig.toml -t # batch with full Python tracebacks
ide4eeg --version # print version and exitThe batch run uses the same code path as the GUI; results are bit-identical.
For programmatic use, see ide4eeg/api.py
(run_file() and step wrappers). The GUI’s Export
Script… action writes a self-contained Python script
reproducing the current configuration without a TOML file — useful for
cluster submission or sharing reproducible analyses.
The Output tab is a results browser. The left side shows a tree of every file in the output directory; the right side previews the selected file (image, table, FIF metadata, MP book info, or text). The bottom row has helper-app launchers (MNE / Svarog / Connectivis).
When the pipeline finishes, the tab auto-refreshes; double-clicking files dispatches them to the appropriate viewer.
Results are saved in a folder named
IDE4EEG_OUT_<input_filename>/:
| Subfolder | When written | Contents |
|---|---|---|
saved_signals/ |
always | Clean epochs as -epo.fif (the primary preprocessing
output) |
saved_steps/ |
when a step’s per-step Save checkbox is checked | Intermediate signal at that point as
<base>-<suffix>-raw.fif |
artifacts_detection/ICA_EOG/ |
when Save components plot and table is checked on the ICA panel | ICA component topomaps, property plots, classification CSV,
-ica.fif, MNE Report HTML |
bad_channels_detection/ |
when Save is checked on the Bad Channels step | Band-power and correlation plots |
artifacts_detection/found_artifacts/ |
when Save is checked on the EEG Artifacts step | Amplitude / slope / muscle plots |
artifacts_detection/video_artifacts/ |
when Save is checked on the Gaze step | Gaze timeline + summary |
filters_plot/ |
when Save filter plots is checked on Filters | Filter frequency-response PNGs |
matching_pursuit/ |
when MP decomposition runs (forced save) | MP atom book .db file |
analysis/MNE/<entry_id>/ |
when MNE-based analyses run | One subdir per catalog entry (psd_multitaper/,
tfr_morlet/, erp_dipole_fit/, …) holding that
entry’s plots + CSVs |
Per-step save toggles default off. The cleaned
epochs in saved_signals/ are always written —
that’s the primary pipeline output that all analyses depend on.
The output path is controlled by two config parameters on the Input tab:
output_path — output root directory;
defaults to the input signal folder.
IDE4EEG_OUT_<filename> is created automatically with
preprocessing/ and analysis/ inside.overwrite_output — if
false, each run creates a timestamped subfolder
(YYYYMMDD_HHMMSS_<rest|epochs>); if
true, results are overwritten in place.The navigation row above the tree has four buttons:
IDE4EEG_OUT_*
results for the currently loaded signal.IDE4EEG_OUT_* results.IDE4EEG_OUT_* subdirectories are shown (duplicates
skipped).Hidden noise (.DS_Store, Thumbs.db,
___electrodes.dat, ___head.ply,
___brain.ply) is filtered from the tree — those companion
files travel with their main artefact (the dipoles.csv) and
aren’t useful to browse on their own.
The preview pane (right side) auto-renders by extension:
.png, .jpg,
.jpeg, .gif, .bmp) — inline
display, double-click to open in the system viewer..txt, .log,
.toml, .xml, .json) — text view
with monospace font..fif) — metadata summary (sfreq,
duration, channels, info description) + View in MNE
action..db) — book metadata +
Open in Book Viewer action (§8.4)..tag) — text view of the
BrainTech XML tag file.The view row below the tree has buttons that operate on the currently selected file:
.fif in MNE’s
interactive browser. (Disabled for non-FIF.).tag and .db files so the launch
includes the markers and atom book if present. Available for
.fif, .edf, .bdf,
.raw, .vhdr, .set,
.tag, .db..dat
(electrode/connectivity) or *dipoles.csv in
Connectivis..dat results in
the current folder (dipoles + connectivity) together in one Connectivis
scene.Double-click in the tree dispatches by extension automatically:
.fif, .raw,
.edf, .bdf, .vhdr) → Svarog when
a jar is configured; MNE fallback (for .fif) or OS default
otherwise.*-epo.fif (mne.Epochs) → MNE epochs browser.*-ica.fif (mne.preprocessing.ICA) → MNE
component-topomap overview..db (empi MP book) → IDE4EEG MP Book Viewer..dat (electrode positions / connectivity arrows) →
Connectivis.The double-click dispatch obeys the Use MNE viewer instead of Svarog option (§1.5); the explicit “in SVAROG” / “in MNE” buttons override that preference.
The Help tab is an in-app reader for this manual. The left pane is a filterable table of contents; the right pane shows the rendered Markdown with a search bar at the top.
You don’t have to read the manual cover-to-cover to use IDE4EEG — every interesting widget in the GUI has a [?] button or a hover tooltip. The Help tab is where the long form lives, and where the [?] buttons jump to when you click their Open in Manual link.
Most preprocessing and analysis sections have a [?]
button next to the panel title (and in some cases, next to individual
fields). Clicking it opens a popup with a short explanation of what the
section does, followed by an Open in Manual:
The link uses the heading text directly — no manual maintenance of anchor IDs. If a heading in this manual is renamed, the [?] button continues to work as long as the manual heading and the GUI [?] button reference the same wording.
Every label, input field, button, and checkbox in IDE4EEG has a tooltip describing what it does and (where applicable) the TOML key it controls. Hover over a widget for a second to see it.
Tooltips are hand-wrapped at ≤ 66 characters per line because Qt does
not auto-wrap setToolTip() strings — every \n
becomes a physical break. This means the wrap point you see in the GUI
is the same one the source code carries.
The right pane of the Help tab renders this
USER_MANUAL.md file as HTML with a small CSS stylesheet
that adapts to system light / dark mode. Hand-wrapped Markdown
paragraphs (one source line ≈ one rendered line) are accumulated into
single HTML paragraphs at render time so prose flows correctly
regardless of source-file wrapping.
Two search affordances:
The text-search box widens the main window to ≥ 900 px when activated so long lines aren’t broken across multiple visible lines (which would defeat scroll-to-match).
When you use IDE4EEG in published research, please cite the methods
you used. The full reference database lives in
ide4eeg/references.py and is exposed via
references.refs_for_step(step_key) and
references.format_all(). The same database backs the
references shown at the bottom of each preprocessing/analysis section in
this manual.
Selected references by topic:
A complete BibTeX export will be added in a future release. For now,
python -c "from ide4eeg.references import format_all; print(format_all())"
prints a sorted, one-line-per-reference summary of every entry.
IDE4EEG bundles four external helper applications. The first three are launched on demand from the GUI as subprocesses; the fourth is built-in.
On first launch of a fresh install, SVAROG (with
bundled empi), ConnectiVIS, and an Adoptium Temurin JRE 17 are
auto-downloaded — they are treated as crucial parts of the package. The
GUI shows a modal progress dialog with a Cancel button ~200 ms after the
main window appears; CLI / batch mode
(ide4eeg --run config.toml) runs the same flow silently
with throttled stdout progress and auto-consents to the Java install.
Only missing tools are fetched, so a hand-pinned Svarog at
~/.obci/svarog/ is preserved. Per-tool errors are
accumulated rather than aborting the chain — if ConnectiVIS fails to
download, SVAROG and Java still install. See §1.2 for the path layout and per-row
Download recent overwrite contract.
Svarog is the open-source EEG signal viewer developed at the Braintech Lab, University of Warsaw. IDE4EEG launches Svarog whenever a synchronised, time-aligned interactive view is needed:
choose_bad_channels = "manual" / "both") —
Svarog’s --select-mode bad_channels.selector = "manual" / "both") — Svarog’s
--select-mode ica_components.--select-mode bad_epochs..db
book is alongside a .fif signal, Svarog can show the atom
map atop the signal scroll.When Svarog is unavailable (no jar configured, or the Use MNE viewer instead of Svarog option is on), the same review windows fall back to MNE’s interactive plots — except the MP Book Viewer (§8.4), which is built-in and Svarog-independent.
Runtime requirements (Svarog 4.20):
brew install --cask temurin (macOS) or
winget install EclipseAdoptium.Temurin.17.JDK (Windows);
CLI / batch mode auto-consents. On Linux (and macOS without Homebrew)
the post-install dialog surfaces a manual-install hint with the adoptium.net URL.svarog-standalone-<version>-<jvm-tag>.jsa — and
reuses it on subsequent runs, saving ~380 ms (≈12%) off cold-start. JDK
11/12 skip AppCDS silently (JEP 350 dynamic CDS archives didn’t ship
until 13) and launch normally. The archive auto-regenerates when the jar
is newer; the <jvm-tag> hashes the path to the
java binary, so a JDK upgrade on a different install path
generates a fresh archive automatically. AppCDS only kicks in via
Svarog’s launcher (or via IDE4EEG’s Python wrapper, which opts into the
same .jsa filename so it shares the cache for the same
(jar, JVM) pair) — calling
java -jar svarog-standalone-*.jar directly bypasses it and
pays the full cold start every time.The Config tab’s Download recent button installs the
latest CI build into ~/.obci/svarog/. The bundled empi
binary inside the SVAROG artifact is auto-extracted alongside the jar in
the same step — there is no separate download for empi.
Connectivis is a 3D viewer for EEG source-reconstruction results: it shows dipole sources as oriented cones on the cortex and directed connectivity as coloured arrows between electrodes. IDE4EEG launches it automatically when dipole or connectivity results are produced, or via the 3D view / 3D view all buttons on the Output tab. It is auto-downloaded on first launch alongside SVAROG (see §1.2); the Config tab’s Download recent button refreshes it independently.
Runtime requirements (Connectivis 2.0+):
maven-assembly-plugin; no separate dependencies need to be
installed.pl.edu.fuw.connectivis, organised into five Maven modules
(cvis-model, cvis-io,
cvis-animation, cvis-renderer,
cvis-gui); 3D rendering uses jogamp Java3D 1.7.2.Connectivis is the modernised rewrite of Trans3D / Glowa3D (CC
Otwarte Systemy Komputerowe) for the Biomedical Physics Division,
Faculty of Physics, University of Warsaw. The CLI accepts
.dat connectivity matrices with bundled 10-20 electrode
positions, or a custom --electrodes montage.dat.
Dipoles panel (right-side controls):
[min, max].data: min – max unit: grey readout of
the attribute range across loaded dipoles.Signal arrows panel (connectivity):
Scalp / Cortex panels: show/hide checkbox + transparency slider each. The cortex carries per-vertex Desikan-Killiany atlas colours from FreeSurfer.
Show panel: four checkboxes gating visibility of electrodes, dipoles, electrode name labels, and signal arrows.
Brain atlas legend (View menu): clickable list of cortex regions — clicking a region flashes it on the mesh for 3 seconds. “Plain cortex” toggle removes atlas colours.
Colors window (View menu): colormaps for signal arrows and dipoles, electrode colour, background colour.
Mouse and keyboard.
End: front view. Home: side view.
+/-: zoom.File formats.
___dipoles.csv / ___erp_dipoles.csv /
___mxne_dipoles.csv /
___mne_inverse_<method>_dipoles.csv /
___lcmv_dipoles.csv — dipole parameters with MNI-mm
positions. Connectivis reads columns by name; required:
ori_x/y/z + mni_x/y/z_mm (preferred) or
pos_x/y/z (fallback). Optional: amplitude_nAm,
gof_pct, frequency_Hz for size/colour modes.
The mne_inverse and lcmv files are written
only when the entry’s n_peaks > 0.<base>___<tag>_<data_label>_<method>_<band>.dat
— inter-electrode connectivity matrices (square k × k,
;electrodes = + ;band = +
;title = headers). One file per (method × band) — see §4.1.3 for the band table.___electrodes.dat — electrode positions in MNI mm
(name x y z per line).___brain.ply / ___head.ply — cortex and
scalp surfaces (PLY with per-vertex RGB).empi is the
GPU-accelerated Matching Pursuit decomposition engine (Różański 2024).
IDE4EEG invokes it via subprocess for §3.2.9 MP
decomposition; the binary ships inside the SVAROG artifact (Svarog’s
mp/ subdirectory) and is auto-extracted in the same
first-launch step that fetches Svarog — there is no separate empi
download.
empi’s CLI is fully documented in its README; IDE4EEG exposes its
parameters under [matching_pursuit] in TOML and the MP
Decomposition panel in the GUI. The Config tab’s Download
recent (Svarog row) refreshes the matching empi build alongside
Svarog.
The MP Book Viewer is a standalone interactive window for browsing
Matching Pursuit decomposition results produced by empi. Unlike the
other helpers, it ships built-in
(ide4eeg.analysis.mp_bookviewer) and runs as a Qt window
inside the IDE4EEG process.
It displays Wigner time-frequency energy maps, original signal waveforms, and signal reconstructions from selected atoms.
Three ways to open an empi .db file:
From the Output tab — select an empi
.db file in the file tree and click Open in Book
Viewer.
From within the viewer — click
Open… and choose a .db file.
From the command line:
python3 -m ide4eeg.analysis.mp_bookviewer # opens file dialog
python3 -m ide4eeg.analysis.mp_bookviewer path/to.db # opens directlyFour panels stacked vertically:
| Panel | Content |
|---|---|
| Wigner map | Time-frequency energy density computed from Gabor atoms. Each atom contributes a 2D Gaussian blob. Atoms are shown as white dots; selected atoms have pink rings. Filtered-out atoms appear as small grey dots. |
| Signal | Original signal waveform (read from the samples table
in the .db file). |
| Recon | Full reconstruction from the currently filtered atoms. |
| Selected | Reconstruction from only the manually selected atoms. |
A colour bar next to the Wigner map shows the energy scale. The bottom status bar shows atom parameters when an atom is clicked.
| Action | Control |
|---|---|
| Next / previous segment | Toolbar ◀ / ▶ buttons, or Left / Right arrow keys |
| Next / previous channel | Toolbar ▲ / ▼ buttons, or Up / Down arrow keys |
| Zoom in / out | Scroll wheel (centred on cursor). Wigner map: zooms time and frequency; signal panels: time only. |
| Reset zoom | Double-click on the Wigner map, or press Escape |
| Change colour palette | Palette dropdown (jet, hot, viridis, inferno, gray) |
| Change energy scale | Scale dropdown: linear (raw values), log (log₁₀(1 + energy)), sqrt (√energy) |
Click an atom dot on the Wigner map to toggle its selection:
Useful for selective signal reconstruction — pick specific time-frequency components to see how they contribute to the signal.
Click Filter… on the toolbar to show the filter bar. Enter criteria to restrict which atoms are displayed and used for the Wigner map and reconstruction:
| Criterion | Description |
|---|---|
| Freq (min – max) | Keep atoms whose centre frequency is in this range (Hz). |
| Time (min – max) | Keep atoms whose centre time is in this range (s). |
| Energy ≥ | Keep atoms with energy at or above this threshold. |
| Iter ≤ | Keep atoms from the first N iterations only (higher-energy atoms are found first). |
Click Apply (or press Enter in any field) to recompute the Wigner map. Reset clears all criteria. Filtered-out atoms are shown as faint grey dots on the map.
This is nonlinear filtering: the energy map is recomputed from the selected atom subset, not just masked. The reconstruction panel reflects only the filtered atoms.
| Key | Action |
|---|---|
| ← / → | Previous / next segment |
| ↑ / ↓ | Previous / next channel |
| Escape | Reset zoom to full range |
| Enter (in filter field) | Apply filter |
You’re already running IDE4EEG, so you’ve cleared the basic install. This appendix is the reference for what’s actually required and where the optional pieces fit in.
IDE4EEG is developed on Python 3.14 (current
target). The supported range is Python 3.11–3.14: 3.11
is the de facto floor (the input loader uses the stdlib
tomllib module, added in 3.11) and 3.14 is what active
development happens on. Tested on 3.12 and 3.14; 3.11 and 3.13 are
expected to work but not currently exercised. Anything older lacks
tomllib and will fail at import.
python3 -m venv .venv && source .venv/bin/activate
pip install ide4eeg # lite — EEG core only, always succeeds
# or
pip install ide4eeg[video] # adds OpenCV / PyAV / imageio / InsightFace / onnxruntimeThe lite install is the recommended starting point: it succeeds on every supported (Python, OS, arch) cell and fits in ~500 MB. Video processing (face detection + gaze artifact tagging) is opt-in. You can also install the video stack from inside the GUI later — Config tab → Tool Paths → Video stack → Install all video tools drives the same pip-install in a streaming-log dialog. L2CS-Net (the default eye-gaze backend) is always installed in-app via the Download recent button next to L2CS-Net, regardless of which scope you pick on the command line — PyPI bans direct VCS dependencies and L2CS only exists at a GitHub URL.
To launch:
ide4eeg # GUI, blank state (default)
ide4eeg myconfig.toml # GUI with config preloaded
ide4eeg --run myconfig.toml # batch run, no GUI
ide4eeg --run myconfig.toml -t # batch with full Python tracebacks
python -m ide4eeg # equivalent fallback if the
# console script isn't on PATHIn GUI mode the optional positional path is just a shortcut for
clicking Load pipeline settings .toml right after launch — it
preloads the form so the user can review / edit before running. Batch
mode (--run) requires a config path explicitly; there is no
current-directory default.
Some packages in the [video] extra have wheel gaps on
specific (Python, OS, arch) cells. The lite install
(pip install ide4eeg) is unaffected.
| Cell | Affected | Resolution |
|---|---|---|
| Intel Mac + Python 3.14 | onnxruntime (no cp314 x86_64 wheel) |
Stay on lite install — facetag will be unavailable. To enable facetag here, downgrade to Python 3.13 (cp313 wheels exist). The in-app Install button reaches the same wheel-resolution failure but surfaces it in a friendlier dialog. |
| Linux + Python 3.14 (any arch) | insightface, stringzilla (no cp314 wheels
— pip falls back to building from sdist) |
Install build tools once, then either
pip install ide4eeg[video] or use the in-app installer.
Debian/Ubuntu: sudo apt install build-essential. Fedora:
sudo dnf groupinstall "Development Tools". Python 3.13 has
cp313 wheels and avoids the build entirely. |
| Apple Silicon, Windows, Linux + Python ≤ 3.13 | none | Both lite and [video] install scopes work
directly. |
git clone https://gitlab.com/fuw_software/ide4eeg.git
cd ide4eeg
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt # = pip install -e .[video]
ide4eeg # editable -- ide4eeg/*.py edits go liveIDE4EEG uses three external helper applications (Svarog, Connectivis, empi) that are JVM- or native-binary-based.
ide4eeg --run myconfig.toml) that don’t open Svarog or
Connectivis are the one path that doesn’t need Java at all.The default requirements.txt install ships with
face detection + InsightFace head-pose as the gaze
backend. To switch to L2CS-Net (true per-eye gaze
direction, recommended for most experimental designs), use the
L2CS-Net (gaze detection) Download
button on the Config tab.
The button installs PyTorch + torchvision + the l2cs
Python package + the Gaze360 weights checkpoint (~96 MB, MIT-mirrored
from Ahmednull/L2CS-Net) in one shot. Total install ~500 MB on macOS /
Linux-CPU (PyTorch CPU wheel), up to ~2 GB on Linux with CUDA wheels. An
Uninstall button removes everything.
See §3.2.12 Gaze for the choice between the two backends.
| Format | Input file | Companion files required |
|---|---|---|
| BrainTech | .raw |
.tag, .xml |
| BrainVision | .vhdr |
.vmrk, .eeg |
| MNE-FIF | .fif |
(none) |
| EEGLAB | .set |
(none) |
| EDF / EDF+ | .edf |
(none) — annotations read automatically |
| BDF / BDF+ | .bdf |
(none) — annotations read automatically |
Channel-type assignments differ per format — see §2.3.1 for the layered
inference pipeline. For polysomnographic mixed-modality recordings (PSG,
MASS, Sleep-EDF, Physionet-PSG), EDF/BDF channels default to
eeg in MNE; IDE4EEG’s overlay re-runs
chtype_heuristic on those defaults so
EOG/EMG/ECG/respiration/oximetry channels are correctly typed.
To add a new format, see Appendix D.2.
Below is a fully-commented config.toml showing
all parameters — both the commonly-set ones and the
hidden defaults. Copy as a starting point and remove or adjust as
needed.
# =====================================================================
# IDE4EEG -- Complete Configuration Reference
# =====================================================================
# --- Paths and general settings ---
input_path = "examples/dipole_spindles/dipoles_example.raw"
output_path = "None" # "None" = save next to input file
segmentation = { mode = "epochs" } # "rest" (timed windows) or "epochs"
overwrite_output = false
# --- Preprocessing toggles ---
prepare_eeg_artifacts = false
prepare_ica = false
prepare_video_artifacts = false
# facetag_frame_skip = 3 # process every 3rd frame (faster)
# facetag_downscale = 0.5 # half resolution (faster)
# facetag_backend = "insightface" # only "insightface" is supported
# facetag_gaze_method = "l2cs" # "l2cs" (default) or "insightface"
# trim_start = 0.0 # crop signal start (seconds)
# trim_end = # crop signal end (omit = full signal)
# --- Analysis toggles ---
# MP decomposition is enabled by adding "mp_decomposition" to
# preprocessing.step_order — there is no top-level toggle.
prepare_eeg_profiles = false
prepare_connectivity_analysis = false
prepare_dipole_fitting = false
# mne_catalog = ["erp_butterfly", "cluster_permutation"] # external analyses
# --- Signal setup ---
electrodes_layout = "standard_1005"
re_reference = ['M1', 'M2'] # list, "average", or "None"
# --- Advanced: signal trimming (hidden defaults) ---
# resample_freq = 512 # Hz; downsample if native sfreq is higher
# cover_time = None # deprecated, ignored
# threshold_time = None # deprecated, ignored
# --- Advanced: amplitude thresholding (hidden defaults) ---
# amplitude_max_threshold = 150e-6 # V (150 µV)
# amplitude_nchan_threshold = 0.33 # fraction of channels
# amplitude_time_threshold = 0.02 # seconds (20 ms)
# amplitude_l_freq = 1 # Hz
# amplitude_h_freq = 40 # Hz
# --- Parallelism ---
[parallelism]
n_jobs = 1 # 1 = sequential (MNE default); raise to opt in
[choosing_channels]
choose_bad_channels = "auto" # "auto", "manual", or "both"
dropped_channels = ["Audio", "Sample_Counter", "Photo"]
selected_channels = "all"
check_final_channels = true
# --- Advanced: channel quality (hidden defaults) ---
# correlation_window = [0.75, 0.975]
# correlation_badch_threshold = 0.95
# [choosing_channels.badchs_params]
# slow_oscillations = [1, [2, 10], "-", [1e3, 1e4, 1e5]]
# noise_power = [1, [52, 98], "-", [2e1, 2e2, 2e3]]
[filters]
show_filt = false
plot_filt = false
method = "iir" # "iir" or "fir"
highpass_freq = 0.5 # Hz (0 = off)
lowpass_freq = 30 # Hz (0 = off)
notch_freq = 50 # 50 or 60 Hz (0 = off)
[artifacts]
artifact_threshold = 0.3
# --- Advanced: full rejection_parameters dict (hidden default) ---
# Normally not overridden. See Appendix E.4 for the default values.
[ICA_EOG]
method = "picard" # "picard", "infomax", or "fastica"
selector = "iclabel" # "iclabel", "find_bads", "manual", "both"
fit_highpass_hz = 1.0 # Winkler 2015 fit-time HP; 0 = off
reject_uv = 500 # PTP µV — drop crazy segments pre-fit
n_components = "rank" # "rank" | int | float(0..1)
iclabel_keep = ["brain", "other"]
iclabel_min_prob = 0.0
# --- Only used when selector ∈ {find_bads, both} ---
find_bads_eog_ch = ["Fp1", "Fp2", "Fpz"]
find_bads_ecg = true
find_bads_muscle = true
# --- Advanced ---
decim = 1 # fit subsampling factor
random_state = 42
save = false # write audit tree + cleaned -raw.fif
[rest]
rest_duration = [0, ""] # [start_s, end_s]; "" = full signal
window_length = 20 # seconds
check_rest = true
[epochs]
start_offset = -0.3 # seconds before event
stop_offset = 0.7 # seconds after event
epochs_baseline = "None" # "None", [start, end], or ["None", "None"]
check_epochs = true
# --- Advanced (hidden defaults) ---
# reject_dict = {eeg = 500e-6} # V; peak-to-peak max
# flat_dict = {eeg = 1e-6} # V; peak-to-peak min
[epochs.tags]
selected = ["event_name_1", "event_name_2"]
# Legacy: AUTO = true (auto-discover target/nontarget)
[time_domain]
[time_domain.cluster_test]
step_down_p = 0.05
n_permutations = 1024
[connectivity]
methods = ["gdtf"] # list (or comma-separated string)
mvar_method = "yw" # yw, ns, vm
mvar_order = 0 # 0 = auto (Akaike)
resolution = 100
# --- EEG profiles ---
# [eeg_profiles]
# mode = "count" # "count", "time_percentage", "single_occurrence"
# channel = 0
# (Bin width is always the MP segment length — not configurable.)
# --- Dipole fitting ---
# [dipole_fitting]
# ref_channel = "average"
# montage = "standard_1005"
# max_iterations = 0 # 0 = all atoms
# min_gof = 0 # 0 = no constraint
# --- MP filter (preprocessing) ---
# [matching_pursuit.filter]
# mode = "remove" # "keep" or "remove"
# freq_min = 0
# freq_max = 0python3 --version
See Appendix A.1 for the supported range.
ModuleNotFoundErrorMake sure the virtual environment is activated. You should see
(venv) or (.venv) at the start of your
terminal prompt.
deactivate
Check that:
.raw, .vhdr,
.fif, .set, .edf, or
.bdf..raw needs
.tag and .xml).input_path in your config points to the correct
file or directory.Add a reader function in ide4eeg/input/input.py that
returns (mne_signal, events_desc_id) where
mne_signal is an mne.io.RawArray with EEG
channels and a STIM channel, and events_desc_id is a dict
{'event_name': integer_id, ...}.
Then add the file extension to file_extension in
find_file_paths and a dispatch case in
read_file. Adding a call to
_refine_channel_types(raw) at the end gives the new format
the same channel-type heuristic used for BrainTech and EDF.
Add entries to filters_dict in
ide4eeg/input/input.py. Each entry needs:
highpass_, lowpass_,
or stopband_.scipy.signal.iirdesign: wp,
ws, gpass, gstop,
ftype.use_filtfilt = True for zero-phase filtering.In the GUI, load a file and check the desired events in the Events panel (Preprocessing tab, Segmentation setup section). For TOML configs:
[epochs.tags]
selected = ["event_1", "event_2", "event_3"]The cleaned epochs are always written to
preprocessing/saved_signals/ as -epo.fif files
— this is the primary pipeline output. Intermediate signals from
individual preprocessing steps are written to
preprocessing/saved_steps/ only when their per-step
Save checkbox is checked.
Save checkboxes default to off. When checked, the
step’s intermediate output (transformed signal, detection plots, ICA
components, etc.) is written under preprocessing/ after the
next pipeline run. If you ran the pipeline before checking the box, run
it again to produce the saved output.
Your rejection criteria may be too strict. Try:
artifact_threshold (e.g. from 0.3 to
0.5).reject_dict
(e.g. {eeg = 800e-6} for noisier data).flat_dict if your signal has low
amplitude.amplitude_max_threshold to allow
higher-amplitude segments through trimming.stats/ to understand
which channels and epochs were rejected.Add them directly to your config.toml. Top-level
parameters (like resample_freq, cover_time) go
at the root level. Section-specific parameters go inside their section
(e.g. reject_dict inside [epochs]). See Appendix E for the
full list, or Appendix C for a
complete config template.
Detailed cluster information is saved in a .txt file in
the MNE/ folder.
IDE4EEG supports two filtering methods:
scipy.signal.iirdesign. Stored in second-order
sections (SOS) format and applied zero-phase via
sosfiltfilt. The forward+backward pass squares the filter’s
magnitude response — a -3 dB design becomes
-6 dB at cutoff — and doubles the effective filter order.
Filter-response plots show the single-pass design, not
the realised doubled response.| Filter | Typical value | Purpose |
|---|---|---|
| Highpass | 0.1–1.0 Hz | Remove electrode drift and DC offset |
| Lowpass | 30–100 Hz | Remove high-frequency noise (EMG, electrical) |
| Notch | 50 Hz (Europe/Asia) or 60 Hz (Americas) | Remove power line interference |
Set any cutoff to 0 to disable that filter.
No. Filters are designed automatically for the signal’s actual sampling rate. Resampling is optional — set the resample frequency to 0 to keep the original rate. Resampling may still be useful to reduce data size (e.g. 2048 Hz → 512 Hz) before analysis.
Previous versions of IDE4EEG (P3ACE) required resampling to 512 Hz
before filtering because filters were defined as named presets
(e.g. highpass_05hz) with hardcoded
scipy.signal.iirdesign parameters. The current version
specifies filters by cutoff frequency in Hz and designs them
automatically for any sampling rate. Key technical changes:
ba
format (numerator/denominator polynomial coefficients). The
ba form is numerically unstable for high-order filters —
floating-point errors can push poles outside the unit circle, creating
an unstable filter. SOS decomposes the filter into cascaded biquad
stages, stable for arbitrarily high orders.For a complete description of the pipeline, backends, gaze methods, and all parameters, see §3.2.12 Gaze.
When recording EEG from children, the mother (or another adult) is often visible in the video. When the child turns away from the screen, only the mother’s face may be detected and falsely matched as the subject, producing incorrect “looking” reports.
The Exclude faces directory lets you specify photos of people to reject. Any detected face that is closer to an exclude reference than to the subject reference is automatically rejected. Leave the field empty to disable exclusion.
Two acceleration options:
facetag_frame_skip):
process every Nth frame instead of every frame. Skipped frames inherit
the result of the last analysed frame. At 30 fps,
facetag_frame_skip = 5 gives ~5× speedup with minimal
accuracy loss.facetag_downscale): shrink
each frame before face detection. facetag_downscale = 0.5
(half resolution) roughly halves processing time per frame. Avoid values
below 0.3 if the infant’s face is small in the frame.Both options can be combined. For example,
facetag_frame_skip = 3 with
facetag_downscale = 0.5 gives roughly 6× overall
speedup.
Reference photos are images of the subject looking directly at the camera. You need 3–5 such images.
</> to step one frame,
<</>> to jump to the next frame
with a detected face. Saved faces appear as cropped thumbnails below the
video; click a thumbnail to remove it.By default, picked photos are saved into
<output>/IDE4EEG_OUT_<base>/preprocessing/reference_faces/
so they live alongside the rest of the per-recording results (and
exclude_faces/ for the exclude set). Use Change
folder to override the location.
Click the 👁 (eye) button on the Gaze panel header. This opens the Review Video Artifacts window (auto-loading any previous results), showing the video and EEG side-by-side with the detected intervals and Confirm / Dismiss / Save Changes actions.
Which signal is shown? The review window shows the
most-recent upstream snapshot — i.e. the signal after
any preceding preprocessing steps that have their Save
checkbox enabled (filtering, resampling, bad-channel rejection, montage,
ICA, etc.). When you’ve already run the pipeline once with
[filters].save = true, the gaze review canvas shows the
filtered signal — same data the gaze detector saw. When no
upstream snapshot exists yet, the review window falls back to the raw
input file. Freshness is checked the same way as the eye button — a
stale [cfg:<hash>] is regenerated.
EEG signal plot colours.
Controls.
.tag file.Double-click any interval row to jump to its start time.
ide4eeg, so CLI and
GUI results are identical.The following parameters have sensible defaults set internally in
check_and_prepare_config (input.py). They are
not included in the default config.toml
but can be added to override the defaults. Parameters that belong to a
specific section ([choosing_channels],
[epochs], etc.) must be placed in that section; top-level
parameters go at the root.
Top-level parameters.
| Parameter | Type | Default | Description |
|---|---|---|---|
resample_freq |
int | 512 |
Target sampling frequency (Hz). 0 = keep original rate. |
cover_time |
float or None |
None |
Deprecated — ignored. Use trim_start /
trim_end. |
threshold_time |
float or None |
None |
Deprecated — ignored. Use trim_start /
trim_end. |
When both trim_start and trim_end are
unset, the signal is auto-trimmed to first_tag - 5 s
through last_tag + 5 s.
Top-level parameters.
| Parameter | Type | Default | Description |
|---|---|---|---|
amplitude_max_threshold |
float | 150e-6 (150 µV) |
Voltage threshold (V) for bad-segment detection. |
amplitude_nchan_threshold |
float | 0.33 |
Fraction of channels that must exceed the threshold to mark a time point bad. |
amplitude_time_threshold |
float | 0.02 (20 ms) |
Expansion window (s) around detected bad samples. |
amplitude_l_freq |
float | 1 |
Bandpass lower edge (Hz) for the amplitude check. |
amplitude_h_freq |
float | 40 |
Bandpass upper edge (Hz) for the amplitude check. |
Inside [choosing_channels].
| Parameter | Default | Description |
|---|---|---|
badchs_params |
(see below) | Dictionary of band-power detection rules for noise-based bad channel detection. |
correlation_window |
[0.75, 0.975] |
Acceptable inter-channel correlation range. |
correlation_badch_threshold |
0.95 |
Fraction of channel pairs that must be “bad” to flag the channel. |
badchs_params structure. Each entry:
"name": [window_s, [freq_low, freq_high], direction, [thr1, thr2, thr3]]
window_s — window width in seconds.[freq_low, freq_high] — frequency band (Hz).direction — "-" means power below
thresholds is good (flag high-power channels); "+" means
above is good.[thr1, thr2, thr3] — three escalating thresholds
(µV²).Default:
[choosing_channels.badchs_params]
slow_oscillations = [1, [2, 10], "-", [1e3, 1e4, 1e5]]
noise_power = [1, [52, 98], "-", [2e1, 2e2, 2e3]]Inside [artifacts].
| Parameter | Default | Description |
|---|---|---|
rejection_parameters |
(see below) | Full dictionary of artifact detection thresholds. Normally not overridden as a whole. |
In the GUI, these appear under the collapsible “Advanced parameters” panel in the Artifacts section.
| Parameter | Default | Description |
|---|---|---|
SlopeWindow |
0.0704 |
Sliding window (s) for slope and outlier detection. |
SlopesAbsThr |
150 |
Absolute peak-to-peak threshold (µV) for slopes. |
SlopesStdThr |
7 |
Peak-to-peak as multiple of standard deviation. |
SlopesMedThr |
6 |
Peak-to-peak as multiple of median. |
OutliersMergeWin |
0.1 |
Window (s) for merging nearby outlier detections. |
OutliersAbsThr |
150 |
Absolute amplitude threshold (µV) for outliers. |
OutliersStdThr |
7 |
Amplitude as multiple of standard deviation. |
OutliersMedThr |
13 |
Amplitude as multiple of median. |
MusclesWindow |
0.5 |
Sliding window (s) for muscle artifact detection. |
MusclesFreqRange |
[40, 90] |
Frequency band (Hz) for muscle spectral density. |
MusclesAbsThr |
0.15 |
Absolute spectral density threshold (µV²). |
MusclesStdThr |
7 |
Spectral density as multiple of standard deviation. |
MusclesMedThr |
5 |
Spectral density as multiple of median. |
Inside [epochs].
| Parameter | Default | Description |
|---|---|---|
reject_dict |
{eeg = 500e-6} |
Peak-to-peak amplitude max (V). Set to "None" to
disable. |
flat_dict |
{eeg = 1e-6} |
Peak-to-peak amplitude min (V) for flat-signal rejection. |
Inside [ICA_EOG]. See §3.2.7 ICA for the full
table; the parameters below are the ones not normally set in a standard
config.
| Parameter | Default | Description |
|---|---|---|
decim |
1 |
Subsampling factor for the fit (only safe with strong fit-time HP). |
random_state |
42 |
RNG seed. |
iclabel_min_prob |
0.0 |
Minimum classifier confidence for the top-1 label. Below this
threshold the component falls into other. |
For the basic field on the Config tab and what n_jobs
accelerates, see §1.3.
The GUI shows you everything you need to decide, so you don’t have to guess. After typing a number in the Parallel jobs field, three lines appear below it:
→ sequential (no parallelism) when the field is
1, or → N parallel workers when larger.8 physical / 16 logical cores, 16 GB RAM, each worker ≈ 250 MB baseline + working memory.
Always shown. Physical core count is probed via psutil if
installed, otherwise via sysctl (macOS),
/proc/cpuinfo (Linux), or ctypes (Windows),
with logical-cores-divided-by-two as a conservative fallback.n_jobs × ~250 MB) exceeds one
third of detected RAM → risk of swap thrashing.Practical rule of thumb: start with 2 and raise
it one step at a time while watching the warning line and your
system monitor. On a typical 8-physical-core / 16 GB machine,
2 to 4 is the sweet spot for most pipelines.
Expect a 3–5× speedup on long connectivity-bootstrap runs or MNE catalog
batches at the top of that range; smaller jobs are dominated by joblib
spawn overhead and will not speed up as much.
IDE4EEG installs a global
joblib.parallel_config(backend="loky", inner_max_num_threads=1)
once per pipeline run. The inner_max_num_threads=1 setting
forces NumPy’s BLAS backend (OpenBLAS / MKL / Accelerate) to use a
single thread inside each worker. Without this pin, N
joblib workers on a machine where BLAS defaults to
cpu_count threads would create N × cpu_count
OS threads, exhausting CPUs and triggering OS-level scheduler
pathologies. The pin guarantees that the total CPU load equals
n_jobs — exactly what the warning line shows. It also makes
bit-exact reproducibility of floating-point sums possible regardless of
n_jobs, because BLAS summation order is deterministic with
a single thread.
Concretely: setting n_jobs = 4 on a 16-thread BLAS will
use four CPU cores, not 64.
The empi Matching Pursuit binary has its own
--cpu-workers switch ([matching_pursuit] →
cpu_workers in TOML, “use [ ] / N cores” field in the
Preprocess tab). The default is to inherit from
parallelism.n_jobs — the Preprocess field shows
the inherited value as grey italic placeholder text and refreshes live
whenever you edit the Config tab field.
You only need to set matching_pursuit.cpu_workers
explicitly if empi’s memory model lets you go higher than joblib allows:
empi workers share memory inside a single C++ process (~10–50 MB each),
while joblib loky workers are full Python interpreters (~250 MB each).
On a RAM-constrained machine the two can reasonably diverge; the
explicit override exists for exactly that case. Otherwise leave it
blank.
IDE4EEG’s hash-based caching (§3.4.4) uses a
Merkle-style chain of per-step hashes: each step’s hash covers its own
config plus the hash of every preceding step that influenced its input.
The chain root is the source EEG file’s identity (name + size + mtime).
When a saved snapshot’s stamped [cfg:<hash>] matches
the current config’s hash, the snapshot is reused; otherwise it’s
regenerated.
What’s included in / excluded from each step’s hash.
[filters] for the filtering step),
preprocessing.step_order (so reordering invalidates), and
the recursive hashes of all preceding steps._input_dir, _input_file_name, etc.), output
paths, log levels, the parallelism block (parallelism
doesn’t affect results, only speed), and tool paths._MP_CFG_RUNTIME_FIELDS and the
[matching_pursuit.filter] sub-block — mp_filter parameters
are reconstruction, not decomposition, so editing them doesn’t
invalidate the book.Three invariants keep the chain stable:
choosing_channels.bad_channels, ICA populating
ICA_EOG.bad_sources) freeze their hash before the
mutation. Otherwise the second run’s hash wouldn’t match the first run’s
stamp._review_bad_channels, _review_ica_components,
_review_bad_epochs) are passed as closures, not bound
methods. Deepcopying a bound method would try to deepcopy
self (the QMainWindow), which fails on Qt’s C++ state._run_truncated_pipeline; the runner’s preamble
(preprocessing.py) and _launch_pipeline use
force=False so an upstream pin survives. This guarantees
that a re-entry click on the same eye sees the same hash the truncated
run stamped.These invariants are tested by
tests/test_view_step_result.py and
tests/test_preamble_phase1.py.
Two ways to capture and re-run a configuration outside the GUI:
config.toml — declarative TOML, the
same format the GUI uses internally. Save with Save
Config…, run with
ide4eeg --run myconfig.toml..py file calling ide4eeg.api.run_file() with
the current parameters. Generate with Export Script…
(Config tab or File → Export Script…), run with
python3 generated_script.py.Both share the same code path under the hood — same preprocessing pipeline, same analysis modules, bit-identical results.
| Aspect | config.toml |
Exported script |
|---|---|---|
| Format | declarative TOML | imperative Python |
| Round-trips with the GUI | yes (Load Config…) | one-way export only |
| Verbosity | compact (only the keys you set) | one keyword arg per non-default value |
| Loops over files / subjects | needs an outer shell script | inline Python for loop |
| Conditional logic / parameter sweeps | needs templating | native Python branching |
| Custom callbacks (manual-review hooks) | not expressible | yes (run_file(..., bad_channels_hook=fn)) |
| Reads as documentation | requires manual lookup of TOML keys | reads as a worked example of ide4eeg.api |
| Diffability across runs | excellent (line-by-line text diff) | OK (kwargs block diffs cleanly) |
| External invocation | ide4eeg --run cfg.toml |
python3 script.py |
| IDE4EEG location | implicit (whatever’s on PYTHONPATH) |
explicit sys.path.insert(0, "<repo>") line at the
top |
| Disabled features | every key persists in the file | feature-gated sub-configs are pruned (e.g. [ICA_EOG] is
omitted when prepare_ica = false) |
config.toml.py into the results folder alongside the outputs. The
sys.path.insert(...) line records exactly which IDE4EEG
checkout produced the results — no separate “what version was this?”
question.pandas / matplotlib follow-ups inline.ide4eeg.api.run_file() surface explicit and is a good
starting point for users learning the programmatic API.bad_channels_hook to
integrate IDE4EEG with another review tool.When a pipeline runs (via either route), the
resolved config — including any defaults applied
internally and any in-flight modifications (e.g. per-analysis narrowing)
— is saved as config.toml in the output directory. Even if
you launched with the exported script, the output folder still gets a
TOML you can later Load Config… back into the GUI.