51 #include <Eigen/Sparse>
52 #include <unsupported/Eigen/KroneckerProduct>
65 #include <QtConcurrent>
72 using namespace MNELIB;
73 using namespace UTILSLIB;
74 using namespace FSLIB;
75 using namespace Eigen;
76 using namespace FIFFLIB;
92 , source_rr(MatrixX3f::Zero(0,3))
93 , source_nn(MatrixX3f::Zero(0,3))
109 , source_rr(MatrixX3f::Zero(0,3))
110 , source_nn(MatrixX3f::Zero(0,3))
112 if(!
read(p_IODevice, *
this, force_fixed,
surf_ori, include, exclude, bExcludeBads))
114 printf(
"\tForward solution not found.\n");
122 : info(p_MNEForwardSolution.info)
123 , source_ori(p_MNEForwardSolution.source_ori)
124 , surf_ori(p_MNEForwardSolution.surf_ori)
125 , coord_frame(p_MNEForwardSolution.coord_frame)
126 , nsource(p_MNEForwardSolution.nsource)
127 , nchan(p_MNEForwardSolution.nchan)
128 , sol(p_MNEForwardSolution.sol)
129 , sol_grad(p_MNEForwardSolution.sol_grad)
130 , mri_head_t(p_MNEForwardSolution.mri_head_t)
131 , src(p_MNEForwardSolution.src)
132 , source_rr(p_MNEForwardSolution.source_rr)
133 , source_nn(p_MNEForwardSolution.source_nn)
164 qint32 p_iClusterSize,
168 QString p_sMethod)
const
170 printf(
"Cluster forward solution using %s.\n", p_sMethod.toUtf8().constData());
175 if(!IOUtils::check_matching_chnames_conventions(p_pNoise_cov.
names, p_pInfo.
ch_names) && !p_pNoise_cov.
names.isEmpty() && !p_pInfo.
ch_names.isEmpty()) {
176 if(IOUtils::check_matching_chnames_conventions(p_pNoise_cov.
names, p_pInfo.
ch_names,
true)) {
177 qWarning(
"MNEForwardSolution::cluster_forward_solution - Cov names do match with info channel names but have a different naming convention.");
180 qWarning(
"MNEForwardSolution::cluster_forward_solution - Cov channel names do not match with info channel names.");
192 printf(
"Error: Fixed orientation not implemented jet!\n");
205 MatrixXd t_G_Whitened(0,0);
206 bool t_bUseWhitened =
false;
214 MatrixXd p_outWhitener;
215 qint32 p_outNumNonZero;
217 this->
prepare_forward(p_pInfo, p_pNoise_cov,
false, p_outFwdInfo, t_G_Whitened, p_outNoiseCov, p_outWhitener, p_outNumNonZero);
218 printf(
"\tWhitening the forward solution.\n");
220 t_G_Whitened = p_outWhitener*t_G_Whitened;
221 t_bUseWhitened =
true;
232 for(qint32 h = 0; h < this->
src.
size(); ++h )
240 for(qint32 j = 0; j < h; ++j)
241 offset += this->
src[j].nuse;
244 printf(
"Cluster Left Hemisphere\n");
246 printf(
"Cluster Right Hemisphere\n");
248 const Annotation annotation = p_AnnotationSet[h];
250 VectorXi label_ids = t_CurrentColorTable.
getLabelIds();
253 VectorXi vertno_labeled = VectorXi::Zero(this->
src[h].vertno.rows());
256 for(qint32 i = 0; i < vertno_labeled.rows(); ++i)
257 vertno_labeled[i] = p_AnnotationSet[h].getLabelIds()[this->
src[h].vertno[i]];
260 QList<RegionData> m_qListRegionDataIn;
265 for (qint32 i = 0; i < label_ids.rows(); ++i)
267 if (label_ids[i] != 0)
269 QString curr_name = t_CurrentColorTable.
struct_names[i];
270 printf(
"\tCluster %d / %ld %s...", i+1, label_ids.rows(), curr_name.toUtf8().constData());
275 VectorXi idcs = VectorXi::Zero(vertno_labeled.rows());
279 for(qint32 j = 0; j < vertno_labeled.rows(); ++j)
281 if(vertno_labeled[j] == label_ids[i])
287 idcs.conservativeResize(c);
290 MatrixXd t_G(this->
sol->data.rows(), idcs.rows()*3);
291 MatrixXd t_G_Whitened_Roi(t_G_Whitened.rows(), idcs.rows()*3);
293 for(qint32 j = 0; j < idcs.rows(); ++j)
295 t_G.block(0, j*3, t_G.rows(), 3) = this->
sol->data.block(0, (idcs[j]+offset)*3, t_G.rows(), 3);
297 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);
300 qint32 nSens = t_G.rows();
301 qint32 nSources = t_G.cols()/3;
309 t_sensG.
nClusters = ceil((
double)nSources/(
double)p_iClusterSize);
315 printf(
"%d Cluster(s)... ", t_sensG.
nClusters);
318 t_sensG.
matRoiG = MatrixXd(t_G.cols()/3, 3*nSens);
320 t_sensG.
matRoiGWhitened = MatrixXd(t_G_Whitened_Roi.cols()/3, 3*nSens);
322 for(qint32 j = 0; j < nSens; ++j)
324 for(qint32
k = 0;
k < t_sensG.
matRoiG.rows(); ++
k)
325 t_sensG.
matRoiG.block(
k,j*3,1,3) = t_G.block(j,
k*3,1,3);
335 m_qListRegionDataIn.append(t_sensG);
341 printf(
"failed! Label contains no sources.\n");
349 printf(
"Clustering... ");
350 QFuture< RegionDataOut > res;
351 res = QtConcurrent::mapped(m_qListRegionDataIn, &RegionData::cluster);
352 res.waitForFinished();
357 MatrixXd t_G_partial;
361 QList<RegionData>::const_iterator itIn;
362 itIn = m_qListRegionDataIn.begin();
363 QFuture<RegionDataOut>::const_iterator itOut;
364 for (itOut = res.constBegin(); itOut != res.constEnd(); ++itOut)
366 nClusters = itOut->ctrs.rows();
367 nSens = itOut->ctrs.cols()/3;
368 t_G_partial = MatrixXd::Zero(nSens, nClusters*3);
376 for(qint32 j = 0; j < nSens; ++j)
377 for(qint32
k = 0;
k < nClusters; ++
k)
378 t_G_partial.block(j,
k*3, 1, 3) = itOut->ctrs.block(
k,j*3,1,3);
383 for(qint32 j = 0; j < nClusters; ++j)
385 VectorXi clusterIdcs = VectorXi::Zero(itOut->roiIdx.rows());
386 VectorXd clusterDistance = VectorXd::Zero(itOut->roiIdx.rows());
387 MatrixX3f clusterSource_rr = MatrixX3f::Zero(itOut->roiIdx.rows(), 3);
388 qint32 nClusterIdcs = 0;
389 for(qint32
k = 0;
k < itOut->roiIdx.rows(); ++
k)
391 if(itOut->roiIdx[
k] == j)
393 clusterIdcs[nClusterIdcs] = itIn->idcs[
k];
395 qint32 offset = h == 0 ? 0 : this->
src[0].nuse;
396 clusterSource_rr.row(nClusterIdcs) = this->
source_rr.row(offset + itIn->idcs[
k]);
397 clusterDistance[nClusterIdcs] = itOut->D(
k,j);
401 clusterIdcs.conservativeResize(nClusterIdcs);
402 clusterSource_rr.conservativeResize(nClusterIdcs,3);
403 clusterDistance.conservativeResize(nClusterIdcs);
405 VectorXi clusterVertnos = VectorXi::Zero(clusterIdcs.size());
406 for(qint32
k = 0;
k < clusterVertnos.size(); ++
k)
407 clusterVertnos(
k) = this->
src[h].vertno[clusterIdcs(
k)];
409 p_fwdOut.
src[h].cluster_info.clusterVertnos.append(clusterVertnos);
410 p_fwdOut.
src[h].cluster_info.clusterSource_rr.append(clusterSource_rr);
411 p_fwdOut.
src[h].cluster_info.clusterDistances.append(clusterDistance);
412 p_fwdOut.
src[h].cluster_info.clusterLabelIds.append(label_ids[itOut->iLabelIdxOut]);
413 p_fwdOut.
src[h].cluster_info.clusterLabelNames.append(t_CurrentColorTable.
getNames()[itOut->iLabelIdxOut]);
419 if(t_G_partial.rows() > 0 && t_G_partial.cols() > 0)
421 t_G_new.conservativeResize(t_G_partial.rows(), t_G_new.cols() + t_G_partial.cols());
422 t_G_new.block(0, t_G_new.cols() - t_G_partial.cols(), t_G_new.rows(), t_G_partial.cols()) = t_G_partial;
425 for(qint32
k = 0;
k < nClusters; ++
k)
429 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());
430 double sqec_min = sqec;
433 for(qint32 j = 1; j < itIn->idcs.rows(); ++j)
435 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());
448 qint32 sel_idx = itIn->idcs[j_min];
450 p_fwdOut.
src[h].cluster_info.centroidVertno.append(this->
src[h].vertno[sel_idx]);
451 p_fwdOut.
src[h].cluster_info.centroidSource_rr.append(this->
src[h].rr.row(this->src[h].vertno[sel_idx]));
457 p_fwdOut.
src[h].vertno[count] = p_fwdOut.
src[h].cluster_info.clusterLabelIds[count];
474 p_fwdOut.
src[h].vertno.conservativeResize(count);
482 qint32 totalNumOfClust = 0;
483 for (qint32 h = 0; h < 2; ++h)
484 totalNumOfClust += p_fwdOut.
src[h].cluster_info.clusterVertnos.
size();
487 p_D = MatrixXd::Zero(this->
sol->data.cols(), totalNumOfClust);
489 p_D = MatrixXd::Zero(this->
sol->data.cols(), totalNumOfClust*3);
496 qint32 currentCluster = 0;
497 for (qint32 h = 0; h < 2; ++h)
499 int hemiOffset = h == 0 ? 0 : t_vertnos[0].size();
500 for(qint32 i = 0; i < p_fwdOut.
src[h].cluster_info.clusterVertnos.
size(); ++i)
503 MNEMath::intersect(t_vertnos[h], p_fwdOut.
src[h].cluster_info.clusterVertnos[i], idx_sel);
509 idx_sel.array() += hemiOffset;
513 double selectWeight = 1.0/idx_sel.size();
516 for(qint32 j = 0; j < idx_sel.size(); ++j)
517 p_D.col(currentCluster)[idx_sel(j)] = selectWeight;
521 qint32 clustOffset = currentCluster*3;
522 for(qint32 j = 0; j < idx_sel.size(); ++j)
524 qint32 idx_sel_Offset = idx_sel(j)*3;
526 p_D(idx_sel_Offset,clustOffset) = selectWeight;
528 p_D(idx_sel_Offset+1, clustOffset+1) = selectWeight;
530 p_D(idx_sel_Offset+2, clustOffset+2) = selectWeight;
692 p_fwdOut.
sol->data = t_G_new;
693 p_fwdOut.
sol->ncol = t_G_new.cols();
707 qint32 np = isFixed ? p_fwdOut.
sol->data.cols() : p_fwdOut.
sol->data.cols()/3;
709 if(p_iNumDipoles > np)
712 VectorXi sel(p_iNumDipoles);
714 float t_fStep = (float)np/(
float)p_iNumDipoles;
716 for(qint32 i = 0; i < p_iNumDipoles; ++i)
718 float t_fCurrent = ((float)i)*t_fStep;
719 sel[i] = (quint32)floor(t_fCurrent);
724 p_D = MatrixXd::Zero(p_fwdOut.
sol->data.cols(), p_iNumDipoles);
725 for(qint32 i = 0; i < p_iNumDipoles; ++i)
730 p_D = MatrixXd::Zero(p_fwdOut.
sol->data.cols(), p_iNumDipoles*3);
731 for(qint32 i = 0; i < p_iNumDipoles; ++i)
732 for(qint32 j = 0; j < 3; ++j)
733 p_D((sel[i]*3)+j, (i*3)+j) = 1;
783 p_fwdOut.
sol->data = this->
sol->data * p_D;
785 MatrixX3f rr(p_iNumDipoles,3);
787 MatrixX3f nn(p_iNumDipoles,3);
789 for(qint32 i = 0; i < p_iNumDipoles; ++i)
798 p_fwdOut.
sol->ncol = p_fwdOut.
sol->data.cols();
800 p_fwdOut.
nsource = p_iNumDipoles;
809 printf(
"\tCreating the depth weighting matrix...\n");
820 d = (G.array().square()).rowwise().sum();
825 qint32 n_pos = G.cols() / 3;
826 d = VectorXd::Zero(n_pos);
828 for (qint32
k = 0;
k < n_pos; ++
k)
830 Gk = G.block(0,3*
k, G.rows(), 3);
831 JacobiSVD<MatrixXd> svd(Gk.transpose()*Gk);
832 d[
k] = svd.singularValues().maxCoeff();
837 if(patch_areas.cols() > 0)
840 printf(
"\tToDo!!!!! >>> Patch areas taken into account in the depth weighting\n");
844 VectorXd w = d.cwiseInverse();
847 MNEMath::sort<double>(ws,
false);
848 double weight_limit = pow(limit, 2);
849 if (!limit_depth_chs)
855 limit = ws[ind] * weight_limit;
860 limit = ws[ws.size()-1];
863 if (ws[ws.size()-1] > weight_limit * ws[0])
865 double th = weight_limit * ws[0];
866 for(qint32 i = 0; i < ws.size(); ++i)
879 printf(
"\tlimit = %d/%ld = %f", n_limit + 1, d.size(), sqrt(limit / ws[0]));
880 double scale = 1.0 / limit;
881 printf(
"\tscale = %g exp = %g", scale, exp);
883 VectorXd t_w = w.array() / limit;
884 for(qint32 i = 0; i < t_w.size(); ++i)
885 t_w[i] = t_w[i] > 1 ? 1 : t_w[i];
886 wpp = t_w.array().pow(exp);
890 depth_prior.
data = wpp;
893 depth_prior.
data.resize(wpp.rows()*3, 1);
896 for(qint32 i = 0; i < wpp.rows(); ++i)
900 depth_prior.
data(idx, 0) = v;
901 depth_prior.
data(idx+1, 0) = v;
902 depth_prior.
data(idx+2, 0) = v;
907 depth_prior.
diag =
true;
908 depth_prior.
dim = depth_prior.
data.rows();
909 depth_prior.
nfree = 1;
919 qint32 n_sources = this->
sol->data.cols();
921 if (0 <= loose && loose <= 1)
923 qDebug() <<
"this->surf_ori" << this->
surf_ori;
926 printf(
"\tForward operator is not oriented in surface coordinates. loose parameter should be None not %f.", loose);
928 printf(
"\tSetting loose to %f.\n", loose);
933 printf(
"\tIgnoring loose parameter with forward operator with fixed orientation.\n");
939 if(loose < 0 || loose > 1)
941 qWarning(
"Warning: Loose value should be in interval [0,1] not %f.\n", loose);
942 loose = loose > 1 ? 1 : 0;
943 printf(
"Setting loose to %f.\n", loose);
948 orient_prior.
data = VectorXd::Ones(n_sources);
949 if(!is_fixed_ori && (0 <= loose && loose <= 1))
951 printf(
"\tApplying loose dipole orientations. Loose value of %f.\n", loose);
952 for(qint32 i = 0; i < n_sources; i+=3)
953 orient_prior.
data.block(i,0,2,1).array() *= loose;
956 orient_prior.
diag =
true;
957 orient_prior.
dim = orient_prior.
data.size();
958 orient_prior.
nfree = 1;
966 const QStringList& exclude)
const
970 if(include.size() == 0 && exclude.size() == 0)
973 RowVectorXi sel = FiffInfo::pick_channels(fwd.
sol->row_names, include, exclude);
976 quint32 nuse = sel.size();
980 printf(
"Nothing remains after picking. Returning original forward solution.\n");
983 printf(
"\t%d out of %d channels remain after picking\n", nuse, fwd.
nchan);
986 MatrixXd newData(nuse, fwd.
sol->data.cols());
987 for(quint32 i = 0; i < nuse; ++i)
988 newData.row(i) = fwd.
sol->data.row(sel[i]);
990 fwd.
sol->data = newData;
991 fwd.
sol->nrow = nuse;
993 QStringList ch_names;
994 for(qint32 i = 0; i < sel.cols(); ++i)
995 ch_names << fwd.
sol->row_names[sel(i)];
997 fwd.
sol->row_names = ch_names;
999 QList<FiffChInfo> chs;
1000 for(qint32 i = 0; i < sel.cols(); ++i)
1001 chs.append(fwd.
info.
chs[sel(i)]);
1006 for(qint32 i = 0; i < fwd.
info.
bads.size(); ++i)
1007 if(ch_names.contains(fwd.
info.
bads[i]))
1013 newData.resize(nuse, fwd.
sol_grad->data.cols());
1014 for(quint32 i = 0; i < nuse; ++i)
1015 newData.row(i) = fwd.
sol_grad->data.row(sel[i]);
1018 QStringList row_names;
1019 for(qint32 i = 0; i < sel.cols(); ++i)
1020 row_names << fwd.
sol_grad->row_names[sel(i)];
1021 fwd.
sol_grad->row_names = row_names;
1031 VectorXi selVertices;
1034 for(qint32 i = 0; i < p_qListLabels.size(); ++i)
1036 VectorXi currentSelection;
1039 selVertices.conservativeResize(iSize+currentSelection.size());
1040 selVertices.block(iSize,0,currentSelection.size(),1) = currentSelection;
1041 iSize = selVertices.size();
1044 MNEMath::sort(selVertices,
false);
1048 MatrixX3f rr(selVertices.size(),3);
1049 MatrixX3f nn(selVertices.size(),3);
1051 for(qint32 i = 0; i < selVertices.size(); ++i)
1053 rr.block(i, 0, 1, 3) = selectedFwd.
source_rr.row(selVertices[i]);
1054 nn.block(i, 0, 1, 3) = selectedFwd.
source_nn.row(selVertices[i]);
1060 VectorXi selSolIdcs = tripletSelection(selVertices);
1061 MatrixXd G(selectedFwd.
sol->data.rows(),selSolIdcs.size());
1063 qint32 rows = G.rows();
1065 for(qint32 i = 0; i < selSolIdcs.size(); ++i)
1066 G.block(0, i, rows, 1) = selectedFwd.
sol->data.col(selSolIdcs[i]);
1068 selectedFwd.
sol->data = G;
1069 selectedFwd.
sol->nrow = selectedFwd.
sol->data.rows();
1070 selectedFwd.
sol->ncol = selectedFwd.
sol->data.cols();
1071 selectedFwd.
nsource = selectedFwd.
sol->ncol / 3;
1082 RowVectorXi sel =
info.
pick_types(meg, eeg,
false, include, exclude);
1084 QStringList include_ch_names;
1085 for(qint32 i = 0; i < sel.cols(); ++i)
1088 return this->pick_channels(include_ch_names);
1099 MatrixXd &p_outWhitener,
1100 qint32 &p_outNumNonZero)
const
1102 QStringList fwd_ch_names, ch_names;
1103 for(qint32 i = 0; i < this->
info.
chs.size(); ++i)
1104 fwd_ch_names << this->
info.
chs[i].ch_name;
1107 for(qint32 i = 0; i < p_info.
chs.size(); ++i)
1108 if(!p_info.
bads.contains(p_info.
chs[i].ch_name)
1109 && !p_noise_cov.
bads.contains(p_info.
chs[i].ch_name)
1110 && p_noise_cov.
names.contains(p_info.
chs[i].ch_name)
1111 && fwd_ch_names.contains(p_info.
chs[i].ch_name))
1112 ch_names << p_info.
chs[i].ch_name;
1114 qint32 n_chan = ch_names.size();
1115 printf(
"Computing inverse operator with %d channels.\n", n_chan);
1123 p_outNumNonZero = 0;
1124 VectorXi t_vecNonZero = VectorXi::Zero(n_chan);
1125 for(qint32 i = 0; i < p_outNoiseCov.
eig.rows(); ++i)
1127 if(p_outNoiseCov.
eig[i] > 0)
1129 t_vecNonZero[p_outNumNonZero] = i;
1133 if(p_outNumNonZero > 0)
1134 t_vecNonZero.conservativeResize(p_outNumNonZero);
1136 if(p_outNumNonZero > 0)
1140 qWarning(
"Warning in MNEForwardSolution::prepare_forward: if (p_pca) havent been debugged.");
1141 p_outWhitener = MatrixXd::Zero(n_chan, p_outNumNonZero);
1143 for(qint32 i = 0; i < p_outNumNonZero; ++i)
1144 p_outWhitener.col(t_vecNonZero[i]) = p_outNoiseCov.
eigvec.col(t_vecNonZero[i]).array() / sqrt(p_outNoiseCov.
eig(t_vecNonZero[i]));
1145 printf(
"\tReducing data rank to %d.\n", p_outNumNonZero);
1149 printf(
"Creating non pca whitener.\n");
1150 p_outWhitener = MatrixXd::Zero(n_chan, n_chan);
1151 for(qint32 i = 0; i < p_outNumNonZero; ++i)
1152 p_outWhitener(t_vecNonZero[i],t_vecNonZero[i]) = 1.0 / sqrt(p_outNoiseCov.
eig(t_vecNonZero[i]));
1154 p_outWhitener *= p_outNoiseCov.
eigvec;
1158 VectorXi fwd_idx = VectorXi::Zero(ch_names.size());
1159 VectorXi info_idx = VectorXi::Zero(ch_names.size());
1161 qint32 count_fwd_idx = 0;
1162 qint32 count_info_idx = 0;
1163 for(qint32 i = 0; i < ch_names.size(); ++i)
1165 idx = fwd_ch_names.indexOf(ch_names[i]);
1168 fwd_idx[count_fwd_idx] = idx;
1171 idx = p_info.
ch_names.indexOf(ch_names[i]);
1174 info_idx[count_info_idx] = idx;
1178 fwd_idx.conservativeResize(count_fwd_idx);
1179 info_idx.conservativeResize(count_info_idx);
1181 gain.resize(count_fwd_idx, this->
sol->data.cols());
1182 for(qint32 i = 0; i < count_fwd_idx; ++i)
1183 gain.row(i) = this->
sol->data.row(fwd_idx[i]);
1185 p_outFwdInfo = p_info.
pick_info(info_idx);
1187 printf(
"\tTotal rank is %d\n", p_outNumNonZero);
1196 const QStringList& include,
1197 const QStringList& exclude,
1202 printf(
"Reading forward solution from %s...\n", t_pStream->streamName().toUtf8().constData());
1203 if(!t_pStream->open())
1208 QList<FiffDirNode::SPtr> fwds = t_pStream->dirtree()->dir_tree_find(FIFFB_MNE_FORWARD_SOLUTION);
1210 if (fwds.size() == 0)
1213 std::cout <<
"No forward solutions in " << t_pStream->streamName().toUtf8().constData();
1219 QList<FiffDirNode::SPtr> parent_mri = t_pStream->dirtree()->dir_tree_find(FIFFB_MNE_PARENT_MRI_FILE);
1220 if (parent_mri.size() == 0)
1223 std::cout <<
"No parent MRI information in " << t_pStream->streamName().toUtf8().constData();
1231 std::cout <<
"Could not read the source spaces\n";
1236 for(qint32
k = 0;
k < t_SourceSpace.
size(); ++
k)
1245 bads = t_pStream->read_bad_channels(t_pStream->dirtree());
1248 printf(
"\t%lld bad channels ( ",bads.size());
1249 for(qint32 i = 0; i < bads.size(); ++i)
1250 printf(
"\"%s\" ", bads[i].toUtf8().constData());
1261 for(qint32
k = 0;
k < fwds.size(); ++
k)
1263 if(!fwds[
k]->find_tag(t_pStream, FIFF_MNE_INCLUDED_METHODS, t_pTag))
1266 std::cout <<
"Methods not listed for one of the forward solutions\n";
1269 if (*t_pTag->toInt() == FIFFV_MNE_MEG)
1271 printf(
"MEG solution found\n");
1274 else if(*t_pTag->toInt() == FIFFV_MNE_EEG)
1276 printf(
"EEG solution found\n");
1283 if (read_one(t_pStream, megnode, megfwd))
1285 if (megfwd.
source_ori == FIFFV_MNE_FIXED_ORI)
1286 ori = QString(
"fixed");
1288 ori = QString(
"free");
1289 printf(
"\tRead MEG forward solution (%d sources, %d channels, %s orientations)\n", megfwd.
nsource,megfwd.
nchan,ori.toUtf8().constData());
1292 if (read_one(t_pStream, eegnode, eegfwd))
1294 if (eegfwd.
source_ori == FIFFV_MNE_FIXED_ORI)
1295 ori = QString(
"fixed");
1297 ori = QString(
"free");
1298 printf(
"\tRead EEG forward solution (%d sources, %d channels, %s orientations)\n", eegfwd.
nsource,eegfwd.
nchan,ori.toUtf8().constData());
1308 if (megfwd.
sol->data.cols() != eegfwd.
sol->data.cols() ||
1314 std::cout <<
"The MEG and EEG forward solutions do not match\n";
1319 fwd.
sol->data = MatrixXd(megfwd.
sol->nrow + eegfwd.
sol->nrow, megfwd.
sol->ncol);
1321 fwd.
sol->data.block(0,0,megfwd.
sol->nrow,megfwd.
sol->ncol) = megfwd.
sol->data;
1322 fwd.
sol->data.block(megfwd.
sol->nrow,0,eegfwd.
sol->nrow,eegfwd.
sol->ncol) = eegfwd.
sol->data;
1323 fwd.
sol->nrow = megfwd.
sol->nrow + eegfwd.
sol->nrow;
1324 fwd.
sol->row_names.append(eegfwd.
sol->row_names);
1337 printf(
"\tMEG and EEG forward solutions combined\n");
1350 std::cout <<
"MRI/head coordinate transformation not found\n";
1363 std::cout <<
"MRI/head coordinate transformation not found\n";
1372 t_pStream->read_meas_info_base(t_pStream->dirtree(), fwd.
info);
1382 std::cout <<
"Only forward solutions computed in MRI or head coordinates are acceptable";
1389 for(qint32
k = 0;
k < t_SourceSpace.
size(); ++
k)
1390 nuse += t_SourceSpace[
k].nuse;
1393 qDebug() <<
"Source spaces do not match the forward solution.\n";
1397 printf(
"\tSource spaces transformed to the forward solution coordinate frame\n");
1398 fwd.
src = t_SourceSpace;
1407 for(qint32
k = 0;
k < t_SourceSpace.
size();++
k)
1409 for(qint32 q = 0; q < t_SourceSpace[
k].nuse; ++q)
1411 fwd.
source_rr.block(q,0,1,3) = t_SourceSpace[
k].rr.block(t_SourceSpace[
k].vertno(q),0,1,3);
1412 fwd.
source_nn.block(q,0,1,3) = t_SourceSpace[
k].nn.block(t_SourceSpace[
k].vertno(q),0,1,3);
1414 nuse += t_SourceSpace[
k].nuse;
1421 printf(
"\tChanging to fixed-orientation forward solution...");
1423 MatrixXd tmp = fwd.
source_nn.transpose().cast<
double>();
1424 SparseMatrix<double>* fix_rot = MNEMath::make_block_diag(tmp,1);
1425 fwd.
sol->data *= (*fix_rot);
1431 SparseMatrix<double> t_matKron;
1432 SparseMatrix<double> t_eye(3,3);
1433 for (qint32 i = 0; i < 3; ++i)
1434 t_eye.insert(i,i) = 1.0f;
1435 t_matKron = kroneckerProduct(*fix_rot,t_eye);
1448 printf(
"\tConverting to surface-based source orientations...");
1450 bool use_ave_nn =
false;
1451 if(t_SourceSpace[0].patch_inds.
size() > 0)
1454 printf(
"\tAverage patch normals will be employed in the rotation to the local surface coordinates...\n");
1462 qWarning(
"Warning source_ori: Rotating the source coordinate system haven't been verified --> Singular Vectors U are different from MATLAB!");
1464 for(qint32
k = 0;
k < t_SourceSpace.
size();++
k)
1467 for (qint32 q = 0; q < t_SourceSpace[
k].nuse; ++q)
1468 fwd.
source_rr.block(q+nuse,0,1,3) = t_SourceSpace[
k].rr.block(t_SourceSpace[
k].vertno(q),0,1,3);
1470 for (qint32 p = 0; p < t_SourceSpace[
k].nuse; ++p)
1478 VectorXi t_vIdx = t_SourceSpace[
k].pinfo[t_SourceSpace[
k].patch_inds[p]];
1479 Matrix3Xf t_nn(3, t_vIdx.size());
1480 for(qint32 i = 0; i < t_vIdx.size(); ++i)
1481 t_nn.col(i) = t_SourceSpace[
k].nn.block(t_vIdx[i],0,1,3).transpose();
1482 nn = t_nn.rowwise().sum();
1483 nn.array() /= nn.norm();
1486 nn = t_SourceSpace[
k].nn.block(t_SourceSpace[
k].vertno(p),0,1,3).transpose();
1488 Matrix3f tmp = Matrix3f::Identity(nn.rows(), nn.rows()) - nn*nn.transpose();
1490 JacobiSVD<MatrixXf> t_svd(tmp, Eigen::ComputeThinU);
1492 VectorXf t_s = t_svd.singularValues();
1493 MatrixXf U = t_svd.matrixU();
1494 MNEMath::sort<float>(t_s, U);
1499 if ((nn.transpose() * U.block(0,2,3,1))(0,0) < 0)
1501 fwd.
source_nn.block(pp, 0, 3, 3) = U.transpose();
1504 nuse += t_SourceSpace[
k].nuse;
1506 MatrixXd tmp = fwd.
source_nn.transpose().cast<
double>();
1507 SparseMatrix<double>* surf_rot = MNEMath::make_block_diag(tmp,3);
1509 fwd.
sol->data *= *surf_rot;
1513 SparseMatrix<double> t_matKron;
1514 SparseMatrix<double> t_eye(3,3);
1515 for (qint32 i = 0; i < 3; ++i)
1516 t_eye.insert(i,i) = 1.0f;
1517 t_matKron = kroneckerProduct(*surf_rot,t_eye);
1525 printf(
"\tCartesian source orientations...");
1528 for(qint32
k = 0;
k < t_SourceSpace.
size(); ++
k)
1530 for (qint32 q = 0; q < t_SourceSpace[
k].nuse; ++q)
1531 fwd.
source_rr.block(q+nuse,0,1,3) = t_SourceSpace[
k].rr.block(t_SourceSpace[
k].vertno(q),0,1,3);
1533 nuse += t_SourceSpace[
k].nuse;
1536 MatrixXf t_ones = MatrixXf::Ones(fwd.
nsource,1);
1537 Matrix3f t_eye = Matrix3f::Identity();
1538 fwd.
source_nn = kroneckerProduct(t_ones,t_eye);
1546 QStringList exclude_bads = exclude;
1547 if (bads.size() > 0)
1549 for(qint32
k = 0;
k < bads.size(); ++
k)
1550 if(!exclude_bads.contains(bads[
k],Qt::CaseInsensitive))
1551 exclude_bads << bads[
k];
1701 std::cout <<
"Source orientation tag not found.";
1710 std::cout <<
"Coordinate frame tag not found.";
1719 std::cout <<
"Number of sources not found.";
1723 one.
nsource = *t_pTag->toInt();
1725 if(!p_Node->find_tag(p_pStream,
FIFF_NCHAN, t_pTag))
1728 printf(
"Number of channels not found.");
1732 one.
nchan = *t_pTag->toInt();
1734 if(p_pStream->read_named_matrix(p_Node, FIFF_MNE_FORWARD_SOLUTION, *one.
sol.data()))
1735 one.
sol->transpose_named_matrix();
1739 printf(
"Forward solution data not found .");
1744 if(p_pStream->read_named_matrix(p_Node, FIFF_MNE_FORWARD_SOLUTION_GRAD, *one.
sol_grad.data()))
1745 one.
sol_grad->transpose_named_matrix();
1749 if (one.
sol->data.rows() != one.
nchan ||
1753 printf(
"Forward solution matrix has wrong dimensions.\n");
1763 printf(
"Forward solution gradient matrix has wrong dimensions.\n");
1775 if(
info.
chs.size() != G.rows())
1777 printf(
"Error G.rows() and length of info.chs do not match: %ld != %lli", G.rows(),
info.
chs.size());
1784 for(qint32 i = 0; i < sel.size(); ++i)
1785 G.row(i) = G.row(sel[i]);
1786 G.conservativeResize(sel.size(), G.cols());
1787 printf(
"\t%ld planar channels", sel.size());
1794 for(qint32 i = 0; i < sel.size(); ++i)
1795 G.row(i) = G.row(sel[i]);
1796 G.conservativeResize(sel.size(), G.cols());
1797 printf(
"\t%ld magnetometer or axial gradiometer channels", sel.size());
1804 for(qint32 i = 0; i < sel.size(); ++i)
1805 G.row(i) = G.row(sel[i]);
1806 G.conservativeResize(sel.size(), G.cols());
1807 printf(
"\t%ld EEG channels\n", sel.size());
1810 printf(
"Could not find MEG or EEG channels\n");
1821 qWarning(
"Warning: Only surface-oriented, free-orientation forward solutions can be converted to fixed orientaton.\n");
1825 for(qint32 i = 2; i < this->
sol->data.cols(); i += 3)
1826 this->
sol->data.col(count) = this->
sol->data.col(i);
1827 this->
sol->data.conservativeResize(this->
sol->data.rows(), count);
1828 this->
sol->ncol = this->
sol->ncol / 3;
1830 printf(
"\tConverted the forward solution into the fixed-orientation mode.\n");
1837 MatrixX3f matSourceVertLeft, matSourceVertRight, matSourcePositions;
1839 if(lPickedLabels.isEmpty()) {
1840 qWarning() <<
"MNEForwardSolution::getSourcePositionsByLabel - picked label list is empty. Returning.";
1841 return matSourcePositions;
1844 if(tSurfSetInflated.
isEmpty()) {
1845 qWarning() <<
"MNEForwardSolution::getSourcePositionsByLabel - tSurfSetInflated is empty. Returning.";
1846 return matSourcePositions;
1850 for(
int j = 0; j < this->
src[0].vertno.rows(); ++j) {
1851 for(
int k = 0;
k < lPickedLabels.size();
k++) {
1852 if(this->
src[0].vertno(j) == lPickedLabels.at(
k).label_id) {
1853 matSourceVertLeft.conservativeResize(matSourceVertLeft.rows()+1,3);
1854 matSourceVertLeft.row(matSourceVertLeft.rows()-1) = tSurfSetInflated[0].rr().row(this->
src[0].cluster_info.centroidVertno.at(j)) - tSurfSetInflated[0].offset().transpose();
1860 for(
int j = 0; j < this->
src[1].vertno.rows(); ++j) {
1861 for(
int k = 0;
k < lPickedLabels.size();
k++) {
1862 if(this->
src[1].vertno(j) == lPickedLabels.at(
k).label_id) {
1863 matSourceVertRight.conservativeResize(matSourceVertRight.rows()+1,3);
1864 matSourceVertRight.row(matSourceVertRight.rows()-1) = tSurfSetInflated[1].rr().row(this->
src[1].cluster_info.centroidVertno.at(j)) - tSurfSetInflated[1].offset().transpose();
1870 for(
int j = 0; j < this->
src[0].vertno.rows(); ++j) {
1871 for(
int k = 0;
k < lPickedLabels.size();
k++) {
1872 for(
int l = 0; l < lPickedLabels.at(
k).vertices.rows(); l++) {
1873 if(this->
src[0].vertno(j) == lPickedLabels.at(
k).vertices(l) && lPickedLabels.at(
k).hemi == 0) {
1874 matSourceVertLeft.conservativeResize(matSourceVertLeft.rows()+1,3);
1875 matSourceVertLeft.row(matSourceVertLeft.rows()-1) = tSurfSetInflated[0].rr().row(this->
src[0].vertno(j)) - tSurfSetInflated[0].offset().transpose();
1882 for(
int j = 0; j < this->
src[1].vertno.rows(); ++j) {
1883 for(
int k = 0;
k < lPickedLabels.size();
k++) {
1884 for(
int l = 0; l < lPickedLabels.at(
k).vertices.rows(); l++) {
1885 if(this->
src[1].vertno(j) == lPickedLabels.at(
k).vertices(l) && lPickedLabels.at(
k).hemi == 1) {
1886 matSourceVertRight.conservativeResize(matSourceVertRight.rows()+1,3);
1887 matSourceVertRight.row(matSourceVertRight.rows()-1) = tSurfSetInflated[1].rr().row(this->
src[1].vertno(j)) - tSurfSetInflated[1].offset().transpose();
1895 matSourcePositions.resize(matSourceVertLeft.rows()+matSourceVertRight.rows(),3);
1896 matSourcePositions << matSourceVertLeft, matSourceVertRight;
1898 return matSourcePositions;