64 #include <Eigen/Dense>
72 #include <QtConcurrent/QtConcurrent>
78 using namespace Eigen;
79 using namespace INVERSELIB;
80 using namespace FIFFLIB;
81 using namespace FWDLIB;
94 : m_sensors(sensorSet),
104 if(m_sensors != sensorSet) {
105 m_sensors = sensorSet;
112 const MatrixXd& matProjectors,
114 const MatrixXd& matCoilsHead,
117 fit(matProjectedData,matProjectors,hpiModelParameters,matCoilsHead,
false,hpiFitResult);
123 const MatrixXd& matProjectors,
125 const MatrixXd& matCoilsHead,
126 const bool bOrderFrequencies,
129 if(matProjectedData.rows() != matProjectors.rows()) {
130 std::cout<<
"HPIFit::fit - Projector and data dimensions do not match. Returning."<<std::endl;
132 }
else if(hpiModelParameters.iNHpiCoils()!= matCoilsHead.rows()) {
133 std::cout<<
"HPIFit::fit - Number of coils and hpi digitizers do not match. Returning."<<std::endl;
135 }
else if(matProjectedData.rows()==0 || matProjectors.rows()==0) {
136 std::cout<<
"HPIFit::fit - No data or Projectors passed. Returning."<<std::endl;
138 }
else if(m_sensors.ncoils() != matProjectedData.rows()) {
139 std::cout<<
"HPIFit::fit - Number of channels in sensors and data do not match. Returning."<<std::endl;
143 const MatrixXd matAmplitudes = computeAmplitudes(matProjectedData,
146 const MatrixXd matCoilsSeed = computeSeedPoints(matAmplitudes,
147 hpiFitResult.devHeadTrans,
148 hpiFitResult.errorDistances,
151 CoilParam fittedCoilParams = dipfit(matCoilsSeed,
154 hpiModelParameters.iNHpiCoils(),
159 if(bOrderFrequencies) {
160 const std::vector<int> vecOrder = findCoilOrder(fittedCoilParams.pos,
163 fittedCoilParams.pos = order(vecOrder,fittedCoilParams.pos);
164 hpiFitResult.hpiFreqs = order(vecOrder,hpiModelParameters.
vecHpiFreqs());
167 hpiFitResult.GoF = computeGoF(fittedCoilParams.dpfiterror);
169 hpiFitResult.fittedCoils = getFittedPointSet(fittedCoilParams.pos);
171 hpiFitResult.devHeadTrans = computeDeviceHeadTransformation(fittedCoilParams.pos,
174 hpiFitResult.errorDistances = computeEstimationError(fittedCoilParams.pos,
176 hpiFitResult.devHeadTrans);
181 Eigen::MatrixXd HPIFit::computeAmplitudes(
const Eigen::MatrixXd& matProjectedData,
185 MatrixXd matTopo = m_signalModel.
fitData(hpiModelParameters,matProjectedData);
186 matTopo.transposeInPlace();
189 const int iNumCoils = hpiModelParameters.iNHpiCoils();
191 MatrixXd matAmpSine(matProjectedData.cols(), iNumCoils);
192 MatrixXd matAmpCosine(matProjectedData.cols(), iNumCoils);
194 matAmpSine = matTopo.leftCols(iNumCoils);
195 matAmpCosine = matTopo.middleCols(iNumCoils,iNumCoils);
198 for(
int j = 0; j < iNumCoils; ++j) {
201 fNS = matAmpSine.col(j).array().square().sum();
202 fNC = matAmpCosine.col(j).array().square().sum();
204 matAmpSine.col(j) = matAmpCosine.col(j);
213 Eigen::MatrixXd HPIFit::computeSeedPoints(
const Eigen::MatrixXd& matAmplitudes,
215 const QVector<double>& vecError,
216 const Eigen::MatrixXd& matCoilsHead)
218 const int iNumCoils = matCoilsHead.rows();
219 MatrixXd matCoilsSeed = MatrixXd::Zero(iNumCoils,3);
221 const double dError = std::accumulate(vecError.begin(), vecError.end(), .0) / vecError.size();
223 if(transDevHead.
trans != MatrixXd::Identity(4,4).cast<
float>() && dError < 0.010) {
225 matCoilsSeed = transDevHead.
apply_inverse_trans(matCoilsHead.cast<
float>()).cast<
double>();
228 VectorXi vecChIdcs(iNumCoils);
230 for (
int j = 0; j < iNumCoils; j++) {
232 VectorXd::Index indMax;
233 matAmplitudes.col(j).maxCoeff(&indMax);
234 if(indMax < m_sensors.ncoils()) {
237 vecChIdcs(j) = iChIdx;
240 for (
int j = 0; j < vecChIdcs.rows(); ++j) {
241 if(vecChIdcs(j) < m_sensors.ncoils()) {
242 Vector3d r0 = m_sensors.r0(vecChIdcs(j));
243 Vector3d ez = m_sensors.ez(vecChIdcs(j));
244 matCoilsSeed.row(j) = (-1 * ez * 0.03 + r0);
253 CoilParam HPIFit::dipfit(
const MatrixXd matCoilsSeed,
255 const MatrixXd& matData,
257 const MatrixXd& matProjectors,
258 const int iMaxIterations,
259 const float fAbortError)
263 QList<HPIFitData> lCoilData;
265 for(qint32 i = 0; i < iNumCoils; ++i) {
267 coilData.m_coilPos = matCoilsSeed.row(i);
268 coilData.m_sensorData = matData.col(i);
269 coilData.m_sensors = sensors;
270 coilData.m_matProjector = matProjectors;
271 coilData.m_iMaxIterations = iMaxIterations;
272 coilData.m_fAbortError = fAbortError;
274 lCoilData.append(coilData);
279 if(!lCoilData.isEmpty()) {
286 QFuture<void> future = QtConcurrent::map(lCoilData,
288 future.waitForFinished();
291 for(qint32 i = 0; i < lCoilData.size(); ++i) {
292 coil.pos.row(i) = lCoilData.at(i).m_coilPos;
293 coil.mom = lCoilData.at(i).m_errorInfo.moment.transpose();
294 coil.dpfiterror(i) = lCoilData.at(i).m_errorInfo.error;
295 coil.dpfitnumitr(i) = lCoilData.at(i).m_errorInfo.numIterations;
305 std::vector<int> HPIFit::findCoilOrder(
const MatrixXd& matCoilsDev,
306 const MatrixXd& matCoilsHead)
309 MatrixXd matCoilTemp = matCoilsDev;
310 const int iNumCoils = matCoilsDev.rows();
312 std::vector<int> vecOrder(iNumCoils);
313 std::iota(vecOrder.begin(), vecOrder.end(), 0);;
316 const double dErrorMin = 0.010;
317 double dErrorActual = 0.0;
318 double dErrorBest = dErrorMin;
320 MatrixXd matTrans(4,4);
321 std::vector<int> vecOrderBest = vecOrder;
323 bool bSuccess =
false;
326 for(
int i = 0; i < iNumCoils; i++) {
327 matCoilTemp.row(i) = matCoilsDev.row(vecOrder[i]);
329 matTrans = computeTransformation(matCoilsHead,matCoilTemp);
330 dErrorActual = objectTrans(matCoilsHead,matCoilTemp,matTrans);
331 if(dErrorActual < dErrorMin && dErrorActual < dErrorBest) {
333 dErrorBest = dErrorActual;
334 vecOrderBest = vecOrder;
337 }
while (std::next_permutation(vecOrder.begin(), vecOrder.end()));
343 double HPIFit::objectTrans(
const MatrixXd& matHeadCoil,
344 const MatrixXd& matCoil,
345 const MatrixXd& matTrans)
348 const int iNumCoils = matHeadCoil.rows();
349 MatrixXd matTemp = matCoil;
352 matTemp.conservativeResize(matCoil.rows(),matCoil.cols()+1);
353 matTemp.block(0,3,iNumCoils,1).setOnes();
354 matTemp.transposeInPlace();
357 MatrixXd matTestPos = matTrans * matTemp;
360 MatrixXd matDiff = matTestPos.block(0,0,3,iNumCoils) - matHeadCoil.transpose();
361 VectorXd vecError = matDiff.colwise().norm();
364 double dError = matDiff.colwise().norm().mean();;
371 Eigen::MatrixXd HPIFit::order(
const std::vector<int>& vecOrder,
372 const Eigen::MatrixXd& matToOrder)
374 const int iNumCoils = vecOrder.size();
375 MatrixXd matToOrderTemp = matToOrder;
377 for(
int i = 0; i < iNumCoils; i++) {
378 matToOrderTemp.row(i) = matToOrder.row(vecOrder[i]);
380 return matToOrderTemp;
385 QVector<int> HPIFit::order(
const std::vector<int>& vecOrder,
386 const QVector<int>& vecToOrder)
388 const int iNumCoils = vecOrder.size();
389 QVector<int> vecToOrderTemp = vecToOrder;
391 for(
int i = 0; i < iNumCoils; i++) {
392 vecToOrderTemp[i] = vecToOrder[vecOrder[i]];
394 return vecToOrderTemp;
399 Eigen::VectorXd HPIFit::computeGoF(
const Eigen::VectorXd& vecDipFitError)
401 VectorXd vecGoF(vecDipFitError.size());
402 for(
int i = 0; i < vecDipFitError.size(); ++i) {
403 vecGoF(i) = 1 - vecDipFitError(i);
411 const Eigen::MatrixXd& matCoilsHead)
413 const MatrixXd matTrans = computeTransformation(matCoilsHead,matCoilsDev);
414 return FiffCoordTrans::make(1,4,matTrans.cast<
float>(),
true);
419 Eigen::Matrix4d HPIFit::computeTransformation(Eigen::MatrixXd matNH, MatrixXd matBT)
421 MatrixXd matXdiff, matYdiff, matZdiff, matC, matQ;
422 Matrix4d matTransFinal = Matrix4d::Identity(4,4);
423 Matrix4d matRot = Matrix4d::Zero(4,4);
424 Matrix4d matTrans = Matrix4d::Identity(4,4);
425 double dMeanX,dMeanY,dMeanZ,dNormf;
427 for(
int i = 0; i < 15; ++i) {
429 matXdiff = matNH.col(0) - matBT.col(0);
430 matYdiff = matNH.col(1) - matBT.col(1);
431 matZdiff = matNH.col(2) - matBT.col(2);
433 dMeanX = matXdiff.mean();
434 dMeanY = matYdiff.mean();
435 dMeanZ = matZdiff.mean();
438 for (
int j = 0; j < matBT.rows(); ++j) {
439 matBT(j,0) = matBT(j,0) + dMeanX;
440 matBT(j,1) = matBT(j,1) + dMeanY;
441 matBT(j,2) = matBT(j,2) + dMeanZ;
445 matC = matBT.transpose() * matNH;
447 JacobiSVD< MatrixXd > svd(matC ,Eigen::ComputeThinU | ComputeThinV);
449 matQ = svd.matrixU() * svd.matrixV().transpose();
452 if(matQ.determinant() < 0) {
453 matQ(0,2) = matQ(0,2) * -1;
454 matQ(1,2) = matQ(1,2) * -1;
455 matQ(2,2) = matQ(2,2) * -1;
459 matBT = matBT * matQ;
462 dNormf = (matNH.transpose()-matBT.transpose()).norm();
466 for(
int j = 0; j < 3; ++j) {
467 for(
int k = 0;
k < 3; ++
k) {
468 matRot(j,
k) = matQ(
k,j);
473 matTrans(0,3) = dMeanX;
474 matTrans(1,3) = dMeanY;
475 matTrans(2,3) = dMeanZ;
480 matTransFinal = matRot * matTrans * matTransFinal;
482 return matTransFinal;
487 QVector<double> HPIFit::computeEstimationError(
const Eigen::MatrixXd& matCoilsDev,
488 const Eigen::MatrixXd& matCoilsHead,
492 MatrixXd matTemp = matCoilsDev;
493 MatrixXd matTestPos = transDevHead.
apply_trans(matTemp.cast<
float>()).cast<
double>();
494 MatrixXd matDiffPos = matTestPos - matCoilsHead;
497 int iNumCoils = matCoilsDev.rows();
498 QVector<double> vecError(iNumCoils);
499 for(
int i = 0; i < matDiffPos.rows(); ++i) {
500 vecError[i] = matDiffPos.row(i).norm();
510 const int iNumCoils = matCoilsDev.rows();
512 for(
int i = 0; i < iNumCoils; ++i) {
514 digPoint.
kind = FIFFV_POINT_EEG;
516 digPoint.
r[0] = matCoilsDev(i,0);
517 digPoint.
r[1] = matCoilsDev(i,1);
518 digPoint.
r[2] = matCoilsDev(i,2);
520 fittedPointSet << digPoint;
522 return fittedPointSet;
528 const Eigen::MatrixXf& transDevHead,
529 Eigen::MatrixXd& matPosition,
530 const Eigen::VectorXd& vecGoF,
531 const QVector<double>& vecError)
534 Matrix3f matRot = transDevHead.block(0,0,3,3);
536 Eigen::Quaternionf quatHPI(matRot);
537 double dError = std::accumulate(vecError.begin(), vecError.end(), .0) / vecError.size();
539 matPosition.conservativeResize(matPosition.rows()+1, 10);
540 matPosition(matPosition.rows()-1,0) = fTime;
541 matPosition(matPosition.rows()-1,1) = quatHPI.x();
542 matPosition(matPosition.rows()-1,2) = quatHPI.y();
543 matPosition(matPosition.rows()-1,3) = quatHPI.z();
544 matPosition(matPosition.rows()-1,4) = transDevHead(0,3);
545 matPosition(matPosition.rows()-1,5) = transDevHead(1,3);
546 matPosition(matPosition.rows()-1,6) = transDevHead(2,3);
547 matPosition(matPosition.rows()-1,7) = vecGoF.mean();
548 matPosition(matPosition.rows()-1,8) = dError;
549 matPosition(matPosition.rows()-1,9) = 0;