v2.0.0
Loading...
Searching...
No Matches
inv_dics.cpp
Go to the documentation of this file.
1//=============================================================================================================
34
35//=============================================================================================================
36// INCLUDES
37//=============================================================================================================
38
39#include "inv_dics.h"
41
43#include <fiff/fiff_cov.h>
44#include <fiff/fiff_info.h>
45
46#include <QDebug>
47
48//=============================================================================================================
49// EIGEN INCLUDES
50//=============================================================================================================
51
52#include <Eigen/Dense>
53
54//=============================================================================================================
55// USED NAMESPACES
56//=============================================================================================================
57
58using namespace Eigen;
59using namespace INVLIB;
60using namespace MNELIB;
61using namespace FIFFLIB;
62
63//=============================================================================================================
64// DEFINE MEMBER METHODS
65//=============================================================================================================
66
68 const MNEForwardSolution &forward,
69 const std::vector<MatrixXd> &csdMatrices,
70 const VectorXd &frequencies,
71 double reg,
72 bool realFilter,
73 const FiffCov &noiseCov,
74 BeamformerPickOri pickOri,
75 BeamformerWeightNorm weightNorm,
76 bool reduceRank,
77 BeamformerInversion invMethod)
78{
79 InvBeamformer result;
80 result.kind = "DICS";
81
82 const int nFreqs = static_cast<int>(csdMatrices.size());
83 if(nFreqs == 0) {
84 qWarning("InvDICS::makeDICS - No CSD matrices provided!");
85 return result;
86 }
87 if(frequencies.size() != nFreqs) {
88 qWarning("InvDICS::makeDICS - Frequency vector size mismatch with CSD count!");
89 return result;
90 }
91
92 // -----------------------------------------------------------------------
93 // Extract leadfield
94 // -----------------------------------------------------------------------
95 if(!forward.sol || forward.sol->data.size() == 0) {
96 qWarning("InvDICS::makeDICS - Forward solution has no gain matrix!");
97 return result;
98 }
99
100 MatrixXd G = forward.sol->data;
101 const int nChannels = static_cast<int>(G.rows());
102 const int nOrient = (forward.source_ori == FIFFV_MNE_FREE_ORI) ? 3 : 1;
103 const int nSources = static_cast<int>(G.cols()) / nOrient;
104
105 qInfo("InvDICS::makeDICS - Leadfield: %d channels x %d sources (n_orient=%d), %d frequencies",
106 nChannels, nSources, nOrient, nFreqs);
107
108 // -----------------------------------------------------------------------
109 // Whitening matrix
110 // -----------------------------------------------------------------------
111 MatrixXd whitener;
112 if(noiseCov.data.size() > 0 && noiseCov.eig.size() > 0 && noiseCov.eigvec.size() > 0) {
113 VectorXd invSqrtEig(noiseCov.eig.size());
114 for(int i = 0; i < noiseCov.eig.size(); ++i) {
115 invSqrtEig(i) = (noiseCov.eig(i) > 1e-30)
116 ? 1.0 / std::sqrt(noiseCov.eig(i))
117 : 0.0;
118 }
119 whitener = invSqrtEig.asDiagonal() * noiseCov.eigvec.transpose();
120 } else {
121 whitener = MatrixXd::Identity(nChannels, nChannels);
122 }
123
124 MatrixXd projMat = MatrixXd::Identity(nChannels, nChannels);
125
126 // Whiten leadfield (shared across all frequencies)
127 MatrixXd Gw = whitener * G;
128
129 // Source normals
130 MatrixX3d nn = forward.source_nn.cast<double>();
131
132 // -----------------------------------------------------------------------
133 // Compute filter for each frequency
134 // -----------------------------------------------------------------------
135 for(int fi = 0; fi < nFreqs; ++fi) {
136 MatrixXd Cm = csdMatrices[fi];
137
138 if(Cm.rows() != nChannels || Cm.cols() != nChannels) {
139 qWarning("InvDICS::makeDICS - CSD[%d] dimension mismatch!", fi);
140 return InvBeamformer();
141 }
142
143 // Optional: take real part of CSD
144 if(realFilter) {
145 // CSD is provided as real-valued after user extracts real part,
146 // or we ensure it here
147 Cm = Cm.real();
148 }
149
150 // Whiten CSD
151 MatrixXd CmW = whitener * Cm * whitener.transpose();
152 CmW = (CmW + CmW.transpose()) * 0.5; // Ensure symmetry
153
154 // Compute filter
155 MatrixXd W;
156 MatrixX3d mpOri;
157
159 Gw, CmW, reg, nOrient,
160 weightNorm, pickOri, reduceRank, invMethod,
161 nn, W, mpOri);
162
163 if(!ok) {
164 qWarning("InvDICS::makeDICS - Filter computation failed at frequency %d (%.1f Hz)!",
165 fi, frequencies(fi));
166 return InvBeamformer();
167 }
168
169 result.weights.push_back(W);
170
171 // Store max-power orientation from first frequency
172 if(fi == 0 && pickOri == BeamformerPickOri::MaxPower) {
173 result.maxPowerOri = mpOri;
174 }
175 }
176
177 // -----------------------------------------------------------------------
178 // Populate metadata
179 // -----------------------------------------------------------------------
180 result.whitener = whitener;
181 result.proj = projMat;
182 result.chNames = forward.sol->row_names;
183 result.isFreOri = (nOrient == 3 && pickOri != BeamformerPickOri::Normal
184 && pickOri != BeamformerPickOri::MaxPower);
185 result.nSourcesTotal = nSources;
186 result.srcType = "surface";
187 result.weightNorm = weightNorm;
188 result.pickOri = pickOri;
189 result.inversion = invMethod;
190 result.reg = reg;
191 result.rank = static_cast<int>(nChannels);
192 result.sourceNn = forward.source_nn;
193 result.frequencies = frequencies;
194
195 VectorXi verts(0);
196 if(forward.src.size() >= 2) {
197 verts.resize(forward.src[0].vertno.size() + forward.src[1].vertno.size());
198 verts << forward.src[0].vertno, forward.src[1].vertno;
199 } else if(forward.src.size() == 1) {
200 verts = forward.src[0].vertno;
201 }
202 result.vertices = verts;
203
204 qInfo("InvDICS::makeDICS - Done. %d frequency filters computed.", nFreqs);
205
206 return result;
207}
208
209//=============================================================================================================
210
211InvSourceEstimate InvDICS::applyDICSCsd(const std::vector<MatrixXd> &csdMatrices,
212 const VectorXd &frequencies,
213 const InvBeamformer &filters)
214{
215 if(!filters.isValid() || filters.kind != "DICS") {
216 qWarning("InvDICS::applyDICSCsd - Invalid or non-DICS filters!");
217 return InvSourceEstimate();
218 }
219
220 const int nFreqs = static_cast<int>(csdMatrices.size());
221 const int nFilterFreqs = filters.nFreqs();
222
223 if(nFreqs != nFilterFreqs) {
224 qWarning("InvDICS::applyDICSCsd - CSD count (%d) does not match filter count (%d)!",
225 nFreqs, nFilterFreqs);
226 return InvSourceEstimate();
227 }
228
229 const int nOrient = filters.nOrient();
230 const int nSources = filters.nSources();
231 const int nChannels = filters.nChannels();
232
233 // Power matrix: (nSources, nFreqs)
234 MatrixXd powerMat(nSources, nFreqs);
235
236 for(int fi = 0; fi < nFreqs; ++fi) {
237 MatrixXd Cm = csdMatrices[fi];
238
239 // Whiten CSD
240 if(filters.whitener.size() > 0) {
241 Cm = filters.whitener * Cm * filters.whitener.transpose();
242 }
243
244 VectorXd power = InvBeamformerCompute::computePower(Cm, filters.weights[fi], nOrient);
245 powerMat.col(fi) = power;
246 }
247
248 // Use frequency as "time" axis for the source estimate
249 float fmin = (frequencies.size() > 0) ? static_cast<float>(frequencies(0)) : 0.0f;
250 float fstep = (frequencies.size() > 1)
251 ? static_cast<float>(frequencies(1) - frequencies(0))
252 : 1.0f;
253
254 InvSourceEstimate stc(powerMat, filters.vertices, fmin, fstep);
258
259 return stc;
260}
261
262//=============================================================================================================
263
265 float tmin,
266 float tstep,
267 const InvBeamformer &filters,
268 int freqIdx)
269{
270 if(!filters.isValid() || filters.kind != "DICS") {
271 qWarning("InvDICS::applyDICS - Invalid or non-DICS filters!");
272 return InvSourceEstimate();
273 }
274 if(freqIdx < 0 || freqIdx >= filters.nFreqs()) {
275 qWarning("InvDICS::applyDICS - freqIdx %d out of range (0..%d)!",
276 freqIdx, filters.nFreqs() - 1);
277 return InvSourceEstimate();
278 }
279
280 // Apply projection + whitening + spatial filter
281 MatrixXd processed = data;
282 if(filters.proj.size() > 0 && filters.proj.rows() == data.rows()) {
283 processed = filters.proj * processed;
284 }
285 if(filters.whitener.size() > 0 && filters.whitener.rows() == processed.rows()) {
286 processed = filters.whitener * processed;
287 }
288
289 MatrixXd sol = filters.weights[freqIdx] * processed;
290
291 // Combine XYZ if needed
292 const int nOrient = filters.nOrient();
293 if(nOrient == 3 && filters.pickOri != BeamformerPickOri::Vector) {
294 const int nSources = static_cast<int>(sol.rows()) / 3;
295 const int nTimes = static_cast<int>(sol.cols());
296 MatrixXd combined(nSources, nTimes);
297 for(int s = 0; s < nSources; ++s) {
298 combined.row(s) = sol.middleRows(s * 3, 3).colwise().norm();
299 }
300 sol = combined;
301 }
302
303 InvSourceEstimate stc(sol, filters.vertices, tmin, tstep);
307
308 return stc;
309}
Shared beamformer computation routines for LCMV and DICS.
InvDICS class declaration — Dynamic Imaging of Coherent Sources beamformer.
FiffInfo class declaration.
#define FIFFV_MNE_FREE_ORI
FiffCov class declaration.
MNEForwardSolution class declaration.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
covariance data
Definition fiff_cov.h:85
Eigen::MatrixXd eigvec
Definition fiff_cov.h:259
Eigen::VectorXd eig
Definition fiff_cov.h:258
Eigen::MatrixXd data
Definition fiff_cov.h:254
FIFF measurement file information.
Definition fiff_info.h:86
Computed beamformer spatial filter container.
BeamformerPickOri pickOri
Eigen::VectorXd frequencies
BeamformerWeightNorm weightNorm
Eigen::MatrixX3f sourceNn
BeamformerInversion inversion
Eigen::MatrixX3d maxPowerOri
Eigen::VectorXi vertices
std::vector< Eigen::MatrixXd > weights
Eigen::MatrixXd whitener
static Eigen::VectorXd computePower(const Eigen::MatrixXd &Cm, const Eigen::MatrixXd &W, int nOrient)
static bool computeBeamformer(const Eigen::MatrixXd &G, const Eigen::MatrixXd &Cm, double reg, int nOrient, BeamformerWeightNorm weightNorm, BeamformerPickOri pickOri, bool reduceRank, BeamformerInversion invMethod, const Eigen::MatrixX3d &nn, Eigen::MatrixXd &W, Eigen::MatrixX3d &maxPowerOri)
static InvBeamformer makeDICS(const FIFFLIB::FiffInfo &info, const MNELIB::MNEForwardSolution &forward, const std::vector< Eigen::MatrixXd > &csdMatrices, const Eigen::VectorXd &frequencies, double reg=0.05, bool realFilter=true, const FIFFLIB::FiffCov &noiseCov=FIFFLIB::FiffCov(), BeamformerPickOri pickOri=BeamformerPickOri::None, BeamformerWeightNorm weightNorm=BeamformerWeightNorm::UnitNoiseGain, bool reduceRank=false, BeamformerInversion invMethod=BeamformerInversion::Matrix)
Definition inv_dics.cpp:67
static InvSourceEstimate applyDICS(const Eigen::MatrixXd &data, float tmin, float tstep, const InvBeamformer &filters, int freqIdx=0)
Definition inv_dics.cpp:264
static InvSourceEstimate applyDICSCsd(const std::vector< Eigen::MatrixXd > &csdMatrices, const Eigen::VectorXd &frequencies, const InvBeamformer &filters)
Definition inv_dics.cpp:211
InvSourceSpaceType sourceSpaceType
InvOrientationType orientationType
MNELIB::MNESourceSpaces src
FIFFLIB::FiffNamedMatrix::SDPtr sol