160 const QString &method,
163 SparseMatrix<double> &noise_norm,
164 QList<VectorXi> &vertno)
168 if(method.compare(QLatin1String(
"MNE")) != 0)
171 vertno =
src.get_vertno();
173 typedef Eigen::Triplet<double> T;
174 std::vector<T> tripletList;
178 qWarning(
"Label selection needs further debugging.");
180 vertno =
src.label_src_vertno_sel(label, src_sel);
182 if(method.compare(QLatin1String(
"MNE")) != 0)
185 tripletList.reserve(noise_norm.nonZeros());
187 for (qint32 k = 0; k < noise_norm.outerSize(); ++k)
189 for (SparseMatrix<double>::InnerIterator it(noise_norm,k); it; ++it)
192 for(qint32 i = 0; i < src_sel.size(); ++i)
194 if(src_sel[i] == it.row())
201 tripletList.push_back(T(it.row(), it.col(), it.value()));
205 noise_norm = SparseMatrix<double>(src_sel.size(),noise_norm.cols());
206 noise_norm.setFromTriplets(tripletList.begin(), tripletList.end());
211 VectorXi src_sel_new(src_sel.size()*3);
213 for(qint32 i = 0; i < src_sel.size(); ++i)
215 src_sel_new[i*3] = src_sel[i]*3;
216 src_sel_new[i*3+1] = src_sel[i]*3+1;
217 src_sel_new[i*3+2] = src_sel[i]*3+2;
219 src_sel = src_sel_new;
222 for(qint32 i = 0; i < src_sel.size(); ++i)
224 t_eigen_leads.row(i) = t_eigen_leads.row(src_sel[i]);
225 t_source_cov = t_source_cov.row(src_sel[i]);
227 t_eigen_leads.conservativeResize(src_sel.size(), t_eigen_leads.cols());
228 t_source_cov.conservativeResize(src_sel.size(), t_source_cov.cols());
235 qWarning(
"Warning: Pick normal can only be used with a free orientation inverse operator.\n");
242 qWarning(
"The pick_normal parameter is only valid when working with loose orientations.\n");
248 for(qint32 i = 2; i < t_eigen_leads.rows(); i+=3)
250 t_eigen_leads.row(count) = t_eigen_leads.row(i);
253 t_eigen_leads.conservativeResize(count, t_eigen_leads.cols());
256 for(qint32 i = 2; i < t_source_cov.rows(); i+=3)
258 t_source_cov.row(count) = t_source_cov.row(i);
261 t_source_cov.conservativeResize(count, t_source_cov.cols());
265 tripletList.reserve(
reginv.rows());
266 for(qint32 i = 0; i <
reginv.rows(); ++i)
267 tripletList.push_back(T(i, i,
reginv(i,0)));
268 SparseMatrix<double> t_reginv(
reginv.rows(),
reginv.rows());
269 t_reginv.setFromTriplets(tripletList.begin(), tripletList.end());
281 qInfo(
"(eigenleads already weighted)...\n");
282 K = t_eigen_leads*trans;
289 qInfo(
"(eigenleads need to be weighted)...\n");
291 std::vector<T> tripletList2;
292 tripletList2.reserve(t_source_cov.rows());
293 for(qint32 i = 0; i < t_source_cov.rows(); ++i)
294 tripletList2.push_back(T(i, i, sqrt(t_source_cov(i,0))));
295 SparseMatrix<double> t_sourceCov(t_source_cov.rows(),t_source_cov.rows());
296 t_sourceCov.setFromTriplets(tripletList2.begin(), tripletList2.end());
298 K = t_sourceCov*t_eigen_leads*trans;
301 if(method.compare(QLatin1String(
"MNE")) == 0)
302 noise_norm = SparseMatrix<double>();
359 qInfo(
"Cluster kernel using %s.\n", p_sMethod.toUtf8().constData());
361 MatrixXd p_outMT = m_K.transpose();
363 QList<MNEClusterInfo> t_qListMNEClusterInfo;
365 t_qListMNEClusterInfo.append(t_MNEClusterInfo);
366 t_qListMNEClusterInfo.append(t_MNEClusterInfo);
373 qCritical(
"Error: Fixed orientation not implemented yet!\n");
385 for(qint32 h = 0; h < this->
src.size(); ++h )
392 for(qint32 j = 0; j < h; ++j)
393 offset += this->
src[j].nuse;
396 qInfo(
"Cluster Left Hemisphere\n");
398 qInfo(
"Cluster Right Hemisphere\n");
400 FsColortable t_CurrentColorTable = p_AnnotationSet[h].getColortable();
401 VectorXi label_ids = t_CurrentColorTable.
getLabelIds();
404 VectorXi vertno_labeled = VectorXi::Zero(this->
src[h].vertno.rows());
406 for(qint32 i = 0; i < vertno_labeled.rows(); ++i)
407 vertno_labeled[i] = p_AnnotationSet[h].getLabelIds()[this->
src[h].vertno[i]];
410 QList<RegionMT> m_qListRegionMTIn;
415 for (qint32 i = 0; i < label_ids.rows(); ++i)
417 if (label_ids[i] != 0)
419 QString curr_name = t_CurrentColorTable.
struct_names[i];
420 qInfo(
"\tCluster %d / %ld %s...", i+1, label_ids.rows(), curr_name.toUtf8().constData());
425 VectorXi idcs = VectorXi::Zero(vertno_labeled.rows());
429 for(qint32 j = 0; j < vertno_labeled.rows(); ++j)
431 if(vertno_labeled[j] == label_ids[i])
437 idcs.conservativeResize(c);
440 MatrixXd t_MT(p_outMT.rows(), idcs.rows()*3);
442 for(qint32 j = 0; j < idcs.rows(); ++j)
443 t_MT.block(0, j*3, t_MT.rows(), 3) = p_outMT.block(0, (idcs[j]+offset)*3, t_MT.rows(), 3);
445 qint32 nSens = t_MT.rows();
446 qint32 nSources = t_MT.cols()/3;
452 t_sensMT.
idcs = idcs;
454 t_sensMT.
nClusters = ceil(
static_cast<double>(nSources)/
static_cast<double>(p_iClusterSize));
458 qInfo(
"%d Cluster(s)... ", t_sensMT.
nClusters);
461 t_sensMT.
matRoiMT = MatrixXd(t_MT.cols()/3, 3*nSens);
463 for(qint32 j = 0; j < nSens; ++j)
464 for(qint32 k = 0; k < t_sensMT.
matRoiMT.rows(); ++k)
465 t_sensMT.
matRoiMT.block(k,j*3,1,3) = t_MT.block(j,k*3,1,3);
469 m_qListRegionMTIn.append(t_sensMT);
475 qInfo(
"failed! FsLabel contains no sources.\n");
483 qInfo(
"Clustering... ");
484 QFuture< RegionMTOut > res;
486 res.waitForFinished();
491 MatrixXd t_MT_partial;
495 QList<RegionMT>::const_iterator itIn;
496 itIn = m_qListRegionMTIn.begin();
497 QFuture<RegionMTOut>::const_iterator itOut;
498 for (itOut = res.constBegin(); itOut != res.constEnd(); ++itOut)
500 nClusters = itOut->ctrs.rows();
501 nSens = itOut->ctrs.cols()/3;
502 t_MT_partial = MatrixXd::Zero(nSens, nClusters*3);
507 for(qint32 j = 0; j < nSens; ++j)
508 for(qint32 k = 0; k < nClusters; ++k)
509 t_MT_partial.block(j, k*3, 1, 3) = itOut->ctrs.block(k,j*3,1,3);
514 for(qint32 j = 0; j < nClusters; ++j)
516 VectorXi clusterIdcs = VectorXi::Zero(itOut->roiIdx.rows());
517 VectorXd clusterDistance = VectorXd::Zero(itOut->roiIdx.rows());
518 qint32 nClusterIdcs = 0;
519 for(qint32 k = 0; k < itOut->roiIdx.rows(); ++k)
521 if(itOut->roiIdx[k] == j)
523 clusterIdcs[nClusterIdcs] = itIn->idcs[k];
524 clusterDistance[nClusterIdcs] = itOut->D(k,j);
528 clusterIdcs.conservativeResize(nClusterIdcs);
529 clusterDistance.conservativeResize(nClusterIdcs);
531 VectorXi clusterVertnos = VectorXi::Zero(clusterIdcs.size());
532 for(qint32 k = 0; k < clusterVertnos.size(); ++k)
533 clusterVertnos(k) = this->
src[h].vertno[clusterIdcs(k)];
535 t_qListMNEClusterInfo[h].clusterVertnos.append(clusterVertnos);
542 if(t_MT_partial.rows() > 0 && t_MT_partial.cols() > 0)
544 t_MT_new.conservativeResize(t_MT_partial.rows(), t_MT_new.cols() + t_MT_partial.cols());
545 t_MT_new.block(0, t_MT_new.cols() - t_MT_partial.cols(), t_MT_new.rows(), t_MT_partial.cols()) = t_MT_partial;
548 for(qint32 k = 0; k < nClusters; ++k)
552 double sqec = sqrt((itIn->matRoiMTOrig.block(0, j*3, itIn->matRoiMTOrig.rows(), 3) - t_MT_partial.block(0, k*3, t_MT_partial.rows(), 3)).array().pow(2).sum());
553 double sqec_min = sqec;
555 for(qint32 j = 1; j < itIn->idcs.rows(); ++j)
557 sqec = sqrt((itIn->matRoiMTOrig.block(0, j*3, itIn->matRoiMTOrig.rows(), 3) - t_MT_partial.block(0, k*3, t_MT_partial.rows(), 3)).array().pow(2).sum());
580 qint32 totalNumOfClust = 0;
581 for (qint32 h = 0; h < 2; ++h)
582 totalNumOfClust += t_qListMNEClusterInfo[h].clusterVertnos.size();
585 p_D = MatrixXd::Zero(p_outMT.cols(), totalNumOfClust);
587 p_D = MatrixXd::Zero(p_outMT.cols(), totalNumOfClust*3);
589 QList<VectorXi> t_vertnos =
src.get_vertno();
591 qint32 currentCluster = 0;
592 for (qint32 h = 0; h < 2; ++h)
594 int hemiOffset = h == 0 ? 0 : t_vertnos[0].size();
595 for(qint32 i = 0; i < t_qListMNEClusterInfo[h].clusterVertnos.size(); ++i)
598 Linalg::intersect(t_vertnos[h], t_qListMNEClusterInfo[h].clusterVertnos[i], idx_sel);
600 idx_sel.array() += hemiOffset;
602 double selectWeight = 1.0/idx_sel.size();
605 for(qint32 j = 0; j < idx_sel.size(); ++j)
606 p_D.col(currentCluster)[idx_sel(j)] = selectWeight;
610 qint32 clustOffset = currentCluster*3;
611 for(qint32 j = 0; j < idx_sel.size(); ++j)
613 qint32 idx_sel_Offset = idx_sel(j)*3;
615 p_D(idx_sel_Offset,clustOffset) = selectWeight;
617 p_D(idx_sel_Offset+1, clustOffset+1) = selectWeight;
619 p_D(idx_sel_Offset+2, clustOffset+2) = selectWeight;
642 bool limit_depth_chs)
652 qWarning(
"Warning: Forward solution is not surface-oriented. A surface-oriented solution is recommended for fixed-orientation inverse operators.");
656 if(fixed && loose > 0)
658 qWarning(
"Warning: When invoking make_inverse_operator with fixed = true, the loose parameter is ignored.\n");
662 if(is_fixed_ori && !fixed)
664 qWarning(
"Warning: Setting fixed parameter = true. Because the given forward operator has fixed orientation and can only be used to make a fixed-orientation inverse operator.\n");
670 qCritical(
"Forward solution is not oriented in surface coordinates. loose parameter should be 0 not %f.", loose);
674 if(loose < 0 || loose > 1)
676 qWarning(
"Warning: Loose value should be in interval [0,1] not %f.\n", loose);
677 loose = loose > 1 ? 1 : 0;
678 qInfo(
"Setting loose to %f.\n", loose);
681 if(depth < 0 || depth > 1)
683 qWarning(
"Warning: Depth value should be in interval [0,1] not %f.\n", depth);
684 depth = depth > 1 ? 1 : 0;
685 qInfo(
"Setting depth to %f.\n", depth);
705 MatrixXd patch_areas;
713 p_depth_prior->data = MatrixXd::Ones(gain.cols(), gain.cols());
715 p_depth_prior->diag =
true;
716 p_depth_prior->dim = gain.cols();
717 p_depth_prior->nfree = 1;
723 if(depth < 0 || depth > 1)
726 qInfo(
"\tPicking elements from free-orientation depth prior into fixed-orientation one.\n");
732 qWarning(
"Warning: For a fixed-orientation inverse, the forward solution must be surface-oriented. Skipping fixed conversion.\n");
738 for(qint32 i = 2; i < p_depth_prior->data.rows(); i+=3)
740 p_depth_prior->data.row(count) = p_depth_prior->data.row(i);
743 p_depth_prior->data.conservativeResize(count, 1);
751 qInfo(
"\tComputing inverse operator with %lld channels.\n", gain_info.
ch_names.size());
756 qInfo(
"\tCreating the source covariance matrix\n");
764 p_source_cov->data.array() *= p_orient_prior->data.array();
773 qInfo(
"\tWhitening the forward solution.\n");
784 qInfo(
"\tAdjusting source covariance matrix.\n");
785 RowVectorXd source_std = p_source_cov->data.array().sqrt().transpose();
787 for(qint32 i = 0; i < gain.rows(); ++i)
788 gain.row(i) = gain.row(i).array() * source_std.array();
790 double trace_GRGT = (gain * gain.transpose()).trace();
791 double scaling_source_cov =
static_cast<double>(n_nzero) / trace_GRGT;
793 p_source_cov->data.array() *= scaling_source_cov;
795 gain.array() *= sqrt(scaling_source_cov);
800 qInfo(
"Computing SVD of whitened and weighted lead field matrix.\n");
801 JacobiSVD<MatrixXd> svd(gain, ComputeThinU | ComputeThinV);
803 VectorXd p_sing = svd.singularValues();
804 MatrixXd t_U = svd.matrixU();
807 svd.matrixU().rows(),
812 p_sing = svd.singularValues();
813 MatrixXd t_V = svd.matrixV();
816 svd.matrixV().cols(),
820 qInfo(
"\tlargest singular value = %f\n", p_sing.maxCoeff());
821 qInfo(
"\tscaling factor to adjust the trace = %f\n", trace_GRGT);
826 bool has_meg =
false;
827 bool has_eeg =
false;
829 RowVectorXd ch_idx(
info.chs.size());
831 for(qint32 i = 0; i <
info.chs.size(); ++i)
839 ch_idx.conservativeResize(count);
841 for(qint32 i = 0; i < ch_idx.size(); ++i)
843 QString ch_type =
info.channel_type(ch_idx[i]);
844 if (ch_type ==
"eeg")
846 if ((ch_type ==
"mag") || (ch_type ==
"grad"))
852 if(has_eeg && has_meg)
866 inv.
nchan = p_sing.rows();
895 qCritical(
"The number of averages should be positive\n");
898 qInfo(
"Preparing the inverse operator for use...\n");
903 float scale =
static_cast<float>(inv.
nave)/
static_cast<float>(
nave);
911 qInfo(
"\tScaled noise and source covariance from nave = %d to nave = %d\n",inv.
nave,
nave);
916 VectorXd tmp = inv.
sing.cwiseProduct(inv.
sing) + VectorXd::Constant(inv.
sing.size(), lambda2);
917 inv.
reginv = VectorXd(inv.
sing.cwiseQuotient(tmp));
918 qInfo(
"\tCreated the regularized inverter\n");
925 qInfo(
"\tCreated an SSP operator (subspace dimension = %d)\n",ncomp);
940 for (k = ncomp; k < inv.
noise_cov->dim; ++k)
952 qInfo(
"\tCreated the whitener using a full noise covariance matrix (%d small eigenvalues omitted)\n", inv.
noise_cov->dim - nnzero);
962 qInfo(
"\tCreated the whitener using a diagonal noise covariance matrix (%d small eigenvalues discarded)\n",ncomp);
969 VectorXd noise_norm = VectorXd::Zero(inv.
eigen_leads->nrow);
970 VectorXd noise_weight;
973 qInfo(
"\tComputing noise-normalization factors (dSPM)...");
974 noise_weight = VectorXd(inv.
reginv);
978 qInfo(
"\tComputing noise-normalization factors (sLORETA)...");
979 VectorXd tmp = (VectorXd::Constant(inv.
sing.size(), 1) + inv.
sing.cwiseProduct(inv.
sing)/lambda2);
980 noise_weight = inv.
reginv.cwiseProduct(tmp.cwiseSqrt());
988 noise_norm[k] = sqrt(one.dot(one));
997 one = c*(inv.
eigen_leads->data.row(k).transpose()).cwiseProduct(noise_weight);
998 noise_norm[k] = sqrt(one.dot(one));
1005 VectorXd noise_norm_new;
1011 noise_norm_new = t.cwiseSqrt();
1013 VectorXd vOnes = VectorXd::Ones(noise_norm_new.size());
1014 VectorXd tmp = vOnes.cwiseQuotient(noise_norm_new.cwiseAbs());
1016 typedef Eigen::Triplet<double> T;
1017 std::vector<T> tripletList;
1018 tripletList.reserve(noise_norm_new.size());
1019 for(qint32 i = 0; i < noise_norm_new.size(); ++i)
1020 tripletList.push_back(T(i, i, tmp[i]));
1022 inv.
noisenorm = SparseMatrix<double>(noise_norm_new.size(),noise_norm_new.size());
1023 inv.
noisenorm.setFromTriplets(tripletList.begin(), tripletList.end());
1043 qInfo(
"Reading inverse operator decomposition from %s...\n",t_pStream->streamName().toUtf8().constData());
1045 if(!t_pStream->open())
1051 if ( invs_list.size()== 0)
1053 qCritical(
"No inverse solutions in %s\n", t_pStream->streamName().toUtf8().constData());
1061 if (parent_mri.size() == 0)
1063 qCritical(
"No parent MRI information in %s", t_pStream->streamName().toUtf8().constData());
1066 qInfo(
"\tReading inverse operator info...");
1073 qCritical(
"Modalities not found\n");
1078 inv.
methods = *t_pTag->toInt();
1082 qCritical(
"Source orientation constraints not found\n");
1089 qCritical(
"Number of sources not found\n");
1092 inv.
nsource = *t_pTag->toInt();
1099 qCritical(
"Coordinate frame tag not found\n");
1108 qCritical(
"Source orientation information not found\n");
1112 inv.
source_nn = t_pTag->toFloatMatrix();
1119 qInfo(
"\tReading inverse operator decomposition...");
1122 qCritical(
"Singular values not found\n");
1126 inv.
sing = Map<const VectorXf>(t_pTag->toFloat(), t_pTag->size()/4).cast<
double>();
1137 qCritical(
"Error reading eigenleads named matrix.\n");
1148 qCritical(
"Error reading eigenfields named matrix.\n");
1157 qInfo(
"\tNoise covariance matrix read.\n");
1161 qCritical(
"\tError: Not able to read noise covariance matrix.\n");
1167 qInfo(
"\tSource covariance matrix read.\n");
1171 qCritical(
"\tError: Not able to read source covariance matrix.\n");
1179 qInfo(
"\tOrientation priors read.\n");
1186 qInfo(
"\tDepth priors read.\n");
1194 qInfo(
"\tfMRI priors read.\n");
1205 qCritical(
"\tError: Could not read the source spaces.\n");
1208 for (qint32 k = 0; k < inv.
src.
size(); ++k)
1216 qCritical(
"MRI/head coordinate transformation not found\n");
1227 qCritical(
"MRI/head coordinate transformation not found");
1237 t_pStream->read_meas_info_base(t_pStream->dirtree(), inv.
info);
1244 qCritical(
"Only inverse solutions computed in MRI or head coordinates are acceptable");
1252 inv.
projs = t_pStream->read_proj(t_pStream->dirtree());
1258 qCritical(
"Could not transform source space.\n");
1260 qInfo(
"\tSource spaces transformed to the inverse solution coordinate frame\n");