This vignette demonstrates EEG source localization methods available in PhysioEEG. Source localization (also called source imaging) addresses the inverse problem: estimating brain source activity from scalp-recorded potentials. The EEG inverse problem is ill-posed (infinitely many source configurations can produce the same scalp pattern), so additional constraints are needed to obtain a unique solution.
PhysioEEG provides:
library(PhysioEEG)
# Create 19-channel EEG data (10-20 system)
pe <- make_eeg(n_time = 2500, n_channels = 19, sr = 500)
# Assign standard electrode positions (required for source localization)
pe <- eegMontage(pe, system = "10-20")
# Verify positions are set
head(SummarizedExperiment::colData(pe))For best results, preprocess the data before source localization:
The forward model describes how neural source activity maps to scalp electrode potentials. It is represented by a leadfield matrix of dimensions (n_electrodes x n_sources * 3), where each source has three orientation columns (x, y, z dipole directions).
The simplest approach models the head as an infinite homogeneous conducting medium with conductivity 0.33 S/m:
# Build forward model with 100 source locations
fm_sphere <- eegForwardModel(pe, method = "spherical", n_sources = 100,
assay_name = "rereferenced")
# Inspect the leadfield
dim(fm_sphere$leadfield)
# [1] 19 300 (19 electrodes x 100 sources x 3 orientations)
# Source positions are distributed inside a sphere of radius 0.8
head(fm_sphere$source_positions)
# Electrode positions
print(fm_sphere$electrode_positions)The Berg & Scherg (1994) 3-shell approximation accounts for the different conductivities of brain, skull, and scalp using virtual dipoles at scaled eccentricities:
The leadfield encodes the sensitivity pattern of each electrode to each source:
Source estimation uses the forward model and data to reconstruct brain activity. PhysioEEG supports three distributed source imaging methods:
MNE minimizes the L2 norm of the source distribution. It provides the minimum-energy solution but tends to favor superficial sources:
pe_mne <- eegSourceEstimate(pe, fm_sphere, method = "mne",
lambda = 0.05,
assay_name = "rereferenced")
# Source estimates are stored as a time x (n_sources*3) matrix
src_data <- SummarizedExperiment::assay(pe_mne, "source")
dim(src_data)
# [1] 2500 300 (2500 time points x 100 sources x 3 orientations)sLORETA normalizes MNE by the resolution matrix diagonal, providing zero-localization error for point sources:
eLORETA uses iterative weight estimation to achieve exact localization for point sources, even with noise:
The three methods differ in their spatial resolution, depth bias, and noise sensitivity:
# Compute source power (RMS across orientations) for each method
.rms_power <- function(pe_src, n_sources) {
src <- SummarizedExperiment::assay(pe_src, "source")
power <- numeric(n_sources)
for (j in seq_len(n_sources)) {
cols <- ((j - 1) * 3 + 1):(j * 3)
power[j] <- mean(rowSums(src[, cols]^2))
}
power
}
mne_power <- .rms_power(pe_mne, 100)
sloreta_power <- .rms_power(pe_sloreta, 100)
eloreta_power <- .rms_power(pe_eloreta, 100)
# Find peak source for each method
cat("MNE peak source:", which.max(mne_power), "\n")
cat("sLORETA peak source:", which.max(sloreta_power), "\n")
cat("eLORETA peak source:", which.max(eloreta_power), "\n")Beamformers are spatial filters that estimate the power at each source location by constructing a filter that passes activity from the target location while suppressing contributions from elsewhere.
The Linearly Constrained Minimum Variance (LCMV) beamformer operates in the time domain using the data covariance matrix:
pe_lcmv <- eegBeamformer(pe, fm_sphere, method = "lcmv",
assay_name = "rereferenced")
# Source power is stored as a n_sources x 1 matrix
bf_power <- SummarizedExperiment::assay(pe_lcmv, "beamformer")
dim(bf_power)
# [1] 100 1
# Find the source with maximum power
peak_source <- which.max(bf_power[, 1])
cat("LCMV peak source:", peak_source, "\n")
cat("Peak power:", bf_power[peak_source, 1], "\n")Dynamic Imaging of Coherent Sources (DICS) operates in the frequency domain, using the cross-spectral density matrix in a specified frequency band:
# Alpha-band (8-13 Hz) source localization
pe_dics <- eegBeamformer(pe, fm_sphere, method = "dics",
freq_range = c(8, 13),
assay_name = "rereferenced")
dics_power <- SummarizedExperiment::assay(pe_dics, "beamformer")
peak_dics <- which.max(dics_power[, 1])
cat("DICS alpha peak source:", peak_dics, "\n")
# Compare with beta-band source
pe_dics_beta <- eegBeamformer(pe, fm_sphere, method = "dics",
freq_range = c(13, 30),
assay_name = "rereferenced")
dics_beta <- SummarizedExperiment::assay(pe_dics_beta, "beamformer")
cat("DICS beta peak source:", which.max(dics_beta[, 1]), "\n")eegSourcePower() computes frequency-band source maps
from previously estimated source time series. It requires prior source
estimation via eegSourceEstimate():
PhysioEEG provides eegPlotSource() for visualizing
source localization results on a 2D brain projection.
Source locations are shown as points, sized and colored by amplitude. Only sources above a percentile threshold are displayed:
# Create source data for visualization
src_positions <- fm_sphere$source_positions
src_amplitudes <- bf_power[, 1]
src_df <- data.frame(
x = src_positions$x,
y = src_positions$y,
amplitude = src_amplitudes
)
# Plot with 80th percentile threshold (show top 20% sources)
eegPlotSource(pe, source_data = src_df, method = "scatter",
threshold_pct = 80)The flat map mode uses interpolation to create a continuous image of source activity:
# LCMV result
lcmv_df <- data.frame(
x = src_positions$x,
y = src_positions$y,
amplitude = bf_power[, 1]
)
p_lcmv <- eegPlotSource(pe, source_data = lcmv_df, method = "scatter",
threshold_pct = 80)
# DICS alpha result
dics_df <- data.frame(
x = src_positions$x,
y = src_positions$y,
amplitude = dics_power[, 1]
)
p_dics <- eegPlotSource(pe, source_data = dics_df, method = "scatter",
threshold_pct = 80)
# Display side by side (requires patchwork or gridExtra)
print(p_lcmv)
print(p_dics)| Feature | Distributed (MNE/LORETA) | Beamformer (LCMV/DICS) |
|---|---|---|
| Output | Full time course per source | Power per source |
| Multiple sources | Well-suited | Can suppress correlated sources |
| Depth bias | MNE biased to surface | Less depth bias |
| Regularization | Lambda parameter | Data covariance |
| Frequency specificity | Post-hoc via eegSourcePower | DICS: built-in |
Source localization accuracy improves with more electrodes. The 19-channel 10-20 system provides limited spatial resolution. For research applications, 64 or more channels are recommended:
Higher signal-to-noise ratio (SNR) improves source estimation. Strategies to improve SNR include:
The regularization parameter lambda controls the
trade-off between data fit and solution smoothness:
# Sweep regularization values
lambdas <- c(0.001, 0.01, 0.05, 0.1, 0.5)
for (lam in lambdas) {
pe_src <- eegSourceEstimate(pe, fm_sphere, method = "mne",
lambda = lam,
assay_name = "rereferenced")
src <- SummarizedExperiment::assay(pe_src, "source")
max_power <- max(rowSums(src^2))
cat(sprintf("lambda = %.3f -> max source power = %.2e\n", lam, max_power))
}