31#include <QSharedPointer>
40#include <Eigen/Eigenvalues>
41#include <Eigen/Geometry>
56 const Eigen::MatrixXf& matPointCloud,
62 const VectorXf& vecWeights)
69 if(matPointCloud.rows() == 0){
70 qWarning() <<
"[MNELIB::icp] Passed point cloud is empty.";
75 int iNP = matPointCloud.rows();
76 float fMSEPrev = 0.0f;
79 MatrixXf matP0 = matPointCloud;
80 MatrixXf matPk = matP0;
81 MatrixXf matYk(matPk.rows(),matPk.cols());
82 MatrixXf matDiff = matYk;
83 VectorXf vecSE(matDiff.rows());
93 for(
int iIter = 0; iIter < iMaxIter; ++iIter) {
96 if(!mneSurfacePoints->find_closest_on_surface(matPk, iNP, matYk, vecNearest, vecDist)) {
97 qWarning() <<
"[MNELIB::icp] find_closest_on_surface was not successful.";
103 qWarning() <<
"[MNELIB::icp] point cloud registration not successful";
107 transICP.
trans = matTrans;
111 vecDist = vecDist.cwiseProduct(vecDist);
112 fMSE = vecDist.sum() / iNP;
113 fRMSE = std::sqrt(fMSE);
115 if(std::sqrt(std::fabs(fMSE - fMSEPrev)) < fTol) {
116 transFromTo = transICP;
117 qInfo() <<
"[MNELIB::icp] ICP was successful and converged after " << iIter +1 <<
" iterations with RMSE dist: " << fRMSE * 1000 <<
" mm.";
121 qInfo() <<
"[MNELIB::icp] ICP iteration " << iIter + 1 <<
" with RMSE: " << fRMSE * 1000 <<
" mm.";
123 transFromTo = transICP;
125 qWarning() <<
"[MNELIB::icp] Maximum number of " << iMaxIter <<
" iterations exceeded with RMSE: " << fRMSE * 1000 <<
" mm.";
132 const MatrixXf& matDstPoint,
133 Eigen::Matrix4f& matTrans,
136 const VectorXf& vecWeights)
146 MatrixXf matP = matSrcPoint;
147 MatrixXf matX = matDstPoint;
148 VectorXf vecW = vecWeights;
155 Matrix4f matQ = Matrix4f::Identity(4,4);
156 Matrix3f matScale = Matrix3f::Identity(3,3);
157 Matrix3f matRot = Matrix3f::Identity(3,3);
163 if(matSrcPoint.size() != matDstPoint.size()) {
164 qWarning() <<
"[MNELIB::fitMatchedPoints] Point clouds do not match.";
169 if(vecWeights.isZero()) {
170 vecMuP = matP.colwise().mean();
171 vecMuX = matX.colwise().mean();
172 matDot = matP.transpose() * matX;
173 matDot = matDot / matP.rows();
175 vecW = vecWeights / vecWeights.sum();
176 vecMuP = vecW.transpose() * matP;
177 vecMuX = vecW.transpose() * matX;
179 MatrixXf matXWeighted = matX;
180 for(
int i = 0; i < (vecW.size()); ++i) {
181 matXWeighted.row(i) = matXWeighted.row(i) * vecW(i);
183 matDot = matP.transpose() * (matXWeighted);
187 matSigmaPX = matDot - (vecMuP * vecMuX.transpose());
188 matAij = matSigmaPX - matSigmaPX.transpose();
189 vecDelta(0) = matAij(1,2); vecDelta(1) = matAij(2,0); vecDelta(2) = matAij(0,1);
190 fTrace = matSigmaPX.trace();
192 matQ.block(0,1,1,3) = vecDelta.transpose();
193 matQ.block(1,0,3,1) = vecDelta;
194 matQ.block(1,1,3,3) = matSigmaPX + matSigmaPX.transpose() - fTrace * MatrixXf::Identity(3,3);
197 SelfAdjointEigenSolver<MatrixXf> es(matQ);
198 Vector4f vecEigVec = es.eigenvectors().col(matQ.cols()-1);
201 Quaternionf quatRot(vecEigVec(0),vecEigVec(1),vecEigVec(2),vecEigVec(3));
203 matRot = quatRot.matrix();
207 MatrixXf matDevX = matX.rowwise() - vecMuX.transpose();
208 MatrixXf matDevP = matP.rowwise() - vecMuP.transpose();
209 matDevX = matDevX.cwiseProduct(matDevX);
210 matDevP = matDevP.cwiseProduct(matDevP);
212 if(!vecWeights.isZero()) {
213 for(
int i = 0; i < (vecW.size()); ++i) {
214 matDevX.row(i) = matDevX.row(i) * vecW(i);
215 matDevP.row(i) = matDevP.row(i) * vecW(i);
219 fScale = std::sqrt(matDevX.sum() / matDevP.sum());
224 vecTrans = vecMuX - fScale * matRot * vecMuP;
227 matTrans.block<3,3>(0,0) = matRot;
228 matTrans.block<3,1>(0,3) = vecTrans;
229 matTrans(3,3) = 1.0f;
230 matTrans.block<1,3>(3,0) = MatrixXf::Zero(1,3);
237 const MatrixXf& matPointCloud,
240 MatrixXf& matTakePoint,
244 int iNP = matPointCloud.rows();
245 MatrixXf matP = matPointCloud;
246 MatrixXf matYk(matPointCloud.rows(),matPointCloud.cols());
257 if(!mneSurfacePoints->find_closest_on_surface(matP, iNP, matYk, vecNearest, vecDist)) {
258 qWarning() <<
"[MNELIB::icp] find_closest_on_surface was not successful.";
262 for(
int i = 0; i < vecDist.size(); ++i) {
263 if(std::fabs(vecDist(i)) < fMaxDist) {
264 vecTake.conservativeResize(vecTake.size()+1);
265 vecTake(vecTake.size()-1) = i;
266 matTakePoint.conservativeResize(matTakePoint.rows()+1,3);
267 matTakePoint.row(matTakePoint.rows()-1) = matPointCloud.row(i);
273 qInfo() <<
"[MNELIB::discardOutliers] " << iDiscarded <<
"digitizers discarded.";
Iterative Closest Point alignment between MEG digitiser points and an MRI head surface.
Geometric projection of a 3D point onto the closest cortex triangle.
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
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, in-memory data structures and high-level readers/writers.
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
Eigen::Matrix< float, 4, 4, Eigen::DontAlign > trans
QSharedPointer< MNEProjectToSurface > SPtr