v2.0.0
Loading...
Searching...
No Matches
inv_trap_music.cpp
Go to the documentation of this file.
1//=============================================================================================================
19
20//=============================================================================================================
21// INCLUDES
22//=============================================================================================================
23
24#include "inv_trap_music.h"
25
26//=============================================================================================================
27// EIGEN INCLUDES
28//=============================================================================================================
29
30#include <Eigen/SVD>
31#include <Eigen/Dense>
32
33//=============================================================================================================
34// QT INCLUDES
35//=============================================================================================================
36
37#include <QDebug>
38
39//=============================================================================================================
40// STD INCLUDES
41//=============================================================================================================
42
43#include <cmath>
44
45//=============================================================================================================
46// USED NAMESPACES
47//=============================================================================================================
48
49using namespace INVLIB;
50using namespace Eigen;
51
52//=============================================================================================================
53// DEFINE MEMBER METHODS
54//=============================================================================================================
55
56InvTrapMusic::InvTrapMusic(int iMaxSources, double dThreshold)
57: m_iMaxSources(iMaxSources)
58, m_dThreshold(dThreshold)
59{
60}
61
62//=============================================================================================================
63
64QList<TrapMusicDipole> InvTrapMusic::compute(const MatrixXd& matLeadField,
65 const MatrixXd& matData,
66 const MatrixXd& matSourcePos,
67 int iNOrient) const
68{
69 QList<TrapMusicDipole> dipoles;
70
71 const int nCh = static_cast<int>(matLeadField.rows());
72 const int nSrc = static_cast<int>(matSourcePos.rows());
73
74 if (nCh == 0 || matData.rows() != nCh || matLeadField.cols() != static_cast<Eigen::Index>(nSrc) * iNOrient) {
75 qWarning() << "[InvTrapMusic::compute] Dimension mismatch.";
76 return dipoles;
77 }
78
79 // Estimate signal subspace dimension from SVD of data
80 JacobiSVD<MatrixXd> dataSvd(matData, ComputeThinU);
81 const VectorXd& singVals = dataSvd.singularValues();
82
83 // Determine signal subspace dimension: look for a significant gap in singular values
84 int nSignal = 1;
85 for (int i = 1; i < singVals.size(); ++i) {
86 if (singVals[i] < singVals[0] * 0.05) // Below 5% of max
87 break;
88 ++nSignal;
89 }
90 nSignal = std::max(nSignal, m_iMaxSources);
91 nSignal = std::min(nSignal, static_cast<int>(std::min(dataSvd.matrixU().cols(),
92 static_cast<Index>(nCh / 2))));
93
94 // Signal subspace: U_s columns
95 MatrixXd signalSubspace = dataSvd.matrixU().leftCols(nSignal);
96
97 // Projector for the found sources (starts as identity)
98 MatrixXd projector = MatrixXd::Identity(nCh, nCh);
99
100 for (int iter = 0; iter < m_iMaxSources; ++iter) {
101 // Project the lead field and signal subspace
102 MatrixXd projLF = projector * matLeadField;
103 MatrixXd projSS = projector * signalSubspace;
104
105 // Re-orthogonalise the projected signal subspace
106 int ssDim = static_cast<int>(projSS.cols()) - iter;
107 if (ssDim < 1)
108 break;
109 ssDim = std::min(ssDim, static_cast<int>(projSS.cols()));
110
111 JacobiSVD<MatrixXd> ssSvd(projSS, ComputeThinU);
112 MatrixXd truncSS = ssSvd.matrixU().leftCols(ssDim); // Truncation step
113
114 // Scan correlations
115 VectorXd correlations = scanCorrelations(projLF, truncSS, iNOrient);
116
117 // Find best source
118 Index bestIdx = 0;
119 double bestCorr = correlations.maxCoeff(&bestIdx);
120
121 if (bestCorr < m_dThreshold)
122 break;
123
124 // Build dipole result
125 TrapMusicDipole dipole;
126 dipole.sourceIdx = static_cast<int>(bestIdx);
127 dipole.correlation = bestCorr;
128 dipole.position = matSourcePos.row(bestIdx).transpose();
129
130 // Estimate orientation from lead field columns
131 int colStart = static_cast<int>(bestIdx) * iNOrient;
132 if (iNOrient == 1) {
133 dipole.orientation = Vector3d(0, 0, 1);
134 } else {
135 // Project lead field columns onto signal subspace to get orientation
136 MatrixXd lfSrc = matLeadField.block(0, colStart, nCh, iNOrient);
137 MatrixXd proj = truncSS * truncSS.transpose() * lfSrc;
138 JacobiSVD<MatrixXd> orientSvd(proj, ComputeThinV);
139 Vector3d orient = orientSvd.matrixV().col(0);
140 orient.normalize();
141 dipole.orientation = orient;
142 }
143
144 dipoles.append(dipole);
145
146 // Project out the found source from the lead field (RAP step)
147 MatrixXd lfSrc = matLeadField.block(0, colStart, nCh, iNOrient);
148 MatrixXd lfOrth = lfSrc.householderQr().householderQ() *
149 MatrixXd::Identity(nCh, iNOrient);
150 projector = projector - lfOrth * lfOrth.transpose() * projector;
151 }
152
153 return dipoles;
154}
155
156//=============================================================================================================
157
158VectorXd InvTrapMusic::scanCorrelations(const MatrixXd& matLeadField,
159 const MatrixXd& matSignalSubspace,
160 int iNOrient)
161{
162 const int nSrcTotal = static_cast<int>(matLeadField.cols()) / iNOrient;
163 VectorXd correlations(nSrcTotal);
164
165 // Projector onto signal subspace
166 MatrixXd P_s = matSignalSubspace * matSignalSubspace.transpose();
167
168 for (int s = 0; s < nSrcTotal; ++s) {
169 int colStart = s * iNOrient;
170 MatrixXd G_s = matLeadField.block(0, colStart, matLeadField.rows(), iNOrient);
171
172 // MUSIC correlation: ||P_s * G_s||_F / ||G_s||_F
173 MatrixXd projG = P_s * G_s;
174 double normProjG = projG.norm();
175 double normG = G_s.norm();
176
177 correlations[s] = (normG > 1e-15) ? (normProjG / normG) : 0.0;
178 }
179
180 return correlations;
181}
Truncated RAP-MUSIC (TRAP-MUSIC) source-localisation algorithm — sub-space truncation per iteration f...
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
Result of a TRAP-MUSIC source scan.
Eigen::Vector3d position
Eigen::Vector3d orientation
static Eigen::VectorXd scanCorrelations(const Eigen::MatrixXd &matLeadField, const Eigen::MatrixXd &matSignalSubspace, int iNOrient)
Compute the MUSIC-type subspace correlation for all source locations.
InvTrapMusic(int iMaxSources=5, double dThreshold=0.85)
Construct TRAP-MUSIC scanner.
QList< TrapMusicDipole > compute(const Eigen::MatrixXd &matLeadField, const Eigen::MatrixXd &matData, const Eigen::MatrixXd &matSourcePos, int iNOrient=3) const
Compute TRAP-MUSIC source localization.