Convert electrode positions form 3D to 2D
Sometimes you have 3D montage but you need 2D montage. How to convert one to another? The function to_positions
should help.
using UnfoldMakie
using CairoMakie
using TopoPlots
using PyMNE;
Precompiling PyMNE...
Info Given PyMNE was explicitly requested, output will be shown live
CondaPkg Found dependencies: /home/runner/.julia/packages/PyMNE/HGgbW/CondaPkg.toml
CondaPkg Found dependencies: /home/runner/.julia/packages/PythonCall/WMWY0/CondaPkg.toml
CondaPkg Found dependencies: /home/runner/.julia/packages/PythonPlot/oS8x4/CondaPkg.toml
CondaPkg Initialising pixi
│ /home/runner/.julia/artifacts/cefba4912c2b400756d043a2563ef77a0088866b/bin/pixi
│ init
│ --format pixi
└ /home/runner/work/UnfoldMakie.jl/UnfoldMakie.jl/docs/.CondaPkg
✔ Created /home/runner/work/UnfoldMakie.jl/UnfoldMakie.jl/docs/.CondaPkg/pixi.toml
CondaPkg Wrote /home/runner/work/UnfoldMakie.jl/UnfoldMakie.jl/docs/.CondaPkg/pixi.toml
│ [dependencies]
│ openssl = ">=3, <3.1"
│ uv = ">=0.4"
│ libstdcxx-ng = ">=3.4,<13.0"
│ matplotlib = ">=1"
│
│ [dependencies.python]
│ channel = "anaconda"
│ version = ">=3.8,<4, >=3.4,<4"
│
│ [project]
│ name = ".CondaPkg"
│ platforms = ["linux-64"]
│ channels = ["anaconda", "conda-forge"]
│ channel-priority = "disabled"
│ description = "automatically generated by CondaPkg.jl"
│
│ [pypi-dependencies]
└ mne = ">=1.4"
CondaPkg Installing packages
│ /home/runner/.julia/artifacts/cefba4912c2b400756d043a2563ef77a0088866b/bin/pixi
│ install
└ --manifest-path /home/runner/work/UnfoldMakie.jl/UnfoldMakie.jl/docs/.CondaPkg/pixi.toml
✔ The default environment has been installed.
5763.0 ms ✓ PyMNE
1 dependency successfully precompiled in 6 seconds. 51 already precompiled.
1 dependency had output during precompilation:
┌ PyMNE
│ [Output was shown above]
└
Precompiling UnfoldMakiePyMNEExt...
1509.5 ms ✓ PyMNE
Info Given UnfoldMakiePyMNEExt was explicitly requested, output will be shown live
┌ Warning: Module PyMNE with build ID fafbfcfd-8fff-79e9-0000-00cf2c81edd6 is missing from the cache.
│ This may mean PyMNE [6c5003b2-cbe8-491c-a0d1-70088e6a0fd6] does not support precompilation but is imported by a module that does.
└ @ Base loading.jl:2541
1189.9 ms ? UnfoldMakie → UnfoldMakiePyMNEExt
1 dependency successfully precompiled in 3 seconds. 391 already precompiled.
1 dependency precompiled but a different version is currently loaded. Restart julia to access the new version. Otherwise, loading dependents of this package may trigger further precompilation to work with the unexpected version.
1 dependencies failed but may be precompilable after restarting julia
1 dependency had output during precompilation:
┌ UnfoldMakie → UnfoldMakiePyMNEExt
│ [Output was shown above]
└
┌ Warning: Module PyMNE with build ID fafbfcfd-8fff-79e9-0000-00cf2c81edd6 is missing from the cache.
│ This may mean PyMNE [6c5003b2-cbe8-491c-a0d1-70088e6a0fd6] does not support precompilation but is imported by a module that does.
└ @ Base loading.jl:2541
Get positions from MNE
Generate an MNE structure taken from mne documentation
biosemi_montage = PyMNE.channels.make_standard_montage("biosemi64")
n_channels = length(biosemi_montage.ch_names)
fake_info =
PyMNE.create_info(ch_names = biosemi_montage.ch_names, sfreq = 250.0, ch_types = "eeg")
data = rand(n_channels, 1) * 1e-6
fake_evoked = PyMNE.EvokedArray(data, fake_info)
fake_evoked.set_montage(biosemi_montage)
pos = to_positions(fake_evoked)
64-element Vector{Point{2, Float64}}:
[0.34935274876763384, 0.8914697618166454]
[0.2285454515991697, 0.8299153694246765]
[0.3239207434789678, 0.8210416675361911]
[0.38009721906796695, 0.7346772379558053]
[0.28116929732806945, 0.7288908776450373]
[0.19598811803244962, 0.729048111114921]
[0.13267223773863893, 0.7340421555641459]
[0.07111784534666996, 0.6132348583956815]
[0.1381378125628734, 0.6118022611559795]
[0.2400936018380034, 0.6086174147856195]
⋮
[0.612772564740614, 0.34981504242270145]
[0.5864401194985318, 0.2239606378043266]
[0.6853680412384293, 0.2297469981150946]
[0.7705492205340492, 0.22958976464521094]
[0.8338651008278599, 0.22459572019598625]
[0.8710458357539724, 0.19758233504967118]
[0.7379918869673291, 0.12872250633545543]
[0.6426165950875308, 0.13759620822394072]
[0.6171845897988648, 0.06716811394348651]
Projecting from 3D montage to 2D
pos3d = hcat(values(pyconvert(Dict, biosemi_montage.get_positions()["ch_pos"]))...)
pos2 = to_positions(pos3d)
f = Figure(size = (600, 300))
scatter(f[1, 1], pos3d[1:2, :], axis = (title = "Dropping third dimension",))
scatter(f[1, 2], pos2, axis = (title = "Projection form 3D to 2D",))
f

As you can see, the "naive" transformation of simply dropping the third dimension does not really work (left). Instead, we have to project the channels onto a sphere and unfold it (right).
This page was generated using Literate.jl.