v2.0.0
Loading...
Searching...
No Matches
ieeg_surface_map.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "ieeg_surface_map.h"
18
19//=============================================================================================================
20// STL INCLUDES
21//=============================================================================================================
22
23#include <cmath>
24#include <vector>
25
26//=============================================================================================================
27// USED NAMESPACES
28//=============================================================================================================
29
30using namespace IEEGLIB;
31
32//=============================================================================================================
33// DEFINE MEMBER METHODS
34//=============================================================================================================
35
36Eigen::SparseMatrix<double> IeegSurfaceMap::weights(const Eigen::MatrixX3f& contacts,
37 const Eigen::MatrixX3f& vertices,
38 double distance,
40 bool project)
41{
42 const Eigen::Index nVert = vertices.rows();
43 const Eigen::Index nContacts = contacts.rows();
44 Eigen::SparseMatrix<double> w(nVert, nContacts);
45 if (nVert == 0 || nContacts == 0 || distance <= 0.0)
46 return w;
47
48 // Linear fall-off within reach, per vertex: (contact, weight)
49 const Eigen::MatrixX3d rr = vertices.cast<double>();
50 std::vector<std::vector<std::pair<Eigen::Index, double>>> near(static_cast<size_t>(nVert));
51 const double reach2 = distance * distance;
52 for (Eigen::Index c = 0; c < nContacts; ++c) {
53 Eigen::RowVector3d pos = contacts.row(c).cast<double>();
54 if (!pos.allFinite())
55 continue;
56 Eigen::VectorXd d2 = (rr.rowwise() - pos).rowwise().squaredNorm();
57 if (project) {
58 Eigen::Index nearest = 0;
59 d2.minCoeff(&nearest);
60 pos = rr.row(nearest);
61 d2 = (rr.rowwise() - pos).rowwise().squaredNorm();
62 }
63 for (Eigen::Index v = 0; v < nVert; ++v) {
64 if (d2(v) <= reach2)
65 near[size_t(v)].emplace_back(c, 1.0 - std::sqrt(d2(v)) / distance);
66 }
67 }
68
69 std::vector<Eigen::Triplet<double>> entries;
70 for (Eigen::Index v = 0; v < nVert; ++v) {
71 auto& list = near[size_t(v)];
72 if (list.empty())
73 continue;
75 // The nearest contact has the largest weight; the first one wins a tie, as numpy's argmax
76 auto best = list.begin();
77 for (auto it = list.begin(); it != list.end(); ++it) {
78 if (it->second > best->second)
79 best = it;
80 }
81 entries.emplace_back(v, best->first, mode == IeegSurfaceMapMode::Single ? best->second : 1.0);
82 continue;
83 }
84 double norm = 1.0;
85 if (mode == IeegSurfaceMapMode::Weighted) {
86 double sum = 0.0;
87 for (const auto& [c, weight] : list)
88 sum += weight;
89 norm = sum > 0.0 ? sum : 1.0;
90 }
91 for (const auto& [c, weight] : list) {
92 if (weight > 0.0)
93 entries.emplace_back(v, c, weight / norm);
94 }
95 }
96 w.setFromTriplets(entries.begin(), entries.end());
97 return w;
98}
Map sEEG / ECoG contact values onto surface vertices, as MNE-Python's stc_near_sensors.
Intracranial EEG (sEEG, ECoG): implantation, bipolar referencing, cortical mapping.
IeegSurfaceMapMode
How the weights of contacts near one vertex combine; the modes of MNE-Python's stc_near_sensors.
static Eigen::SparseMatrix< double > weights(const Eigen::MatrixX3f &contacts, const Eigen::MatrixX3f &vertices, double distance=0.01, IeegSurfaceMapMode mode=IeegSurfaceMapMode::Sum, bool project=true)