72#include <QtConcurrent/QtConcurrent>
79using namespace INVERSELIB;
80using namespace FIFFLIB;
81using 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);
181Eigen::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);
213Eigen::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);
253CoilParam 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;
305std::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()));
343double 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();;
371Eigen::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;
385QVector<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;
399Eigen::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);
419Eigen::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;
487QVector<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;
FiffDigPointSet class declaration.
FiffCov class declaration.
IOUtils class declaration.
MNEMath class declaration.
FwdCoilSet class declaration.
HPIFitData class declaration.
HpiModelParameters class declaration.
SensorSet class declaration.
HpiDataUpdater class declaration.
HPIFit class declaration.
SignalModel class declaration.
Coordinate transformation description.
static FiffCoordTrans make(int from, int to, const Eigen::Matrix3f &rot, const Eigen::VectorXf &move)
Eigen::MatrixX3f apply_inverse_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
Eigen::Matrix< float, 4, 4, Eigen::DontAlign > trans
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
Digitization point description.
Holds a set of digitizer points.
void checkForUpdate(const SensorSet &sensorSet)
static void storeHeadPosition(float fTime, const Eigen::MatrixXf &matTransDevHead, Eigen::MatrixXd &matPosition, const Eigen::VectorXd &vecGoF, const QVector< double > &vecError)
void fit(const Eigen::MatrixXd &matProjectedData, const Eigen::MatrixXd &matProjectors, const HpiModelParameters &hpiModelParameters, const Eigen::MatrixXd &matCoilsHead, HpiFitResult &hpiFitResult)
HPI Fit algorithm data structure.
void doDipfitConcurrent()
Brief description of this class.
QVector< int > vecHpiFreqs() const
Brief description of this class.
Eigen::MatrixXd fitData(const HpiModelParameters &hpiModelParameters, const Eigen::MatrixXd &matData)