/* * icp6Dsvd implementation * * Copyright (C) Kai Lingemann, Andreas Nuechter * * Released under the GPL version 3. * */ /** @file * @brief Implementation of the ICP error function minimization via SVD * @author Kai Lingemann. Institute of Computer Science, University of Osnabrueck, Germany. * @author Andreas Nuechter. Institute of Computer Science, University of Osnabrueck, Germany. */ #include "slam6d/icp6Dsvd.h" #include "slam6d/globals.icc" #include using std::ios; using std::resetiosflags; using std::setiosflags; #include "newmat/newmat.h" #include "newmat/newmatap.h" using namespace NEWMAT; /** * computes the rotation matrix consisting * of a rotation and translation that * minimizes the root-mean-square error of the * point pairs using the SVD PARAMETERS * vector of point pairs, rotation matrix * @param pairs Vector of point pairs (pairs of corresponding points) * @param *alignfx The resulting transformation matrix * @return Error estimation of the matching (rms) */ double icp6D_SVD::Point_Point_Align(const vector& pairs, double *alignfx, const double centroid_m[3], const double centroid_d[3]) { double error = 0; double sum = 0.0; // Get centered PtPairs double** m = new double*[pairs.size()]; double** d = new double*[pairs.size()]; for(unsigned int i = 0; i < pairs.size(); i++){ m[i] = new double[3]; d[i] = new double[3]; m[i][0] = pairs[i].p1.x - centroid_m[0]; m[i][1] = pairs[i].p1.y - centroid_m[1]; m[i][2] = pairs[i].p1.z - centroid_m[2]; d[i][0] = pairs[i].p2.x - centroid_d[0]; d[i][1] = pairs[i].p2.y - centroid_d[1]; d[i][2] = pairs[i].p2.z - centroid_d[2]; sum += sqr(pairs[i].p1.x - pairs[i].p2.x) + sqr(pairs[i].p1.y - pairs[i].p2.y) + sqr(pairs[i].p1.z - pairs[i].p2.z) ; } error = sqrt(sum / (double)pairs.size()); if (!quiet) { cout.setf(ios::basefield); cout << "SVD RMS point-to-point error = " << resetiosflags(ios::adjustfield) << setiosflags(ios::internal) << resetiosflags(ios::floatfield) << setiosflags(ios::fixed) << std::setw(10) << std::setprecision(7) << error << " using " << std::setw(6) << (int)pairs.size() << " points" << endl; } // Fill H matrix Matrix H(3,3), R(3,3); for(int j = 0; j < 3; j++){ for(int k = 0; k < 3; k++){ H(j+1, k+1) = 0.0; } } for(unsigned int i = 0; i < pairs.size(); i++){ for(int j = 0; j < 3; j++){ for(int k = 0; k < 3; k++){ H(j+1, k+1) += d[i][j]*m[i][k]; } } } Matrix U(3,3); DiagonalMatrix Lamda(3); Matrix V(3,3); // Make SVD SVD(H, Lamda, U, V); // Get rotation R = V*(U.t()); // Calculate translation double translation[3]; ColumnVector col_vec(3); for(int j = 0; j < 3; j++) col_vec(j+1) = centroid_d[j]; ColumnVector r_time_colVec = ColumnVector(R*col_vec); translation[0] = centroid_m[0] - r_time_colVec(1); translation[1] = centroid_m[1] - r_time_colVec(2); translation[2] = centroid_m[2] - r_time_colVec(3); // Fill result alignfx[0] = R(1,1); alignfx[1] = R(2,1); alignfx[2] = 0; alignfx[2] = R(3,1); alignfx[3] = 0; alignfx[4] = R(1,2); alignfx[5] = R(2,2); alignfx[6] = R(3,2); alignfx[7] = 0; alignfx[8] = R(1,3); alignfx[9] = R(2,3); alignfx[10] = R(3,3); alignfx[11] = 0; alignfx[12] = translation[0]; alignfx[13] = translation[1]; alignfx[14] = translation[2]; alignfx[15] = 1; for(unsigned int i = 0; i < pairs.size(); i++){ delete [] m[i]; delete [] d[i]; } delete [] m; delete [] d; return error; } /** * computes the rotation matrix consisting * of a rotation and translation that * minimizes the root-mean-square error of the * point pairs using the SVD PARAMETERS * vector of point pairs, rotation matrix * @param pairs Vector of point pairs (pairs of corresponding points) * @param *alignfx The resulting transformation matrix * @return Error estimation of the matching (rms) */ double icp6D_SVD::Point_Point_Align_Parallel(const int openmp_num_threads, const unsigned int n[OPENMP_NUM_THREADS], const double sum[OPENMP_NUM_THREADS], const double centroid_m[OPENMP_NUM_THREADS][3], const double centroid_d[OPENMP_NUM_THREADS][3], const double Si[OPENMP_NUM_THREADS][9], double *alignxf) { double s = 0.0; double ret; unsigned int pairs_size = 0; double cm[3] = {0.0, 0.0, 0.0}; // centroid m double cd[3] = {0.0, 0.0, 0.0}; // centroid d // Implementation according to the paper // "The Parallel Iterative Closest Point Algorithm" // by Langis / Greenspan / Godin, IEEE 3DIM 2001 // formula (4) // // The same information are given in (ecrm2007.pdf) // Andreas Nüchter. Parallelization of Scan Matching // for Robotic 3D Mapping. In Proceedings of the 3rd // European Conference on Mobile Robots (ECMR '07), // Freiburg, Germany, September 2007 for (int i = 0; i < openmp_num_threads; i++) { s += sum[i]; pairs_size += n[i]; cm[0] += n[i] * centroid_m[i][0]; cm[1] += n[i] * centroid_m[i][1]; cm[2] += n[i] * centroid_m[i][2]; cd[0] += n[i] * centroid_d[i][0]; cd[1] += n[i] * centroid_d[i][1]; cd[2] += n[i] * centroid_d[i][2]; } cm[0] /= pairs_size; cm[1] /= pairs_size; cm[2] /= pairs_size; cd[0] /= pairs_size; cd[1] /= pairs_size; cd[2] /= pairs_size; ret = sqrt(s / (double)pairs_size); if (!quiet) { cout.setf(ios::basefield); cout << "PSVD RMS point-to-point error = " << resetiosflags(ios::adjustfield) << setiosflags(ios::internal) << resetiosflags(ios::floatfield) << setiosflags(ios::fixed) << std::setw(10) << std::setprecision(7) << ret << " using " << std::setw(6) << pairs_size << " points" << endl; } Matrix H(3,3), R(3,3); for(int j = 0; j < 3; j++){ for(int k = 0; k < 3; k++){ H(j+1, k+1) = 0.0; } } // formula (5) for (int i = 0; i < openmp_num_threads; i++) { for(int j = 0; j < 3; j++){ for(int k = 0; k < 3; k++){ // H(j+1, k+1) += Si[i][j*3+k] + n[i] * ((centroid_m[i][j] - cm[j]) * (centroid_d[i][k] - cd[k])) ; H(j+1, k+1) += Si[i][k*3+j] + n[i] * ((centroid_d[i][j] - cd[j]) * (centroid_m[i][k] - cm[k])) ; } } } Matrix U(3,3); DiagonalMatrix Lamda(3); Matrix V(3,3); // Make SVD SVD(H, Lamda, U, V); // Get rotation R = V*(U.t()); // Calculate translation double translation[3]; ColumnVector col_vec(3); for(int j = 0; j < 3; j++) { col_vec(j+1) = cd[j]; } ColumnVector r_time_colVec = ColumnVector(R * col_vec); translation[0] = cm[0] - r_time_colVec(1); translation[1] = cm[1] - r_time_colVec(2); translation[2] = cm[2] - r_time_colVec(3); // Fill result alignxf[0] = R(1,1); alignxf[1] = R(2,1); alignxf[2] = 0; alignxf[2] = R(3,1); alignxf[3] = 0; alignxf[4] = R(1,2); alignxf[5] = R(2,2); alignxf[6] = R(3,2); alignxf[7] = 0; alignxf[8] = R(1,3); alignxf[9] = R(2,3); alignxf[10] = R(3,3); alignxf[11] = 0; alignxf[12] = translation[0]; alignxf[13] = translation[1]; alignxf[14] = translation[2]; alignxf[15] = 1; return ret; }