Expand description
§exg — EEG/ECG/EMG preprocessing in pure Rust
exg is a pure-Rust library for EEG preprocessing with 100% numerical
parity against MNE-Python. Every DSP step is ported
from MNE and verified against ground-truth test vectors (200+ tests).
No Python, no BLAS, no C libraries — pure Rust + RustFFT.
§Workspace
| Crate | Description |
|---|---|
exg | Core DSP, file I/O, generic preprocessing pipeline |
exg-luna | LUNA seizure-detection pipeline |
exg-source | Source localisation (eLORETA, MNE/dSPM/sLORETA) |
§Features
| Category | Capabilities |
|---|---|
| File I/O | FIF, EDF/EDF+, CSV readers; HDF5 (feature-gated); safetensors export |
| Filters | Highpass, lowpass, bandpass, notch — all MNE-parity _firwin_design |
| DSP | FFT polyphase resampling, average reference, overlap-add convolution |
| Normalisation | Global z-score, channel-wise z-score, per-epoch baseline correction |
| Montages | TCP bipolar (22-ch), Siena unipolar (29-ch), SEED-V unipolar (62-ch) |
| Pipelines | Generic preprocess(), LUNA-specific via exg-luna crate |
| Source localisation | eLORETA, MNE/dSPM/sLORETA (via exg-source crate) |
§Quick start — generic pipeline
use exg::{preprocess, PipelineConfig, fiff::open_raw};
use ndarray::Array2;
let raw = open_raw("data/sample1_raw.fif").unwrap();
let data = raw.read_all_data().unwrap();
let chan_pos: Array2<f32> = Array2::zeros((raw.info.n_chan, 3));
let cfg = PipelineConfig::default(); // 256 Hz · 0.5 Hz HP · 5 s epochs
let epochs = preprocess(data.mapv(|v| v as f32), chan_pos, raw.info.sfreq as f32, &cfg).unwrap();§Quick start — LUNA seizure-detection pipeline
The LUNA pipeline lives in the separate exg-luna crate:
ⓘ
use exg::edf::open_raw_edf;
use exg_luna::{preprocess_luna, LunaPipelineConfig};
let raw = open_raw_edf("recording.edf").unwrap();
let data = raw.read_all_data().unwrap();
let ch_names = raw.channel_names();
let cfg = LunaPipelineConfig::default(); // 0.1–75 Hz BP · 60 Hz notch · TCP montage
let epochs = preprocess_luna(data, &ch_names, raw.header.sample_rate, &cfg).unwrap();§Individual DSP steps
use exg::{resample::resample, filter::*, reference::*, normalize::*, epoch::*};
use ndarray::Array2;
let mut data: Array2<f32> = Array2::zeros((22, 7680));
// Resample 512 → 256 Hz
let mut data = resample(&data, 512.0, 256.0).unwrap();
// Bandpass 0.1–75 Hz
let h = design_bandpass(0.1, 75.0, 256.0);
apply_fir_zero_phase(&mut data, &h).unwrap();
// Notch 60 Hz
let h = design_notch(60.0, 256.0, None, None);
apply_fir_zero_phase(&mut data, &h).unwrap();
// Average reference → channel-wise z-score → epoch
average_reference_inplace(&mut data);
zscore_channelwise_inplace(&mut data);
let epochs = epoch(&data, 1280); // [E, 22, 1280]Re-exports§
pub use config::PipelineConfig;pub use csv::read_eeg;pub use edf::open_raw_edf;pub use edf::RawEdf;pub use edf::EdfHeader;pub use edf::SignalHeader;pub use edf::EdfAnnotation;pub use epoch::epoch;pub use epoch::epoch_and_baseline;pub use fiff::open_raw;pub use fiff::RawFif;pub use fiff::BufferRecord;pub use fiff::ChannelInfo;pub use fiff::MeasInfo;pub use fiff::read_meas_info;pub use fiff::TagHeader;pub use fiff::read_tag_header;pub use fiff::read_i32;pub use fiff::read_f32;pub use fiff::read_f64;pub use fiff::read_string;pub use fiff::read_i32_array;pub use fiff::read_f32_array;pub use fiff::read_f64_array;pub use fiff::read_raw_bytes;pub use fiff::read_directory;pub use fiff::Node;pub use fiff::build_tree;pub use fiff::read_tree;pub use fiff::scan_directory;pub use fiff::try_load_directory;pub use filter::auto_trans_bandwidth;pub use filter::auto_trans_bandwidth_lowpass;pub use filter::auto_filter_length;pub use filter::design_highpass;pub use filter::design_lowpass;pub use filter::design_bandpass;pub use filter::design_notch;pub use filter::firwin;pub use filter::hamming;pub use filter::apply_fir_zero_phase;pub use filter::filter_1d;pub use io::RawData;pub use io::StWriter;pub use io::write_batch;pub use montage::make_bipolar;pub use montage::normalize_channel_name;pub use montage::pick_channels;pub use montage::TCP_MONTAGE;pub use montage::SIENA_CHANNELS;pub use montage::SEED_V_CHANNELS;pub use montage::BipolarDef;pub use normalize::zscore_global_inplace;pub use normalize::zscore_channelwise_inplace;pub use normalize::baseline_correct_inplace;pub use reference::average_reference_inplace;pub use resample::resample;pub use resample::resample_1d;pub use resample::auto_npad;pub use resample::rational_approx;pub use resample::final_length;pub use exg_source as source_localization;
Modules§
- config
- Pipeline configuration.
- csv
- CSV reader for EEG data.
- edf
- EDF/EDF+ file reader.
- epoch
- Fixed-length epoching.
- fiff
- FIFF file format reader.
- filter
- FIR filter design and application.
- io
- Safetensors I/O for the preprocessing pipeline.
- montage
- Bipolar montage conversion for EEG data.
- normalize
- Z-score normalisation and epoch baseline correction.
- reference
- Average reference: subtract the mean across channels at each time point.
- resample
- FFT-based rational resampler exactly matching MNE’s
resample(..., method='fft'). - xdf
- XDF (Extensible Data Format) file writer.
Structs§
- Eloreta
Options - Options for the eLORETA iterative solver.
- Forward
Operator - Forward operator (gain matrix + metadata).
- Inverse
Operator - Prepared inverse operator, ready for application to data.
- Noise
Cov - Noise covariance matrix.
- Source
Estimate - Source-space estimate produced by
apply_inverse. - Sphere
Model - Parameters of a multi-shell spherical head model.
Enums§
- Inverse
Method - Choice of inverse method.
- PickOri
- How to handle source orientations in free-orientation inverse solutions.
- Regularization
- Regularisation strategy for covariance estimation.
- Source
Orientation - Source orientation constraint.
Functions§
- apply_
inverse - Apply an inverse operator to sensor-space data.
- apply_
inverse_ epochs - Apply inverse operator to each epoch in a batch.
- apply_
inverse_ epochs_ full - Apply inverse to epochs with full options.
- apply_
inverse_ full - Apply inverse with full control over orientation picking and eLORETA options.
- compute_
covariance - Compute noise covariance from continuous data
[n_channels, n_times]. - compute_
covariance_ epochs - Compute noise covariance from epoched data
[n_epochs, n_channels, n_times]. - estimate_
snr - Estimate SNR as a function of time.
- get_
cross_ talk - Cross-talk function (CTF) for a given source index.
- get_
point_ spread - Point-spread function (PSF) for a given source index.
- grid_
source_ space - Generate a volume source space on a regular 3-D grid.
- ico_
n_ vertices - Expected vertex count for a given icosahedron subdivision level.
- ico_
source_ space - Generate a source space by subdividing an icosahedron.
- make_
inverse_ operator - Build an inverse operator from a forward model and noise covariance.
- make_
resolution_ matrix - Compute the resolution matrix
R = K @ G. - make_
sphere_ forward - Compute a fixed-orientation EEG forward model using a spherical head.
- make_
sphere_ forward_ free - Compute a free-orientation EEG forward model using a spherical head.
- peak_
localisation_ error - Compute peak localisation error for each source.
- preprocess
- Run the full EEG preprocessing pipeline on a single continuous recording.
- relative_
amplitude - Compute relative amplitude for each PSF.
- spatial_
spread - Compute spatial spread (half-max width) for each PSF.
- zero_
bad_ channels - Zero-fill channels whose normalised name appears in
bad.