You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

637 lines
16 KiB
C++

/*
* 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 <iomanip>
using std::ios;
using std::resetiosflags;
using std::setiosflags;
#include <cfloat>
#include <cmath>
#include <iostream>
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<PtPair>& 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<n; i++) {
// cout << setprecision (10) << pairs[i].p1.x << " " << pairs[i].p1.y << " " << pairs[i].p1.z << endl;
// cout << pairs[i].p2.x << " " << pairs[i].p2.y << " " << pairs[i].p2.z << endl;
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) ;
S[0][0] += pairs[i].p2.x * pairs[i].p1.x;
S[0][1] += pairs[i].p2.x * pairs[i].p1.y;
S[0][2] += pairs[i].p2.x * pairs[i].p1.z;
S[1][0] += pairs[i].p2.y * pairs[i].p1.x;
S[1][1] += pairs[i].p2.y * pairs[i].p1.y;
S[1][2] += pairs[i].p2.y * pairs[i].p1.z;
S[2][0] += pairs[i].p2.z * pairs[i].p1.x;
S[2][1] += pairs[i].p2.z * pairs[i].p1.y;
S[2][2] += pairs[i].p2.z * pairs[i].p1.z;
}
double error = sqrt(sum / n);
if (!quiet) {
cout.setf(ios::basefield);
cout << "QUAT 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;
}
double fact = 1 / double(n);
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
S[i][j] *= fact;
S[0][0] -= centroid_d[0] * centroid_m[0];
S[0][1] -= centroid_d[0] * centroid_m[1];
S[0][2] -= centroid_d[0] * centroid_m[2];
S[1][0] -= centroid_d[1] * centroid_m[0];
S[1][1] -= centroid_d[1] * centroid_m[1];
S[1][2] -= centroid_d[1] * centroid_m[2];
S[2][0] -= centroid_d[2] * centroid_m[0];
S[2][1] -= centroid_d[2] * centroid_m[1];
S[2][2] -= centroid_d[2] * centroid_m[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);
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] = centroid_m[0] - m[0][0]*centroid_d[0] - m[0][1]*centroid_d[1] - m[0][2]*centroid_d[2];
alignfx[13] = centroid_m[1] - m[1][0]*centroid_d[0] - m[1][1]*centroid_d[1] - m[1][2]*centroid_d[2];
alignfx[14] = centroid_m[2] - m[2][0]*centroid_d[0] - m[2][1]*centroid_d[1] - m[2][2]*centroid_d[2];
return error;
}
// transform a quaternion to a rotation matrix
void icp6D_QUAT::quaternion2matrix(double *q, double m[3][3])
{
double q00 = q[0]*q[0];
double q11 = q[1]*q[1];
double q22 = q[2]*q[2];
double q33 = q[3]*q[3];
double q03 = q[0]*q[3];
double q13 = q[1]*q[3];
double q23 = q[2]*q[3];
double q02 = q[0]*q[2];
double q12 = q[1]*q[2];
double q01 = q[0]*q[1];
m[0][0] = q00 + q11 - q22 - q33;
m[1][1] = q00 - q11 + q22 - q33;
m[2][2] = q00 - q11 - q22 + q33;
m[0][1] = 2.0*(q12-q03);
m[1][0] = 2.0*(q12+q03);
m[0][2] = 2.0*(q13+q02);
m[2][0] = 2.0*(q13-q02);
m[1][2] = 2.0*(q23-q01);
m[2][1] = 2.0*(q23+q01);
}
int icp6D_QUAT::ferrari(double a, double b, double c, double d, double rts[4])
/*
solve the quartic equation -
x**4 + a*x**3 + b*x**2 + c*x + d = 0
input -
a,b,c,e - coeffs of equation.
output -
nquar - number of real roots.
rts - array of root values.
method : Ferrari - Lagrange
Theory of Equations, H.W. Turnbull p. 140 (1947)
calls cubic, qudrtc
*/
{
int nquar,n1,n2;
double asq,ainv2;
double v1[4],v2[4];
double p,q,r;
double y;
double e,f,esq,fsq,ef;
double g,gg,h,hh;
asq = a*a;
p = b;
q = a*c-4.0*d;
r = (asq - 4.0*b)*d + c*c;
y = cubic(p,q,r);
esq = .25*asq - b - y;
if (esq < 0.0) return(0);
else {
fsq = .25*y*y - d;
if (fsq < 0.0) return(0);
else {
ef = -(.25*a*y + .5*c);
if ( ((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))
|| (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;
}