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