49#include <QSharedPointer>
58#include <Eigen/Eigenvalues>
59#include <Eigen/Geometry>
74 const Eigen::MatrixXf& matPointCloud,
80 const VectorXf& vecWeights)
87 if(matPointCloud.rows() == 0){
88 qWarning() <<
"[MNELIB::icp] Passed point cloud is empty.";
93 int iNP = matPointCloud.rows();
94 float fMSEPrev = 0.0f;
97 MatrixXf matP0 = matPointCloud;
98 MatrixXf matPk = matP0;
99 MatrixXf matYk(matPk.rows(),matPk.cols());
100 MatrixXf matDiff = matYk;
101 VectorXf vecSE(matDiff.rows());
111 for(
int iIter = 0; iIter < iMaxIter; ++iIter) {
114 if(!mneSurfacePoints->find_closest_on_surface(matPk, iNP, matYk, vecNearest, vecDist)) {
115 qWarning() <<
"[MNELIB::icp] find_closest_on_surface was not successful.";
121 qWarning() <<
"[MNELIB::icp] point cloud registration not successful";
125 transICP.
trans = matTrans;
129 vecDist = vecDist.cwiseProduct(vecDist);
130 fMSE = vecDist.sum() / iNP;
131 fRMSE = std::sqrt(fMSE);
133 if(std::sqrt(std::fabs(fMSE - fMSEPrev)) < fTol) {
134 transFromTo = transICP;
135 qInfo() <<
"[MNELIB::icp] ICP was successful and converged after " << iIter +1 <<
" iterations with RMSE dist: " << fRMSE * 1000 <<
" mm.";
139 qInfo() <<
"[MNELIB::icp] ICP iteration " << iIter + 1 <<
" with RMSE: " << fRMSE * 1000 <<
" mm.";
141 transFromTo = transICP;
143 qWarning() <<
"[MNELIB::icp] Maximum number of " << iMaxIter <<
" iterations exceeded with RMSE: " << fRMSE * 1000 <<
" mm.";
150 const MatrixXf& matDstPoint,
151 Eigen::Matrix4f& matTrans,
154 const VectorXf& vecWeights)
164 MatrixXf matP = matSrcPoint;
165 MatrixXf matX = matDstPoint;
166 VectorXf vecW = vecWeights;
173 Matrix4f matQ = Matrix4f::Identity(4,4);
174 Matrix3f matScale = Matrix3f::Identity(3,3);
175 Matrix3f matRot = Matrix3f::Identity(3,3);
181 if(matSrcPoint.size() != matDstPoint.size()) {
182 qWarning() <<
"[MNELIB::fitMatchedPoints] Point clouds do not match.";
187 if(vecWeights.isZero()) {
188 vecMuP = matP.colwise().mean();
189 vecMuX = matX.colwise().mean();
190 matDot = matP.transpose() * matX;
191 matDot = matDot / matP.rows();
193 vecW = vecWeights / vecWeights.sum();
194 vecMuP = vecW.transpose() * matP;
195 vecMuX = vecW.transpose() * matX;
197 MatrixXf matXWeighted = matX;
198 for(
int i = 0; i < (vecW.size()); ++i) {
199 matXWeighted.row(i) = matXWeighted.row(i) * vecW(i);
201 matDot = matP.transpose() * (matXWeighted);
205 matSigmaPX = matDot - (vecMuP * vecMuX.transpose());
206 matAij = matSigmaPX - matSigmaPX.transpose();
207 vecDelta(0) = matAij(1,2); vecDelta(1) = matAij(2,0); vecDelta(2) = matAij(0,1);
208 fTrace = matSigmaPX.trace();
210 matQ.block(0,1,1,3) = vecDelta.transpose();
211 matQ.block(1,0,3,1) = vecDelta;
212 matQ.block(1,1,3,3) = matSigmaPX + matSigmaPX.transpose() - fTrace * MatrixXf::Identity(3,3);
215 SelfAdjointEigenSolver<MatrixXf> es(matQ);
216 Vector4f vecEigVec = es.eigenvectors().col(matQ.cols()-1);
219 Quaternionf quatRot(vecEigVec(0),vecEigVec(1),vecEigVec(2),vecEigVec(3));
221 matRot = quatRot.matrix();
225 MatrixXf matDevX = matX.rowwise() - vecMuX.transpose();
226 MatrixXf matDevP = matP.rowwise() - vecMuP.transpose();
227 matDevX = matDevX.cwiseProduct(matDevX);
228 matDevP = matDevP.cwiseProduct(matDevP);
230 if(!vecWeights.isZero()) {
231 for(
int i = 0; i < (vecW.size()); ++i) {
232 matDevX.row(i) = matDevX.row(i) * vecW(i);
233 matDevP.row(i) = matDevP.row(i) * vecW(i);
237 fScale = std::sqrt(matDevX.sum() / matDevP.sum());
242 vecTrans = vecMuX - fScale * matRot * vecMuP;
245 matTrans.block<3,3>(0,0) = matRot;
246 matTrans.block<3,1>(0,3) = vecTrans;
247 matTrans(3,3) = 1.0f;
248 matTrans.block<1,3>(3,0) = MatrixXf::Zero(1,3);
255 const MatrixXf& matPointCloud,
258 MatrixXf& matTakePoint,
262 int iNP = matPointCloud.rows();
263 MatrixXf matP = matPointCloud;
264 MatrixXf matYk(matPointCloud.rows(),matPointCloud.cols());
275 if(!mneSurfacePoints->find_closest_on_surface(matP, iNP, matYk, vecNearest, vecDist)) {
276 qWarning() <<
"[MNELIB::icp] find_closest_on_surface was not successful.";
280 for(
int i = 0; i < vecDist.size(); ++i) {
281 if(std::fabs(vecDist(i)) < fMaxDist) {
282 vecTake.conservativeResize(vecTake.size()+1);
283 vecTake(vecTake.size()-1) = i;
284 matTakePoint.conservativeResize(matTakePoint.rows()+1,3);
285 matTakePoint.row(matTakePoint.rows()-1) = matPointCloud.row(i);
291 qInfo() <<
"[MNELIB::discardOutliers] " << iDiscarded <<
"digitizers discarded.";
FiffCoordTrans class declaration.
Core MNE data structures (source spaces, source estimates, hemispheres).
bool performIcp(const QSharedPointer< MNELIB::MNEProjectToSurface > mneSurfacePoints, const Eigen::MatrixXf &matPointCloud, FIFFLIB::FiffCoordTrans &transFromTo, float &fRMSE, bool bScale=false, int iMaxIter=20, float fTol=0.001, const Eigen::VectorXf &vecWeights=vecDefaultWeights)
bool fitMatchedPoints(const Eigen::MatrixXf &matSrcPoint, const Eigen::MatrixXf &matDstPoint, Eigen::Matrix4f &matTrans, float fScale=1.0, bool bScale=false, const Eigen::VectorXf &vecWeights=vecDefaultWeights)
bool discard3DPointOutliers(const QSharedPointer< MNELIB::MNEProjectToSurface > mneSurfacePoints, const Eigen::MatrixXf &matPointCloud, const FIFFLIB::FiffCoordTrans &transFromTo, Eigen::VectorXi &vecTake, Eigen::MatrixXf &matTakePoint, float fMaxDist=0.0)
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
Coordinate transformation description.
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
Eigen::Matrix< float, 4, 4, Eigen::DontAlign > trans
QSharedPointer< MNEProjectToSurface > SPtr