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) {
95 if (!mneSurfacePoints->find_closest_on_surface(matPk, iNP, matYk, vecNearest, vecDist)) {
96 qWarning() <<
"[MNELIB::icp] find_closest_on_surface was not successful.";
101 if (!
fitMatchedPoints(matP0, matYk, matTrans, fScale, bScale, vecWeights)) {
102 qWarning() <<
"[MNELIB::icp] point cloud registration not successful";
106 transICP.
trans = matTrans;
110 vecDist = vecDist.cwiseProduct(vecDist);
111 fMSE = vecDist.sum() / iNP;
112 fRMSE = std::sqrt(fMSE);
114 if (std::sqrt(std::fabs(fMSE - fMSEPrev)) < fTol) {
115 transFromTo = transICP;
116 qInfo() <<
"[MNELIB::icp] ICP was successful and converged after " << iIter + 1 <<
" iterations with RMSE dist: " << fRMSE * 1000 <<
" mm.";
120 qInfo() <<
"[MNELIB::icp] ICP iteration " << iIter + 1 <<
" with RMSE: " << fRMSE * 1000 <<
" mm.";
122 transFromTo = transICP;
124 qWarning() <<
"[MNELIB::icp] Maximum number of " << iMaxIter <<
" iterations exceeded with RMSE: " << fRMSE * 1000 <<
" mm.";
131 const MatrixXf& matDstPoint,
132 Eigen::Matrix4f& matTrans,
135 const VectorXf& vecWeights)
145 MatrixXf matP = matSrcPoint;
146 MatrixXf matX = matDstPoint;
147 VectorXf vecW = vecWeights;
154 Matrix4f matQ = Matrix4f::Identity(4, 4);
155 Matrix3f matScale = Matrix3f::Identity(3, 3);
156 Matrix3f matRot = Matrix3f::Identity(3, 3);
162 if (matSrcPoint.size() != matDstPoint.size()) {
163 qWarning() <<
"[MNELIB::fitMatchedPoints] Point clouds do not match.";
168 if (vecWeights.isZero()) {
169 vecMuP = matP.colwise().mean();
170 vecMuX = matX.colwise().mean();
171 matDot = matP.transpose() * matX;
172 matDot = matDot / matP.rows();
174 vecW = vecWeights / vecWeights.sum();
175 vecMuP = vecW.transpose() * matP;
176 vecMuX = vecW.transpose() * matX;
178 MatrixXf matXWeighted = matX;
179 for (
int i = 0; i < (vecW.size()); ++i) {
180 matXWeighted.row(i) = matXWeighted.row(i) * vecW(i);
182 matDot = matP.transpose() * (matXWeighted);
186 matSigmaPX = matDot - (vecMuP * vecMuX.transpose());
187 matAij = matSigmaPX - matSigmaPX.transpose();
188 vecDelta(0) = matAij(1, 2);
189 vecDelta(1) = matAij(2, 0);
190 vecDelta(2) = matAij(0, 1);
191 fTrace = matSigmaPX.trace();
193 matQ.block(0, 1, 1, 3) = vecDelta.transpose();
194 matQ.block(1, 0, 3, 1) = vecDelta;
195 matQ.block(1, 1, 3, 3) = matSigmaPX + matSigmaPX.transpose() - fTrace * MatrixXf::Identity(3, 3);
198 SelfAdjointEigenSolver<MatrixXf> es(matQ);
199 Vector4f vecEigVec = es.eigenvectors().col(matQ.cols() - 1);
202 Quaternionf quatRot(vecEigVec(0), vecEigVec(1), vecEigVec(2), vecEigVec(3));
204 matRot = quatRot.matrix();
208 MatrixXf matDevX = matX.rowwise() - vecMuX.transpose();
209 MatrixXf matDevP = matP.rowwise() - vecMuP.transpose();
210 matDevX = matDevX.cwiseProduct(matDevX);
211 matDevP = matDevP.cwiseProduct(matDevP);
213 if (!vecWeights.isZero()) {
214 for (
int i = 0; i < (vecW.size()); ++i) {
215 matDevX.row(i) = matDevX.row(i) * vecW(i);
216 matDevP.row(i) = matDevP.row(i) * vecW(i);
220 fScale = std::sqrt(matDevX.sum() / matDevP.sum());
225 vecTrans = vecMuX - fScale * matRot * vecMuP;
228 matTrans.block<3, 3>(0, 0) = matRot;
229 matTrans.block<3, 1>(0, 3) = vecTrans;
230 matTrans(3, 3) = 1.0f;
231 matTrans.block<1, 3>(3, 0) = MatrixXf::Zero(1, 3);
238 const MatrixXf& matPointCloud,
241 MatrixXf& matTakePoint,
245 int iNP = matPointCloud.rows();
246 MatrixXf matP = matPointCloud;
247 MatrixXf matYk(matPointCloud.rows(), matPointCloud.cols());
257 if (fMaxDist > 0.0) {
258 if (!mneSurfacePoints->find_closest_on_surface(matP, iNP, matYk, vecNearest, vecDist)) {
259 qWarning() <<
"[MNELIB::icp] find_closest_on_surface was not successful.";
263 for (
int i = 0; i < vecDist.size(); ++i) {
264 if (std::fabs(vecDist(i)) < fMaxDist) {
265 vecTake.conservativeResize(vecTake.size() + 1);
266 vecTake(vecTake.size() - 1) = i;
267 matTakePoint.conservativeResize(matTakePoint.rows() + 1, 3);
268 matTakePoint.row(matTakePoint.rows() - 1) = matPointCloud.row(i);
274 qInfo() <<
"[MNELIB::discardOutliers] " << iDiscarded <<
"digitizers discarded.";
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
Iterative Closest Point alignment between MEG digitiser points and an MRI head surface.
Geometric projection of a 3D point onto the closest cortex triangle.
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