Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
TestRotationConversion.cpp
1 /*
2  This file is part of the BIAS library (Basic ImageAlgorithmS).
3 
4  Copyright (C) 2003-2009 (see file CONTACT for details)
5  Multimediale Systeme der Informationsverarbeitung
6  Institut fuer Informatik
7  Christian-Albrechts-Universitaet Kiel
8 
9 
10  BIAS is free software; you can redistribute it and/or modify
11  it under the terms of the GNU Lesser General Public License as published by
12  the Free Software Foundation; either version 2.1 of the License, or
13  (at your option) any later version.
14 
15  BIAS is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU Lesser General Public License for more details.
19 
20  You should have received a copy of the GNU Lesser General Public License
21  along with BIAS; if not, write to the Free Software
22  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23  */
24 
25 /**
26  @file TestRotationConversion.cpp
27  @relates Quaternion
28  @brief Test the conversion between rotation representations for
29  consistency (quaternion <-> rotation matrix)
30  @ingroup g_tests
31  @author MIP
32 */
33 
34 #include <Base/Common/CompareFloatingPoint.hh>
35 #include <Base/Geometry/Quaternion.hh>
36 #include <Base/Math/Random.hh>
37 
38 using namespace BIAS;
39 using namespace std;
40 
41 
42 Random randomizer;
43 
44 double maxerror = -1.0;
45 
46 const double EPS = 5e-10;
47 
48 Quaternion<double> GenerateRandomQuaternion();
49 
50 Quaternion<double> GenerateRandomQuaternion(double angle);
51 
52 RMatrixBase GenerateRandomRMatrix();
53 
54 RMatrixBase GenerateRandomRMatrix(double angle);
55 
56 void TestConversionQuaternionMatrix();
57 
58 bool CompareQuaternions(const Quaternion<double> &Q1,
59  const Quaternion<double> &Q2);
60 
61 bool CompareRMatrices(const RMatrixBase &R1,
62  const RMatrixBase &R2);
63 
64 int main(int argc, char *argv[])
65 {
66  TestConversionQuaternionMatrix();
67 
68  return 0;
69 }
70 
71 Quaternion<double> GenerateRandomQuaternion()
72 {
73  double angle = randomizer.GetUniformDistributed(-2.0*M_PI, 2.0*M_PI);
74  return GenerateRandomQuaternion(angle);
75 }
76 
77 Quaternion<double> GenerateRandomQuaternion(double angle)
78 {
80  Q[3] = cos(0.5*angle);
81  Q[0] = Q[1] = Q[2] = sin(0.5*angle);
82  Vector3<double> axis;
83  do {
84  axis.Set(randomizer.GetUniformDistributed(-1.0, 1.0),
85  randomizer.GetUniformDistributed(-1.0, 1.0),
86  randomizer.GetUniformDistributed(-1.0, 1.0));
87  } while (Equal(axis.NormL2(), 0.0));
88  axis.Normalize();
89  for (int j = 0; j < 3; j++) Q[j] *= axis[j];
90  Q.Normalize();
91  return Q;
92 }
93 
94 bool CompareQuaternions(const Quaternion<double> &Q1,
95  const Quaternion<double> &Q2)
96 {
97  // check unit norm constraint
98  if (Equal(Q1.NormL2(), 0.0)) { BIASABORT; }
99  if (Equal(Q2.NormL2(), 0.0)) { BIASABORT; }
100 
101  // multiplication with inverse must result in [0 0 0 +-1]
102  const Quaternion<double> Qid(0, 0, 0, 1);
103  Quaternion<double> Q(Q1);
104  Q.Mult(Q2.Inverse());
105 
106  // compare entries with identity quaternion
107  bool ok = true;
108  for (int j = 0; j < 3; j++){
109  if (fabs(fabs(Q[j])-Qid[j]) > maxerror)
110  maxerror = fabs(fabs(Q[j])-Qid[j]);
111  if (!Equal(fabs(Q[j]), Qid[j], EPS))
112  ok = false;
113  }
114  if (!ok) {
115  BIASERR("Inconsistent conversion quaternion -> rotation matrix -> "
116  "quaternion:\n- srcQ*dstQ.Inverse() = " <<Q/Q.NormL2()
117  <<"\n- srcQ = "<<Q1<<"\n- dstQ = "<<Q2<<"\n max. error = "
118  <<maxerror<< endl);
119  }
120  return ok;
121 }
122 
123 RMatrixBase GenerateRandomRMatrix()
124 {
125  double angle = randomizer.GetUniformDistributed(-2.0*M_PI, 2.0*M_PI);
126  return GenerateRandomRMatrix(angle);
127 }
128 
129 RMatrixBase GenerateRandomRMatrix(double angle)
130 {
131  RMatrixBase R;
132  Vector3<double> axis;
133  do {
134  axis.Set(randomizer.GetUniformDistributed(-1.0, 1.0),
135  randomizer.GetUniformDistributed(-1.0, 1.0),
136  randomizer.GetUniformDistributed(-1.0, 1.0));
137  } while (Equal(axis.NormL2(), 0.0));
138  axis.Normalize();
139  R.Set(axis, angle);
140  return R;
141 }
142 
143 bool CompareRMatrices(const RMatrixBase &R1,
144  const RMatrixBase &R2)
145 {
146  // multiplication with inverse must result in I
147  const RMatrixBase I(MatrixIdentity);
148  Matrix3x3<double> R(R1);
149  R *= R2.Transpose();
150 
151  // compare entries with I
152  bool ok = true;
153  for (int j = 0; j < 3; j++){
154  for (int k = 0; k < 3; k++){
155  if (fabs(R[j][k]-I[j][k]) > maxerror)
156  maxerror = fabs(R[j][k]-I[j][k]);
157  if (!Equal(R[j][k], I[j][k], EPS))
158  ok = false;
159  }
160  }
161 
162  if (!ok) {
163  BIASERR("Inconsistent conversion rotation matrix -> quaternion -> "
164  "rotation matrix:\n- srcR*dstR.Inverse() = " <<R
165  <<"\n- srcR = "<<R1<<"\n- dstR = "<<R2<<"\n max. error = "
166  <<maxerror<< endl);
167  }
168  return ok;
169 }
170 
171 void TestConversionQuaternionMatrix()
172 {
173  // Test with random rotations
174  {
175  const int num = 10000;
176 
177  cout << "\ntest rotation conversion for " << num << " random rotations"
178  << "\n------------------------------------------------------\n"
179  << "- test quaternion -> rotation matrix -> quaternion : ";
180  Quaternion<double> srcQ, dstQ;
181  RMatrixBase R;
182  maxerror = -1.0;
183  for (int n = 0; n < num; n++)
184  {
185  srcQ = GenerateRandomQuaternion();
186  if (R.SetFromQuaternion(srcQ) != 0) { BIASABORT; }
187  if (R.GetQuaternion(dstQ) != 0) { BIASABORT; }
188  if (!CompareQuaternions(srcQ, dstQ)){
189  cout << "max. error = " << setprecision(30) << maxerror << endl;
190  cerr << "[!] Error for sample id " << n
191  << " : source quaternion " << srcQ/srcQ.NormL2()
192  << ", resulting quaternion " << dstQ/dstQ.NormL2() << endl;
193  BIASABORT;
194  }
195  }
196  cout << "max. error = " << setprecision(30) << maxerror << endl;
197 
198  cout << "- test rotation matrix -> quaternion -> rotation matrix : ";
200  RMatrixBase srcR, dstR;
201  maxerror = -1.0;
202  for (int n = 0; n < num; n++)
203  {
204  srcR = GenerateRandomRMatrix();
205  if (srcR.GetQuaternion(Q) != 0) { BIASABORT; }
206  if (dstR.SetFromQuaternion(Q) != 0) { BIASABORT; }
207  if (!CompareRMatrices(srcR, dstR)) {
208  cout << "max. error = " << setprecision(30) << maxerror << endl;
209  cerr << "[!] Error for sample id " << n
210  << ", source rotation matrix " << srcR
211  << ", resulting rotation matrix " << dstR << endl;
212  BIASABORT;
213  }
214  }
215  cout << "max. error = " << setprecision(30) << maxerror << endl;
216  }
217 
218  // Test with rotations about -360, ..., 0, 90, 180, 270, 360 degree
219  {
220  const int num = 1000;
221  const double angleStep = 0.5*M_PI;
222  for (int k = -4; k <= 4; k++)
223  {
224  const double angle = k*angleStep;
225  cout << "\ntest rotation conversion for " << num << " rotations "
226  << "with rotation angle " << angle*180.0/M_PI << " degree"
227  << "\n------------------------------------------------------\n"
228  << "- test quaternion -> rotation matrix -> quaternion : ";
229  Quaternion<double> srcQ, dstQ;
230  RMatrixBase R;
231  maxerror = -1.0;
232  for (int n = 0; n < num; n++)
233  {
234  srcQ = GenerateRandomQuaternion(angle);
235  if (R.SetFromQuaternion(srcQ) != 0) { BIASABORT; }
236  if (R.GetQuaternion(dstQ) != 0) { BIASABORT; }
237  if (!CompareQuaternions(srcQ, dstQ)) {
238  cout << "max. error = " << setprecision(30) << maxerror << endl;
239  cerr << "[!] Error for sample id " << n << " with "
240  << angle*180.0/M_PI << " degree, "
241  << "source quaternion " << srcQ/srcQ.NormL2()
242  << ", resulting quaternion " << dstQ/dstQ.NormL2() << endl;
243  BIASABORT;
244  }
245  }
246  cout << "max. error = " << setprecision(30) << maxerror << endl;
247 
248  cout << "- test rotation matrix -> quaternion -> rotation matrix : ";
250  RMatrixBase srcR, dstR;
251  maxerror = -1.0;
252  for (int n = 0; n < num; n++)
253  {
254  srcR = GenerateRandomRMatrix(angle);
255  if (srcR.GetQuaternion(Q) != 0) { BIASABORT; }
256  if (dstR.SetFromQuaternion(Q)!= 0) { BIASABORT; }
257  if (!CompareRMatrices(srcR, dstR)) {
258  cout << "max. error = " << setprecision(30) << maxerror << endl;
259  cerr << "[!] Error for sample id " << n << " with "
260  << angle*180.0/M_PI << " degree, "
261  << "source rotation matrix " << srcR
262  << ", resulting rotation matrix " << dstR << endl;
263  BIASABORT;
264  }
265  }
266  cout << "max. error = " << setprecision(30) << maxerror << endl;
267  }
268  }
269 
270  // Special test case (esquivel 03/2011)
271  {
272  cout << "\nevaluate special test case which is known for problems"
273  << "\n------------------------------------------------------\n";
274  RMatrixBase Rsrc, Rdst;
276  Rsrc[0][0] = Rsrc[2][2] = 0.0;
277  Rsrc[1][1] = Rsrc[0][2] = Rsrc[2][0] = -1.0;
278  Rsrc.GetQuaternion(q);
279  Rdst.SetFromQuaternion(q);
280  double diff = (Rsrc-Rdst).NormL2();
281  cout << "- rotation by " << Rsrc.GetRotationAngle()*180.0/M_PI
282  << " degree around axis " << Rsrc.GetRotationAxis()
283  << "\n- rotation matrix " << Rsrc
284  << "\n-> to quaternion " << q
285  << "\n-> to rotation matrix " << Rdst
286  << "\ndifference = " << setprecision(30) << diff << endl;
287  const double eps = 5e-10;
288 
289  if (!Equal(diff, 0.0, eps)) {
290  BIASERR("[!] Inconsistent conversion rotation matrix -> quaternion -> "
291  "rotation matrix for special test case, difference is "
292  << setprecision(30) << diff);
293  BIASABORT;
294  }
295 
296  cout << endl;
297  }
298 }
void Set(const T *pv)
copy the array of vectorsize beginning at *T to this-&gt;data_
Definition: Vector3.hh:532
BIAS::Vector3< ROTATION_MATRIX_TYPE > GetRotationAxis() const
Interface for axis component of GetRotationAxisAngle()
int SetFromQuaternion(const Quaternion< ROTATION_MATRIX_TYPE > &q)
Set rotation matrix from a quaternion.
double GetUniformDistributed(const double min, const double max)
on succesive calls return uniform distributed random variable between min and max ...
Definition: Random.hh:84
double NormL2() const
Return the L2 norm: sqrt(a^2 + b^2 + c^2 + d^2)
Definition: Vector4.hh:510
int GetQuaternion(Quaternion< ROTATION_MATRIX_TYPE > &quat) const
Calculates quaternion representation for this rotation matrix.
void Normalize()
Scales quaternion to unit length, i.e.
Definition: Quaternion.hh:236
void Set(const Vector3< ROTATION_MATRIX_TYPE > &w, const ROTATION_MATRIX_TYPE phi)
Set from rotation axis w and angle phi (in rad)
Quaternion< QUAT_TYPE > Inverse() const
returns the Inverse rotation Quaternion
Definition: Quaternion.hh:42
void Mult(const Quaternion< QUAT_TYPE > &quat)
quaternion multiplication: this= this * quat
Definition: Quaternion.hh:73
bool Equal(const T left, const T right, const T eps)
comparison function for floating point values See http://www.boost.org/libs/test/doc/components/test_...
Matrix3x3< T > Transpose() const
returns transposed matrix tested 12.06.2002
Definition: Matrix3x3.cpp:167
Implements a 3D rotation matrix.
Definition: RMatrixBase.hh:44
ROTATION_MATRIX_TYPE GetRotationAngle() const
Interface for angle component of GetRotationAxisAngle()
Vector3< T > & Normalize()
normalize this vector to length 1
Definition: Vector3.hh:663
class for producing random numbers from different distributions
Definition: Random.hh:51
double NormL2() const
the L2 norm sqrt(a^2 + b^2 + c^2)
Definition: Vector3.hh:633