Example 3: Source Imaging (ESI) Pipeline¶
Localizing cortical sources from scalp EEG using MNE's fsaverage template
head — the sequence testing/full_verify.py's esi section exercises in
full, including both inverse methods.
1. Prepare a session (through epoching)¶
Source imaging needs a montage, a reference, and epochs — the same prep as the processing pipeline, condensed:
load_neuro(file_path="/data/synthetic-raw.fif", session_id="esi")
set_montage(session_id="esi", montage="standard_1020")
set_reference(session_id="esi", ref_channels="average")
find_events(session_id="esi")
epoch_neuro(session_id="esi", tmin=-0.2, tmax=0.5, reject_uv=None)
2. Build the head model and forward solution¶
fetch_template_head(session_id="esi", spacing="ico5")
# -> {"subject": "fsaverage", "spacing": "ico5", "n_sources": 20484,
# "note": "Template head model ready. Next: compute_forward."}
The first call downloads the fsaverage dataset; it's cached on disk for every call after that.
compute_forward(session_id="esi")
# -> {"n_sources": 20484, "n_channels": 20,
# "note": "Leadfield ready. Next: compute_noise_covariance."}
3. Noise covariance and the inverse operator¶
compute_noise_covariance(session_id="esi", method="empirical")
# -> {"method": "empirical", "rank": 20, ...}
make_inverse_operator(session_id="esi")
# -> {"loose": 0.2, "depth": 0.8, "note": "Inverse operator ready. Next: apply_inverse."}
4. Localize sources¶
apply_inverse(session_id="esi", method="dSPM", snr=3.0)
# -> {"n_sources": 20484, "peak": {
# "vertex": ..., "hemisphere": "lh", "latency_ms": 72.0, "amplitude": 8.08
# }, ...}
full_verify.py's evaluation pass confirms the peak amplitude is finite and
positive — a sanity check that the inverse actually localized something,
not just that the call didn't error.
Alternative — a beamformer instead of minimum-norm:
apply_lcmv_beamformer(session_id="esi", reg=0.05)
# -> {"method": "LCMV", "peak": {"hemisphere": "lh", "latency_ms": ..., ...}}
5. Map onto named anatomical regions¶
extract_label_timecourses(session_id="esi", top_n=5)
# -> {"n_regions": 68, "top_regions": [
# {"region": "isthmuscingulate-rh", "hemisphere": "rh",
# "peak_amplitude": ..., "peak_latency_ms": ...},
# ...
# ]}
Regions come from the Desikan-Killiany atlas (parcellation="aparc", 68
named cortical regions) — human-readable ROI names, not raw vertex indices.
6. Visualize¶
plot_source_timecourses(session_id="esi", top_n=5)
# -> {"image": "data:image/png;base64,...", "kind": "source_timecourses"}
plot_source_brain(session_id="esi")
# -> {"image": None, "note": "3D rendering backend not available in this
# environment. Use plot_source_timecourses for a 2D ROI summary."}
# (or a real screenshot PNG if `pip install neuro-mcp[viz3d]` is installed
# with a working offscreen renderer)
plot_source_timecourses works everywhere (pure matplotlib, headless);
plot_source_brain degrades gracefully instead of failing when no 3D
backend is available — check for image: null and fall back rather than
treating it as an error.
Full sequence at a glance¶
load_neuro -> set_montage -> set_reference -> find_events -> epoch_neuro ->
fetch_template_head -> compute_forward -> compute_noise_covariance ->
make_inverse_operator -> apply_inverse (or apply_lcmv_beamformer) ->
extract_label_timecourses -> plot_source_timecourses / plot_source_brain
For an interactive, self-contained-HTML version of this same source estimate
(volumetric slices + ERP butterfly, no separate apply_inverse call needed —
it computes its own), see
Interactive Visualization Generation's
visualize_esi.