IDE4EEG v0.6 - User Manual

IDE4EEG User Manual

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.


Conventions and MNE integration

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.

SI units and the V vs µV convention

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:

Legacy IDE4EEG-developed code paths — predating the move onto MNE — kept their thresholds in µV directly for human readability:

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.

Number entry — decimal separator and sign

IDE4EEG uses . (period) as the decimal separator everywhere, regardless of the system locale. This matches:

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.

Where MNE code is invoked directly

Preprocessing. The following steps wrap MNE transparently — IDE4EEG supplies the parameters, MNE does the work:

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.


1. Config tab

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.

1.1 Loading and saving config files

The bottom row of the tab has four buttons (also reachable via the File menu):

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.

1.2 External tool paths

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:

  1. mpv (default since Svarog 4.20) — bundled with the standalone ZIPs and declared as 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.
  2. JavaFX 17.0.15 — embedded fallback. Built into the full Svarog standalone JAR; the default light JAR excludes the native JavaFX libraries to keep the download small. Codec coverage is narrow: MP4/H.264/AAC, FLV, FXM, M3U8 only — JavaFX cannot play MKV, WebM, AVI, or MOV containers, so this fallback only covers studies recorded as MP4. Recordings from OBS, ffmpeg-piped webcams, or any pipeline producing MKV need mpv or VLC.
  3. VLC — opt-in for users with an existing system VLC install. No download needed; Svarog detects and uses it automatically when neither mpv nor JavaFX is present. Plays the same broad container set as mpv (MKV/MP4/AVI/WebM/MOV), so it’s the practical workaround for MKV recordings on macOS without Homebrew.

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:

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 extras

The 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:

  1. First attempt — validate against certifi’s bundled root CA list. Works for almost everyone; certifi is in requirements.txt.
  2. Fallback attempt — if certifi fails (e.g. corporate networks with TLS-inspecting proxies), retry against the platform’s default CA store.

If both fail, the error dialog shows platform-specific remediation:

1.2.1 Where IDE4EEG stores files

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:

Migration of legacy paths. Existing installs may have files in three old locations:

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:

1.3 Parallel jobs

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.

1.4 Output directory and naming (overview)

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).

1.5 Other Options

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.

1.6 Constraints editor (advanced)

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.


2. Input tab

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.

2.1 Choosing input files

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.rawrecording.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.

2.2 Signal info panel

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.

2.3 Channel types

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.

2.3.1 Channel type inference

Every channel is tagged with an MNE channel type (eeg, eog, emg, ecg, bio, stim, misc, …). IDE4EEG relies on this type for:

Where types come from, in order of authority:

  1. 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.

  2. 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.

  3. 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.

  4. User overrides — see §2.3.2.

2.3.2 User overrides

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 name

Keys 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.

2.3.3 Adaptive per-type filter buttons

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:

  1. User override from choosing_channels.type_overrides (authoritative)
  2. File-format reader’s native metadata (FIF, BrainVision, EEGLAB)
  3. Readmanager’s chtype_heuristic (BrainTech; also applied to EDF/BDF channels defaulted to eeg by MNE)
  4. Fallback to eeg for any channel not matched by the above

If 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.


3. Preprocess tab

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:

  1. Preamble — fixed and always runs. Just one panel: Segmentation setup, where you choose between event-locked epochs and timed-window (rest) mode and pick which events to use.
  2. Reorderable steps — twelve steps you can enable individually and reorder via ↑/↓ arrows on each panel header. Trim, Resampling, Bad channels, Montage, Reference, Filtering, ICA, Channel selection (manual exclusion), MP decomposition, MP filter, EEG artifacts, and Gaze.
  3. Postamble — fixed and always runs. Cuts the continuous signal into epochs, drops bad ones, optionally lets you review them manually, and saves the final cleaned epochs.

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.

3.1 Preamble: Segmentation setup

GUI panel: Segmentation setup (always visible, non-checkable) Pipeline phase: Preamble (always runs)

3.1.1 Purpose & non-destruction invariant

The preamble prepares the segmentation coordinate system before the main preprocessing chain runs. It does two things:

  1. Builds the event-ID dictionary — a pure config operation that maps the user’s selected event names (GUI event panel, or [epochs.tags] selected = [...] in TOML) to integer IDs, consumed later by mne.Epochs / _cut_segments.
  2. Attaches synthetic rest markers — only in timed-windows mode. 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.

3.1.2 Mode: timed windows (rest)

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.

3.1.3 Mode: event-locked epochs

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"]

3.1.4 👁 segments preview

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.

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).

3.2 Reorderable steps

3.2.0 Step order and constraints

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:

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.

3.2.1 Trim signal

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.

  1. Resolve trim boundaries: use trim_start / trim_end from config, or auto-crop to first_tag - 5 s through last_tag + 5 s when unset.
  2. Clamp boundaries to [0, signal_duration].
  3. Call 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.

3.2.2 Resampling

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.

3.2.3 Bad channel detection

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):

  1. Cut the signal into windows (default 1 s, Hann-windowed).
  2. Compute spectral power per window via FFT in the target band.
  3. Per channel, count the fraction of windows where power exceeds a threshold.
  4. Three escalating thresholds are defined (absolute, e.g. 10³, 10⁴, 10⁵ µV²). Channels exceeding more thresholds are more likely flagged.
  5. Auto-threshold: median(scores) + 4 * std(scores_below_threshold), clamped to [20%, 60%] of total windows.

B. Correlation-based detection.

  1. Bandpass the signal to 1–40 Hz.
  2. Compute the Pearson inter-channel correlation matrix.
  3. Per channel, count the fraction of pairs where correlation falls outside correlation_window (default [0.75, 0.975]).
  4. Flag the channel if that fraction exceeds 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:

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.

3.2.4 Montage

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).

3.2.5 Reference

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.

3.2.6 Filtering

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.

3.2.7 ICA — artifact subspace removal

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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. Component labelling. Four selector modes:

  6. 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.

Output (when save = true). Under <preproc_dir>/saved_steps/:

When save_components_audit = true, under <preproc_dir>/artifacts_detection/ICA_EOG/:

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.

3.2.8 Channel selection (manual exclusion)

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.

  1. Parse selected_channels ("all" = keep everything, or a list of names).
  2. Parse dropped_channels (names to exclude).
  3. Final channel set: selected_channels \ dropped_channels.
  4. Drop all other channels from the MNE Raw object.
  5. Optional interactive review: open MNE’s signal browser for visual confirmation.
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.

3.2.9 Matching Pursuit decomposition

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:

  1. Start with the full signal as the residual: R_0(t) = x(t).
  2. At iteration n, find the Gabor atom g_n that maximises the inner product with the residual: g_n = argmax_g |<R_n, g>|.
  3. Subtract the atom: R_{n+1}(t) = R_n(t) - <R_n, g_n> * g_n(t).
  4. Repeat until residual energy drops below (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:

  1. matching_pursuit.cpu_workers > 0 — explicit user override (takes precedence).
  2. Otherwise, parallelism.n_jobs from [parallelism] (the unified pipeline knob).
  3. 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

3.2.10 MP Filter (nonlinear filtering)

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.

  1. Read the MP book produced by the decomposition step.
  2. For each segment and channel, filter the atom list based on user-specified criteria:
  3. Depending on mode:
  4. Reconstruct: x_recon(t) = sum_i A_i * g_i(t) over selected Gabor atoms.
  5. Replace the original segment in the Raw object with the reconstruction.
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).

3.2.11 EEG artifact detection

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.

  1. Bandpass-filter the signal (0.5–30 Hz).
  2. Per channel, iteratively compute robust standard deviation s (remove samples > k*s until convergence).
  3. Compute three thresholds (absolute / median-based / std-based) per parameter (OutliersAbsThr, OutliersMedThr, OutliersStdThr).
  4. Threshold = min of the three. Flag samples where |signal| > threshold.
  5. Merge nearby flagged regions within OutliersMergeWin (default 0.1 s).

B. Slope detection (peak-to-peak). Detects rapid voltage changes using sliding-window p2p amplitude.

  1. Bandpass-filter the signal (0.5–30 Hz).
  2. Per channel, compute p2p in sliding windows of SlopeWindow (default 0.0704 s); run twice (normal + half-window-shifted) and concatenate.
  3. Same three-threshold scheme.
  4. Map flagged windows back to individual samples.

C. Muscle artifact detection. Detects high-frequency EMG contamination via spectral power in the 40–90 Hz band.

  1. For each channel, sliding window of MusclesWindow (default 0.5 s) with Hann taper.
  2. FFT and extract power in MusclesFreqRange (default [40, 90] Hz).
  3. Same three-threshold scheme.

D. Amplitude thresholding. Broadband check applied before the per-channel detectors.

  1. Bandpass to amplitude_l_freqamplitude_h_freq Hz (default 1–40).
  2. At each time point, count channels where |amplitude| > amplitude_max_threshold (default 150 µV).
  3. Mark time points where > amplitude_nchan_threshold (default 0.33) of channels exceed the threshold.
  4. Fill small gaps (< 2× 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.

3.2.12 Gaze (video artifact detection)

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:

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.

These options combine multiplicatively.

3.3 Postamble

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.

3.3.1 Cut segments

GUI panel: Cut Segments (always enabled, non-checkable)

Slices the continuous preprocessed signal into discrete segments (MNE Epochs objects) for analysis.

Uses MNE’s mne.Epochs() constructor with reject and flat dicts for immediate p2p rejection.

3.3.2 Drop bad segments (peak-to-peak 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.

  1. Compute p2p amplitude per segment per channel: PTP(seg, ch) = max(signal) - min(signal).
  2. Determine the rejection threshold:
  3. Reject a segment if any channel’s p2p exceeds the threshold.
  4. Flat detection: reject segments where p2p < 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).

3.3.3 Manual segment review

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.

3.3.4 Save segments

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.

3.3.5 Per-segment statistics

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.

3.4 Pipeline-level features

3.4.1 Save toggles (🗄️)

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:

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.

3.4.2 View Step Result (👁)

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.

  1. If Svarog is installed AND both files share the same sample rate, the button launches Svarog in split view: one window, two plots, horizontally scroll-locked. Uses Svarog’s --split-signal flag (requires a Svarog jar that supports it).
  2. Otherwise (no Svarog jar, or rate mismatch after a resample), it falls back to two independent MNE interactive windows side by side.

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.

3.4.3 Status badges (Extras / Manual review)

Each preprocessing step’s header row carries up to two compact status badges (small monochrome icons) next to its title:

The badges let you see at a glance which steps will produce extra output or pause for user input, without expanding every panel.

3.4.4 Hash-based caching (overview)

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.

3.4.5 MP book reuse

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_decomposition TOML key was removed. To enable MP, add "mp_decomposition" to preprocessing.step_order. Configs that still use the old key log an error and run without MP.


4. Analysis tab

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).

4.0 Run buttons & preprocessing-skip logic

4.0.1 What “Run” means per analysis

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.

4.0.2 Skipping preprocessing

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:

4.0.3 Event-dependent vs continuous-signal analyses

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.

4.1 UW-developed analyses

4.1.1 MMP → dipole sources

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.

  1. Read macroatoms from the MMP book. Each macroatom groups all channels for one MP iteration, sharing time, frequency, and scale parameters while having per-channel amplitude and phase.
  2. Determine amplitude signs via circular mean of phases across channels.
  3. Construct an MNE EvokedArray from the macroatom’s spatial pattern.
  4. Fit the dipole using mne.fit_dipole() with the BEM model.
  5. Record: position (x, y, z), orientation, goodness-of-fit (% variance explained), amplitude.
  6. Optionally compute distance to nearest cortical surface voxel (requires nibabel + fsaverage source space).

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/):

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):

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:

  1. FreeSurfer recon-all must have completed for the subject.
  2. BEM solution (*-bem-sol.fif) — mne.make_bem_model + mne.make_bem_solution (or mne.make_forward_solution).
  3. Head-to-MRI transform (*-trans.fif) mapping HEAD coordinates to the subject’s MRI. Created by mne.gui.coregistration or mne coreg.
  4. Parcellation annotations (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.

4.1.2 EEG profiles

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/):

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

4.1.3 Connectivity (DTF/PDC)

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.

4.2 MNE-wrapped analyses

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_*.

4.2.0 Per-category channel selection

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>.channelslist[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.

4.2.1 ERP

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.

4.2.2 Spectra

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).

4.2.3 Time-frequency

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:

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.

4.2.4 Spatial

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

4.2.5 Comparison

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)

4.2.6 Source estimation

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.

a. ERP dipole fit (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.

b. MNE / dSPM / sLORETA / eLORETA inverse (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.

c. LCMV beamformer (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.

d. MxNE / iRMxNE sparse (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.

Choosing a source-estimation entry

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.


5. Run tab

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.

5.1 Run Pipeline button

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.

5.2 Input validation

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.

5.2.1 Stage 5 consistency-rule framework

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

5.3 Live log panel

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.

5.4 Stopping a run

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.

5.5 What gets saved on success or failure

When the pipeline runs (via Run Pipeline or per-analysis [Run]), output files land in IDE4EEG_OUT_<filename>/:

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.

5.6 What config is actually used?

In all cases, the GUI collects the current state of all widgets into a config dict at the moment you click Run. This means:

5.7 Command-line execution

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 exit

The 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.


6. Output tab

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.

6.1 Output directory structure

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:

6.2 Browsing the tree

The navigation row above the tree has four buttons:

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:

6.3 Opening files in helper apps

The view row below the tree has buttons that operate on the currently selected file:

Double-click in the tree dispatches by extension automatically:

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.


7. Help tab

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.

7.1 Context help — the [?] buttons

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: link that switches to the Help tab and scrolls to the corresponding section.

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.

7.2 Tooltips

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.

7.3 In-app manual viewer

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).

7.4 Citing IDE4EEG and references

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.


8. Helper applications

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.

8.1 Svarog

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:

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):

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.

8.2 Connectivis

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+):

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):

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.

File formats.

8.3 empi

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.

8.4 MP Book Viewer

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.

8.4.1 Opening a file

Three ways to open an empi .db file:

  1. From the Output tab — select an empi .db file in the file tree and click Open in Book Viewer.

  2. From within the viewer — click Open… and choose a .db file.

  3. From the command line:

    python3 -m ide4eeg.analysis.mp_bookviewer              # opens file dialog
    python3 -m ide4eeg.analysis.mp_bookviewer path/to.db   # opens directly

8.4.2 Layout

Four 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)

8.4.4 Atom selection

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.

8.4.5 Atom filter bar

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.

8.4.6 Keyboard shortcuts

Key Action
/ Previous / next segment
/ Previous / next channel
Escape Reset zoom to full range
Enter (in filter field) Apply filter

Appendix A. Installation

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.

A.1 Python environment

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 / onnxruntime

The 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 PATH

In 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.

Known wheel gaps

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.

Development install (from a checkout)

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 live

A.2 Java + helper apps

IDE4EEG uses three external helper applications (Svarog, Connectivis, empi) that are JVM- or native-binary-based.

A.3 Video processing libraries (PyTorch + L2CS)

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.


Appendix B. Supported EEG formats

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.


Appendix C. Complete config.toml reference

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 = 0

Appendix D. FAQ

D.1 General

How do I check my Python version?

python3 --version

See Appendix A.1 for the supported range.

I get ModuleNotFoundError

Make sure the virtual environment is activated. You should see (venv) or (.venv) at the start of your terminal prompt.

How do I deactivate the virtual environment?

deactivate

D.2 File formats

IDE4EEG does not recognise my file

Check that:

  1. The file extension is .raw, .vhdr, .fif, .set, .edf, or .bdf.
  2. All companion files are present (e.g. .raw needs .tag and .xml).
  3. The input_path in your config points to the correct file or directory.

How do I add a new format?

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.

D.3 Preprocessing

How do I define custom filters?

Add entries to filters_dict in ide4eeg/input/input.py. Each entry needs:

How do I select which events to include?

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"]

Where are the clean signals saved?

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.

I checked a step’s Save checkbox but I don’t see any output. Why?

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.

D.4 Analysis

All epochs were rejected — what should I do?

Your rejection criteria may be too strict. Try:

How do I customise the hidden default parameters?

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.

Cluster test plots show no clusters, but the log mentions some

Detailed cluster information is saved in a .txt file in the MNE/ folder.

D.5 Resampling & filtering

How does filtering work?

IDE4EEG supports two filtering methods:

What frequencies should I use?

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.

Do I need to resample?

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.

Comparison with previous versions

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:

D.6 Video artifacts

For a complete description of the pipeline, backends, gaze methods, and all parameters, see §3.2.12 Gaze.

What are exclude faces?

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.

How can I speed up video artifact detection?

Two acceleration options:

Both options can be combined. For example, facetag_frame_skip = 3 with facetag_downscale = 0.5 gives roughly 6× overall speedup.

How do I create reference photos?

Reference photos are images of the subject looking directly at the camera. You need 3–5 such images.

  1. Select from video (recommended): in the Preprocessing tab, click Reference faces on the Gaze panel. The tool opens the video, lets you scrub through frames and click on the subject’s face to save it. Use </> 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.
  2. Manual: place 3–5 JPEG/PNG images in a folder. Each image must contain exactly one face (the subject) looking at the camera.

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.

How do I review detections?

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.

Double-click any interval row to jump to its start time.

D.7 GUI tips


Appendix E. Advanced / hidden parameters

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.

E.1 Signal trimming

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.

E.2 Amplitude thresholding

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.

E.3 Channel quality (advanced)

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]]

Default:

[choosing_channels.badchs_params]
slow_oscillations = [1, [2, 10], "-", [1e3, 1e4, 1e5]]
noise_power = [1, [52, 98], "-", [2e1, 2e2, 2e3]]

E.4 Artifact rejection thresholds (advanced)

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.

E.5 Epoch rejection

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.

E.6 ICA / EOG (advanced)

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.

E.7 Parallelism (advanced)

For the basic field on the Config tab and what n_jobs accelerates, see §1.3.

How to choose a value

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:

  1. Primary label→ sequential (no parallelism) when the field is 1, or → N parallel workers when larger.
  2. System info line — detected hardware, e.g. 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.
  3. Warning line (yellow, only when your value is risky). Multiple warnings can stack:

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.

Why BLAS is pinned to one thread per worker

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.

MP decomposition inherits this value

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.

E.8 Hash-based caching internals

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.

Three invariants keep the chain stable:

  1. Hash never includes mutated config. Side-effecting steps (bad-channel detection appending to 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.
  2. No deepcopy of bound methods. Manual-review hooks (_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.
  3. Pin BEFORE truncation. The GUI eye-click path force-pins the hash against the user-level pre-truncation cfg in _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.


Appendix F. config.toml vs Export Script

Two ways to capture and re-run a configuration outside the GUI:

  1. config.toml — declarative TOML, the same format the GUI uses internally. Save with Save Config…, run with ide4eeg --run myconfig.toml.
  2. Exported Python script — a self-contained .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.

F.1 Side-by-side

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)

F.2 When to prefer config.toml

F.3 When to prefer the exported script

F.4 What both leave on disk

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.