Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
AbsoluteOrientation.cpp
1 #include "AbsoluteOrientation.hh"
2 #include <Base/Math/Matrix3x3.hh>
3 #include <Base/Math/Matrix.hh>
4 #include <Base/Math/Vector.hh>
5 #include <Base/Geometry/Quaternion.hh>
6 #include <MathAlgo/SVD.hh>
7 #include <MathAlgo/SVD3x3.hh>
8 #include <iostream>
9 
10 using namespace std;
11 using namespace BIAS;
12 
13 const unsigned int AbsoluteOrientation::MIN_CORRESPONDENCES = 3;
14 
15 AbsoluteOrientation::AbsoluteOrientation()
16  : epsilon_(1e-14), rotationAlgorithm_(HORN_ALGORITHM)
17 {
18 }
19 
21 {
22 }
23 
24 int AbsoluteOrientation::
25 Compute_(const std::vector< BIAS::Vector3<double> > &X,
26  const std::vector< BIAS::Vector3<double> > &Y,
28  double &ds, bool enforceScale, const std::vector<double> &w)
29 {
30  const unsigned int num = X.size();
31  const bool useWeights = (w.size() > 0);
32  if (num < MIN_CORRESPONDENCES) {
33  return -1;
34  }
35  BIASASSERT(X.size() == num && Y.size() == num);
36  BIASASSERT(!useWeights || w.size() == num);
37 
38  // Compute centroids CX, CY of point sets X, Y
39  BIAS::Vector3<double> centX(0.0, 0.0, 0.0), centY(0.0, 0.0, 0.0);
40  if (useWeights) {
41  double sumWeights = 0.0;
42  for (register unsigned int i = 0; i < num; i++) {
43  BIASASSERT(w[i] >= 0.0);
44  sumWeights += w[i];
45  centX.AddIP(w[i]*X[i]);
46  centY.AddIP(w[i]*Y[i]);
47  }
48  centX.DivideIP(sumWeights);
49  centY.DivideIP(sumWeights);
50  } else {
51  for (register unsigned int i = 0; i < num; i++) {
52  centX.AddIP(X[i]);
53  centY.AddIP(Y[i]);
54  }
55  centX.DivideIP((double)num);
56  centY.DivideIP((double)num);
57  }
58 
59  // Normalize point sets with respect to their centroids
60  std::vector< BIAS::Vector3<double> > normX;
61  std::vector< BIAS::Vector3<double> > normY;
62  normX.reserve(num);
63  normY.reserve(num);
64  for (register unsigned int i = 0; i < num; i++) {
65  // If the normalised point is close to (0,0,0), we skip it, because we
66  // can not compute the cross product with such points
67  const Vector3<double> normPointX = X[i] - centX;
68  const Vector3<double> normPointY = Y[i] - centY;
69 
70  if (normPointX.NormL2() > epsilon_ && normPointY.NormL2() > epsilon_) {
71  normX.push_back(X[i] - centX);
72  normY.push_back(Y[i] - centY);
73  }
74 /*
75 #ifdef BIAS_DEBUG
76  else {
77  BIASWARN("Skipping correspondence pair - at least one point is too close "
78  "to the centroid!");
79  }
80 #endif
81 */
82  }
83 
84  // We may have skipped too many points near the centroids...
85  if (normX.size() < MIN_CORRESPONDENCES) {
86  return -1;
87  }
88 
89  // Compute rotation
90  int res = ComputeRotation(normX, normY, dR, w);
91 
92  // Compute scale (opt.) and translation
93  if (res == 0) {
94  if (!enforceScale) {
95  double sumX = 0, sumY = 0;
96  if (useWeights) {
97  for (register unsigned int i = 0; i < num; i++) {
98  sumX += w[i]*normX[i].ScalarProduct(normX[i]);
99  sumY += w[i]*normY[i].ScalarProduct(normY[i]);
100  }
101  } else {
102  for (register unsigned int i = 0; i < num; i++) {
103  sumX += normX[i].ScalarProduct(normX[i]);
104  sumY += normY[i].ScalarProduct(normY[i]);
105  }
106  }
107  BIASASSERT(sumX > epsilon_);
108  ds = sqrt(sumY/sumX);
109  }
110  dt = centY - ds*dR*centX;
111  }
112 
113  return res;
114 }
115 
117 ComputeRotation(const std::vector< BIAS::Vector3<double> > &X,
118  const std::vector< BIAS::Vector3<double> > &Y,
119  BIAS::RMatrix &dR, const std::vector<double> &w)
120 {
121  switch (rotationAlgorithm_)
122  {
123  case HORN_ALGORITHM:
124  return ComputeRotationHorn_(X, Y, dR, w);
125  case KABSCH_ALGORITHM:
126  return ComputeRotationKabsch_(X, Y, dR, w);
127  }
128  BIASERR("Unknown rotation estimation method given!");
129  return -1;
130 }
131 
132 int AbsoluteOrientation::
133 ComputeRotationHorn_(const std::vector< BIAS::Vector3<double> > &X,
134  const std::vector< BIAS::Vector3<double> > &Y,
135  BIAS::RMatrix &dR, const std::vector<double> &w)
136 {
137  const unsigned int num = X.size();
138  const bool useWeights = (w.size() > 0);
139  if (num < MIN_CORRESPONDENCES) {
140  return -1;
141  }
142  BIASASSERT(X.size() == num && Y.size() == num);
143  BIASASSERT(!useWeights || w.size() == num);
144 
145  int res = 0;
148 
149  // Compute covariance matrix S = X'Y from corresponding 3d points
150  if (useWeights) {
151  for (register unsigned int i = 0; i < num; i++) {
152  BIASASSERT(X[i].NormL2() > epsilon_ && Y[i].NormL2() > epsilon_);
153  S += w[i]*X[i].OuterProduct(Y[i]);
154  }
155  } else {
156  for (register unsigned int i = 0; i < num; i++) {
157  BIASASSERT(X[i].NormL2() > epsilon_ && Y[i].NormL2() > epsilon_);
158  S += X[i].OuterProduct(Y[i]);
159  }
160  }
161 
162  // Solve Total Least Squares problem via SVD
163  N[0][0] = S[0][0]+S[1][1]+S[2][2];
164  N[0][1] = S[1][2]-S[2][1];
165  N[0][2] = S[2][0]-S[0][2];
166  N[0][3] = S[0][1]-S[1][0];
167  N[1][0] = S[1][2]-S[2][1];
168  N[1][1] = S[0][0]-S[1][1]-S[2][2];
169  N[1][2] = S[0][1]+S[1][0];
170  N[1][3] = S[2][0]+S[0][2];
171  N[2][0] = S[2][0]-S[0][2];
172  N[2][1] = S[0][1]+S[1][0];
173  N[2][2] =-S[0][0]+S[1][1]-S[2][2];
174  N[2][3] = S[1][2]+S[2][1];
175  N[3][0] = S[0][1]-S[1][0];
176  N[3][1] = S[2][0]+S[0][2];
177  N[3][2] = S[1][2]+S[2][1];
178  N[3][3] =-S[0][0]-S[1][1]+S[2][2];
179 
180  BIAS::SVD svd;
181  res = svd.Compute(N);
182 
183  if (res == 0) {
186  v4 = svd.GetV().GetCol(0);
187  dq.SetQuaternion(v4[0], v4[1], v4[2], v4[3]);
188  dq.Normalize();
189  dR.SetFromQuaternion(dq);
190  }
191 
192  return res;
193 }
194 
195 int AbsoluteOrientation::
196 ComputeRotationKabsch_(const std::vector< BIAS::Vector3<double> > &X,
197  const std::vector< BIAS::Vector3<double> > &Y,
198  BIAS::RMatrix &dR, const std::vector<double> &w)
199 {
200  const unsigned int num = X.size();
201  const bool useWeights = (w.size() > 0);
202  if (num < MIN_CORRESPONDENCES) {
203  return -1;
204  }
205  BIASASSERT(X.size() == num && Y.size() == num);
206  BIASASSERT(!useWeights || w.size() == num);
207 
208  int res = 0;
210 
211  // Compute covariance matrix S = X'Y from corresponding 3d points
212  if (useWeights) {
213  for (register unsigned int i = 0; i < num; i++) {
214  BIASASSERT(X[i].NormL2() > epsilon_ && Y[i].NormL2() > epsilon_);
215  S += w[i]*Y[i].OuterProduct(X[i]);
216  }
217  } else {
218  for (register unsigned int i = 0; i < num; i++) {
219  BIASASSERT(X[i].NormL2() > epsilon_ && Y[i].NormL2() > epsilon_);
220  S += Y[i].OuterProduct(X[i]);
221  }
222  }
223 
224  // Compute optimal rotation matrix via SVD
225  BIAS::SVD3x3 svd;
226  res = svd.Compute(BIAS::Matrix<double>(S));
227 
228  if (res == 0) {
230  // correct rotation matrix to insure a right-handed coordinate system
231  if (S.GetDeterminant() < 0) D[2][2] = -1.0;
232  // calculate optimal rotation matrix
233  BIAS::Matrix<double> UD(3, 3, MatrixZero), UDVT(3, 3, MatrixZero);
234  svd.GetU().Mult(D, UD);
235  UD.Mult(svd.GetVT(), UDVT);
236  dR = BIAS::RMatrix(UDVT);
237  }
238 
239  return res;
240 }
241 
243 GetResidualErrors(std::vector<double> &errors,
244  const std::vector< BIAS::Vector3<double> > &X,
245  const std::vector< BIAS::Vector3<double> > &Y,
246  const BIAS::RMatrix &dR, const BIAS::Vector3<double> &dt,
247  const double &ds, const std::vector<double> &w)
248 {
249  const unsigned int num = X.size();
250  const bool useWeights = (w.size() > 0);
251 
252  BIASASSERT(X.size() == num && Y.size() == num);
253  BIASASSERT(!useWeights || w.size() == num);
254 
255  // Evaluate error residuals and compute sum of squared errors
256  errors.resize(num);
257  double error = 0.0;
259  if (useWeights) {
260  for (register unsigned int i = 0; i < num; i++) {
261  e = Y[i] - ds*dR*X[i] - dt;
262  errors[i] = w[i]*e.ScalarProduct(e);
263  error += errors[i];
264  }
265  } else {
266  for (register unsigned int i = 0; i < num; i++) {
267  e = Y[i] - ds*dR*X[i] - dt;
268  errors[i] = e.ScalarProduct(e);
269  error += errors[i];
270  }
271  }
272  return error;
273 }
274 
276 GetResidualErrors(std::vector<double> &errors,
277  const std::vector< BIAS::Vector3<double> > &X,
278  const std::vector< BIAS::Vector3<double> > &Y,
279  const std::vector< BIAS::Matrix3x3<double> > &CovX,
280  const std::vector< BIAS::Matrix3x3<double> > &CovY,
281  const BIAS::RMatrix &dR, const BIAS::Vector3<double> &dt,
282  const double &ds)
283 {
284  const unsigned int num = X.size();
285 
286  BIASASSERT(X.size() == num && Y.size() == num);
287  BIASASSERT(CovX.size() == num && CovY.size() == num);
288 
289  // Evaluate error residuals and compute sum of squared errors
290  errors.resize(num);
291  double error = 0.0;
292  BIAS::Matrix3x3<double> Cov, invCov;
293  BIAS::Vector3<double> e, invCovE;
294  for (register unsigned int i = 0; i < num; i++) {
295  Cov = ds*ds*dR*(CovX[i]*dR.Transpose()) + CovY[i];
296  if (Cov.GetInverse(invCov) != 0) {
297  BEXCEPTION("Unable to invert covariance matrix: " << Cov);
298  }
299  e = Y[i] - ds*dR*X[i] - dt;
300  invCov.Mult(e, invCovE);
301  errors[i] = e.ScalarProduct(invCovE);
302  error += errors[i];
303  }
304  return error;
305 }
306 
308 GetResidualErrors(std::vector<double> &errors,
309  const std::vector< BIAS::Vector3<double> > &X,
310  const std::vector< BIAS::Vector3<double> > &Y,
311  const BIAS::RMatrix &dR, const std::vector<double> &w)
312 {
313  return GetResidualErrors(errors, X, Y,
314  dR, BIAS::Vector3<double>(0,0,0), 1.0, w);
315 }
316 
318 GetResidualErrors(std::vector<double> &errors,
319  const std::vector< BIAS::Vector3<double> > &X,
320  const std::vector< BIAS::Vector3<double> > &Y,
321  const std::vector< BIAS::Matrix3x3<double> > &CovX,
322  const std::vector< BIAS::Matrix3x3<double> > &CovY,
323  const BIAS::RMatrix &dR)
324 {
325  return GetResidualErrors(errors, X, Y, CovX, CovY,
326  dR, BIAS::Vector3<double>(0,0,0), 1.0);
327 }
computes and holds the singular value decomposition of a rectangular (not necessarily quadratic) Matr...
Definition: SVD.hh:92
static const unsigned int MIN_CORRESPONDENCES
int Compute(const Matrix< double > &M, double ZeroThreshold=DEFAULT_DOUBLE_ZERO_THRESHOLD)
set a new matrix and compute its decomposition.
Definition: SVD3x3.cpp:68
void ScalarProduct(const Vector3< T > &argvec, T &result) const
scalar product (=inner product) of two vectors, storing the result in result
Definition: Vector3.hh:603
Vector< T > GetCol(const int &col) const
return a copy of column &quot;col&quot;, zero based counting
Definition: Matrix.cpp:252
int SetFromQuaternion(const Quaternion< ROTATION_MATRIX_TYPE > &q)
Set rotation matrix from a quaternion.
int Compute(const Matrix< double > &M, double ZeroThreshold=DEFAULT_DOUBLE_ZERO_THRESHOLD)
set a new matrix and compute its decomposition.
Definition: SVD.cpp:102
void SetQuaternion(QUAT_TYPE real, QUAT_TYPE i, QUAT_TYPE j, QUAT_TYPE k)
Sets all parts of quaternion in mathematical order, real part first, then 1.,2.
Definition: Quaternion.hh:175
3D rotation matrix
Definition: RMatrix.hh:49
void Normalize()
Scales quaternion to unit length, i.e.
Definition: Quaternion.hh:236
void Mult(const Vector3< T > &argvec, Vector3< T > &destvec) const
matrix - vector multiplicate this matrix with Vector3, storing the result in destvec calculates: dest...
Definition: Matrix3x3.hh:302
Matrix< double > GetV() const
return V
Definition: SVD.hh:396
int GetInverse(Matrix3x3< T > &inv) const
Matrix inversion: inverts this and stores resulty in argument inv.
Definition: Matrix3x3.cpp:373
singular value decomposition for 3x3 matrices
Definition: SVD3x3.hh:74
const Matrix< double > & GetVT() const
return VT (=transposed(V))
Definition: SVD.hh:177
double GetResidualErrors(std::vector< double > &errors, const std::vector< Vector3< double > > &X, const std::vector< Vector3< double > > &Y, const RMatrix &dR, const Vector3< double > &dt, const double &ds=1.0, const std::vector< double > &w=std::vector< double >(0))
Computes residual errors of 3D point sets X and Y with respect to given rotation dR, translation dt and isometric scale ds.
void Mult(const Matrix< T > &arg, Matrix< T > &result) const
matrix multiplication, result is not allocated
Definition: Matrix.hh:913
Matrix3x3< T > Transpose() const
returns transposed matrix tested 12.06.2002
Definition: Matrix3x3.cpp:167
const Matrix< double > & GetU() const
return U U is a m x m orthogonal matrix
Definition: SVD.hh:187
double NormL2() const
the L2 norm sqrt(a^2 + b^2 + c^2)
Definition: Vector3.hh:633
int ComputeRotation(const std::vector< Vector3< double > > &X, const std::vector< Vector3< double > > &Y, RMatrix &dR, const std::vector< double > &w=std::vector< double >(0))
Computes rotation dR between corresponding 3D ray sets X and Y using the currently set algorithm...