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
484 ++itIn;
485 }
486
487 qInfo("[done]\n");
488 }
489
490 //
491 // Cluster operator D (sources x clusters)
492 //
493 qint32 totalNumOfClust = 0;
494 for (qint32 h = 0; h < 2; ++h)
495 totalNumOfClust += t_qListMNEClusterInfo[h].clusterVertnos.size();
496
497 if (isFixedOrient())
498 p_D = MatrixXd::Zero(p_outMT.cols(), totalNumOfClust);
499 else
500 p_D = MatrixXd::Zero(p_outMT.cols(), totalNumOfClust * 3);
501
502 QList<VectorXi> t_vertnos = src.get_vertno();
503
504 qint32 currentCluster = 0;
505 for (qint32 h = 0; h < 2; ++h) {
506 int hemiOffset = h == 0 ? 0 : t_vertnos[0].size();
507 for (qint32 i = 0; i < t_qListMNEClusterInfo[h].clusterVertnos.size(); ++i) {
508 VectorXi idx_sel;
509 Linalg::intersect(t_vertnos[h], t_qListMNEClusterInfo[h].clusterVertnos[i], idx_sel);
510
511 idx_sel.array() += hemiOffset;
512
513 double selectWeight = 1.0 / idx_sel.size();
514 if (isFixedOrient()) {
515 for (qint32 j = 0; j < idx_sel.size(); ++j)
516 p_D.col(currentCluster)[idx_sel(j)] = selectWeight;
517 } else {
518 qint32 clustOffset = currentCluster * 3;
519 for (qint32 j = 0; j < idx_sel.size(); ++j) {
520 qint32 idx_sel_Offset = idx_sel(j) * 3;
521 //x
522 p_D(idx_sel_Offset, clustOffset) = selectWeight;
523 //y
524 p_D(idx_sel_Offset + 1, clustOffset + 1) = selectWeight;
525 //z
526 p_D(idx_sel_Offset + 2, clustOffset + 2) = selectWeight;
527 }
528 }
529 ++currentCluster;
530 }
531 }
532
533 //
534 // Put it all together
535 //
536 p_outMT = t_MT_new;
537
538 return p_outMT;
539}
540
541//=============================================================================================================
542
544 MNEForwardSolution forward,
545 const FiffCov& p_noise_cov,
546 float loose,
547 float depth,
548 bool fixed,
549 bool limit_depth_chs)
550{
551 bool is_fixed_ori = forward.isFixedOrient();
553
554 // Loose and fixed constraints are relative to the cortical normal, as in mne-python's _prepare_forward.
555 if (!is_fixed_ori && (fixed || loose < 1.0f)) {
556 forward.convert_to_surf_ori();
557 }
558
559 //Check parameters
560 if (fixed && loose > 0) {
561 qWarning("Warning: When invoking make_inverse_operator with fixed = true, the loose parameter is ignored.\n");
562 loose = 0.0f;
563 }
564
565 if (is_fixed_ori && !fixed) {
566 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");
567 fixed = true;
568 }
569
570 if (forward.source_ori == -1 && loose > 0) {
571 qCritical("Forward solution is not oriented in surface coordinates. loose parameter should be 0 not %f.", loose);
572 return inv;
573 }
574
575 if (loose < 0 || loose > 1) {
576 qWarning("Warning: Loose value should be in interval [0,1] not %f.\n", loose);
577 loose = loose > 1 ? 1 : 0;
578 qInfo("Setting loose to %f.\n", loose);
579 }
580
581 if (depth < 0 || depth > 1) {
582 qWarning("Warning: Depth value should be in interval [0,1] not %f.\n", depth);
583 depth = depth > 1 ? 1 : 0;
584 qInfo("Setting depth to %f.\n", depth);
585 }
586
587 //
588 // 1. Read the bad channels
589 // 2. Read the necessary data from the forward solution matrix file
590 // 3. Load the projection data
591 // 4. Load the sensor noise covariance matrix and attach it to the forward
592 //
593 FiffInfo gain_info;
594 MatrixXd gain;
595 MatrixXd whitener;
596 qint32 n_nzero;
597 FiffCov p_outNoiseCov;
598 forward.prepare_forward(info, p_noise_cov, false, gain_info, gain, p_outNoiseCov, whitener, n_nzero);
599
600 //
601 // 5. Compose the depth weight matrix
602 //
603 FiffCov::SDPtr p_depth_prior;
604 MatrixXd patch_areas;
605 if (depth > 0) {
606 // TODO: load patch_areas from forward solution
607 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)));
608 } else {
609 p_depth_prior = FiffCov::SDPtr(new FiffCov());
610 p_depth_prior->data = MatrixXd::Ones(gain.cols(), 1);
611 p_depth_prior->kind = FIFFV_MNE_DEPTH_PRIOR_COV;
612 p_depth_prior->diag = true;
613 p_depth_prior->dim = gain.cols();
614 p_depth_prior->nfree = 1;
615 }
616
617 // Deal with fixed orientation forward / inverse
618 if (fixed) {
619 if (depth < 0 || depth > 1) {
620 // TODO: convert free-orientation depth prior to fixed-orientation
621 qInfo("\tPicking elements from free-orientation depth prior into fixed-orientation one.\n");
622 }
623 if (!is_fixed_ori) {
624 if (!forward.surf_ori) {
625 qWarning("Warning: For a fixed-orientation inverse, the forward solution must be surface-oriented. Skipping fixed conversion.\n");
626 } else {
627 // Convert to the fixed orientation forward solution now
628 qint32 count = 0;
629 for (qint32 i = 2; i < p_depth_prior->data.rows(); i += 3) {
630 p_depth_prior->data.row(count) = p_depth_prior->data.row(i);
631 gain.col(count) = gain.col(i);
632 ++count;
633 }
634 p_depth_prior->data.conservativeResize(count, 1);
635 p_depth_prior->dim = count;
636 gain.conservativeResize(Eigen::NoChange, count);
637
638 forward.to_fixed_ori();
639 is_fixed_ori = forward.isFixedOrient();
640 }
641 }
642 }
643 qInfo("\tComputing inverse operator with %lld channels.\n", static_cast<long long>(gain_info.ch_names.size()));
644
645 //
646 // 6. Compose the source covariance matrix
647 //
648 qInfo("\tCreating the source covariance matrix\n");
649 FiffCov::SDPtr p_source_cov = p_depth_prior;
650
651 // apply loose orientations
652 FiffCov::SDPtr p_orient_prior;
653 if (!is_fixed_ori) {
654 p_orient_prior = FiffCov::SDPtr(new FiffCov(forward.compute_orient_prior(loose)));
655 p_source_cov->data.array() *= p_orient_prior->data.array();
656 }
657
658 // 7. Apply fMRI weighting (not done)
659
660 //
661 // 8. Apply the linear projection to the forward solution
662 // 9. Apply whitening to the forward computation matrix
663 //
664 qInfo("\tWhitening the forward solution.\n");
665 gain = whitener * gain;
666
667 // 10. Exclude the source space points within the labels (not done)
668
669 //
670 // 11. Do appropriate source weighting to the forward computation matrix
671 //
672
673 // Adjusting Source Covariance matrix to make trace of G*R*G' equal
674 // to number of sensors.
675 qInfo("\tAdjusting source covariance matrix.\n");
676 RowVectorXd source_std = p_source_cov->data.array().sqrt().transpose();
677
678 for (qint32 i = 0; i < gain.rows(); ++i)
679 gain.row(i) = gain.row(i).array() * source_std.array();
680
681 double trace_GRGT = (gain * gain.transpose()).trace();
682 double scaling_source_cov = static_cast<double>(n_nzero) / trace_GRGT;
683
684 p_source_cov->data.array() *= scaling_source_cov;
685
686 gain.array() *= sqrt(scaling_source_cov);
687
688 //
689 // 12. Decompose the combined matrix
690 //
691 qInfo("Computing SVD of whitened and weighted lead field matrix.\n");
692 JacobiSVD<MatrixXd> svd(gain, ComputeThinU | ComputeThinV);
693 // TODO: verify whether explicit sorting is necessary
694 VectorXd p_sing = svd.singularValues();
695 MatrixXd t_U = svd.matrixU();
696 Linalg::sort<double>(p_sing, t_U);
697 FiffNamedMatrix::SDPtr p_eigen_fields = FiffNamedMatrix::SDPtr(new FiffNamedMatrix(svd.matrixU().cols(),
698 svd.matrixU().rows(),
699 defaultQStringList,
700 gain_info.ch_names,
701 t_U.transpose()));
702
703 p_sing = svd.singularValues();
704 MatrixXd t_V = svd.matrixV();
705 Linalg::sort<double>(p_sing, t_V);
706 FiffNamedMatrix::SDPtr p_eigen_leads = FiffNamedMatrix::SDPtr(new FiffNamedMatrix(svd.matrixV().rows(),
707 svd.matrixV().cols(),
708 defaultQStringList,
709 defaultQStringList,
710 t_V));
711 qInfo("\tlargest singular value = %f\n", p_sing.maxCoeff());
712 qInfo("\tscaling factor to adjust the trace = %f\n", trace_GRGT);
713
714 qint32 p_nave = 1.0;
715
716 // Handle methods
717 bool has_meg = false;
718 bool has_eeg = false;
719
720 RowVectorXd ch_idx(info.chs.size());
721 qint32 count = 0;
722 for (qint32 i = 0; i < info.chs.size(); ++i) {
723 if (gain_info.ch_names.contains(info.chs[i].ch_name)) {
724 ch_idx[count] = i;
725 ++count;
726 }
727 }
728 ch_idx.conservativeResize(count);
729
730 for (qint32 i = 0; i < ch_idx.size(); ++i) {
731 QString ch_type = info.channel_type(ch_idx[i]);
732 if (ch_type == "eeg")
733 has_eeg = true;
734 if ((ch_type == "mag") || (ch_type == "grad"))
735 has_meg = true;
736 }
737
738 qint32 p_iMethods;
739
740 if (has_eeg && has_meg)
741 p_iMethods = FIFFV_MNE_MEG_EEG;
742 else if (has_meg)
743 p_iMethods = FIFFV_MNE_MEG;
744 else
745 p_iMethods = FIFFV_MNE_EEG;
746
747 // We set this for consistency with mne C code written inverses
748 if (depth == 0)
749 p_depth_prior = FiffCov::SDPtr();
750
751 inv.eigen_fields = p_eigen_fields;
752 inv.eigen_leads = p_eigen_leads;
753 inv.sing = p_sing;
754 inv.nchan = p_sing.rows();
755 inv.nave = p_nave;
756 inv.depth_prior = p_depth_prior;
757 // Fix the kind so I/O round-trips write FIFFV_MNE_SOURCE_COV.
758 p_source_cov->kind = FIFFV_MNE_SOURCE_COV;
759 inv.source_cov = p_source_cov;
760 inv.noise_cov = FiffCov::SDPtr(new FiffCov(p_outNoiseCov));
761 inv.orient_prior = p_orient_prior;
762 inv.projs = info.projs;
763 inv.eigen_leads_weighted = false;
764 inv.source_ori = forward.source_ori;
765 inv.mri_head_t = forward.mri_head_t;
766 inv.methods = p_iMethods;
767 inv.nsource = forward.nsource;
768 inv.coord_frame = forward.coord_frame;
769 inv.source_nn = forward.source_nn;
770 inv.src = forward.src;
771 inv.info = forward.info;
772 inv.info.bads = info.bads;
773
774 return inv;
775}
776
777//=============================================================================================================
778
779MNEInverseOperator MNEInverseOperator::prepare_inverse_operator(qint32 nAve, float lambda2, bool dSPM, bool sLORETA) const
780{
781 if (nAve <= 0) {
782 qCritical("The number of averages should be positive\n");
783 return MNEInverseOperator();
784 }
785 qInfo("Preparing the inverse operator for use...\n");
786 MNEInverseOperator inv(*this);
787 //
788 // Scale some of the stuff
789 //
790 float scale = static_cast<float>(inv.nave) / static_cast<float>(nAve);
791 inv.noise_cov->data *= scale;
792 inv.noise_cov->eig *= scale;
793 inv.source_cov->data *= scale;
794 //
795 if (inv.eigen_leads_weighted)
796 inv.eigen_leads->data *= sqrt(scale);
797 //
798 qInfo("\tScaled noise and source covariance from nave = %d to nave = %d\n", inv.nave, nAve);
799 inv.nave = nAve;
800 //
801 // Create the diagonal matrix for computing the regularized inverse
802 //
803 VectorXd tmp = inv.sing.cwiseProduct(inv.sing) + VectorXd::Constant(inv.sing.size(), lambda2);
804 inv.reginv = VectorXd(inv.sing.cwiseQuotient(tmp));
805 qInfo("\tCreated the regularized inverter\n");
806 //
807 // Create the projection operator
808 //
809
810 qint32 ncomp = FiffProj::make_projector(inv.projs, inv.noise_cov->names, inv.proj);
811 if (ncomp > 0)
812 qInfo("\tCreated an SSP operator (subspace dimension = %d)\n", ncomp);
813
814 //
815 // Create the whitener
816 //
817 inv.whitener = MatrixXd::Zero(inv.noise_cov->dim, inv.noise_cov->dim);
818
819 qint32 nnzero, k;
820 if (inv.noise_cov->diag == 0) {
821 //
822 // Omit the zeroes due to projection. The eigenvalues are sorted per channel type, not
823 // globally, so the zeroes are not necessarily the first ncomp (mne-python compute_whitener).
824 //
825 nnzero = 0;
826
827 for (k = 0; k < inv.noise_cov->dim; ++k) {
828 if (inv.noise_cov->eig[k] > 0) {
829 inv.whitener(k, k) = 1.0 / sqrt(inv.noise_cov->eig[k]);
830 ++nnzero;
831 }
832 }
833 //
834 // Rows of eigvec are the eigenvectors
835 //
836 inv.whitener *= inv.noise_cov->eigvec;
837 qInfo("\tCreated the whitener using a full noise covariance matrix (%d small eigenvalues omitted)\n", inv.noise_cov->dim - nnzero);
838 } else {
839 //
840 // No need to omit the zeroes due to projection
841 //
842 for (k = 0; k < inv.noise_cov->dim; ++k)
843 inv.whitener(k, k) = 1.0 / sqrt(inv.noise_cov->data(k, 0));
844
845 qInfo("\tCreated the whitener using a diagonal noise covariance matrix (%d small eigenvalues discarded)\n", ncomp);
846 }
847 //
848 // Finally, compute the noise-normalization factors
849 //
850 if (dSPM || sLORETA) {
851 VectorXd noise_norm = VectorXd::Zero(inv.eigen_leads->nrow);
852 VectorXd noise_weight;
853 if (dSPM) {
854 qInfo("\tComputing noise-normalization factors (dSPM)...");
855 noise_weight = VectorXd(inv.reginv);
856 } else {
857 qInfo("\tComputing noise-normalization factors (sLORETA)...");
858 VectorXd sLoretaScale = (VectorXd::Constant(inv.sing.size(), 1) + inv.sing.cwiseProduct(inv.sing) / lambda2);
859 noise_weight = inv.reginv.cwiseProduct(sLoretaScale.cwiseSqrt());
860 }
861 VectorXd one;
862 if (inv.eigen_leads_weighted) {
863 for (k = 0; k < inv.eigen_leads->nrow; ++k) {
864 one = inv.eigen_leads->data.block(k, 0, 1, inv.eigen_leads->data.cols()).cwiseProduct(noise_weight);
865 noise_norm[k] = sqrt(one.dot(one));
866 }
867 } else {
868 double c;
869 for (k = 0; k < inv.eigen_leads->nrow; ++k) {
870 c = sqrt(inv.source_cov->data(k, 0));
871 one = c * (inv.eigen_leads->data.row(k).transpose()).cwiseProduct(noise_weight);
872 noise_norm[k] = sqrt(one.dot(one));
873 }
874 }
875
876 //
877 // Compute the final result
878 //
879 VectorXd noise_norm_new = noise_norm;
880 if (inv.source_ori == FIFFV_MNE_FREE_ORI) {
881 // For free orientations the variances at three consecutive entries
882 // must be squared and summed, yielding one factor per source location.
883 VectorXd t = Linalg::combine_xyz(noise_norm.transpose());
884 noise_norm_new = t.cwiseSqrt();
885 }
886 VectorXd vOnes = VectorXd::Ones(noise_norm_new.size());
887 VectorXd noiseNormInv = vOnes.cwiseQuotient(noise_norm_new.cwiseAbs());
888
889 typedef Eigen::Triplet<double> T;
890 std::vector<T> tripletList;
891 tripletList.reserve(noise_norm_new.size());
892 for (qint32 i = 0; i < noise_norm_new.size(); ++i)
893 tripletList.push_back(T(i, i, noiseNormInv[i]));
894
895 inv.noisenorm = SparseMatrix<double>(noise_norm_new.size(), noise_norm_new.size());
896 inv.noisenorm.setFromTriplets(tripletList.begin(), tripletList.end());
897
898 qInfo("[done]\n");
899 } else {
900 inv.noisenorm = SparseMatrix<double>();
901 }
902
903 return inv;
904}
905
906//=============================================================================================================
907
909{
910 //
911 // Open the file, create directory
912 //
913 FiffStream::SPtr t_pStream(new FiffStream(&p_IODevice));
914 qInfo("Reading inverse operator decomposition from %s...\n", t_pStream->streamName().toUtf8().constData());
915
916 if (!t_pStream->open())
917 return false;
918 //
919 // Find all inverse operators
920 //
921 QList<FiffDirNode::SPtr> invs_list = t_pStream->dirtree()->dir_tree_find(FIFFB_MNE_INVERSE_SOLUTION);
922 if (invs_list.size() == 0) {
923 qCritical("No inverse solutions in %s\n", t_pStream->streamName().toUtf8().constData());
924 return false;
925 }
926 FiffDirNode::SPtr invs = invs_list[0];
927 //
928 // Parent MRI data
929 //
930 QList<FiffDirNode::SPtr> parent_mri = t_pStream->dirtree()->dir_tree_find(FIFFB_MNE_PARENT_MRI_FILE);
931 if (parent_mri.size() == 0) {
932 qCritical("No parent MRI information in %s", t_pStream->streamName().toUtf8().constData());
933 return false;
934 }
935 qInfo("\tReading inverse operator info...");
936 //
937 // Methods and source orientations
938 //
939 FiffTag::UPtr t_pTag;
940 if (!invs->find_tag(t_pStream, FIFF_MNE_INCLUDED_METHODS, t_pTag)) {
941 qCritical("Modalities not found\n");
942 return false;
943 }
944
945 inv = MNEInverseOperator();
946 inv.methods = *t_pTag->toInt();
947 //
948 if (!invs->find_tag(t_pStream, FIFF_MNE_SOURCE_ORIENTATION, t_pTag)) {
949 qCritical("Source orientation constraints not found\n");
950 return false;
951 }
952 inv.source_ori = *t_pTag->toInt();
953 //
954 if (!invs->find_tag(t_pStream, FIFF_MNE_SOURCE_SPACE_NPOINTS, t_pTag)) {
955 qCritical("Number of sources not found\n");
956 return false;
957 }
958 inv.nsource = *t_pTag->toInt();
959 inv.nchan = 0;
960 //
961 // Coordinate frame
962 //
963 if (!invs->find_tag(t_pStream, FIFF_MNE_COORD_FRAME, t_pTag)) {
964 qCritical("Coordinate frame tag not found\n");
965 return false;
966 }
967 inv.coord_frame = *t_pTag->toInt();
968 //
969 // Units of the source estimates. Optional: files written before this was
970 // recorded simply do not carry it, so a missing tag is not an error.
971 //
972 if (invs->find_tag(t_pStream, FIFF_MNE_INVERSE_SOURCE_UNIT, t_pTag))
973 inv.units = *t_pTag->toInt();
974 else
975 inv.units = -1;
976 //
977 // The actual source orientation vectors
978 //
979 if (!invs->find_tag(t_pStream, FIFF_MNE_INVERSE_SOURCE_ORIENTATIONS, t_pTag)) {
980 qCritical("Source orientation information not found\n");
981 return false;
982 }
983
984 inv.source_nn = t_pTag->toFloatMatrix();
985 inv.source_nn.transposeInPlace();
986
987 qInfo("[done]\n");
988 //
989 // The SVD decomposition...
990 //
991 qInfo("\tReading inverse operator decomposition...");
992 if (!invs->find_tag(t_pStream, FIFF_MNE_INVERSE_SING, t_pTag)) {
993 qCritical("Singular values not found\n");
994 return false;
995 }
996
997 inv.sing = Map<const VectorXf>(t_pTag->toFloat(), t_pTag->size() / 4).cast<double>();
998 inv.nchan = inv.sing.rows();
999 //
1000 // The eigenleads and eigenfields
1001 //
1002 inv.eigen_leads_weighted = false;
1003 if (!t_pStream->read_named_matrix(invs, FIFF_MNE_INVERSE_LEADS, *inv.eigen_leads.data())) {
1004 inv.eigen_leads_weighted = true;
1005 if (!t_pStream->read_named_matrix(invs, FIFF_MNE_INVERSE_LEADS_WEIGHTED, *inv.eigen_leads.data())) {
1006 qCritical("Error reading eigenleads named matrix.\n");
1007 return false;
1008 }
1009 }
1010 //
1011 // Having the eigenleads as columns is better for the inverse calculations
1012 //
1013 inv.eigen_leads->transpose_named_matrix();
1014
1015 if (!t_pStream->read_named_matrix(invs, FIFF_MNE_INVERSE_FIELDS, *inv.eigen_fields.data())) {
1016 qCritical("Error reading eigenfields named matrix.\n");
1017 return false;
1018 }
1019 qInfo("[done]\n");
1020 //
1021 // Read the covariance matrices
1022 //
1023 if (t_pStream->read_cov(invs, FIFFV_MNE_NOISE_COV, *inv.noise_cov.data())) {
1024 qInfo("\tNoise covariance matrix read.\n");
1025 } else {
1026 qCritical("\tError: Not able to read noise covariance matrix.\n");
1027 return false;
1028 }
1029
1030 if (t_pStream->read_cov(invs, FIFFV_MNE_SOURCE_COV, *inv.source_cov.data())) {
1031 qInfo("\tSource covariance matrix read.\n");
1032 } else {
1033 qCritical("\tError: Not able to read source covariance matrix.\n");
1034 return false;
1035 }
1036 //
1037 // Read the various priors
1038 //
1039 if (t_pStream->read_cov(invs, FIFFV_MNE_ORIENT_PRIOR_COV, *inv.orient_prior.data())) {
1040 qInfo("\tOrientation priors read.\n");
1041 } else
1042 inv.orient_prior->clear();
1043
1044 if (t_pStream->read_cov(invs, FIFFV_MNE_DEPTH_PRIOR_COV, *inv.depth_prior.data())) {
1045 qInfo("\tDepth priors read.\n");
1046 } else {
1047 inv.depth_prior->clear();
1048 }
1049 if (t_pStream->read_cov(invs, FIFFV_MNE_FMRI_PRIOR_COV, *inv.fmri_prior.data())) {
1050 qInfo("\tfMRI priors read.\n");
1051 } else {
1052 inv.fmri_prior->clear();
1053 }
1054 //
1055 // Read the source spaces
1056 //
1057 if (!MNESourceSpaces::readFromStream(t_pStream, false, inv.src)) {
1058 qCritical("\tError: Could not read the source spaces.\n");
1059 return false;
1060 }
1061 for (qint32 k = 0; k < inv.src.size(); ++k)
1062 inv.src[k].id = inv.src[k].find_source_space_hemi();
1063 //
1064 // Get the MRI <-> head coordinate transformation
1065 //
1067 if (!parent_mri[0]->find_tag(t_pStream, FIFF_COORD_TRANS, t_pTag)) {
1068 qCritical("MRI/head coordinate transformation not found\n");
1069 return false;
1070 } else {
1071 mri_head_t = t_pTag->toCoordTrans();
1073 mri_head_t.invert_transform();
1075 qCritical("MRI/head coordinate transformation not found");
1076 return false;
1077 }
1078 }
1079 }
1080 inv.mri_head_t = mri_head_t;
1081
1082 //
1083 // get parent MEG info
1084 //
1085 t_pStream->read_meas_info_base(t_pStream->dirtree(), inv.info);
1086
1087 //
1088 // Transform the source spaces to the correct coordinate frame
1089 // if necessary
1090 //
1092 qCritical("Only inverse solutions computed in MRI or head coordinates are acceptable");
1093 //
1094 // Number of averages is initially one
1095 //
1096 inv.nave = 1;
1097 //
1098 // We also need the SSP operator
1099 //
1100 inv.projs = t_pStream->read_proj(t_pStream->dirtree());
1101
1102 // proj, whitener, reginv, and noisenorm are filled in by prepare_inverse_operator()
1103
1105 qCritical("Could not transform source space.\n");
1106 }
1107 qInfo("\tSource spaces transformed to the inverse solution coordinate frame\n");
1108 //
1109 // Done!
1110 //
1111
1112 return true;
1113}
1114
1115//=============================================================================================================
1116
1117bool MNEInverseOperator::write(QIODevice& p_IODevice)
1118{
1119 FiffStream::SPtr t_pStream = FiffStream::start_file(p_IODevice);
1120 if (!t_pStream) {
1121 return false;
1122 }
1123 qInfo("Write inverse operator decomposition in %s...", t_pStream->streamName().toUtf8().constData());
1124 this->writeToStream(t_pStream.data());
1125 t_pStream->end_file();
1126 return true;
1127}
1128
1129//=============================================================================================================
1130
1132{
1134
1135 qInfo("\tWriting inverse operator info...\n");
1136
1137 p_pStream->write_int(FIFF_MNE_INCLUDED_METHODS, &this->methods);
1140 p_pStream->write_int(FIFF_MNE_COORD_FRAME, &this->coord_frame);
1141
1142 // mne-python reads this to label the source estimates. It is optional
1143 // there, but omitting it leaves inv["units"] as None, so an operator that
1144 // travelled through MNE-CPP lost information the original file carried.
1145 if (this->units > 0)
1146 p_pStream->write_int(FIFF_MNE_INVERSE_SOURCE_UNIT, &this->units);
1148 VectorXf tmp_sing = this->sing.cast<float>();
1149 p_pStream->write_float(FIFF_MNE_INVERSE_SING, tmp_sing.data(), tmp_sing.size());
1150
1151 //
1152 // The eigenleads and eigenfields
1153 //
1154 if (this->eigen_leads_weighted) {
1155 FiffNamedMatrix tmpMatrix(*this->eigen_leads.data());
1156 tmpMatrix.transpose_named_matrix();
1158 } else {
1159 FiffNamedMatrix tmpMatrix(*this->eigen_leads.data());
1160 tmpMatrix.transpose_named_matrix();
1161 p_pStream->write_named_matrix(FIFF_MNE_INVERSE_LEADS, tmpMatrix);
1162 }
1163
1164 p_pStream->write_named_matrix(FIFF_MNE_INVERSE_FIELDS, *this->eigen_fields.data());
1165 qInfo("\t[done]\n");
1166 //
1167 // write the covariance matrices
1168 //
1169 qInfo("\tWriting noise covariance matrix.");
1170 p_pStream->write_cov(*this->noise_cov.data());
1171
1172 qInfo("\tWriting source covariance matrix.\n");
1173 p_pStream->write_cov(*this->source_cov.data());
1174 //
1175 // write the various priors
1176 //
1177 qInfo("\tWriting orientation priors.\n");
1178 if (this->orient_prior && !this->orient_prior->isEmpty())
1179 p_pStream->write_cov(*this->orient_prior.data());
1180 if (this->depth_prior && !this->depth_prior->isEmpty())
1181 p_pStream->write_cov(*this->depth_prior.data());
1182 if (this->fmri_prior && !this->fmri_prior->isEmpty())
1183 p_pStream->write_cov(*this->fmri_prior.data());
1184
1185 //
1186 // Parent MRI data
1187 //
1189 // write the MRI <-> head coordinate transformation
1190 p_pStream->write_coord_trans(this->mri_head_t);
1192
1193 //
1194 // Parent MEG measurement info
1195 //
1196 p_pStream->write_info_base(this->info);
1197
1198 //
1199 // Write the source spaces
1200 //
1201 if (!src.isEmpty())
1202 this->src.writeToStream(p_pStream);
1203
1204 //
1205 // We also need the SSP operator
1206 //
1207 p_pStream->write_proj(this->projs);
1208 //
1209 // Done!
1210 //
1212}
constexpr auto FIFF_MNE_SOURCE_SPACE_NPOINTS
constexpr auto FIFFV_MNE_ORIENT_PRIOR_COV
constexpr auto FIFFV_COORD_HEAD
constexpr auto FIFFV_MNE_FMRI_PRIOR_COV
constexpr auto FIFFV_MNE_MEG
constexpr auto FIFF_MNE_COORD_FRAME
constexpr auto FIFFV_MNE_SOURCE_COV
constexpr auto FIFF_MNE_INVERSE_FIELDS
constexpr auto FIFFB_MNE_INVERSE_SOLUTION
constexpr auto FIFF_MNE_INVERSE_LEADS_WEIGHTED
constexpr auto FIFFV_MNE_DEPTH_PRIOR_COV
constexpr auto FIFF_MNE_INVERSE_SOURCE_UNIT
constexpr auto FIFF_MNE_SOURCE_ORIENTATION
constexpr auto FIFFV_MNE_MEG_EEG
constexpr auto FIFFB_MNE_PARENT_MRI_FILE
constexpr auto FIFFV_MNE_EEG
constexpr auto FIFFV_COORD_MRI
constexpr auto FIFF_MNE_INVERSE_SOURCE_ORIENTATIONS
constexpr auto FIFF_MNE_INVERSE_SING
constexpr auto FIFFV_MNE_NOISE_COV
constexpr auto FIFF_MNE_INCLUDED_METHODS
constexpr auto FIFF_MNE_INVERSE_LEADS
constexpr auto FIFFV_MNE_FREE_ORI
constexpr auto FIFF_COORD_TRANS
Definition fiff_file.h:466
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)