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