/* * icp6Dquat implementation * * Copyright (C) Andreas Nuechter * * Released under the GPL version 3. * */ /** @file * @brief Implementation of the ICP error function minimization via quaternions * @author Andreas Nuechter. Jacobs University Bremen gGmbH, Germany. */ #include "slam6d/icp6Dquat.h" #include "slam6d/globals.icc" #include using std::ios; using std::resetiosflags; using std::setiosflags; #include #include #include using std::cout; using std::cerr; using std::endl; #ifdef _MSC_VER #define cbrt(x) pow(x,1/3) #endif /** * computes the rotation matrix consisting * of a rotation and translation that * minimizes the root-mean-square error of the * point pairs using the Quaternion method of Horn * 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_QUAT::Point_Point_Align(const vector& pairs, double *alignfx, const double centroid_m[3], const double centroid_d[3]) { int n = pairs.size(); double sum = 0.0; // the quaternion double q[7]; double S[3][3]; double Q[4][4]; int i,j; // calculate the cross covariance matrix for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) S[i][j] = 0; for (i=0; i 0.0)&&(y > 0.0)&&(c > 0.0)) || ((a > 0.0)&&(y < 0.0)&&(c < 0.0)) || ((a < 0.0)&&(y > 0.0)&&(c < 0.0)) || ((a < 0.0)&&(y < 0.0)&&(c > 0.0)) || (a == 0.0)||(y == 0.0)||(c == 0.0) ) { /* use ef - */ if ((b < 0.0)&&(y < 0.0)&&(esq > 0.0)) { e = sqrt(esq); f = ef/e; } else if ((d < 0.0) && (fsq > 0.0)) { f = sqrt(fsq); e = ef/f; } else { e = sqrt(esq); f = sqrt(fsq); if (ef < 0.0) f = -f; } } else { e = sqrt(esq); f = sqrt(fsq); if (ef < 0.0) f = -f; } /* note that e >= 0.0 */ ainv2 = a*.5; g = ainv2 - e; gg = ainv2 + e; if ( ((b > 0.0)&&(y > 0.0)) || ((b < 0.0)&&(y < 0.0)) ) { if (( a > 0.0) && (e != 0.0)) g = (b + y)/gg; else if (e != 0.0) gg = (b + y)/g; } if ((y == 0.0)&&(f == 0.0)) { h = 0.0; hh = 0.0; } else if ( ((f > 0.0)&&(y < 0.0)) || ((f < 0.0)&&(y > 0.0)) ) { hh = -.5*y + f; h = d/hh; } else { h = -.5*y - f; hh = d/h; } n1 = qudrtc(gg,hh,v1); n2 = qudrtc(g,h,v2); nquar = n1+n2; rts[0] = v1[0]; rts[1] = v1[1]; rts[n1+0] = v2[0]; rts[n1+1] = v2[1]; return nquar; } } } /* ferrari */ int icp6D_QUAT::qudrtc(double b, double c, double rts[4]) /* solve the quadratic equation - x**2+b*x+c = 0 */ { int nquad; double rtdis; double dis = b*b-4.0*c; if (dis >= 0.0) { nquad = 2; rtdis = sqrt(dis); if (b > 0.0) rts[0] = ( -b - rtdis)*.5; else rts[0] = ( -b + rtdis)*.5; if (rts[0] == 0.0) rts[1] = -b; else rts[1] = c/rts[0]; } else { nquad = 0; rts[0] = 0.0; rts[1] = 0.0; } return nquad; } /* qudrtc */ /**************************************************/ double icp6D_QUAT::cubic(double p, double q, double r) /* find the lowest real root of the cubic - x**3 + p*x**2 + q*x + r = 0 input parameters - p,q,r - coeffs of cubic equation. output- cubic - a real root. method - see D.E. Littlewood, "A University Algebra" pp.173 - 6 Charles Prineas April 1981 */ { //int nrts; double po3,po3sq,qo3; double uo3,u2o3,uo3sq4,uo3cu4; double v,vsq,wsq; double m,mcube=0.0,n; double muo3,s,scube,t,cosk,sinsqk; double root; double doubmax = sqrt(DBL_MAX); m = 0.0; //nrts =0; if ((p > doubmax) || (p < -doubmax)) { root = -p; } else if ((q > doubmax) || (q < -doubmax)) { if (q > 0.0) root = -r/q; else root = -sqrt(-q); } else if ((r > doubmax)|| (r < -doubmax)) { root = -cbrt(r); } else { po3 = p/3.0; po3sq = po3*po3; if (po3sq > doubmax) root = -p; else { v = r + po3*(po3sq + po3sq - q); if ((v > doubmax) || (v < -doubmax)) root = -p; else { vsq = v*v; qo3 = q/3.0; uo3 = qo3 - po3sq; u2o3 = uo3 + uo3; if ((u2o3 > doubmax) || (u2o3 < -doubmax)) { if (p == 0.0) { if (q > 0.0) root = -r/q; else root = -sqrt(-q); } else root = -q/p; } uo3sq4 = u2o3*u2o3; if (uo3sq4 > doubmax) { if (p == 0.0) { if (q > 0.0) root = -r/q; else root = -sqrt(fabs(q)); } else root = -q/p; } uo3cu4 = uo3sq4*uo3; wsq = uo3cu4 + vsq; if (wsq >= 0.0) { // // cubic has one real root // //nrts = 1; if (v <= 0.0) mcube = ( -v + sqrt(wsq))*.5; if (v > 0.0) mcube = ( -v - sqrt(wsq))*.5; m = cbrt(mcube); if (m != 0.0) n = -uo3/m; else n = 0.0; root = m + n - po3; } else { //nrts = 3; // // cubic has three real roots // if (uo3 < 0.0) { muo3 = -uo3; s = sqrt(muo3); scube = s*muo3; t = -v/(scube+scube); cosk = cos(acos(t)/3.0); if (po3 < 0.0) root = (s+s)*cosk - po3; else { sinsqk = 1.0 - cosk*cosk; if (sinsqk < 0.0) sinsqk = 0.0; root = s*( -cosk - sqrt(3*sinsqk)) - po3; } } else // // cubic has multiple root - // root = cbrt(v) - po3; } } } } return root; } /* cubic */ /***************************************/ // calculate the maximum eigenvector of a symmetric // 4x4 matrix // from B. Horn 1987 Closed-form solution of absolute // orientation using unit quaternions (J.Opt.Soc.Am.A) void icp6D_QUAT::maxEigenVector(double Q[4][4], double ev[4]) { double N[4][4]; double rts[4]; double c[4]; // find the coeffs for the characteristic eqn. characteristicPol(Q, c); // find roots ferrari(c[0], c[1], c[2], c[3], rts); // find maximum root = maximum eigenvalue double l = rts[0]; if (rts[1] > l) l = rts[1]; if (rts[2] > l) l = rts[2]; if (rts[3] > l) l = rts[3]; // create the Q - l*I matrix N[0][0]=Q[0][0]-l;N[0][1]=Q[0][1] ;N[0][2]=Q[0][2]; N[0][3]=Q[0][3]; N[1][0]=Q[1][0]; N[1][1]=Q[1][1]-l;N[1][2]=Q[1][2]; N[1][3]=Q[1][3]; N[2][0]=Q[2][0]; N[2][1]=Q[2][1] ;N[2][2]=Q[2][2]-l;N[2][3]=Q[2][3]; N[3][0]=Q[3][0]; N[3][1]=Q[3][1] ;N[3][2]=Q[3][2];N[3][3]=Q[3][3]-l; // the columns of the inverted matrix should be multiples of // the eigenvector, pick the largest int ipiv[4]; double best[4], curr[4]; if (LU_factor(N, ipiv)) { cerr << "maxEigenVector():" << endl; cerr << "LU_factor failed!" << endl; cerr << "return identity quaternion" << endl; ev[0] = 1.0; ev[1] = ev[2] = ev[3] = 0.0; return; } best[1] = best[2] = best[3] = 0; best[0] = 1; LU_solve(N, ipiv, best); double len = best[0]*best[0] + best[1]*best[1] + best[2]*best[2] + best[3]*best[3]; for (int i=1; i<4; i++) { curr[0] = curr[1] = curr[2] = curr[3] = 0; curr[i] = 1; LU_solve(N, ipiv, curr); double tlen = curr[0]*curr[0] + curr[1]*curr[1] + curr[2]*curr[2] + curr[3]*curr[3]; if (tlen > len) { len = tlen; best[0] = curr[0]; best[1] = curr[1]; best[2] = curr[2]; best[3] = curr[3]; } } // normalize the result len = 1.0/sqrt(len); ev[0] = best[0]*len; ev[1] = best[1]*len; ev[2] = best[2]*len; ev[3] = best[3]*len; } // find the coefficients of the characteristic eqn. // l^4 + a l^3 + b l^2 + c l + d = 0 // for a symmetric 4x4 matrix void icp6D_QUAT::characteristicPol(double Q[4][4], double c[4]) { // squares double q01_2 = Q[0][1] * Q[0][1]; double q02_2 = Q[0][2] * Q[0][2]; double q03_2 = Q[0][3] * Q[0][3]; double q12_2 = Q[1][2] * Q[1][2]; double q13_2 = Q[1][3] * Q[1][3]; double q23_2 = Q[2][3] * Q[2][3]; // other factors double q0011 = Q[0][0] * Q[1][1]; double q0022 = Q[0][0] * Q[2][2]; double q0033 = Q[0][0] * Q[3][3]; double q0102 = Q[0][1] * Q[0][2]; double q0103 = Q[0][1] * Q[0][3]; double q0223 = Q[0][2] * Q[2][3]; double q1122 = Q[1][1] * Q[2][2]; double q1133 = Q[1][1] * Q[3][3]; double q1223 = Q[1][2] * Q[2][3]; double q2233 = Q[2][2] * Q[3][3]; // a c[0] = -Q[0][0] - Q[1][1] - Q[2][2] - Q[3][3]; // b c[1] = - q01_2 - q02_2 - q03_2 + q0011 - q12_2 - q13_2 + q0022 + q1122 - q23_2 + q0033 + q1133 + q2233; // c c[2] = (q02_2 + q03_2 + q23_2)*Q[1][1] - 2*q0102*Q[1][2] + (q12_2 + q13_2 + q23_2)*Q[0][0] + (q01_2 + q03_2 - q0011 + q13_2 - q1133)*Q[2][2] - 2*Q[0][3]*q0223 - 2*(q0103 + q1223)*Q[1][3] + (q01_2 + q02_2 - q0011 + q12_2 - q0022)*Q[3][3]; // d c[3] = 2*(-Q[0][2]*Q[0][3]*Q[1][2] + q0103*Q[2][2] - Q[0][1]*q0223 + Q[0][0]*q1223)*Q[1][3] + q02_2*q13_2 - q03_2*q1122 - q13_2*q0022 + 2*Q[0][3]*Q[1][1]*q0223 - 2*q0103*q1223 + q01_2*q23_2 - q0011*q23_2 - q02_2*q1133 + q03_2*q12_2 + 2*q0102*Q[1][2]*Q[3][3] - q12_2*q0033 - q01_2*q2233 + q0011*q2233; } double icp6D_QUAT::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 *alignfx) { 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) 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 << "PQUAT 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; } double S[3][3]; double Q[4][4]; int i,j; for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) S[i][j] = 0; // calculate the cross covariance matrix // 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++){ S[j][k] += Si[i][k*3+j] + n[i] * ((centroid_d[i][j] - cd[j]) * (centroid_m[i][k] - cm[k])) ; } } } S[0][0] -= cd[0] * cm[0]; S[0][1] -= cd[0] * cm[1]; S[0][2] -= cd[0] * cm[2]; S[1][0] -= cd[1] * cm[0]; S[1][1] -= cd[1] * cm[1]; S[1][2] -= cd[1] * cm[2]; S[2][0] -= cd[2] * cm[0]; S[2][1] -= cd[2] * cm[1]; S[2][2] -= cd[2] * cm[2]; // calculate the 4x4 symmetric matrix Q double trace = S[0][0] + S[1][1] + S[2][2]; double A23 = S[1][2] - S[2][1]; double A31 = S[2][0] - S[0][2]; double A12 = S[0][1] - S[1][0]; Q[0][0] = trace; Q[0][1] = Q[1][0] = A23; Q[0][2] = Q[2][0] = A31; Q[0][3] = Q[3][0] = A12; for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) Q[i+1][j+1] = S[i][j]+S[j][i]-(i==j ? trace : 0); // the quaternion double q[7]; maxEigenVector(Q, q); // calculate the rotation matrix double m[3][3]; // rot matrix quaternion2matrix(q, m); M4identity(alignfx); alignfx[0] = m[0][0]; alignfx[1] = m[1][0]; alignfx[2] = m[2][0]; alignfx[3] = 0.0; alignfx[4] = m[0][1]; alignfx[5] = m[1][1]; alignfx[6] = m[2][1]; alignfx[7] = 0.0; alignfx[8] = m[0][2]; alignfx[9] = m[1][2]; alignfx[10] = m[2][2]; alignfx[11] = 0.0; // calculate the translation vector, alignfx[12] = cm[0] - m[0][0]*cd[0] - m[0][1]*cd[1] - m[0][2]*cd[2]; alignfx[13] = cm[1] - m[1][0]*cd[0] - m[1][1]*cd[1] - m[1][2]*cd[2]; alignfx[14] = cm[2] - m[2][0]*cd[0] - m[2][1]*cd[1] - m[2][2]*cd[2]; return ret; }