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