v2.0.0
Loading...
Searching...
No Matches
mne_forward_solution.cpp
Go to the documentation of this file.
1//=============================================================================================================
18
19//=============================================================================================================
20// INCLUDES
21//=============================================================================================================
22
24
25#include <fiff/fiff.h>
27
28//=============================================================================================================
29// EIGEN INCLUDES
30//=============================================================================================================
31
32#include <Eigen/SVD>
33#include <Eigen/Dense>
34#include <Eigen/Sparse>
35#include <unsupported/Eigen/KroneckerProduct>
36
37//=============================================================================================================
38
39#include <fs/fs_colortable.h>
40#include <fs/fs_label.h>
41#include <fs/fs_surfaceset.h>
42#include <math/linalg.h>
43#include <math/kmeans.h>
44
45#include <algorithm>
46#include <QtConcurrent>
47#include <QFuture>
48#include <QRegularExpression>
49
50//=============================================================================================================
51// USED NAMESPACES
52//=============================================================================================================
53
54using namespace MNELIB;
55using namespace FSLIB;
56using namespace UTILSLIB;
57using namespace Eigen;
58using namespace FIFFLIB;
59
60//=============================================================================================================
61
62namespace {
63
64bool check_matching_chnames_conventions(const QStringList& chNamesA, const QStringList& chNamesB, bool bCheckForNewNamingConvention = false)
65{
66 bool bMatching = false;
67
68 if(chNamesA.isEmpty()) {
69 qWarning("Warning in check_matching_chnames_conventions - chNamesA list is empty. Nothing to compare");
70 }
71 if(chNamesB.isEmpty()) {
72 qWarning("Warning in check_matching_chnames_conventions - chNamesB list is empty. Nothing to compare");
73 }
74
75 QString replaceStringOldConv, replaceStringNewConv;
76
77 for(int i = 0; i < chNamesA.size(); ++i) {
78 if(chNamesB.contains(chNamesA.at(i))) {
79 bMatching = true;
80 } else if(bCheckForNewNamingConvention) {
81 replaceStringNewConv = chNamesA.at(i);
82 replaceStringNewConv.replace(" ","");
83
84 if(chNamesB.contains(replaceStringNewConv)) {
85 bMatching = true;
86 } else {
87 QRegularExpression xRegExp("[0-9]{1,100}");
88 QRegularExpressionMatch match = xRegExp.match(chNamesA.at(i));
89 QStringList xList = match.capturedTexts();
90
91 for(int k = 0; k < xList.size(); ++k) {
92 replaceStringOldConv = chNamesA.at(i);
93 replaceStringOldConv.replace(xList.at(k),QString("%1%2").arg(" ").arg(xList.at(k)));
94
95 if(chNamesB.contains(replaceStringNewConv) || chNamesB.contains(replaceStringOldConv)) {
96 bMatching = true;
97 } else {
98 bMatching = false;
99 }
100 }
101 }
102 }
103 }
104
105 return bMatching;
106}
107
108} // anonymous namespace
109
110//=============================================================================================================
111// CONSTANTS
112//=============================================================================================================
113
114constexpr int FAIL = -1;
115constexpr int OK = 0;
116
117constexpr int X = 0;
118constexpr int Y = 1;
119constexpr int Z = 2;
120
121//=============================================================================================================
122// DEFINE MEMBER METHODS
123//=============================================================================================================
124
126: source_ori(-1)
127, surf_ori(false)
128, coord_frame(-1)
129, nsource(-1)
130, nchan(-1)
131, sol(new FiffNamedMatrix)
133, source_rr(MatrixX3f::Zero(0,3))
134, source_nn(MatrixX3f::Zero(0,3))
135{
136}
137
138//=============================================================================================================
139
140MNEForwardSolution::MNEForwardSolution(QIODevice &p_IODevice, bool force_fixed, bool surf_ori, const QStringList& include, const QStringList& exclude, bool bExcludeBads)
141: source_ori(-1)
143, coord_frame(-1)
144, nsource(-1)
145, nchan(-1)
146, sol(new FiffNamedMatrix)
148, source_rr(MatrixX3f::Zero(0,3))
149, source_nn(MatrixX3f::Zero(0,3))
150{
151 if(!read(p_IODevice, *this, force_fixed, surf_ori, include, exclude, bExcludeBads))
152 {
153 qWarning("\tForward solution not found.");
154 return;
155 }
156}
157
158//=============================================================================================================
159
161: info(p_MNEForwardSolution.info)
162, source_ori(p_MNEForwardSolution.source_ori)
163, surf_ori(p_MNEForwardSolution.surf_ori)
164, coord_frame(p_MNEForwardSolution.coord_frame)
165, nsource(p_MNEForwardSolution.nsource)
166, nchan(p_MNEForwardSolution.nchan)
167, sol(p_MNEForwardSolution.sol)
168, sol_grad(p_MNEForwardSolution.sol_grad)
169, mri_head_t(p_MNEForwardSolution.mri_head_t)
170, mri_filename(p_MNEForwardSolution.mri_filename)
171, mri_id(p_MNEForwardSolution.mri_id)
172, src(p_MNEForwardSolution.src)
173, source_rr(p_MNEForwardSolution.source_rr)
174, source_nn(p_MNEForwardSolution.source_nn)
175{
176}
177
178//=============================================================================================================
179
181{
182 if (this != &other) {
183 info = other.info;
184 source_ori = other.source_ori;
185 surf_ori = other.surf_ori;
186 coord_frame = other.coord_frame;
187 nsource = other.nsource;
188 nchan = other.nchan;
189 sol = other.sol;
190 sol_grad = other.sol_grad;
191 mri_head_t = other.mri_head_t;
193 mri_id = other.mri_id;
194 src = other.src;
195 source_rr = other.source_rr;
196 source_nn = other.source_nn;
197 }
198 return *this;
199}
200
201//=============================================================================================================
202
206
207//=============================================================================================================
208
210{
211 info.clear();
212 source_ori = -1;
213 surf_ori = false;
214 coord_frame = -1;
215 nsource = -1;
216 nchan = -1;
219 mri_head_t.clear();
220 mri_filename.clear();
221 mri_id.clear();
222 src.clear();
223 source_rr = MatrixX3f(0,3);
224 source_nn = MatrixX3f(0,3);
225}
226
227//=============================================================================================================
228
229bool MNEForwardSolution::write(QIODevice& p_IODevice) const
230{
231 //
232 // Classify channels into MEG and EEG index sets
233 //
234 std::vector<int> megIdx, eegIdx;
235 for (int k = 0; k < info.chs.size(); ++k) {
236 fiff_int_t kind = info.chs[k].kind;
237 if (kind == FIFFV_MEG_CH || kind == FIFFV_REF_MEG_CH)
238 megIdx.push_back(k);
239 else if (kind == FIFFV_EEG_CH)
240 eegIdx.push_back(k);
241 }
242 int nmeg = megIdx.size();
243 int neeg = eegIdx.size();
244
245 //
246 // Compute the total number of active source vertices
247 //
248 int nvert = 0;
249 for (int k = 0; k < src.size(); ++k)
250 nvert += src[k].nuse;
251
252 //
253 // Open the file, create the directory
254 //
255 FiffStream::SPtr t_pStream = FiffStream::start_file(p_IODevice);
256 t_pStream->start_block(FIFFB_MNE);
257
258 //
259 // Information from the MRI file
260 //
261 {
262 t_pStream->start_block(FIFFB_MNE_PARENT_MRI_FILE);
263
264 if (!mri_filename.isEmpty())
265 t_pStream->write_string(FIFF_MNE_FILE_NAME, mri_filename);
266 if (!mri_id.isEmpty())
267 t_pStream->write_id(FIFF_PARENT_FILE_ID, mri_id);
268 t_pStream->write_coord_trans(mri_head_t);
269
270 t_pStream->end_block(FIFFB_MNE_PARENT_MRI_FILE);
271 }
272
273 //
274 // Information from the measurement file
275 //
276 {
277 t_pStream->start_block(FIFFB_MNE_PARENT_MEAS_FILE);
278
279 if (!info.filename.isEmpty())
280 t_pStream->write_string(FIFF_MNE_FILE_NAME, info.filename);
281 if (!info.meas_id.isEmpty())
282 t_pStream->write_id(FIFF_PARENT_BLOCK_ID, info.meas_id);
283 t_pStream->write_coord_trans(info.dev_head_t);
284
285 int totalChan = nmeg + neeg;
286 t_pStream->write_int(FIFF_NCHAN, &totalChan);
287
288 // Write channel infos with sequential scanNo
289 QList<FiffChInfo> allChs;
290 for (int k = 0; k < nmeg; ++k)
291 allChs.append(info.chs[megIdx[k]]);
292 for (int k = 0; k < neeg; ++k)
293 allChs.append(info.chs[eegIdx[k]]);
294 for (int p = 0; p < allChs.size(); ++p) {
295 allChs[p].scanNo = p + 1;
296 t_pStream->write_ch_info(allChs[p]);
297 }
298
299 t_pStream->write_bad_channels(info.bads);
300
301 t_pStream->end_block(FIFFB_MNE_PARENT_MEAS_FILE);
302 }
303
304 //
305 // Write the source spaces
306 //
307 for (int k = 0; k < src.size(); ++k) {
308 if (src[k].writeToStream(t_pStream, false) == FIFF_FAIL) {
309 t_pStream->close();
310 return false;
311 }
312 }
313
314 //
315 // Extract sub-matrices for MEG and EEG from the combined sol
316 //
317 auto extractRows = [](const FiffNamedMatrix& combined,
318 const std::vector<int>& rowIdx) -> FiffNamedMatrix
319 {
320 int nRows = rowIdx.size();
321 int nCols = combined.ncol;
322 MatrixXd data(nRows, nCols);
323 QStringList row_names;
324 for (int r = 0; r < nRows; ++r) {
325 data.row(r) = combined.data.row(rowIdx[r]);
326 row_names.append(combined.row_names[rowIdx[r]]);
327 }
328 FiffNamedMatrix sub;
329 sub.nrow = nRows;
330 sub.ncol = nCols;
331 sub.row_names = row_names;
332 sub.col_names = combined.col_names;
333 sub.data = data;
334 return sub;
335 };
336
338 int frame = coord_frame;
339
340 //
341 // MEG forward solution
342 //
343 if (nmeg > 0) {
344 t_pStream->start_block(FIFFB_MNE_FORWARD_SOLUTION);
345
346 int val = FIFFV_MNE_MEG;
347 t_pStream->write_int(FIFF_MNE_INCLUDED_METHODS, &val);
348 t_pStream->write_int(FIFF_MNE_COORD_FRAME, &frame);
349 t_pStream->write_int(FIFF_MNE_SOURCE_ORIENTATION, &ori_val);
350 t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NPOINTS, &nvert);
351 t_pStream->write_int(FIFF_NCHAN, &nmeg);
352
353 FiffNamedMatrix megSol = extractRows(*sol.data(), megIdx);
354 megSol.transpose_named_matrix();
355 t_pStream->write_named_matrix(FIFF_MNE_FORWARD_SOLUTION, megSol);
356
357 if (!sol_grad->isEmpty()) {
358 FiffNamedMatrix megGrad = extractRows(*sol_grad.data(), megIdx);
359 megGrad.transpose_named_matrix();
360 t_pStream->write_named_matrix(FIFF_MNE_FORWARD_SOLUTION_GRAD, megGrad);
361 }
362 t_pStream->end_block(FIFFB_MNE_FORWARD_SOLUTION);
363 }
364
365 //
366 // EEG forward solution
367 //
368 if (neeg > 0) {
369 t_pStream->start_block(FIFFB_MNE_FORWARD_SOLUTION);
370
371 int val = FIFFV_MNE_EEG;
372 t_pStream->write_int(FIFF_MNE_INCLUDED_METHODS, &val);
373 t_pStream->write_int(FIFF_MNE_COORD_FRAME, &frame);
374 t_pStream->write_int(FIFF_MNE_SOURCE_ORIENTATION, &ori_val);
375 t_pStream->write_int(FIFF_NCHAN, &neeg);
376 t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NPOINTS, &nvert);
377
378 FiffNamedMatrix eegSol = extractRows(*sol.data(), eegIdx);
379 eegSol.transpose_named_matrix();
380 t_pStream->write_named_matrix(FIFF_MNE_FORWARD_SOLUTION, eegSol);
381
382 if (!sol_grad->isEmpty()) {
383 FiffNamedMatrix eegGrad = extractRows(*sol_grad.data(), eegIdx);
384 eegGrad.transpose_named_matrix();
385 t_pStream->write_named_matrix(FIFF_MNE_FORWARD_SOLUTION_GRAD, eegGrad);
386 }
387 t_pStream->end_block(FIFFB_MNE_FORWARD_SOLUTION);
388 }
389
390 t_pStream->end_block(FIFFB_MNE);
391 t_pStream->end_file();
392 t_pStream->close();
393 t_pStream.clear();
394
395 //
396 // Update the directory
397 //
398 if (auto* qf = dynamic_cast<QFile*>(&p_IODevice)) {
399 QFile fileIn(qf->fileName());
400 FiffStream::SPtr t_pStreamIn = FiffStream::open_update(fileIn);
401 if (t_pStreamIn) {
402 const auto& dir = t_pStreamIn->dir();
403 for (int i = 0; i < dir.size(); ++i) {
404 if (dir[i]->kind == FIFF_DIR_POINTER) {
405 fiff_int_t dirpos = (fiff_int_t)t_pStreamIn->write_dir_entries(dir);
406 if (dirpos >= 0)
407 t_pStreamIn->write_dir_pointer(dirpos, dir[i]->pos);
408 break;
409 }
410 }
411 t_pStreamIn->close();
412 }
413 }
414
415 return true;
416}
417
418//=============================================================================================================
419
421 qint32 p_iClusterSize,
422 MatrixXd& p_D,
423 const FiffCov &p_pNoise_cov,
424 const FiffInfo &p_pInfo,
425 QString p_sMethod) const
426{
427 qInfo("Cluster forward solution using %s.", p_sMethod.toUtf8().constData());
428
429 MNEForwardSolution p_fwdOut = MNEForwardSolution(*this);
430
431 //Check if cov naming conventions are matching
432 if(!check_matching_chnames_conventions(p_pNoise_cov.names, p_pInfo.ch_names) && !p_pNoise_cov.names.isEmpty() && !p_pInfo.ch_names.isEmpty()) {
433 if(check_matching_chnames_conventions(p_pNoise_cov.names, p_pInfo.ch_names, true)) {
434 qWarning("MNEForwardSolution::cluster_forward_solution - Cov names do match with info channel names but have a different naming convention.");
435 //return p_fwdOut;
436 } else {
437 qWarning("MNEForwardSolution::cluster_forward_solution - Cov channel names do not match with info channel names.");
438 //return p_fwdOut;
439 }
440 }
441
442 //
443 // Check consisty
444 //
445 if(this->isFixedOrient())
446 {
447 qWarning("Error: Fixed orientation not implemented yet!");
448 return p_fwdOut;
449 }
450
451 MatrixXd t_G_Whitened(0,0);
452 bool t_bUseWhitened = false;
453 //
454 //Whiten gain matrix before clustering -> cause diffenerent units Magnetometer, Gradiometer and EEG
455 //
456 if(!p_pNoise_cov.isEmpty() && !p_pInfo.isEmpty())
457 {
458 FiffInfo p_outFwdInfo;
459 FiffCov p_outNoiseCov;
460 MatrixXd p_outWhitener;
461 qint32 p_outNumNonZero;
462 //do whitening with noise cov
463 this->prepare_forward(p_pInfo, p_pNoise_cov, false, p_outFwdInfo, t_G_Whitened, p_outNoiseCov, p_outWhitener, p_outNumNonZero);
464 qInfo("\tWhitening the forward solution.");
465
466 t_G_Whitened = p_outWhitener*t_G_Whitened;
467 t_bUseWhitened = true;
468 }
469
470 //
471 // Assemble input data
472 //
473 qint32 count;
474 qint32 offset;
475
476 MatrixXd t_G_new;
477
478 for(qint32 h = 0; h < this->src.size(); ++h )
479 {
480
481 count = 0;
482 offset = 0;
483
484 // Offset for continuous indexing;
485 if(h > 0)
486 for(qint32 j = 0; j < h; ++j)
487 offset += this->src[j].nuse;
488
489 if(h == 0)
490 qInfo("Cluster Left Hemisphere");
491 else
492 qInfo("Cluster Right Hemisphere");
493
494 const FsAnnotation annotation = p_AnnotationSet[h];
495 FsColortable t_CurrentColorTable = annotation.getColortable();
496 VectorXi label_ids = t_CurrentColorTable.getLabelIds();
497
498 // Get label ids for every vertex
499 VectorXi vertno_labeled = VectorXi::Zero(this->src[h].vertno.rows());
500
501 //ToDo make this more universal -> using FsLabel instead of annotations - obsolete when using Labels
502 for(qint32 i = 0; i < vertno_labeled.rows(); ++i)
503 vertno_labeled[i] = p_AnnotationSet[h].getLabelIds()[this->src[h].vertno[i]];
504
505 std::vector<RegionData> regionDataIn;
506
507 //
508 // Generate cluster input data
509 //
510 for (qint32 i = 0; i < label_ids.rows(); ++i)
511 {
512 if (label_ids[i] != 0)
513 {
514 QString curr_name = t_CurrentColorTable.struct_names[i];//obj.label2AtlasName(label(i));
515 qInfo("\tCluster %d / %ld %s...", i+1, label_ids.rows(), curr_name.toUtf8().constData());
516
517 //
518 // Get source space indeces
519 //
520 VectorXi idcs = VectorXi::Zero(vertno_labeled.rows());
521 qint32 c = 0;
522
523 //Select ROIs //change this use label info with a hash tabel
524 for(qint32 j = 0; j < vertno_labeled.rows(); ++j)
525 {
526 if(vertno_labeled[j] == label_ids[i])
527 {
528 idcs[c] = j;
529 ++c;
530 }
531 }
532 idcs.conservativeResize(c);
533
534 //get selected G
535 MatrixXd t_G(this->sol->data.rows(), idcs.rows()*3);
536 MatrixXd t_G_Whitened_Roi(t_G_Whitened.rows(), idcs.rows()*3);
537
538 for(qint32 j = 0; j < idcs.rows(); ++j)
539 {
540 t_G.block(0, j*3, t_G.rows(), 3) = this->sol->data.block(0, (idcs[j]+offset)*3, t_G.rows(), 3);
541 if(t_bUseWhitened)
542 t_G_Whitened_Roi.block(0, j*3, t_G_Whitened_Roi.rows(), 3) = t_G_Whitened.block(0, (idcs[j]+offset)*3, t_G_Whitened_Roi.rows(), 3);
543 }
544
545 qint32 nSens = t_G.rows();
546 qint32 nSources = t_G.cols()/3;
547
548 if (nSources > 0)
549 {
550 RegionData t_sensG;
551
552 t_sensG.idcs = idcs;
553 t_sensG.iLabelIdxIn = i;
554 t_sensG.nClusters = static_cast<int>(ceil(static_cast<double>(nSources) / static_cast<double>(p_iClusterSize)));
555
556 t_sensG.matRoiGOrig = t_G;
557
558 qInfo("%d Cluster(s)...", t_sensG.nClusters);
559
560 // Reshape Input data -> sources rows; sensors columns
561 t_sensG.matRoiG = MatrixXd(t_G.cols()/3, 3*nSens);
562 if(t_bUseWhitened)
563 t_sensG.matRoiGWhitened = MatrixXd(t_G_Whitened_Roi.cols()/3, 3*nSens);
564
565 for(qint32 j = 0; j < nSens; ++j)
566 {
567 for(qint32 k = 0; k < t_sensG.matRoiG.rows(); ++k)
568 t_sensG.matRoiG.block(k,j*3,1,3) = t_G.block(j,k*3,1,3);
569 if(t_bUseWhitened)
570 for(qint32 k = 0; k < t_sensG.matRoiGWhitened.rows(); ++k)
571 t_sensG.matRoiGWhitened.block(k,j*3,1,3) = t_G_Whitened_Roi.block(j,k*3,1,3);
572 }
573
574 t_sensG.bUseWhitened = t_bUseWhitened;
575
576 t_sensG.sDistMeasure = p_sMethod;
577
578 regionDataIn.push_back(std::move(t_sensG));
579
580 qInfo("[added]");
581 }
582 else
583 {
584 qWarning("failed! FsLabel contains no sources.");
585 }
586 }
587 }
588
589 //
590 // Calculate clusters
591 //
592 qInfo("Clustering...");
593 QFuture< RegionDataOut > res;
594 res = QtConcurrent::mapped(regionDataIn, &RegionData::cluster);
595 res.waitForFinished();
596
597 //
598 // Assign results
599 //
600 MatrixXd t_G_partial;
601
602 qint32 nClusters;
603 qint32 nSens;
604 auto itIn = regionDataIn.cbegin();
605 QFuture<RegionDataOut>::const_iterator itOut;
606 for (itOut = res.constBegin(); itOut != res.constEnd(); ++itOut)
607 {
608 nClusters = itOut->ctrs.rows();
609 nSens = itOut->ctrs.cols()/3;
610 t_G_partial = MatrixXd::Zero(nSens, nClusters*3);
611
612 //
613 // Assign the centroid for each cluster to the partial G
614 //
615 //ToDo change this use indeces found with whitened data
616 for(qint32 j = 0; j < nSens; ++j)
617 for(qint32 k = 0; k < nClusters; ++k)
618 t_G_partial.block(j, k*3, 1, 3) = itOut->ctrs.block(k,j*3,1,3);
619
620 //
621 // Get cluster indizes and its distances to the centroid
622 //
623 for(qint32 j = 0; j < nClusters; ++j)
624 {
625 VectorXi clusterIdcs = VectorXi::Zero(itOut->roiIdx.rows());
626 VectorXd clusterDistance = VectorXd::Zero(itOut->roiIdx.rows());
627 MatrixX3f clusterSource_rr = MatrixX3f::Zero(itOut->roiIdx.rows(), 3);
628 qint32 nClusterIdcs = 0;
629 for(qint32 k = 0; k < itOut->roiIdx.rows(); ++k)
630 {
631 if(itOut->roiIdx[k] == j)
632 {
633 clusterIdcs[nClusterIdcs] = itIn->idcs[k];
634
635 qint32 offset = h == 0 ? 0 : this->src[0].nuse;
636 clusterSource_rr.row(nClusterIdcs) = this->source_rr.row(offset + itIn->idcs[k]);
637 clusterDistance[nClusterIdcs] = itOut->D(k,j);
638 ++nClusterIdcs;
639 }
640 }
641 clusterIdcs.conservativeResize(nClusterIdcs);
642 clusterSource_rr.conservativeResize(nClusterIdcs,3);
643 clusterDistance.conservativeResize(nClusterIdcs);
644
645 VectorXi clusterVertnos = VectorXi::Zero(clusterIdcs.size());
646 for(qint32 k = 0; k < clusterVertnos.size(); ++k)
647 clusterVertnos(k) = this->src[h].vertno[clusterIdcs(k)];
648
649 p_fwdOut.src.hemisphereAt(h)->cluster_info.clusterVertnos.append(clusterVertnos);
650 p_fwdOut.src.hemisphereAt(h)->cluster_info.clusterSource_rr.append(clusterSource_rr);
651 p_fwdOut.src.hemisphereAt(h)->cluster_info.clusterDistances.append(clusterDistance);
652 p_fwdOut.src.hemisphereAt(h)->cluster_info.clusterLabelIds.append(label_ids[itOut->iLabelIdxOut]);
653 p_fwdOut.src.hemisphereAt(h)->cluster_info.clusterLabelNames.append(t_CurrentColorTable.getNames()[itOut->iLabelIdxOut]);
654 }
655
656 //
657 // Assign partial G to new LeadField
658 //
659 if(t_G_partial.rows() > 0 && t_G_partial.cols() > 0)
660 {
661 t_G_new.conservativeResize(t_G_partial.rows(), t_G_new.cols() + t_G_partial.cols());
662 t_G_new.block(0, t_G_new.cols() - t_G_partial.cols(), t_G_new.rows(), t_G_partial.cols()) = t_G_partial;
663
664 // Map the centroids to the closest rr
665 for(qint32 k = 0; k < nClusters; ++k)
666 {
667 qint32 j = 0;
668
669 double sqec = sqrt((itIn->matRoiGOrig.block(0, j*3, itIn->matRoiGOrig.rows(), 3) - t_G_partial.block(0, k*3, t_G_partial.rows(), 3)).array().pow(2).sum());
670 double sqec_min = sqec;
671 qint32 j_min = 0;
672 for(qint32 j = 1; j < itIn->idcs.rows(); ++j)
673 {
674 sqec = sqrt((itIn->matRoiGOrig.block(0, j*3, itIn->matRoiGOrig.rows(), 3) - t_G_partial.block(0, k*3, t_G_partial.rows(), 3)).array().pow(2).sum());
675
676 if(sqec < sqec_min)
677 {
678 sqec_min = sqec;
679 j_min = j;
680 }
681 }
682
683 // Take the closest coordinates
684 qint32 sel_idx = itIn->idcs[j_min];
685
686 p_fwdOut.src.hemisphereAt(h)->cluster_info.centroidVertno.append(this->src[h].vertno[sel_idx]);
687 p_fwdOut.src.hemisphereAt(h)->cluster_info.centroidSource_rr.append(this->src[h].rr.row(this->src[h].vertno[sel_idx]));
688 // Option 2 label ID
689 p_fwdOut.src[h].vertno[count] = p_fwdOut.src.hemisphereAt(h)->cluster_info.clusterLabelIds[count];
690
691 ++count;
692 }
693 }
694
695 ++itIn;
696 }
697
698 //
699 // Assemble new hemisphere information
700 //
701 p_fwdOut.src[h].vertno.conservativeResize(count);
702
703 qInfo("[done]");
704 }
705
706 //
707 // Cluster operator D (sources x clusters)
708 //
709 qint32 totalNumOfClust = 0;
710 for (qint32 h = 0; h < 2; ++h)
711 totalNumOfClust += p_fwdOut.src.hemisphereAt(h)->cluster_info.clusterVertnos.size();
712
713 if(this->isFixedOrient())
714 p_D = MatrixXd::Zero(this->sol->data.cols(), totalNumOfClust);
715 else
716 p_D = MatrixXd::Zero(this->sol->data.cols(), totalNumOfClust*3);
717
718 QList<VectorXi> t_vertnos = this->src.get_vertno();
719
720 qint32 currentCluster = 0;
721 for (qint32 h = 0; h < 2; ++h)
722 {
723 int hemiOffset = h == 0 ? 0 : t_vertnos[0].size();
724 for(qint32 i = 0; i < p_fwdOut.src.hemisphereAt(h)->cluster_info.clusterVertnos.size(); ++i)
725 {
726 VectorXi idx_sel;
727 Linalg::intersect(t_vertnos[h], p_fwdOut.src.hemisphereAt(h)->cluster_info.clusterVertnos[i], idx_sel);
728
729 idx_sel.array() += hemiOffset;
730
731 double selectWeight = 1.0/idx_sel.size();
732 if(this->isFixedOrient())
733 {
734 for(qint32 j = 0; j < idx_sel.size(); ++j)
735 p_D.col(currentCluster)[idx_sel(j)] = selectWeight;
736 }
737 else
738 {
739 qint32 clustOffset = currentCluster*3;
740 for(qint32 j = 0; j < idx_sel.size(); ++j)
741 {
742 qint32 idx_sel_Offset = idx_sel(j)*3;
743 //x
744 p_D(idx_sel_Offset,clustOffset) = selectWeight;
745 //y
746 p_D(idx_sel_Offset+1, clustOffset+1) = selectWeight;
747 //z
748 p_D(idx_sel_Offset+2, clustOffset+2) = selectWeight;
749 }
750 }
751 ++currentCluster;
752 }
753 }
754
755 //
756 // Put it all together
757 //
758 p_fwdOut.sol->data = t_G_new;
759 p_fwdOut.sol->ncol = t_G_new.cols();
760
761 p_fwdOut.nsource = p_fwdOut.sol->ncol/3;
762
763 return p_fwdOut;
764}
765
766//=============================================================================================================
767
768MNEForwardSolution MNEForwardSolution::reduce_forward_solution(qint32 p_iNumDipoles, MatrixXd& p_D) const
769{
770 MNEForwardSolution p_fwdOut = MNEForwardSolution(*this);
771
772 bool isFixed = p_fwdOut.isFixedOrient();
773 qint32 np = isFixed ? p_fwdOut.sol->data.cols() : p_fwdOut.sol->data.cols()/3;
774
775 if(p_iNumDipoles > np)
776 return p_fwdOut;
777
778 VectorXi sel(p_iNumDipoles);
779
780 float t_fStep = static_cast<float>(np) / static_cast<float>(p_iNumDipoles);
781
782 for(qint32 i = 0; i < p_iNumDipoles; ++i)
783 {
784 float t_fCurrent = static_cast<float>(i) * t_fStep;
785 sel[i] = (quint32)floor(t_fCurrent);
786 }
787
788 if(isFixed)
789 {
790 p_D = MatrixXd::Zero(p_fwdOut.sol->data.cols(), p_iNumDipoles);
791 for(qint32 i = 0; i < p_iNumDipoles; ++i)
792 p_D(sel[i], i) = 1;
793 }
794 else
795 {
796 p_D = MatrixXd::Zero(p_fwdOut.sol->data.cols(), p_iNumDipoles*3);
797 for(qint32 i = 0; i < p_iNumDipoles; ++i)
798 for(qint32 j = 0; j < 3; ++j)
799 p_D((sel[i]*3)+j, (i*3)+j) = 1;
800 }
801
802 // New gain matrix
803 p_fwdOut.sol->data = this->sol->data * p_D;
804
805 MatrixX3f rr(p_iNumDipoles,3);
806
807 MatrixX3f nn(p_iNumDipoles,3);
808
809 for(qint32 i = 0; i < p_iNumDipoles; ++i)
810 {
811 rr.row(i) = this->source_rr.row(sel(i));
812 nn.row(i) = this->source_nn.row(sel(i));
813 }
814
815 p_fwdOut.source_rr = rr;
816 p_fwdOut.source_nn = nn;
817
818 p_fwdOut.sol->ncol = p_fwdOut.sol->data.cols();
819
820 p_fwdOut.nsource = p_iNumDipoles;
821
822 return p_fwdOut;
823}
824
825//=============================================================================================================
826
827FiffCov MNEForwardSolution::compute_depth_prior(const MatrixXd &Gain, const FiffInfo &gain_info, bool is_fixed_ori, double exp, double limit, const MatrixXd &patch_areas, bool limit_depth_chs)
828{
829 qInfo("\tCreating the depth weighting matrix...");
830
831 MatrixXd G(Gain);
832 // If possible, pick best depth-weighting channels
833 if(limit_depth_chs)
835
836 VectorXd d;
837 // Compute the gain matrix
838 if(is_fixed_ori)
839 {
840 d = (G.array().square()).rowwise().sum();
841 }
842 else
843 {
844 qint32 n_pos = G.cols() / 3;
845 d = VectorXd::Zero(n_pos);
846 MatrixXd Gk;
847 for (qint32 k = 0; k < n_pos; ++k)
848 {
849 Gk = G.block(0,3*k, G.rows(), 3);
850 JacobiSVD<MatrixXd> svd(Gk.transpose()*Gk);
851 d[k] = svd.singularValues().maxCoeff();
852 }
853 }
854
855 // ToDo Currently the fwd solns never have "patch_areas" defined
856 if(patch_areas.cols() > 0)
857 {
858 qWarning("\tToDo!!!!! >>> Patch areas taken into account in the depth weighting");
859 }
860
861 qint32 n_limit;
862 VectorXd w = d.cwiseInverse();
863 VectorXd ws = w;
864 VectorXd wpp;
865 Linalg::sort<double>(ws, false);
866 double weight_limit = pow(limit, 2);
867 if (!limit_depth_chs)
868 {
869 // match old mne-python behavor
870 qint32 ind = 0;
871 ws.minCoeff(&ind);
872 n_limit = ind;
873 limit = ws[ind] * weight_limit;
874 }
875 else
876 {
877 // match C code behavior
878 limit = ws[ws.size()-1];
879 qint32 ind = 0;
880 n_limit = d.size();
881 if (ws[ws.size()-1] > weight_limit * ws[0])
882 {
883 double th = weight_limit * ws[0];
884 for(qint32 i = 0; i < ws.size(); ++i)
885 {
886 if(ws[i] > th)
887 {
888 ind = i;
889 break;
890 }
891 }
892 limit = ws[ind];
893 n_limit = ind;
894 }
895 }
896
897 qInfo("\tlimit = %d/%ld = %f", n_limit + 1, d.size(), sqrt(limit / ws[0]));
898 double scale = 1.0 / limit;
899 qInfo("\tscale = %g exp = %g", scale, exp);
900
901 VectorXd t_w = w.array() / limit;
902 for(qint32 i = 0; i < t_w.size(); ++i)
903 t_w[i] = t_w[i] > 1 ? 1 : t_w[i];
904 wpp = t_w.array().pow(exp);
905
906 FiffCov depth_prior;
907 if(is_fixed_ori)
908 depth_prior.data = wpp;
909 else
910 {
911 depth_prior.data.resize(wpp.rows()*3, 1);
912 qint32 idx = 0;
913 double v;
914 for(qint32 i = 0; i < wpp.rows(); ++i)
915 {
916 idx = i*3;
917 v = wpp[i];
918 depth_prior.data(idx, 0) = v;
919 depth_prior.data(idx+1, 0) = v;
920 depth_prior.data(idx+2, 0) = v;
921 }
922 }
923
924 depth_prior.kind = FIFFV_MNE_DEPTH_PRIOR_COV;
925 depth_prior.diag = true;
926 depth_prior.dim = depth_prior.data.rows();
927 depth_prior.nfree = 1;
928
929 return depth_prior;
930}
931
932//=============================================================================================================
933
935{
936 bool is_fixed_ori = this->isFixedOrient();
937 qint32 n_sources = this->sol->data.cols();
938
939 if (0 <= loose && loose <= 1)
940 {
941 qDebug() << "this->surf_ori" << this->surf_ori;
942 if(loose < 1 && !this->surf_ori)
943 {
944 qWarning("\tForward operator is not oriented in surface coordinates. loose parameter should be None not %f.", loose);
945 loose = 1;
946 qInfo("\tSetting loose to %f.", loose);
947 }
948
949 if(is_fixed_ori)
950 {
951 qInfo("\tIgnoring loose parameter with forward operator with fixed orientation.");
952 loose = 0.0;
953 }
954 }
955 else
956 {
957 if(loose < 0 || loose > 1)
958 {
959 qWarning("Warning: Loose value should be in interval [0,1] not %f.\n", loose);
960 loose = loose > 1 ? 1 : 0;
961 qInfo("Setting loose to %f.", loose);
962 }
963 }
964
965 FiffCov orient_prior;
966 orient_prior.data = VectorXd::Ones(n_sources);
967 if(!is_fixed_ori && (0 <= loose && loose <= 1))
968 {
969 qInfo("\tApplying loose dipole orientations. Loose value of %f.", loose);
970 for(qint32 i = 0; i < n_sources; i+=3)
971 orient_prior.data.block(i,0,2,1).array() *= loose;
972
973 orient_prior.kind = FIFFV_MNE_ORIENT_PRIOR_COV;
974 orient_prior.diag = true;
975 orient_prior.dim = orient_prior.data.size();
976 orient_prior.nfree = 1;
977 }
978 return orient_prior;
979}
980
981//=============================================================================================================
982
984 const QStringList& exclude) const
985{
986 MNEForwardSolution fwd(*this);
987
988 if(include.size() == 0 && exclude.size() == 0)
989 return fwd;
990
991 RowVectorXi sel = FiffInfo::pick_channels(fwd.sol->row_names, include, exclude);
992
993 // Do we have something?
994 quint32 nuse = sel.size();
995
996 if (nuse == 0)
997 {
998 qInfo("Nothing remains after picking. Returning original forward solution.");
999 return fwd;
1000 }
1001 qInfo("\t%d out of %d channels remain after picking", nuse, fwd.nchan);
1002
1003 // Pick the correct rows of the forward operator
1004 MatrixXd newData(nuse, fwd.sol->data.cols());
1005 for(quint32 i = 0; i < nuse; ++i)
1006 newData.row(i) = fwd.sol->data.row(sel[i]);
1007
1008 fwd.sol->data = newData;
1009 fwd.sol->nrow = nuse;
1010
1011 QStringList ch_names;
1012 for(qint32 i = 0; i < sel.cols(); ++i)
1013 ch_names << fwd.sol->row_names[sel(i)];
1014 fwd.nchan = nuse;
1015 fwd.sol->row_names = ch_names;
1016
1017 QList<FiffChInfo> chs;
1018 for(qint32 i = 0; i < sel.cols(); ++i)
1019 chs.append(fwd.info.chs[sel(i)]);
1020 fwd.info.chs = chs;
1021 fwd.info.nchan = nuse;
1022
1023 QStringList bads;
1024 for(qint32 i = 0; i < fwd.info.bads.size(); ++i)
1025 if(ch_names.contains(fwd.info.bads[i]))
1026 bads.append(fwd.info.bads[i]);
1027 fwd.info.bads = bads;
1028
1029 if(!fwd.sol_grad->isEmpty())
1030 {
1031 newData.resize(nuse, fwd.sol_grad->data.cols());
1032 for(quint32 i = 0; i < nuse; ++i)
1033 newData.row(i) = fwd.sol_grad->data.row(sel[i]);
1034 fwd.sol_grad->data = newData;
1035 fwd.sol_grad->nrow = nuse;
1036 QStringList row_names;
1037 for(qint32 i = 0; i < sel.cols(); ++i)
1038 row_names << fwd.sol_grad->row_names[sel(i)];
1039 fwd.sol_grad->row_names = row_names;
1040 }
1041
1042 return fwd;
1043}
1044
1045//=============================================================================================================
1046
1047MNEForwardSolution MNEForwardSolution::pick_regions(const QList<FsLabel> &p_qListLabels) const
1048{
1049 VectorXi selVertices;
1050
1051 qint32 iSize = 0;
1052 for(qint32 i = 0; i < p_qListLabels.size(); ++i)
1053 {
1054 VectorXi currentSelection;
1055 this->src.label_src_vertno_sel(p_qListLabels[i], currentSelection);
1056
1057 selVertices.conservativeResize(iSize+currentSelection.size());
1058 selVertices.block(iSize,0,currentSelection.size(),1) = currentSelection;
1059 iSize = selVertices.size();
1060 }
1061
1062Linalg::sort(selVertices, false);
1063
1064 MNEForwardSolution selectedFwd(*this);
1065
1066 MatrixX3f rr(selVertices.size(),3);
1067 MatrixX3f nn(selVertices.size(),3);
1068
1069 for(qint32 i = 0; i < selVertices.size(); ++i)
1070 {
1071 rr.block(i, 0, 1, 3) = selectedFwd.source_rr.row(selVertices[i]);
1072 nn.block(i, 0, 1, 3) = selectedFwd.source_nn.row(selVertices[i]);
1073 }
1074
1075 selectedFwd.source_rr = rr;
1076 selectedFwd.source_nn = nn;
1077
1078 VectorXi selSolIdcs = tripletSelection(selVertices);
1079 MatrixXd G(selectedFwd.sol->data.rows(),selSolIdcs.size());
1080 qint32 rows = G.rows();
1081
1082 for(qint32 i = 0; i < selSolIdcs.size(); ++i)
1083 G.block(0, i, rows, 1) = selectedFwd.sol->data.col(selSolIdcs[i]);
1084
1085 selectedFwd.sol->data = G;
1086 selectedFwd.sol->nrow = selectedFwd.sol->data.rows();
1087 selectedFwd.sol->ncol = selectedFwd.sol->data.cols();
1088 selectedFwd.nsource = selectedFwd.sol->ncol / 3;
1089
1090 selectedFwd.src = selectedFwd.src.pick_regions(p_qListLabels);
1091
1092 return selectedFwd;
1093}
1094
1095//=============================================================================================================
1096
1097MNEForwardSolution MNEForwardSolution::pick_types(bool meg, bool eeg, const QStringList& include, const QStringList& exclude) const
1098{
1099 RowVectorXi sel = info.pick_types(meg, eeg, false, include, exclude);
1100
1101 QStringList include_ch_names;
1102 for(qint32 i = 0; i < sel.cols(); ++i)
1103 include_ch_names << info.ch_names[sel[i]];
1104
1105 return this->pick_channels(include_ch_names);
1106}
1107
1108//=============================================================================================================
1109
1111 const FiffCov &p_noise_cov,
1112 bool p_pca,
1113 FiffInfo &p_outFwdInfo,
1114 MatrixXd &gain,
1115 FiffCov &p_outNoiseCov,
1116 MatrixXd &p_outWhitener,
1117 qint32 &p_outNumNonZero) const
1118{
1119 QStringList fwd_ch_names, ch_names;
1120 for(qint32 i = 0; i < this->info.chs.size(); ++i)
1121 fwd_ch_names << this->info.chs[i].ch_name;
1122
1123 ch_names.clear();
1124 for(qint32 i = 0; i < p_info.chs.size(); ++i)
1125 if(!p_info.bads.contains(p_info.chs[i].ch_name)
1126 && !p_noise_cov.bads.contains(p_info.chs[i].ch_name)
1127 && p_noise_cov.names.contains(p_info.chs[i].ch_name)
1128 && fwd_ch_names.contains(p_info.chs[i].ch_name))
1129 ch_names << p_info.chs[i].ch_name;
1130
1131 qint32 n_chan = ch_names.size();
1132 qInfo("Computing inverse operator with %d channels.", n_chan);
1133
1134 //
1135 // Handle noise cov
1136 //
1137 p_outNoiseCov = p_noise_cov.prepare_noise_cov(p_info, ch_names);
1138
1139 // Omit the zeroes due to projection
1140 p_outNumNonZero = 0;
1141 VectorXi t_vecNonZero = VectorXi::Zero(n_chan);
1142 for(qint32 i = 0; i < p_outNoiseCov.eig.rows(); ++i)
1143 {
1144 if(p_outNoiseCov.eig[i] > 0)
1145 {
1146 t_vecNonZero[p_outNumNonZero] = i;
1147 ++p_outNumNonZero;
1148 }
1149 }
1150 if(p_outNumNonZero > 0)
1151 t_vecNonZero.conservativeResize(p_outNumNonZero);
1152
1153 if(p_outNumNonZero > 0)
1154 {
1155 if (p_pca)
1156 {
1157 qWarning("Warning in MNEForwardSolution::prepare_forward: if (p_pca) havent been debugged.");
1158 p_outWhitener = MatrixXd::Zero(n_chan, p_outNumNonZero);
1159 // Rows of eigvec are the eigenvectors
1160 for(qint32 i = 0; i < p_outNumNonZero; ++i)
1161 p_outWhitener.col(t_vecNonZero[i]) = p_outNoiseCov.eigvec.col(t_vecNonZero[i]).array() / sqrt(p_outNoiseCov.eig(t_vecNonZero[i]));
1162 qInfo("\tReducing data rank to %d.", p_outNumNonZero);
1163 }
1164 else
1165 {
1166 qInfo("Creating non pca whitener.");
1167 p_outWhitener = MatrixXd::Zero(n_chan, n_chan);
1168 for(qint32 i = 0; i < p_outNumNonZero; ++i)
1169 p_outWhitener(t_vecNonZero[i],t_vecNonZero[i]) = 1.0 / sqrt(p_outNoiseCov.eig(t_vecNonZero[i]));
1170 // Cols of eigvec are the eigenvectors
1171 p_outWhitener *= p_outNoiseCov.eigvec;
1172 }
1173 }
1174
1175 VectorXi fwd_idx = VectorXi::Zero(ch_names.size());
1176 VectorXi info_idx = VectorXi::Zero(ch_names.size());
1177 qint32 idx;
1178 qint32 count_fwd_idx = 0;
1179 qint32 count_info_idx = 0;
1180 for(qint32 i = 0; i < ch_names.size(); ++i)
1181 {
1182 idx = fwd_ch_names.indexOf(ch_names[i]);
1183 if(idx > -1)
1184 {
1185 fwd_idx[count_fwd_idx] = idx;
1186 ++count_fwd_idx;
1187 }
1188 idx = p_info.ch_names.indexOf(ch_names[i]);
1189 if(idx > -1)
1190 {
1191 info_idx[count_info_idx] = idx;
1192 ++count_info_idx;
1193 }
1194 }
1195 fwd_idx.conservativeResize(count_fwd_idx);
1196 info_idx.conservativeResize(count_info_idx);
1197
1198 gain.resize(count_fwd_idx, this->sol->data.cols());
1199 for(qint32 i = 0; i < count_fwd_idx; ++i)
1200 gain.row(i) = this->sol->data.row(fwd_idx[i]);
1201
1202 p_outFwdInfo = p_info.pick_info(info_idx);
1203
1204 qInfo("\tTotal rank is %d", p_outNumNonZero);
1205}
1206
1207//=============================================================================================================
1208
1209bool MNEForwardSolution::read(QIODevice& p_IODevice,
1210 MNEForwardSolution& fwd,
1211 bool force_fixed,
1212 bool surf_ori,
1213 const QStringList& include,
1214 const QStringList& exclude,
1215 bool bExcludeBads)
1216{
1217 FiffStream::SPtr t_pStream(new FiffStream(&p_IODevice));
1218
1219 qInfo("Reading forward solution from %s...", t_pStream->streamName().toUtf8().constData());
1220 if(!t_pStream->open())
1221 return false;
1222 //
1223 // Find all forward solutions
1224 //
1225 QList<FiffDirNode::SPtr> fwds = t_pStream->dirtree()->dir_tree_find(FIFFB_MNE_FORWARD_SOLUTION);
1226
1227 if (fwds.size() == 0)
1228 {
1229 t_pStream->close();
1230 qWarning("No forward solutions in %s", t_pStream->streamName().toUtf8().constData());
1231 return false;
1232 }
1233 //
1234 // Parent MRI data
1235 //
1236 QList<FiffDirNode::SPtr> parent_mri = t_pStream->dirtree()->dir_tree_find(FIFFB_MNE_PARENT_MRI_FILE);
1237 if (parent_mri.size() == 0)
1238 {
1239 t_pStream->close();
1240 qWarning("No parent MRI information in %s", t_pStream->streamName().toUtf8().constData());
1241 return false;
1242 }
1243
1244 MNELIB::MNESourceSpaces t_SourceSpace;
1245 if(!MNELIB::MNESourceSpaces::readFromStream(t_pStream, true, t_SourceSpace))
1246 {
1247 t_pStream->close();
1248 qWarning("Could not read the source spaces");
1249 //ToDo error(me,'Could not read the source spaces (%s)',mne_omit_first_line(lasterr));
1250 return false;
1251 }
1252
1253 for(qint32 k = 0; k < t_SourceSpace.size(); ++k)
1254 t_SourceSpace[k].id = t_SourceSpace[k].find_source_space_hemi();
1255
1256 //
1257 // Bad channel list
1258 //
1259 QStringList bads;
1260 if(bExcludeBads)
1261 {
1262 bads = t_pStream->read_bad_channels(t_pStream->dirtree());
1263 if(bads.size() > 0)
1264 {
1265 qInfo("\t%lld bad channels ( ", bads.size());
1266 for(qint32 i = 0; i < bads.size(); ++i)
1267 qInfo("\"%s\" ", bads[i].toUtf8().constData());
1268 qInfo(") read");
1269 }
1270 }
1271
1272 //
1273 // Locate and read the forward solutions
1274 //
1275 FiffTag::UPtr t_pTag;
1276 FiffDirNode::SPtr megnode;
1277 FiffDirNode::SPtr eegnode;
1278 for(qint32 k = 0; k < fwds.size(); ++k)
1279 {
1280 if(!fwds[k]->find_tag(t_pStream, FIFF_MNE_INCLUDED_METHODS, t_pTag))
1281 {
1282 t_pStream->close();
1283 qWarning("Methods not listed for one of the forward solutions");
1284 return false;
1285 }
1286 if (*t_pTag->toInt() == FIFFV_MNE_MEG)
1287 {
1288 qInfo("MEG solution found");
1289 megnode = fwds[k];
1290 }
1291 else if(*t_pTag->toInt() == FIFFV_MNE_EEG)
1292 {
1293 qInfo("EEG solution found");
1294 eegnode = fwds[k];
1295 }
1296 }
1297
1298 MNEForwardSolution megfwd;
1299 QString ori;
1300 if (read_one(t_pStream, megnode, megfwd))
1301 {
1302 if (megfwd.source_ori == FIFFV_MNE_FIXED_ORI)
1303 ori = QString("fixed");
1304 else
1305 ori = QString("free");
1306 qInfo("\tRead MEG forward solution (%d sources, %d channels, %s orientations)", megfwd.nsource,megfwd.nchan,ori.toUtf8().constData());
1307 }
1308 MNEForwardSolution eegfwd;
1309 if (read_one(t_pStream, eegnode, eegfwd))
1310 {
1311 if (eegfwd.source_ori == FIFFV_MNE_FIXED_ORI)
1312 ori = QString("fixed");
1313 else
1314 ori = QString("free");
1315 qInfo("\tRead EEG forward solution (%d sources, %d channels, %s orientations)", eegfwd.nsource,eegfwd.nchan,ori.toUtf8().constData());
1316 }
1317
1318 //
1319 // Merge the MEG and EEG solutions together
1320 //
1321 fwd.clear();
1322
1323 if (!megfwd.isEmpty() && !eegfwd.isEmpty())
1324 {
1325 if (megfwd.sol->data.cols() != eegfwd.sol->data.cols() ||
1326 megfwd.source_ori != eegfwd.source_ori ||
1327 megfwd.nsource != eegfwd.nsource ||
1328 megfwd.coord_frame != eegfwd.coord_frame)
1329 {
1330 t_pStream->close();
1331 qWarning("The MEG and EEG forward solutions do not match");
1332 return false;
1333 }
1334
1335 fwd = std::move(MNEForwardSolution(megfwd));
1336 fwd.sol->data = MatrixXd(megfwd.sol->nrow + eegfwd.sol->nrow, megfwd.sol->ncol);
1337
1338 fwd.sol->data.block(0,0,megfwd.sol->nrow,megfwd.sol->ncol) = megfwd.sol->data;
1339 fwd.sol->data.block(megfwd.sol->nrow,0,eegfwd.sol->nrow,eegfwd.sol->ncol) = eegfwd.sol->data;
1340 fwd.sol->nrow = megfwd.sol->nrow + eegfwd.sol->nrow;
1341 fwd.sol->row_names.append(eegfwd.sol->row_names);
1342
1343 if (!fwd.sol_grad->isEmpty())
1344 {
1345 fwd.sol_grad->data.resize(megfwd.sol_grad->data.rows() + eegfwd.sol_grad->data.rows(), megfwd.sol_grad->data.cols());
1346
1347 fwd.sol->data.block(0,0,megfwd.sol_grad->data.rows(),megfwd.sol_grad->data.cols()) = megfwd.sol_grad->data;
1348 fwd.sol->data.block(megfwd.sol_grad->data.rows(),0,eegfwd.sol_grad->data.rows(),eegfwd.sol_grad->data.cols()) = eegfwd.sol_grad->data;
1349
1350 fwd.sol_grad->nrow = megfwd.sol_grad->nrow + eegfwd.sol_grad->nrow;
1351 fwd.sol_grad->row_names.append(eegfwd.sol_grad->row_names);
1352 }
1353 fwd.nchan = megfwd.nchan + eegfwd.nchan;
1354 qInfo("\tMEG and EEG forward solutions combined");
1355 }
1356 else if (!megfwd.isEmpty())
1357 fwd = std::move(megfwd); //not copied for the sake of speed
1358 else
1359 fwd = std::move(eegfwd); //not copied for the sake of speed
1360
1361 //
1362 // Get the MRI <-> head coordinate transformation
1363 //
1364 if(!parent_mri[0]->find_tag(t_pStream, FIFF_COORD_TRANS, t_pTag))
1365 {
1366 t_pStream->close();
1367 qWarning("MRI/head coordinate transformation not found");
1368 return false;
1369 }
1370 else
1371 {
1372 fwd.mri_head_t = t_pTag->toCoordTrans();
1373
1375 {
1378 {
1379 t_pStream->close();
1380 qWarning("MRI/head coordinate transformation not found");
1381 return false;
1382 }
1383 }
1384 }
1385
1386 //
1387 // get parent MEG info -> from python package
1388 //
1389 t_pStream->read_meas_info_base(t_pStream->dirtree(), fwd.info);
1390
1391 t_pStream->close();
1392
1393 //
1394 // Transform the source spaces to the correct coordinate frame
1395 // if necessary
1396 //
1398 {
1399 qWarning("Only forward solutions computed in MRI or head coordinates are acceptable");
1400 return false;
1401 }
1402
1403 //
1404 qint32 nuse = 0;
1405 t_SourceSpace.transform_source_space_to(fwd.coord_frame,fwd.mri_head_t);
1406 for(qint32 k = 0; k < t_SourceSpace.size(); ++k)
1407 nuse += t_SourceSpace[k].nuse;
1408
1409 if (nuse != fwd.nsource){
1410 qDebug() << "Source spaces do not match the forward solution.\n";
1411 return false;
1412 }
1413
1414 qInfo("\tSource spaces transformed to the forward solution coordinate frame");
1415 fwd.src = t_SourceSpace; //not new MNESourceSpaces(t_SourceSpace); for sake of speed
1416 //
1417 // Handle the source locations and orientations
1418 //
1419 if (fwd.isFixedOrient() || force_fixed == true)
1420 {
1421 nuse = 0;
1422 fwd.source_rr = MatrixXf::Zero(fwd.nsource,3);
1423 fwd.source_nn = MatrixXf::Zero(fwd.nsource,3);
1424 for(qint32 k = 0; k < t_SourceSpace.size();++k)
1425 {
1426 for(qint32 q = 0; q < t_SourceSpace[k].nuse; ++q)
1427 {
1428 fwd.source_rr.block(q,0,1,3) = t_SourceSpace[k].rr.block(t_SourceSpace[k].vertno(q),0,1,3);
1429 fwd.source_nn.block(q,0,1,3) = t_SourceSpace[k].nn.block(t_SourceSpace[k].vertno(q),0,1,3);
1430 }
1431 nuse += t_SourceSpace[k].nuse;
1432 }
1433 //
1434 // Modify the forward solution for fixed source orientations
1435 //
1437 {
1438 qInfo("\tChanging to fixed-orientation forward solution...");
1439
1440 MatrixXd tmp = fwd.source_nn.transpose().cast<double>();
1441 SparseMatrix<double> fix_rot = Linalg::make_block_diag(tmp,1);
1442 fwd.sol->data *= fix_rot;
1443 fwd.sol->ncol = fwd.nsource;
1445
1446 if (!fwd.sol_grad->isEmpty())
1447 {
1448 SparseMatrix<double> t_matKron;
1449 SparseMatrix<double> t_eye(3,3);
1450 for (qint32 i = 0; i < 3; ++i)
1451 t_eye.insert(i,i) = 1.0f;
1452 t_matKron = kroneckerProduct(fix_rot,t_eye);//kron(fix_rot,eye(3));
1453 fwd.sol_grad->data *= t_matKron;
1454 fwd.sol_grad->ncol = 3*fwd.nsource;
1455 }
1456 qInfo("[done]");
1457 }
1458 }
1459 else if (surf_ori)
1460 {
1461 //
1462 // Rotate the local source coordinate systems
1463 //
1464 qInfo("\tConverting to surface-based source orientations...");
1465
1466 bool use_ave_nn = false;
1467 auto* hemi0 = t_SourceSpace.hemisphereAt(0);
1468 if(hemi0 && hemi0->patch_inds.size() > 0)
1469 {
1470 use_ave_nn = true;
1471 qInfo("\tAverage patch normals will be employed in the rotation to the local surface coordinates...");
1472 }
1473
1474 nuse = 0;
1475 qint32 pp = 0;
1476 fwd.source_rr = MatrixXf::Zero(fwd.nsource,3);
1477 fwd.source_nn = MatrixXf::Zero(fwd.nsource*3,3);
1478
1479 qWarning("Warning source_ori: Rotating the source coordinate system haven't been verified --> Singular Vectors U are different from MATLAB!");
1480
1481 for(qint32 k = 0; k < t_SourceSpace.size();++k)
1482 {
1483
1484 for (qint32 q = 0; q < t_SourceSpace[k].nuse; ++q)
1485 fwd.source_rr.block(q+nuse,0,1,3) = t_SourceSpace[k].rr.block(t_SourceSpace[k].vertno(q),0,1,3);
1486
1487 for (qint32 p = 0; p < t_SourceSpace[k].nuse; ++p)
1488 {
1489 //
1490 // Project out the surface normal and compute SVD
1491 //
1492 Vector3f nn;
1493 if(use_ave_nn)
1494 {
1495 auto* hemiK = t_SourceSpace.hemisphereAt(k);
1496 VectorXi t_vIdx = hemiK->pinfo[hemiK->patch_inds[p]];
1497 Matrix3Xf t_nn(3, t_vIdx.size());
1498 for(qint32 i = 0; i < t_vIdx.size(); ++i)
1499 t_nn.col(i) = t_SourceSpace[k].nn.block(t_vIdx[i],0,1,3).transpose();
1500 nn = t_nn.rowwise().sum();
1501 nn.array() /= nn.norm();
1502 }
1503 else
1504 nn = t_SourceSpace[k].nn.block(t_SourceSpace[k].vertno(p),0,1,3).transpose();
1505
1506 Matrix3f tmp = Matrix3f::Identity(nn.rows(), nn.rows()) - nn*nn.transpose();
1507
1508 JacobiSVD<MatrixXf> t_svd(tmp, Eigen::ComputeThinU);
1509 //Sort singular values and singular vectors
1510 VectorXf t_s = t_svd.singularValues();
1511 MatrixXf U = t_svd.matrixU();
1512 Linalg::sort<float>(t_s, U);
1513
1514 //
1515 // Make sure that ez is in the direction of nn
1516 //
1517 if ((nn.transpose() * U.block(0,2,3,1))(0,0) < 0)
1518 U *= -1;
1519 fwd.source_nn.block(pp, 0, 3, 3) = U.transpose();
1520 pp += 3;
1521 }
1522 nuse += t_SourceSpace[k].nuse;
1523 }
1524 MatrixXd tmp = fwd.source_nn.transpose().cast<double>();
1525 SparseMatrix<double> surf_rot = Linalg::make_block_diag(tmp,3);
1526
1527 fwd.sol->data *= surf_rot;
1528
1529 if (!fwd.sol_grad->isEmpty())
1530 {
1531 SparseMatrix<double> t_matKron;
1532 SparseMatrix<double> t_eye(3,3);
1533 for (qint32 i = 0; i < 3; ++i)
1534 t_eye.insert(i,i) = 1.0f;
1535 t_matKron = kroneckerProduct(surf_rot,t_eye);//kron(surf_rot,eye(3));
1536 fwd.sol_grad->data *= t_matKron;
1537 }
1538 qInfo("[done]");
1539 }
1540 else
1541 {
1542 qInfo("\tCartesian source orientations...");
1543 nuse = 0;
1544 fwd.source_rr = MatrixXf::Zero(fwd.nsource,3);
1545 for(qint32 k = 0; k < t_SourceSpace.size(); ++k)
1546 {
1547 for (qint32 q = 0; q < t_SourceSpace[k].nuse; ++q)
1548 fwd.source_rr.block(q+nuse,0,1,3) = t_SourceSpace[k].rr.block(t_SourceSpace[k].vertno(q),0,1,3);
1549
1550 nuse += t_SourceSpace[k].nuse;
1551 }
1552
1553 MatrixXf t_ones = MatrixXf::Ones(fwd.nsource,1);
1554 Matrix3f t_eye = Matrix3f::Identity();
1555 fwd.source_nn = kroneckerProduct(t_ones,t_eye);
1556
1557 qInfo("[done]");
1558 }
1559
1560 //
1561 // Do the channel selection
1562 //
1563 QStringList exclude_bads = exclude;
1564 if (bads.size() > 0)
1565 {
1566 for(qint32 k = 0; k < bads.size(); ++k)
1567 if(!exclude_bads.contains(bads[k],Qt::CaseInsensitive))
1568 exclude_bads << bads[k];
1569 }
1570
1571 fwd.surf_ori = surf_ori;
1572 fwd = std::move(fwd.pick_channels(include, exclude_bads));
1573
1574 //garbage collecting
1575 t_pStream->close();
1576
1577 return true;
1578}
1579
1580//=============================================================================================================
1581
1582bool MNEForwardSolution::read_one(FiffStream::SPtr& p_pStream,
1583 const FiffDirNode::SPtr& p_Node,
1584 MNEForwardSolution& one)
1585{
1586 //
1587 // Read all interesting stuff for one forward solution
1588 //
1589 if(!p_Node)
1590 return false;
1591
1592 one.clear();
1593 FiffTag::UPtr t_pTag;
1594
1595 if(!p_Node->find_tag(p_pStream, FIFF_MNE_SOURCE_ORIENTATION, t_pTag))
1596 {
1597 p_pStream->close();
1598 qWarning("Source orientation tag not found.");
1599 return false;
1600 }
1601
1602 one.source_ori = *t_pTag->toInt();
1603
1604 if(!p_Node->find_tag(p_pStream, FIFF_MNE_COORD_FRAME, t_pTag))
1605 {
1606 p_pStream->close();
1607 qWarning("Coordinate frame tag not found.");
1608 return false;
1609 }
1610
1611 one.coord_frame = *t_pTag->toInt();
1612
1613 if(!p_Node->find_tag(p_pStream, FIFF_MNE_SOURCE_SPACE_NPOINTS, t_pTag))
1614 {
1615 p_pStream->close();
1616 qWarning("Number of sources not found.");
1617 return false;
1618 }
1619
1620 one.nsource = *t_pTag->toInt();
1621
1622 if(!p_Node->find_tag(p_pStream, FIFF_NCHAN, t_pTag))
1623 {
1624 p_pStream->close();
1625 qWarning("Number of channels not found.");
1626 return false;
1627 }
1628
1629 one.nchan = *t_pTag->toInt();
1630
1631 if(p_pStream->read_named_matrix(p_Node, FIFF_MNE_FORWARD_SOLUTION, *one.sol.data()))
1632 one.sol->transpose_named_matrix();
1633 else
1634 {
1635 p_pStream->close();
1636 qWarning("Forward solution data not found.");
1637 //error(me,'Forward solution data not found (%s)',mne_omit_first_line(lasterr));
1638 return false;
1639 }
1640
1641 if(p_pStream->read_named_matrix(p_Node, FIFF_MNE_FORWARD_SOLUTION_GRAD, *one.sol_grad.data()))
1642 one.sol_grad->transpose_named_matrix();
1643 else
1644 one.sol_grad->clear();
1645
1646 if (one.sol->data.rows() != one.nchan ||
1647 (one.sol->data.cols() != one.nsource && one.sol->data.cols() != 3*one.nsource))
1648 {
1649 p_pStream->close();
1650 qWarning("Forward solution matrix has wrong dimensions.");
1651 //error(me,'Forward solution matrix has wrong dimensions');
1652 return false;
1653 }
1654 if (!one.sol_grad->isEmpty())
1655 {
1656 if (one.sol_grad->data.rows() != one.nchan ||
1657 (one.sol_grad->data.cols() != 3*one.nsource && one.sol_grad->data.cols() != 3*3*one.nsource))
1658 {
1659 p_pStream->close();
1660 qWarning("Forward solution gradient matrix has wrong dimensions.");
1661 //error(me,'Forward solution gradient matrix has wrong dimensions');
1662 }
1663 }
1664 return true;
1665}
1666
1667//=============================================================================================================
1668
1670{
1671 // Figure out which ones have been used
1672 if(info.chs.size() != G.rows())
1673 {
1674 qWarning("Error G.rows() and length of info.chs do not match: %ld != %lli", G.rows(), info.chs.size());
1675 return;
1676 }
1677
1678 RowVectorXi sel = info.pick_types(QString("grad"));
1679 if(sel.size() > 0)
1680 {
1681 for(qint32 i = 0; i < sel.size(); ++i)
1682 G.row(i) = G.row(sel[i]);
1683 G.conservativeResize(sel.size(), G.cols());
1684 qInfo("\t%ld planar channels", sel.size());
1685 }
1686 else
1687 {
1688 sel = info.pick_types(QString("mag"));
1689 if (sel.size() > 0)
1690 {
1691 for(qint32 i = 0; i < sel.size(); ++i)
1692 G.row(i) = G.row(sel[i]);
1693 G.conservativeResize(sel.size(), G.cols());
1694 qInfo("\t%ld magnetometer or axial gradiometer channels", sel.size());
1695 }
1696 else
1697 {
1698 sel = info.pick_types(false, true);
1699 if(sel.size() > 0)
1700 {
1701 for(qint32 i = 0; i < sel.size(); ++i)
1702 G.row(i) = G.row(sel[i]);
1703 G.conservativeResize(sel.size(), G.cols());
1704 qInfo("\t%ld EEG channels", sel.size());
1705 }
1706 else
1707 qWarning("Could not find MEG or EEG channels");
1708 }
1709 }
1710}
1711
1712//=============================================================================================================
1713
1715{
1716 if(!this->surf_ori || this->isFixedOrient())
1717 {
1718 qWarning("Cannot convert to fixed orientation: requires surface-oriented, free-orientation forward solution");
1719 return;
1720 }
1721 qint32 count = 0;
1722 for(qint32 i = 2; i < this->sol->data.cols(); i += 3)
1723 this->sol->data.col(count) = this->sol->data.col(i);//ToDo: is this right? - just take z?
1724 this->sol->data.conservativeResize(this->sol->data.rows(), count);
1725 this->sol->ncol = this->sol->ncol / 3;
1727 qInfo("\tConverted the forward solution into the fixed-orientation mode.");
1728}
1729
1730//=============================================================================================================
1731
1733{
1734 auto* hemi = src.hemisphereAt(0);
1735 return hemi && hemi->isClustered();
1736}
1737
1738//=============================================================================================================
1739
1740MatrixX3f MNEForwardSolution::getSourcePositionsByLabel(const QList<FsLabel> &lPickedLabels, const FsSurfaceSet& tSurfSetInflated)
1741{
1742 MatrixX3f matSourceVertLeft, matSourceVertRight, matSourcePositions;
1743
1744 if(lPickedLabels.isEmpty()) {
1745 qWarning() << "MNEForwardSolution::getSourcePositionsByLabel - picked label list is empty. Returning.";
1746 return matSourcePositions;
1747 }
1748
1749 if(tSurfSetInflated.isEmpty()) {
1750 qWarning() << "MNEForwardSolution::getSourcePositionsByLabel - tSurfSetInflated is empty. Returning.";
1751 return matSourcePositions;
1752 }
1753
1754 if(isClustered()) {
1755 for(int j = 0; j < this->src[0].vertno.rows(); ++j) {
1756 for(int k = 0; k < lPickedLabels.size(); k++) {
1757 if(this->src[0].vertno(j) == lPickedLabels.at(k).label_id) {
1758 matSourceVertLeft.conservativeResize(matSourceVertLeft.rows()+1,3);
1759 matSourceVertLeft.row(matSourceVertLeft.rows()-1) = tSurfSetInflated[0].rr().row(this->src.hemisphereAt(0)->cluster_info.centroidVertno.at(j)) - tSurfSetInflated[0].offset().transpose();
1760 break;
1761 }
1762 }
1763 }
1764
1765 for(int j = 0; j < this->src[1].vertno.rows(); ++j) {
1766 for(int k = 0; k < lPickedLabels.size(); k++) {
1767 if(this->src[1].vertno(j) == lPickedLabels.at(k).label_id) {
1768 matSourceVertRight.conservativeResize(matSourceVertRight.rows()+1,3);
1769 matSourceVertRight.row(matSourceVertRight.rows()-1) = tSurfSetInflated[1].rr().row(this->src.hemisphereAt(1)->cluster_info.centroidVertno.at(j)) - tSurfSetInflated[1].offset().transpose();
1770 break;
1771 }
1772 }
1773 }
1774 } else {
1775 for(int j = 0; j < this->src[0].vertno.rows(); ++j) {
1776 for(int k = 0; k < lPickedLabels.size(); k++) {
1777 for(int l = 0; l < lPickedLabels.at(k).vertices.rows(); l++) {
1778 if(this->src[0].vertno(j) == lPickedLabels.at(k).vertices(l) && lPickedLabels.at(k).hemi == 0) {
1779 matSourceVertLeft.conservativeResize(matSourceVertLeft.rows()+1,3);
1780 matSourceVertLeft.row(matSourceVertLeft.rows()-1) = tSurfSetInflated[0].rr().row(this->src[0].vertno(j)) - tSurfSetInflated[0].offset().transpose();
1781 break;
1782 }
1783 }
1784 }
1785 }
1786
1787 for(int j = 0; j < this->src[1].vertno.rows(); ++j) {
1788 for(int k = 0; k < lPickedLabels.size(); k++) {
1789 for(int l = 0; l < lPickedLabels.at(k).vertices.rows(); l++) {
1790 if(this->src[1].vertno(j) == lPickedLabels.at(k).vertices(l) && lPickedLabels.at(k).hemi == 1) {
1791 matSourceVertRight.conservativeResize(matSourceVertRight.rows()+1,3);
1792 matSourceVertRight.row(matSourceVertRight.rows()-1) = tSurfSetInflated[1].rr().row(this->src[1].vertno(j)) - tSurfSetInflated[1].offset().transpose();
1793 break;
1794 }
1795 }
1796 }
1797 }
1798 }
1799
1800 matSourcePositions.resize(matSourceVertLeft.rows()+matSourceVertRight.rows(),3);
1801 matSourcePositions << matSourceVertLeft, matSourceVertRight;
1802
1803 return matSourcePositions;
1804}
In-memory representation of a FreeSurfer colour/structure lookup table (FreeSurferColorLUT / embedded...
Bi-hemispheric grouping of FreeSurfer surfaces (lh + rh) loaded as a single object.
Reader and in-memory representation of a FreeSurfer/MNE surface label (.label).
constexpr int FAIL
constexpr int Y
constexpr int Z
constexpr int OK
constexpr int X
Forward solution (gain matrix mapping source dipoles to sensor measurements).
Eigen::JacobiSVD< Eigen::Matrix3f > svd(S, Eigen::ComputeFullU|Eigen::ComputeFullV)
#define FIFF_MNE_COORD_FRAME
#define FIFFV_EEG_CH
#define FIFF_MNE_FORWARD_SOLUTION_GRAD
#define FIFF_MNE_SOURCE_ORIENTATION
#define FIFF_MNE_FORWARD_SOLUTION
#define FIFF_FAIL
#define FIFFV_REF_MEG_CH
#define FIFFV_MEG_CH
#define FIFF_MNE_INCLUDED_METHODS
#define FIFF_MNE_SOURCE_SPACE_NPOINTS
#define FIFFV_COORD_HEAD
#define FIFFV_MNE_FIXED_ORI
#define FIFFV_COORD_MRI
#define FIFFB_MNE_FORWARD_SOLUTION
#define FIFFB_MNE_PARENT_MEAS_FILE
#define FIFFV_MNE_MEG
#define FIFFB_MNE
#define FIFFV_MNE_ORIENT_PRIOR_COV
#define FIFF_MNE_FILE_NAME
#define FIFFV_MNE_EEG
#define FIFFB_MNE_PARENT_MRI_FILE
#define FIFFV_MNE_DEPTH_PRIOR_COV
#define FIFFV_MNE_FREE_ORI
#define FIFF_PARENT_BLOCK_ID
Definition fiff_file.h:326
#define FIFF_NCHAN
Definition fiff_file.h:446
#define FIFF_DIR_POINTER
Definition fiff_file.h:317
#define FIFF_COORD_TRANS
Definition fiff_file.h:468
#define FIFF_PARENT_FILE_ID
Definition fiff_file.h:325
Static MATLAB-style FIFF facade: thin wrapper functions kept for parity with the historical mne-matla...
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
Static linear-algebra helpers: SVD-based conditioning, block-diagonal assembly, sorted index pairs.
K-means partitional clustering with multiple distance metrics, initialisations and empty-cluster poli...
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).
FIFF noise / data covariance: matrix, channel names, kind, applied projectors, bads,...
Definition fiff_cov.h:79
fiff_int_t nfree
Definition fiff_cov.h:251
fiff_int_t dim
Definition fiff_cov.h:246
Eigen::MatrixXd eigvec
Definition fiff_cov.h:253
bool isEmpty() const
Definition fiff_cov.h:261
fiff_int_t kind
Definition fiff_cov.h:243
QStringList bads
Definition fiff_cov.h:250
QStringList names
Definition fiff_cov.h:247
Eigen::VectorXd eig
Definition fiff_cov.h:252
Eigen::MatrixXd data
Definition fiff_cov.h:248
FiffCov prepare_noise_cov(const FiffInfo &p_info, const QStringList &p_chNames) const
Definition fiff_cov.cpp:164
QSharedPointer< FiffDirNode > SPtr
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
Definition fiff_info.h:88
FiffInfo pick_info(const Eigen::RowVectorXi &sel=defaultVectorXi) const
static Eigen::RowVectorXi pick_channels(const QStringList &ch_names, const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList)
QList< FiffChInfo > chs
FIFF named matrix: dense / sparse Eigen matrix plus row-name and column-name string lists.
QSharedDataPointer< FiffNamedMatrix > SDPtr
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
static FiffStream::SPtr start_file(QIODevice &p_IODevice)
static FiffStream::SPtr open_update(QIODevice &p_IODevice)
std::unique_ptr< FiffTag > UPtr
Definition fiff_tag.h:164
Single-hemisphere FreeSurfer parcellation: vertex → region label plus embedded colortable.
FsColortable & getColortable()
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
QStringList getNames() const
Container holding the lh and/or rh FsSurface for one subject and one surface kind.
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
static Eigen::SparseMatrix< double > make_block_diag(const Eigen::MatrixXd &A, qint32 n)
Definition linalg.cpp:198
QList< Eigen::VectorXd > clusterDistances
QList< QString > clusterLabelNames
QList< qint32 > clusterLabelIds
QList< Eigen::MatrixX3f > clusterSource_rr
QList< Eigen::VectorXi > clusterVertnos
QList< qint32 > centroidVertno
QList< Eigen::Vector3f > centroidSource_rr
Input parameters for cluster-based forward solution computation on a single cortical region.
Eigen::MatrixXd matRoiGWhitened
Eigen::MatrixXd matRoiGOrig
RegionDataOut cluster() const
In-memory representation of an -fwd.fif forward solution.
bool write(QIODevice &p_IODevice) const
static void restrict_gain_matrix(Eigen::MatrixXd &G, const FIFFLIB::FiffInfo &info)
MNEForwardSolution reduce_forward_solution(qint32 p_iNumDipoles, Eigen::MatrixXd &p_D) const
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
MNEForwardSolution cluster_forward_solution(const FSLIB::FsAnnotationSet &p_AnnotationSet, qint32 p_iClusterSize, Eigen::MatrixXd &p_D=defaultD, const FIFFLIB::FiffCov &p_pNoise_cov=defaultCov, const FIFFLIB::FiffInfo &p_pInfo=defaultInfo, QString p_sMethod="cityblock") const
MNEForwardSolution & operator=(const MNEForwardSolution &other)
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
Eigen::MatrixX3f getSourcePositionsByLabel(const QList< FSLIB::FsLabel > &lPickedLabels, const FSLIB::FsSurfaceSet &tSurfSetInflated)
MNEForwardSolution pick_channels(const QStringList &include=FIFFLIB::defaultQStringList, const QStringList &exclude=FIFFLIB::defaultQStringList) const
FIFFLIB::FiffNamedMatrix::SDPtr sol_grad
static bool read(QIODevice &p_IODevice, MNEForwardSolution &fwd, bool force_fixed=false, bool surf_ori=false, const QStringList &include=FIFFLIB::defaultQStringList, const QStringList &exclude=FIFFLIB::defaultQStringList, bool bExcludeBads=true)
Eigen::VectorXi tripletSelection(const Eigen::VectorXi &p_vecIdxSelection) const
FIFFLIB::FiffCov compute_orient_prior(float loose=0.2)
MNEForwardSolution pick_regions(const QList< FSLIB::FsLabel > &p_qListLabels) const
MNEForwardSolution pick_types(bool meg, bool eeg, const QStringList &include=FIFFLIB::defaultQStringList, const QStringList &exclude=FIFFLIB::defaultQStringList) const
FIFFLIB::FiffNamedMatrix::SDPtr sol
MNEClusterInfo cluster_info
QList< Eigen::VectorXi > pinfo
List of MNESourceSpace objects forming a subject source space.
MNESourceSpaces pick_regions(const QList< FSLIB::FsLabel > &p_qListLabels) const
bool transform_source_space_to(FIFFLIB::fiff_int_t dest, FIFFLIB::FiffCoordTrans &trans)
MNEHemisphere * hemisphereAt(qint32 idx)
static bool readFromStream(FIFFLIB::FiffStream::SPtr &p_pStream, bool add_geom, MNESourceSpaces &p_SourceSpace)