v2.0.0
Loading...
Searching...
No Matches
mne_inverse_operator.cpp
Go to the documentation of this file.
1//=============================================================================================================
22
23//=============================================================================================================
24// INCLUDES
25//=============================================================================================================
26
28#include <fs/fs_label.h>
29#include <math/linalg.h>
30
31#include <iostream>
32#include <QDebug>
33
34//=============================================================================================================
35// QT INCLUDES
36//=============================================================================================================
37
38#include <QFuture>
39#include <QtConcurrent>
40
41//=============================================================================================================
42// EIGEN INCLUDES
43//=============================================================================================================
44
45#include <Eigen/SVD>
46
47//=============================================================================================================
48// USED NAMESPACES
49//=============================================================================================================
50
51using namespace UTILSLIB;
52using namespace MNELIB;
53using namespace MNELIB;
54using namespace FIFFLIB;
55using namespace FSLIB;
56using namespace Eigen;
57
58//=============================================================================================================
59// DEFINE MEMBER METHODS
60//=============================================================================================================
61
63: methods(-1)
64, source_ori(-1)
65, nsource(-1)
66, nchan(-1)
67, coord_frame(-1)
68, units(-1)
72, noise_cov(new FiffCov)
77, nave(-1)
78{
79 qRegisterMetaType<QSharedPointer<MNELIB::MNEInverseOperator>>("QSharedPointer<MNELIB::MNEInverseOperator>");
80 qRegisterMetaType<MNELIB::MNEInverseOperator>("MNELIB::MNEInverseOperator");
81}
82
83//=============================================================================================================
84
86{
88 qRegisterMetaType<QSharedPointer<MNELIB::MNEInverseOperator>>("QSharedPointer<MNELIB::MNEInverseOperator>");
89 qRegisterMetaType<MNELIB::MNEInverseOperator>("MNELIB::MNEInverseOperator");
90}
91
92//=============================================================================================================
93
95 const MNEForwardSolution& forward,
96 const FiffCov& p_noise_cov,
97 float loose,
98 float depth,
99 bool fixed,
100 bool limit_depth_chs)
101{
102 *this = MNEInverseOperator::make_inverse_operator(info, forward, p_noise_cov, loose, depth, fixed, limit_depth_chs);
103 qRegisterMetaType<QSharedPointer<MNELIB::MNEInverseOperator>>("QSharedPointer<MNELIB::MNEInverseOperator>");
104 qRegisterMetaType<MNELIB::MNEInverseOperator>("MNELIB::MNEInverseOperator");
105}
106
107//=============================================================================================================
108
110: info(other.info)
111, methods(other.methods)
112, source_ori(other.source_ori)
113, nsource(other.nsource)
114, nchan(other.nchan)
116, units(other.units)
117, source_nn(other.source_nn)
118, sing(other.sing)
122, noise_cov(other.noise_cov)
123, source_cov(other.source_cov)
126, fmri_prior(other.fmri_prior)
127, src(other.src)
128, mri_head_t(other.mri_head_t)
129, nave(other.nave)
130, projs(other.projs)
131, proj(other.proj)
132, whitener(other.whitener)
133, reginv(other.reginv)
134, noisenorm(other.noisenorm)
135{
136 qRegisterMetaType<QSharedPointer<MNELIB::MNEInverseOperator>>("QSharedPointer<MNELIB::MNEInverseOperator>");
137 qRegisterMetaType<MNELIB::MNEInverseOperator>("MNELIB::MNEInverseOperator");
138}
139
140//=============================================================================================================
141
143
144//=============================================================================================================
145
147 const QString& method,
148 bool pick_normal,
149 MatrixXd& K,
150 SparseMatrix<double>& noise_norm,
151 QList<VectorXi>& vertno)
152{
153 MatrixXd t_eigen_leads = eigen_leads->data;
154 MatrixXd t_source_cov = source_cov->data;
155 if (method.compare(QLatin1String("MNE")) != 0)
156 noise_norm = noisenorm;
157
158 vertno = src.get_vertno();
159
160 typedef Eigen::Triplet<double> T;
161 std::vector<T> tripletList;
162
163 if (!label.isEmpty()) {
164 VectorXi src_sel;
165 vertno = src.label_src_vertno_sel(label, src_sel);
166
167 if (method.compare(QLatin1String("MNE")) != 0 && noise_norm.rows() > 0) {
168 // noisenorm is diagonal, one entry per source.
169 tripletList.clear();
170 tripletList.reserve(src_sel.size());
171 for (qint32 i = 0; i < src_sel.size(); ++i)
172 tripletList.push_back(T(i, i, noise_norm.coeff(src_sel[i], src_sel[i])));
173
174 noise_norm = SparseMatrix<double>(src_sel.size(), src_sel.size());
175 noise_norm.setFromTriplets(tripletList.begin(), tripletList.end());
176 }
177
179 VectorXi src_sel_new(src_sel.size() * 3);
180
181 for (qint32 i = 0; i < src_sel.size(); ++i) {
182 src_sel_new[i * 3] = src_sel[i] * 3;
183 src_sel_new[i * 3 + 1] = src_sel[i] * 3 + 1;
184 src_sel_new[i * 3 + 2] = src_sel[i] * 3 + 2;
185 }
186 src_sel = src_sel_new;
187 }
188
189 // src_sel is ascending with src_sel[i] >= i, so in-place compaction is safe.
190 for (qint32 i = 0; i < src_sel.size(); ++i) {
191 t_eigen_leads.row(i) = t_eigen_leads.row(src_sel[i]);
192 t_source_cov.row(i) = t_source_cov.row(src_sel[i]);
193 }
194 t_eigen_leads.conservativeResize(src_sel.size(), t_eigen_leads.cols());
195 t_source_cov.conservativeResize(src_sel.size(), t_source_cov.cols());
196 }
197
198 if (pick_normal) {
200 qWarning("Warning: Pick normal can only be used with a free orientation inverse operator.\n");
201 return false;
202 }
203
204 bool is_loose = (0 < orient_prior->data(0, 0)) && (orient_prior->data(0, 0) < 1);
205 if (!is_loose) {
206 qWarning("The pick_normal parameter is only valid when working with loose orientations.\n");
207 return false;
208 }
209
210 // keep only the normal components
211 qint32 count = 0;
212 for (qint32 i = 2; i < t_eigen_leads.rows(); i += 3) {
213 t_eigen_leads.row(count) = t_eigen_leads.row(i);
214 ++count;
215 }
216 t_eigen_leads.conservativeResize(count, t_eigen_leads.cols());
217
218 count = 0;
219 for (qint32 i = 2; i < t_source_cov.rows(); i += 3) {
220 t_source_cov.row(count) = t_source_cov.row(i);
221 ++count;
222 }
223 t_source_cov.conservativeResize(count, t_source_cov.cols());
224 }
225
226 tripletList.clear();
227 tripletList.reserve(reginv.rows());
228 for (qint32 i = 0; i < reginv.rows(); ++i)
229 tripletList.push_back(T(i, i, reginv(i, 0)));
230 SparseMatrix<double> t_reginv(reginv.rows(), reginv.rows());
231 t_reginv.setFromTriplets(tripletList.begin(), tripletList.end());
232
233 MatrixXd trans = t_reginv * eigen_fields->data * whitener * proj;
234 //
235 // Transformation into current distributions by weighting the eigenleads
236 // with the weights computed above
237 //
239 //
240 // R^0.5 has been already factored in
241 //
242 qInfo("(eigenleads already weighted)...\n");
243 K = t_eigen_leads * trans;
244 } else {
245 //
246 // R^0.5 has to factored in
247 //
248 qInfo("(eigenleads need to be weighted)...\n");
249
250 std::vector<T> tripletList2;
251 tripletList2.reserve(t_source_cov.rows());
252 for (qint32 i = 0; i < t_source_cov.rows(); ++i)
253 tripletList2.push_back(T(i, i, sqrt(t_source_cov(i, 0))));
254 SparseMatrix<double> t_sourceCov(t_source_cov.rows(), t_source_cov.rows());
255 t_sourceCov.setFromTriplets(tripletList2.begin(), tripletList2.end());
256
257 K = t_sourceCov * t_eigen_leads * trans;
258 }
259
260 if (method.compare(QLatin1String("MNE")) == 0)
261 noise_norm = SparseMatrix<double>();
262
263 //store assembled kernel
264 m_K = K;
265
266 return true;
267}
268
269//=============================================================================================================
270
272{
273 QStringList inv_ch_names = this->eigen_fields->col_names;
274
275 bool t_bContains = true;
276 if (this->eigen_fields->col_names.size() != this->noise_cov->names.size())
277 t_bContains = false;
278 else {
279 for (qint32 i = 0; i < this->noise_cov->names.size(); ++i) {
280 if (inv_ch_names[i] != this->noise_cov->names[i]) {
281 t_bContains = false;
282 break;
283 }
284 }
285 }
286
287 if (!t_bContains) {
288 qCritical("Channels in inverse operator eigen fields do not match noise covariance channels.");
289 return false;
290 }
291
292 QStringList data_ch_names = measInfo.ch_names;
293
294 QStringList missing_ch_names;
295 for (qint32 i = 0; i < inv_ch_names.size(); ++i)
296 if (!data_ch_names.contains(inv_ch_names[i]))
297 missing_ch_names.append(inv_ch_names[i]);
298
299 qint32 n_missing = missing_ch_names.size();
300
301 if (n_missing > 0) {
302 qCritical() << n_missing << "channels in inverse operator are not present in the data (" << missing_ch_names << ")";
303 return false;
304 }
305
306 return true;
307}
308
309//=============================================================================================================
310
311MatrixXd MNEInverseOperator::cluster_kernel(const FsAnnotationSet& p_AnnotationSet, qint32 p_iClusterSize, MatrixXd& p_D, const QString& p_sMethod) const
312{
313 qInfo("Cluster kernel using %s.\n", p_sMethod.toUtf8().constData());
314
315 MatrixXd p_outMT = m_K.transpose();
316
317 QList<MNEClusterInfo> t_qListMNEClusterInfo;
318 MNEClusterInfo t_MNEClusterInfo;
319 t_qListMNEClusterInfo.append(t_MNEClusterInfo);
320 t_qListMNEClusterInfo.append(t_MNEClusterInfo);
321
322 //
323 // Check consistency
324 //
325 if (isFixedOrient()) {
326 qCritical("Error: Fixed orientation not implemented yet!\n");
327 return p_outMT;
328 }
329
330 //
331 // Assemble input data
332 //
333 qint32 offset;
334
335 MatrixXd t_MT_new;
336
337 for (qint32 h = 0; h < this->src.size(); ++h) {
338 offset = 0;
339
340 if (h > 0)
341 for (qint32 j = 0; j < h; ++j)
342 offset += this->src[j].nuse;
343
344 if (h == 0)
345 qInfo("Cluster Left Hemisphere\n");
346 else
347 qInfo("Cluster Right Hemisphere\n");
348
349 FsColortable t_CurrentColorTable = p_AnnotationSet[h].getColortable();
350 VectorXi label_ids = t_CurrentColorTable.getLabelIds();
351
352 // Get label ids for every vertex
353 VectorXi vertno_labeled = VectorXi::Zero(this->src[h].vertno.rows());
354
355 for (qint32 i = 0; i < vertno_labeled.rows(); ++i)
356 vertno_labeled[i] = p_AnnotationSet[h].getLabelIds()[this->src[h].vertno[i]];
357
358 //Qt Concurrent List
359 QList<RegionMT> m_qListRegionMTIn;
360
361 //
362 // Generate cluster input data
363 //
364 for (qint32 i = 0; i < label_ids.rows(); ++i) {
365 if (label_ids[i] != 0) {
366 QString curr_name = t_CurrentColorTable.struct_names[i]; //obj.label2AtlasName(label(i));
367 qInfo("\tCluster %d / %ld %s...", i + 1, label_ids.rows(), curr_name.toUtf8().constData());
368
369 //
370 // Get source space indeces
371 //
372 VectorXi idcs = VectorXi::Zero(vertno_labeled.rows());
373 qint32 c = 0;
374
375 //Select ROIs //change this use label info with a hash tabel
376 for (qint32 j = 0; j < vertno_labeled.rows(); ++j) {
377 if (vertno_labeled[j] == label_ids[i]) {
378 idcs[c] = j;
379 ++c;
380 }
381 }
382 idcs.conservativeResize(c);
383
384 //get selected MT
385 MatrixXd t_MT(p_outMT.rows(), idcs.rows() * 3);
386
387 for (qint32 j = 0; j < idcs.rows(); ++j)
388 t_MT.block(0, j * 3, t_MT.rows(), 3) = p_outMT.block(0, (idcs[j] + offset) * 3, t_MT.rows(), 3);
389
390 qint32 nSens = t_MT.rows();
391 qint32 nSources = t_MT.cols() / 3;
392
393 if (nSources > 0) {
394 RegionMT t_sensMT;
395
396 t_sensMT.idcs = idcs;
397 t_sensMT.iLabelIdxIn = i;
398 t_sensMT.nClusters = ceil(static_cast<double>(nSources) / static_cast<double>(p_iClusterSize));
399
400 t_sensMT.matRoiMTOrig = t_MT;
401
402 qInfo("%d Cluster(s)... ", t_sensMT.nClusters);
403
404 // Reshape Input data -> sources rows; sensors columns
405 t_sensMT.matRoiMT = MatrixXd(t_MT.cols() / 3, 3 * nSens);
406
407 for (qint32 j = 0; j < nSens; ++j)
408 for (qint32 k = 0; k < t_sensMT.matRoiMT.rows(); ++k)
409 t_sensMT.matRoiMT.block(k, j * 3, 1, 3) = t_MT.block(j, k * 3, 1, 3);
410
411 t_sensMT.sDistMeasure = p_sMethod;
412
413 m_qListRegionMTIn.append(t_sensMT);
414
415 qInfo("[added]\n");
416 } else {
417 qInfo("failed! FsLabel contains no sources.\n");
418 }
419 }
420 }
421
422 //
423 // Calculate clusters
424 //
425 qInfo("Clustering... ");
426 QFuture<RegionMTOut> res;
427 res = QtConcurrent::mapped(m_qListRegionMTIn, &RegionMT::cluster);
428 res.waitForFinished();
429
430 //
431 // Assign results
432 //
433 MatrixXd t_MT_partial;
434
435 qint32 nClusters;
436 qint32 nSens;
437 QList<RegionMT>::const_iterator itIn;
438 itIn = m_qListRegionMTIn.begin();
439 QFuture<RegionMTOut>::const_iterator itOut;
440 for (itOut = res.constBegin(); itOut != res.constEnd(); ++itOut) {
441 nClusters = itOut->ctrs.rows();
442 nSens = itOut->ctrs.cols() / 3;
443 t_MT_partial = MatrixXd::Zero(nSens, nClusters * 3);
444
445 //
446 // Assign the centroid for each cluster to the partial G
447 //
448 for (qint32 j = 0; j < nSens; ++j)
449 for (qint32 k = 0; k < nClusters; ++k)
450 t_MT_partial.block(j, k * 3, 1, 3) = itOut->ctrs.block(k, j * 3, 1, 3);
451
452 //
453 // Get cluster indices and their distances to the centroid
454 //
455 for (qint32 j = 0; j < nClusters; ++j) {
456 VectorXi clusterIdcs = VectorXi::Zero(itOut->roiIdx.rows());
457 VectorXd clusterDistance = VectorXd::Zero(itOut->roiIdx.rows());
458 qint32 nClusterIdcs = 0;
459 for (qint32 k = 0; k < itOut->roiIdx.rows(); ++k) {
460 if (itOut->roiIdx[k] == j) {
461 clusterIdcs[nClusterIdcs] = itIn->idcs[k];
462 clusterDistance[nClusterIdcs] = itOut->D(k, j);
463 ++nClusterIdcs;
464 }
465 }
466 clusterIdcs.conservativeResize(nClusterIdcs);
467 clusterDistance.conservativeResize(nClusterIdcs);
468
469 VectorXi clusterVertnos = VectorXi::Zero(clusterIdcs.size());
470 for (qint32 k = 0; k < clusterVertnos.size(); ++k)
471 clusterVertnos(k) = this->src[h].vertno[clusterIdcs(k)];
472
473 t_qListMNEClusterInfo[h].clusterVertnos.append(clusterVertnos);
474 }
475
476 //
477 // Assign partial G to new LeadField
478 //
479 if (t_MT_partial.rows() > 0 && t_MT_partial.cols() > 0) {
480 t_MT_new.conservativeResize(t_MT_partial.rows(), t_MT_new.cols() + t_MT_partial.cols());
481 t_MT_new.block(0, t_MT_new.cols() - t_MT_partial.cols(), t_MT_new.rows(), t_MT_partial.cols()) = t_MT_partial;
482
483 // Map the centroids to the closest rr
484 for (qint32 k = 0; k < nClusters; ++k) {
485 double sqec = sqrt((itIn->matRoiMTOrig.block(0, 0, itIn->matRoiMTOrig.rows(), 3) - t_MT_partial.block(0, k * 3, t_MT_partial.rows(), 3)).array().pow(2).sum());
486 double sqec_min = sqec;
487 qint32 j_min = 0;
488 for (qint32 j = 1; j < itIn->idcs.rows(); ++j) {
489 sqec = sqrt((itIn->matRoiMTOrig.block(0, j * 3, itIn->matRoiMTOrig.rows(), 3) - t_MT_partial.block(0, k * 3, t_MT_partial.rows(), 3)).array().pow(2).sum());
490
491 if (sqec < sqec_min) {
492 sqec_min = sqec;
493 j_min = j;
494 }
495 }
496 (void)j_min;
497 }
498 }
499
500 ++itIn;
501 }
502
503 qInfo("[done]\n");
504 }
505
506 //
507 // Cluster operator D (sources x clusters)
508 //
509 qint32 totalNumOfClust = 0;
510 for (qint32 h = 0; h < 2; ++h)
511 totalNumOfClust += t_qListMNEClusterInfo[h].clusterVertnos.size();
512
513 if (isFixedOrient())
514 p_D = MatrixXd::Zero(p_outMT.cols(), totalNumOfClust);
515 else
516 p_D = MatrixXd::Zero(p_outMT.cols(), totalNumOfClust * 3);
517
518 QList<VectorXi> t_vertnos = src.get_vertno();
519
520 qint32 currentCluster = 0;
521 for (qint32 h = 0; h < 2; ++h) {
522 int hemiOffset = h == 0 ? 0 : t_vertnos[0].size();
523 for (qint32 i = 0; i < t_qListMNEClusterInfo[h].clusterVertnos.size(); ++i) {
524 VectorXi idx_sel;
525 Linalg::intersect(t_vertnos[h], t_qListMNEClusterInfo[h].clusterVertnos[i], idx_sel);
526
527 idx_sel.array() += hemiOffset;
528
529 double selectWeight = 1.0 / idx_sel.size();
530 if (isFixedOrient()) {
531 for (qint32 j = 0; j < idx_sel.size(); ++j)
532 p_D.col(currentCluster)[idx_sel(j)] = selectWeight;
533 } else {
534 qint32 clustOffset = currentCluster * 3;
535 for (qint32 j = 0; j < idx_sel.size(); ++j) {
536 qint32 idx_sel_Offset = idx_sel(j) * 3;
537 //x
538 p_D(idx_sel_Offset, clustOffset) = selectWeight;
539 //y
540 p_D(idx_sel_Offset + 1, clustOffset + 1) = selectWeight;
541 //z
542 p_D(idx_sel_Offset + 2, clustOffset + 2) = selectWeight;
543 }
544 }
545 ++currentCluster;
546 }
547 }
548
549 //
550 // Put it all together
551 //
552 p_outMT = t_MT_new;
553
554 return p_outMT;
555}
556
557//=============================================================================================================
558
560 MNEForwardSolution forward,
561 const FiffCov& p_noise_cov,
562 float loose,
563 float depth,
564 bool fixed,
565 bool limit_depth_chs)
566{
567 bool is_fixed_ori = forward.isFixedOrient();
569
570 // Loose and fixed constraints are relative to the cortical normal, as in mne-python's _prepare_forward.
571 if (!is_fixed_ori && (fixed || loose < 1.0f)) {
572 forward.convert_to_surf_ori();
573 }
574
575 //Check parameters
576 if (fixed && loose > 0) {
577 qWarning("Warning: When invoking make_inverse_operator with fixed = true, the loose parameter is ignored.\n");
578 loose = 0.0f;
579 }
580
581 if (is_fixed_ori && !fixed) {
582 qWarning("Warning: Setting fixed parameter = true. Because the given forward operator has fixed orientation and can only be used to make a fixed-orientation inverse operator.\n");
583 fixed = true;
584 }
585
586 if (forward.source_ori == -1 && loose > 0) {
587 qCritical("Forward solution is not oriented in surface coordinates. loose parameter should be 0 not %f.", loose);
588 return inv;
589 }
590
591 if (loose < 0 || loose > 1) {
592 qWarning("Warning: Loose value should be in interval [0,1] not %f.\n", loose);
593 loose = loose > 1 ? 1 : 0;
594 qInfo("Setting loose to %f.\n", loose);
595 }
596
597 if (depth < 0 || depth > 1) {
598 qWarning("Warning: Depth value should be in interval [0,1] not %f.\n", depth);
599 depth = depth > 1 ? 1 : 0;
600 qInfo("Setting depth to %f.\n", depth);
601 }
602
603 //
604 // 1. Read the bad channels
605 // 2. Read the necessary data from the forward solution matrix file
606 // 3. Load the projection data
607 // 4. Load the sensor noise covariance matrix and attach it to the forward
608 //
609 FiffInfo gain_info;
610 MatrixXd gain;
611 MatrixXd whitener;
612 qint32 n_nzero;
613 FiffCov p_outNoiseCov;
614 forward.prepare_forward(info, p_noise_cov, false, gain_info, gain, p_outNoiseCov, whitener, n_nzero);
615
616 //
617 // 5. Compose the depth weight matrix
618 //
619 FiffCov::SDPtr p_depth_prior;
620 MatrixXd patch_areas;
621 if (depth > 0) {
622 // TODO: load patch_areas from forward solution
623 p_depth_prior = FiffCov::SDPtr(new FiffCov(MNEForwardSolution::compute_depth_prior(gain, gain_info, is_fixed_ori, depth, 10.0, patch_areas, limit_depth_chs)));
624 } else {
625 p_depth_prior = FiffCov::SDPtr(new FiffCov());
626 p_depth_prior->data = MatrixXd::Ones(gain.cols(), 1);
627 p_depth_prior->kind = FIFFV_MNE_DEPTH_PRIOR_COV;
628 p_depth_prior->diag = true;
629 p_depth_prior->dim = gain.cols();
630 p_depth_prior->nfree = 1;
631 }
632
633 // Deal with fixed orientation forward / inverse
634 if (fixed) {
635 if (depth < 0 || depth > 1) {
636 // TODO: convert free-orientation depth prior to fixed-orientation
637 qInfo("\tPicking elements from free-orientation depth prior into fixed-orientation one.\n");
638 }
639 if (!is_fixed_ori) {
640 if (!forward.surf_ori) {
641 qWarning("Warning: For a fixed-orientation inverse, the forward solution must be surface-oriented. Skipping fixed conversion.\n");
642 } else {
643 // Convert to the fixed orientation forward solution now
644 qint32 count = 0;
645 for (qint32 i = 2; i < p_depth_prior->data.rows(); i += 3) {
646 p_depth_prior->data.row(count) = p_depth_prior->data.row(i);
647 gain.col(count) = gain.col(i);
648 ++count;
649 }
650 p_depth_prior->data.conservativeResize(count, 1);
651 p_depth_prior->dim = count;
652 gain.conservativeResize(Eigen::NoChange, count);
653
654 forward.to_fixed_ori();
655 is_fixed_ori = forward.isFixedOrient();
656 }
657 }
658 }
659 qInfo("\tComputing inverse operator with %lld channels.\n", static_cast<long long>(gain_info.ch_names.size()));
660
661 //
662 // 6. Compose the source covariance matrix
663 //
664 qInfo("\tCreating the source covariance matrix\n");
665 FiffCov::SDPtr p_source_cov = p_depth_prior;
666
667 // apply loose orientations
668 FiffCov::SDPtr p_orient_prior;
669 if (!is_fixed_ori) {
670 p_orient_prior = FiffCov::SDPtr(new FiffCov(forward.compute_orient_prior(loose)));
671 p_source_cov->data.array() *= p_orient_prior->data.array();
672 }
673
674 // 7. Apply fMRI weighting (not done)
675
676 //
677 // 8. Apply the linear projection to the forward solution
678 // 9. Apply whitening to the forward computation matrix
679 //
680 qInfo("\tWhitening the forward solution.\n");
681 gain = whitener * gain;
682
683 // 10. Exclude the source space points within the labels (not done)
684
685 //
686 // 11. Do appropriate source weighting to the forward computation matrix
687 //
688
689 // Adjusting Source Covariance matrix to make trace of G*R*G' equal
690 // to number of sensors.
691 qInfo("\tAdjusting source covariance matrix.\n");
692 RowVectorXd source_std = p_source_cov->data.array().sqrt().transpose();
693
694 for (qint32 i = 0; i < gain.rows(); ++i)
695 gain.row(i) = gain.row(i).array() * source_std.array();
696
697 double trace_GRGT = (gain * gain.transpose()).trace();
698 double scaling_source_cov = static_cast<double>(n_nzero) / trace_GRGT;
699
700 p_source_cov->data.array() *= scaling_source_cov;
701
702 gain.array() *= sqrt(scaling_source_cov);
703
704 //
705 // 12. Decompose the combined matrix
706 //
707 qInfo("Computing SVD of whitened and weighted lead field matrix.\n");
708 JacobiSVD<MatrixXd> svd(gain, ComputeThinU | ComputeThinV);
709 // TODO: verify whether explicit sorting is necessary
710 VectorXd p_sing = svd.singularValues();
711 MatrixXd t_U = svd.matrixU();
712 Linalg::sort<double>(p_sing, t_U);
713 FiffNamedMatrix::SDPtr p_eigen_fields = FiffNamedMatrix::SDPtr(new FiffNamedMatrix(svd.matrixU().cols(),
714 svd.matrixU().rows(),
715 defaultQStringList,
716 gain_info.ch_names,
717 t_U.transpose()));
718
719 p_sing = svd.singularValues();
720 MatrixXd t_V = svd.matrixV();
721 Linalg::sort<double>(p_sing, t_V);
722 FiffNamedMatrix::SDPtr p_eigen_leads = FiffNamedMatrix::SDPtr(new FiffNamedMatrix(svd.matrixV().rows(),
723 svd.matrixV().cols(),
724 defaultQStringList,
725 defaultQStringList,
726 t_V));
727 qInfo("\tlargest singular value = %f\n", p_sing.maxCoeff());
728 qInfo("\tscaling factor to adjust the trace = %f\n", trace_GRGT);
729
730 qint32 p_nave = 1.0;
731
732 // Handle methods
733 bool has_meg = false;
734 bool has_eeg = false;
735
736 RowVectorXd ch_idx(info.chs.size());
737 qint32 count = 0;
738 for (qint32 i = 0; i < info.chs.size(); ++i) {
739 if (gain_info.ch_names.contains(info.chs[i].ch_name)) {
740 ch_idx[count] = i;
741 ++count;
742 }
743 }
744 ch_idx.conservativeResize(count);
745
746 for (qint32 i = 0; i < ch_idx.size(); ++i) {
747 QString ch_type = info.channel_type(ch_idx[i]);
748 if (ch_type == "eeg")
749 has_eeg = true;
750 if ((ch_type == "mag") || (ch_type == "grad"))
751 has_meg = true;
752 }
753
754 qint32 p_iMethods;
755
756 if (has_eeg && has_meg)
757 p_iMethods = FIFFV_MNE_MEG_EEG;
758 else if (has_meg)
759 p_iMethods = FIFFV_MNE_MEG;
760 else
761 p_iMethods = FIFFV_MNE_EEG;
762
763 // We set this for consistency with mne C code written inverses
764 if (depth == 0)
765 p_depth_prior = FiffCov::SDPtr();
766
767 inv.eigen_fields = p_eigen_fields;
768 inv.eigen_leads = p_eigen_leads;
769 inv.sing = p_sing;
770 inv.nchan = p_sing.rows();
771 inv.nave = p_nave;
772 inv.depth_prior = p_depth_prior;
773 // Fix the kind so I/O round-trips write FIFFV_MNE_SOURCE_COV.
774 p_source_cov->kind = FIFFV_MNE_SOURCE_COV;
775 inv.source_cov = p_source_cov;
776 inv.noise_cov = FiffCov::SDPtr(new FiffCov(p_outNoiseCov));
777 inv.orient_prior = p_orient_prior;
778 inv.projs = info.projs;
779 inv.eigen_leads_weighted = false;
780 inv.source_ori = forward.source_ori;
781 inv.mri_head_t = forward.mri_head_t;
782 inv.methods = p_iMethods;
783 inv.nsource = forward.nsource;
784 inv.coord_frame = forward.coord_frame;
785 inv.source_nn = forward.source_nn;
786 inv.src = forward.src;
787 inv.info = forward.info;
788 inv.info.bads = info.bads;
789
790 return inv;
791}
792
793//=============================================================================================================
794
795MNEInverseOperator MNEInverseOperator::prepare_inverse_operator(qint32 nAve, float lambda2, bool dSPM, bool sLORETA) const
796{
797 if (nAve <= 0) {
798 qCritical("The number of averages should be positive\n");
799 return MNEInverseOperator();
800 }
801 qInfo("Preparing the inverse operator for use...\n");
802 MNEInverseOperator inv(*this);
803 //
804 // Scale some of the stuff
805 //
806 float scale = static_cast<float>(inv.nave) / static_cast<float>(nAve);
807 inv.noise_cov->data *= scale;
808 inv.noise_cov->eig *= scale;
809 inv.source_cov->data *= scale;
810 //
811 if (inv.eigen_leads_weighted)
812 inv.eigen_leads->data *= sqrt(scale);
813 //
814 qInfo("\tScaled noise and source covariance from nave = %d to nave = %d\n", inv.nave, nAve);
815 inv.nave = nAve;
816 //
817 // Create the diagonal matrix for computing the regularized inverse
818 //
819 VectorXd tmp = inv.sing.cwiseProduct(inv.sing) + VectorXd::Constant(inv.sing.size(), lambda2);
820 inv.reginv = VectorXd(inv.sing.cwiseQuotient(tmp));
821 qInfo("\tCreated the regularized inverter\n");
822 //
823 // Create the projection operator
824 //
825
826 qint32 ncomp = FiffProj::make_projector(inv.projs, inv.noise_cov->names, inv.proj);
827 if (ncomp > 0)
828 qInfo("\tCreated an SSP operator (subspace dimension = %d)\n", ncomp);
829
830 //
831 // Create the whitener
832 //
833 inv.whitener = MatrixXd::Zero(inv.noise_cov->dim, inv.noise_cov->dim);
834
835 qint32 nnzero, k;
836 if (inv.noise_cov->diag == 0) {
837 //
838 // Omit the zeroes due to projection. The eigenvalues are sorted per channel type, not
839 // globally, so the zeroes are not necessarily the first ncomp (mne-python compute_whitener).
840 //
841 nnzero = 0;
842
843 for (k = 0; k < inv.noise_cov->dim; ++k) {
844 if (inv.noise_cov->eig[k] > 0) {
845 inv.whitener(k, k) = 1.0 / sqrt(inv.noise_cov->eig[k]);
846 ++nnzero;
847 }
848 }
849 //
850 // Rows of eigvec are the eigenvectors
851 //
852 inv.whitener *= inv.noise_cov->eigvec;
853 qInfo("\tCreated the whitener using a full noise covariance matrix (%d small eigenvalues omitted)\n", inv.noise_cov->dim - nnzero);
854 } else {
855 //
856 // No need to omit the zeroes due to projection
857 //
858 for (k = 0; k < inv.noise_cov->dim; ++k)
859 inv.whitener(k, k) = 1.0 / sqrt(inv.noise_cov->data(k, 0));
860
861 qInfo("\tCreated the whitener using a diagonal noise covariance matrix (%d small eigenvalues discarded)\n", ncomp);
862 }
863 //
864 // Finally, compute the noise-normalization factors
865 //
866 if (dSPM || sLORETA) {
867 VectorXd noise_norm = VectorXd::Zero(inv.eigen_leads->nrow);
868 VectorXd noise_weight;
869 if (dSPM) {
870 qInfo("\tComputing noise-normalization factors (dSPM)...");
871 noise_weight = VectorXd(inv.reginv);
872 } else {
873 qInfo("\tComputing noise-normalization factors (sLORETA)...");
874 VectorXd sLoretaScale = (VectorXd::Constant(inv.sing.size(), 1) + inv.sing.cwiseProduct(inv.sing) / lambda2);
875 noise_weight = inv.reginv.cwiseProduct(sLoretaScale.cwiseSqrt());
876 }
877 VectorXd one;
878 if (inv.eigen_leads_weighted) {
879 for (k = 0; k < inv.eigen_leads->nrow; ++k) {
880 one = inv.eigen_leads->data.block(k, 0, 1, inv.eigen_leads->data.cols()).cwiseProduct(noise_weight);
881 noise_norm[k] = sqrt(one.dot(one));
882 }
883 } else {
884 double c;
885 for (k = 0; k < inv.eigen_leads->nrow; ++k) {
886 c = sqrt(inv.source_cov->data(k, 0));
887 one = c * (inv.eigen_leads->data.row(k).transpose()).cwiseProduct(noise_weight);
888 noise_norm[k] = sqrt(one.dot(one));
889 }
890 }
891
892 //
893 // Compute the final result
894 //
895 VectorXd noise_norm_new = noise_norm;
896 if (inv.source_ori == FIFFV_MNE_FREE_ORI) {
897 // For free orientations the variances at three consecutive entries
898 // must be squared and summed, yielding one factor per source location.
899 VectorXd t = Linalg::combine_xyz(noise_norm.transpose());
900 noise_norm_new = t.cwiseSqrt();
901 }
902 VectorXd vOnes = VectorXd::Ones(noise_norm_new.size());
903 VectorXd noiseNormInv = vOnes.cwiseQuotient(noise_norm_new.cwiseAbs());
904
905 typedef Eigen::Triplet<double> T;
906 std::vector<T> tripletList;
907 tripletList.reserve(noise_norm_new.size());
908 for (qint32 i = 0; i < noise_norm_new.size(); ++i)
909 tripletList.push_back(T(i, i, noiseNormInv[i]));
910
911 inv.noisenorm = SparseMatrix<double>(noise_norm_new.size(), noise_norm_new.size());
912 inv.noisenorm.setFromTriplets(tripletList.begin(), tripletList.end());
913
914 qInfo("[done]\n");
915 } else {
916 inv.noisenorm = SparseMatrix<double>();
917 }
918
919 return inv;
920}
921
922//=============================================================================================================
923
925{
926 //
927 // Open the file, create directory
928 //
929 FiffStream::SPtr t_pStream(new FiffStream(&p_IODevice));
930 qInfo("Reading inverse operator decomposition from %s...\n", t_pStream->streamName().toUtf8().constData());
931
932 if (!t_pStream->open())
933 return false;
934 //
935 // Find all inverse operators
936 //
937 QList<FiffDirNode::SPtr> invs_list = t_pStream->dirtree()->dir_tree_find(FIFFB_MNE_INVERSE_SOLUTION);
938 if (invs_list.size() == 0) {
939 qCritical("No inverse solutions in %s\n", t_pStream->streamName().toUtf8().constData());
940 return false;
941 }
942 FiffDirNode::SPtr invs = invs_list[0];
943 //
944 // Parent MRI data
945 //
946 QList<FiffDirNode::SPtr> parent_mri = t_pStream->dirtree()->dir_tree_find(FIFFB_MNE_PARENT_MRI_FILE);
947 if (parent_mri.size() == 0) {
948 qCritical("No parent MRI information in %s", t_pStream->streamName().toUtf8().constData());
949 return false;
950 }
951 qInfo("\tReading inverse operator info...");
952 //
953 // Methods and source orientations
954 //
955 FiffTag::UPtr t_pTag;
956 if (!invs->find_tag(t_pStream, FIFF_MNE_INCLUDED_METHODS, t_pTag)) {
957 qCritical("Modalities not found\n");
958 return false;
959 }
960
961 inv = MNEInverseOperator();
962 inv.methods = *t_pTag->toInt();
963 //
964 if (!invs->find_tag(t_pStream, FIFF_MNE_SOURCE_ORIENTATION, t_pTag)) {
965 qCritical("Source orientation constraints not found\n");
966 return false;
967 }
968 inv.source_ori = *t_pTag->toInt();
969 //
970 if (!invs->find_tag(t_pStream, FIFF_MNE_SOURCE_SPACE_NPOINTS, t_pTag)) {
971 qCritical("Number of sources not found\n");
972 return false;
973 }
974 inv.nsource = *t_pTag->toInt();
975 inv.nchan = 0;
976 //
977 // Coordinate frame
978 //
979 if (!invs->find_tag(t_pStream, FIFF_MNE_COORD_FRAME, t_pTag)) {
980 qCritical("Coordinate frame tag not found\n");
981 return false;
982 }
983 inv.coord_frame = *t_pTag->toInt();
984 //
985 // Units of the source estimates. Optional: files written before this was
986 // recorded simply do not carry it, so a missing tag is not an error.
987 //
988 if (invs->find_tag(t_pStream, FIFF_MNE_INVERSE_SOURCE_UNIT, t_pTag))
989 inv.units = *t_pTag->toInt();
990 else
991 inv.units = -1;
992 //
993 // The actual source orientation vectors
994 //
995 if (!invs->find_tag(t_pStream, FIFF_MNE_INVERSE_SOURCE_ORIENTATIONS, t_pTag)) {
996 qCritical("Source orientation information not found\n");
997 return false;
998 }
999
1000 inv.source_nn = t_pTag->toFloatMatrix();
1001 inv.source_nn.transposeInPlace();
1002
1003 qInfo("[done]\n");
1004 //
1005 // The SVD decomposition...
1006 //
1007 qInfo("\tReading inverse operator decomposition...");
1008 if (!invs->find_tag(t_pStream, FIFF_MNE_INVERSE_SING, t_pTag)) {
1009 qCritical("Singular values not found\n");
1010 return false;
1011 }
1012
1013 inv.sing = Map<const VectorXf>(t_pTag->toFloat(), t_pTag->size() / 4).cast<double>();
1014 inv.nchan = inv.sing.rows();
1015 //
1016 // The eigenleads and eigenfields
1017 //
1018 inv.eigen_leads_weighted = false;
1019 if (!t_pStream->read_named_matrix(invs, FIFF_MNE_INVERSE_LEADS, *inv.eigen_leads.data())) {
1020 inv.eigen_leads_weighted = true;
1021 if (!t_pStream->read_named_matrix(invs, FIFF_MNE_INVERSE_LEADS_WEIGHTED, *inv.eigen_leads.data())) {
1022 qCritical("Error reading eigenleads named matrix.\n");
1023 return false;
1024 }
1025 }
1026 //
1027 // Having the eigenleads as columns is better for the inverse calculations
1028 //
1029 inv.eigen_leads->transpose_named_matrix();
1030
1031 if (!t_pStream->read_named_matrix(invs, FIFF_MNE_INVERSE_FIELDS, *inv.eigen_fields.data())) {
1032 qCritical("Error reading eigenfields named matrix.\n");
1033 return false;
1034 }
1035 qInfo("[done]\n");
1036 //
1037 // Read the covariance matrices
1038 //
1039 if (t_pStream->read_cov(invs, FIFFV_MNE_NOISE_COV, *inv.noise_cov.data())) {
1040 qInfo("\tNoise covariance matrix read.\n");
1041 } else {
1042 qCritical("\tError: Not able to read noise covariance matrix.\n");
1043 return false;
1044 }
1045
1046 if (t_pStream->read_cov(invs, FIFFV_MNE_SOURCE_COV, *inv.source_cov.data())) {
1047 qInfo("\tSource covariance matrix read.\n");
1048 } else {
1049 qCritical("\tError: Not able to read source covariance matrix.\n");
1050 return false;
1051 }
1052 //
1053 // Read the various priors
1054 //
1055 if (t_pStream->read_cov(invs, FIFFV_MNE_ORIENT_PRIOR_COV, *inv.orient_prior.data())) {
1056 qInfo("\tOrientation priors read.\n");
1057 } else
1058 inv.orient_prior->clear();
1059
1060 if (t_pStream->read_cov(invs, FIFFV_MNE_DEPTH_PRIOR_COV, *inv.depth_prior.data())) {
1061 qInfo("\tDepth priors read.\n");
1062 } else {
1063 inv.depth_prior->clear();
1064 }
1065 if (t_pStream->read_cov(invs, FIFFV_MNE_FMRI_PRIOR_COV, *inv.fmri_prior.data())) {
1066 qInfo("\tfMRI priors read.\n");
1067 } else {
1068 inv.fmri_prior->clear();
1069 }
1070 //
1071 // Read the source spaces
1072 //
1073 if (!MNESourceSpaces::readFromStream(t_pStream, false, inv.src)) {
1074 qCritical("\tError: Could not read the source spaces.\n");
1075 return false;
1076 }
1077 for (qint32 k = 0; k < inv.src.size(); ++k)
1078 inv.src[k].id = inv.src[k].find_source_space_hemi();
1079 //
1080 // Get the MRI <-> head coordinate transformation
1081 //
1083 if (!parent_mri[0]->find_tag(t_pStream, FIFF_COORD_TRANS, t_pTag)) {
1084 qCritical("MRI/head coordinate transformation not found\n");
1085 return false;
1086 } else {
1087 mri_head_t = t_pTag->toCoordTrans();
1089 mri_head_t.invert_transform();
1091 qCritical("MRI/head coordinate transformation not found");
1092 return false;
1093 }
1094 }
1095 }
1096 inv.mri_head_t = mri_head_t;
1097
1098 //
1099 // get parent MEG info
1100 //
1101 t_pStream->read_meas_info_base(t_pStream->dirtree(), inv.info);
1102
1103 //
1104 // Transform the source spaces to the correct coordinate frame
1105 // if necessary
1106 //
1108 qCritical("Only inverse solutions computed in MRI or head coordinates are acceptable");
1109 //
1110 // Number of averages is initially one
1111 //
1112 inv.nave = 1;
1113 //
1114 // We also need the SSP operator
1115 //
1116 inv.projs = t_pStream->read_proj(t_pStream->dirtree());
1117
1118 // proj, whitener, reginv, and noisenorm are filled in by prepare_inverse_operator()
1119
1121 qCritical("Could not transform source space.\n");
1122 }
1123 qInfo("\tSource spaces transformed to the inverse solution coordinate frame\n");
1124 //
1125 // Done!
1126 //
1127
1128 return true;
1129}
1130
1131//=============================================================================================================
1132
1133bool MNEInverseOperator::write(QIODevice& p_IODevice)
1134{
1135 FiffStream::SPtr t_pStream = FiffStream::start_file(p_IODevice);
1136 if (!t_pStream) {
1137 return false;
1138 }
1139 qInfo("Write inverse operator decomposition in %s...", t_pStream->streamName().toUtf8().constData());
1140 this->writeToStream(t_pStream.data());
1141 t_pStream->end_file();
1142 return true;
1143}
1144
1145//=============================================================================================================
1146
1148{
1150
1151 qInfo("\tWriting inverse operator info...\n");
1152
1153 p_pStream->write_int(FIFF_MNE_INCLUDED_METHODS, &this->methods);
1156 p_pStream->write_int(FIFF_MNE_COORD_FRAME, &this->coord_frame);
1157
1158 // mne-python reads this to label the source estimates. It is optional
1159 // there, but omitting it leaves inv["units"] as None, so an operator that
1160 // travelled through MNE-CPP lost information the original file carried.
1161 if (this->units > 0)
1162 p_pStream->write_int(FIFF_MNE_INVERSE_SOURCE_UNIT, &this->units);
1164 VectorXf tmp_sing = this->sing.cast<float>();
1165 p_pStream->write_float(FIFF_MNE_INVERSE_SING, tmp_sing.data(), tmp_sing.size());
1166
1167 //
1168 // The eigenleads and eigenfields
1169 //
1170 if (this->eigen_leads_weighted) {
1171 FiffNamedMatrix tmpMatrix(*this->eigen_leads.data());
1172 tmpMatrix.transpose_named_matrix();
1174 } else {
1175 FiffNamedMatrix tmpMatrix(*this->eigen_leads.data());
1176 tmpMatrix.transpose_named_matrix();
1177 p_pStream->write_named_matrix(FIFF_MNE_INVERSE_LEADS, tmpMatrix);
1178 }
1179
1180 p_pStream->write_named_matrix(FIFF_MNE_INVERSE_FIELDS, *this->eigen_fields.data());
1181 qInfo("\t[done]\n");
1182 //
1183 // write the covariance matrices
1184 //
1185 qInfo("\tWriting noise covariance matrix.");
1186 p_pStream->write_cov(*this->noise_cov.data());
1187
1188 qInfo("\tWriting source covariance matrix.\n");
1189 p_pStream->write_cov(*this->source_cov.data());
1190 //
1191 // write the various priors
1192 //
1193 qInfo("\tWriting orientation priors.\n");
1194 if (this->orient_prior && !this->orient_prior->isEmpty())
1195 p_pStream->write_cov(*this->orient_prior.data());
1196 if (this->depth_prior && !this->depth_prior->isEmpty())
1197 p_pStream->write_cov(*this->depth_prior.data());
1198 if (this->fmri_prior && !this->fmri_prior->isEmpty())
1199 p_pStream->write_cov(*this->fmri_prior.data());
1200
1201 //
1202 // Parent MRI data
1203 //
1205 // write the MRI <-> head coordinate transformation
1206 p_pStream->write_coord_trans(this->mri_head_t);
1208
1209 //
1210 // Parent MEG measurement info
1211 //
1212 p_pStream->write_info_base(this->info);
1213
1214 //
1215 // Write the source spaces
1216 //
1217 if (!src.isEmpty())
1218 this->src.writeToStream(p_pStream);
1219
1220 //
1221 // We also need the SSP operator
1222 //
1223 p_pStream->write_proj(this->projs);
1224 //
1225 // Done!
1226 //
1228}
#define FIFF_MNE_INVERSE_FIELDS
#define FIFF_MNE_INVERSE_LEADS
#define FIFF_MNE_INVERSE_SOURCE_ORIENTATIONS
#define FIFF_MNE_COORD_FRAME
#define FIFF_MNE_SOURCE_ORIENTATION
#define FIFFV_MNE_NOISE_COV
#define FIFF_MNE_INVERSE_SOURCE_UNIT
#define FIFFV_MNE_FMRI_PRIOR_COV
#define FIFF_MNE_INVERSE_LEADS_WEIGHTED
#define FIFF_MNE_INCLUDED_METHODS
#define FIFFB_MNE_INVERSE_SOLUTION
#define FIFF_MNE_SOURCE_SPACE_NPOINTS
#define FIFFV_COORD_HEAD
#define FIFFV_COORD_MRI
#define FIFFV_MNE_MEG
#define FIFF_MNE_INVERSE_SING
#define FIFFV_MNE_MEG_EEG
#define FIFFV_MNE_SOURCE_COV
#define FIFFV_MNE_ORIENT_PRIOR_COV
#define FIFFV_MNE_EEG
#define FIFFB_MNE_PARENT_MRI_FILE
#define FIFFV_MNE_DEPTH_PRIOR_COV
#define FIFFV_MNE_FREE_ORI
#define FIFF_COORD_TRANS
Definition fiff_file.h:468
Eigen::JacobiSVD< Eigen::Matrix3f > svd(S, Eigen::ComputeFullU|Eigen::ComputeFullV)
Static linear-algebra helpers: SVD-based conditioning, block-diagonal assembly, sorted index pairs.
Reader and in-memory representation of a FreeSurfer/MNE surface label (.label).
Pre-computed inverse operator (whitened SVD of the forward model) for MNE/dSPM/sLORETA.
Core MNE data structures (source spaces, source estimates, hemispheres).
FreeSurfer surface, annotation and parcellation I/O for mne-cpp.
FIFF file I/O, in-memory data structures and high-level readers/writers.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
FIFF noise / data covariance: matrix, channel names, kind, applied projectors, bads,...
Definition fiff_cov.h:82
QSharedDataPointer< FiffCov > SDPtr
Definition fiff_cov.h:88
QSharedPointer< FiffDirNode > SPtr
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
Definition fiff_info.h:90
FIFF named matrix: dense / sparse Eigen matrix plus row-name and column-name string lists.
QSharedDataPointer< FiffNamedMatrix > SDPtr
static fiff_int_t make_projector(const QList< FiffProj > &projs, const QStringList &ch_names, Eigen::MatrixXd &proj, const QStringList &bads=defaultQStringList, Eigen::MatrixXd &U=defaultMatrixXd)
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
fiff_long_t write_cov(const FiffCov &p_FiffCov)
fiff_long_t start_block(fiff_int_t kind)
fiff_long_t write_float_matrix(fiff_int_t kind, const Eigen::MatrixXf &mat)
fiff_long_t write_proj(const QList< FiffProj > &projs)
QSharedPointer< FiffStream > SPtr
fiff_long_t write_int(fiff_int_t kind, const fiff_int_t *data, fiff_int_t nel=1, fiff_int_t next=FIFFV_NEXT_SEQ)
fiff_long_t write_float(fiff_int_t kind, const float *data, fiff_int_t nel=1)
fiff_long_t write_coord_trans(const FiffCoordTrans &trans)
fiff_long_t write_named_matrix(fiff_int_t kind, const FiffNamedMatrix &mat)
static FiffStream::SPtr start_file(QIODevice &p_IODevice)
fiff_long_t write_info_base(const FiffInfoBase &p_FiffInfoBase)
fiff_long_t end_block(fiff_int_t kind, fiff_int_t next=FIFFV_NEXT_SEQ)
std::unique_ptr< FiffTag > UPtr
Definition fiff_tag.h:165
Container holding the lh and/or rh FsAnnotation for one parcellation atlas.
FreeSurfer colour lookup table: region name + RGBA + packed label, indexed by entry.
QStringList struct_names
Eigen::VectorXi getLabelIds() const
A FreeSurfer/MNE surface label: per-vertex indices, Tk-RAS positions and scalar values for one hemisp...
Definition fs_label.h:81
bool isEmpty() const
Definition fs_label.h:184
static Eigen::VectorXd combine_xyz(const Eigen::VectorXd &vec)
Definition linalg.cpp:57
static Eigen::VectorXi sort(Eigen::Matrix< T, Eigen::Dynamic, 1 > &v, bool desc=true)
Definition linalg.h:298
static Eigen::VectorXi intersect(const Eigen::VectorXi &v1, const Eigen::VectorXi &v2, Eigen::VectorXi &idx_sel)
Definition linalg.cpp:160
Cluster table used to compress and reconstruct a clustered leadfield.
In-memory representation of an -fwd.fif forward solution.
static FIFFLIB::FiffCov compute_depth_prior(const Eigen::MatrixXd &Gain, const FIFFLIB::FiffInfo &gain_info, bool is_fixed_ori, double exp=0.8, double limit=10.0, const Eigen::MatrixXd &patch_areas=FIFFLIB::defaultConstMatrixXd, bool limit_depth_chs=false)
MNELIB::MNESourceSpaces src
void prepare_forward(const FIFFLIB::FiffInfo &p_info, const FIFFLIB::FiffCov &p_noise_cov, bool p_pca, FIFFLIB::FiffInfo &p_outFwdInfo, Eigen::MatrixXd &gain, FIFFLIB::FiffCov &p_outNoiseCov, Eigen::MatrixXd &p_outWhitener, qint32 &p_outNumNonZero) const
FIFFLIB::FiffCoordTrans mri_head_t
FIFFLIB::FiffCov compute_orient_prior(float loose=0.2)
Input parameters for multi-threaded KMeans clustering on a single cortical region.
Eigen::MatrixXd matRoiMTOrig
Eigen::MatrixXd matRoiMT
RegionMTOut cluster() const
Run KMeans clustering on this region.
MNEInverseOperator()
Constructs an empty inverse operator with invalid sentinel values.
FIFFLIB::FiffCov::SDPtr fmri_prior
Eigen::MatrixXd cluster_kernel(const FSLIB::FsAnnotationSet &annotationSet, qint32 clusterSize, Eigen::MatrixXd &D, const QString &method=QStringLiteral("cityblock")) const
Cluster the inverse kernel by cortical parcellation.
QList< FIFFLIB::FiffProj > projs
Eigen::SparseMatrix< double > noisenorm
FIFFLIB::FiffCov::SDPtr orient_prior
static MNEInverseOperator make_inverse_operator(const FIFFLIB::FiffInfo &info, MNEForwardSolution forward, const FIFFLIB::FiffCov &noiseCov, float loose=0.2f, float depth=0.8f, bool fixed=false, bool limit_depth_chs=true)
Assemble an inverse operator from a forward solution and noise covariance.
FIFFLIB::FiffNamedMatrix::SDPtr eigen_leads
bool check_ch_names(const FIFFLIB::FiffInfo &info) const
Verify that inverse-operator channels are present in the measurement info.
FIFFLIB::FiffCoordTrans mri_head_t
MNEInverseOperator prepare_inverse_operator(qint32 nave, float lambda2, bool dSPM, bool sLORETA=false) const
Prepare the inverse operator for source estimation.
FIFFLIB::FiffCov::SDPtr depth_prior
bool assemble_kernel(const FSLIB::FsLabel &label, const QString &method, bool pick_normal, Eigen::MatrixXd &K, Eigen::SparseMatrix< double > &noise_norm, QList< Eigen::VectorXi > &vertno)
Assemble the inverse kernel matrix.
void writeToStream(FIFFLIB::FiffStream *p_pStream)
Write the inverse operator into an already-open FIFF stream.
~MNEInverseOperator()
Destructor.
FIFFLIB::FiffCov::SDPtr noise_cov
FIFFLIB::FiffCov::SDPtr source_cov
static bool read_inverse_operator(QIODevice &p_IODevice, MNEInverseOperator &inv)
Read an inverse operator from a FIFF file.
bool isFixedOrient() const
Check whether the inverse operator uses fixed source orientations.
bool write(QIODevice &p_IODevice)
Write the inverse operator to a FIFF file.
FIFFLIB::FiffNamedMatrix::SDPtr eigen_fields
bool transform_source_space_to(FIFFLIB::fiff_int_t dest, FIFFLIB::FiffCoordTrans &trans)
static qint32 find_source_space_hemi(MNESourceSpace &p_SourceSpace)
static bool readFromStream(FIFFLIB::FiffStream::SPtr &p_pStream, bool add_geom, MNESourceSpaces &p_SourceSpace)