1 // This file is part of OpenMVG, an Open Multiple View Geometry C++ library.
2 
3 // Copyright (c) 2012, 2013 Pierre MOULON.
4 
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #include "openMVG/geometry/rigid_transformation3D_srt.hpp"
10 #include "openMVG/system/logger.hpp"
11 
12 #include <unsupported/Eigen/NonLinearOptimization>
13 #include <unsupported/Eigen/NumericalDiff>
14 
15 namespace openMVG{
16 namespace geometry{
17 
FindRTS(const Mat & x1,const Mat & x2,double * S,Vec3 * t,Mat3 * R)18 bool FindRTS
19 (
20   const Mat &x1,
21   const Mat &x2,
22   double * S,
23   Vec3 * t,
24   Mat3 * R
25 )
26 {
27   if ( x1.cols() < 3 || x2.cols() < 3 )
28   {
29     OPENMVG_LOG_ERROR << "At least 3 points are required for computing a RTS transform.";
30     return false;
31   }
32 
33   assert( 3 == x1.rows() );
34   assert( 3 <= x1.cols() );
35   assert( x1.rows() == x2.rows() );
36   assert( x1.cols() == x2.cols() );
37 
38   // Get the transformation via Umeyama's least squares algorithm. This returns
39   // a matrix of the form:
40   // [ s * R t]
41   // [ 0 1]
42   // from which we can extract the scale, rotation, and translation.
43   const Eigen::Matrix4d transform = Eigen::umeyama( x1, x2, true );
44 
45   // Check critical cases
46   *R = transform.topLeftCorner<3, 3>();
47   if ( R->determinant() < 0 )
48   {
49     return false;
50   }
51   *S = pow( R->determinant(), 1.0 / 3.0 );
52   // Check for degenerate case (if all points have the same value...)
53   if ( *S < std::numeric_limits<double>::epsilon() )
54   {
55     OPENMVG_LOG_ERROR << "Degeneration configuration for a RTS transform estimation.";
56     return false;
57   }
58 
59   // Extract transformation parameters
60   *S = pow( R->determinant(), 1.0 / 3.0 );
61   *R /= *S;
62   *t = transform.topRightCorner<3, 1>();
63 
64   return true;
65 }
66 
lm_SRTRefine_functor(int inputs,int values,const Mat & x1,const Mat & x2,const double & S,const Mat3 & R,const Vec & t)67 lm_SRTRefine_functor::lm_SRTRefine_functor( int inputs, int values,
68                       const Mat &x1, const Mat &x2,
69                       const double &S, const Mat3 & R, const Vec &t ): Functor<double>( inputs, values ),
70   x1_( x1 ), x2_( x2 ), t_( t ), R_( R ), S_( S ) { }
71 
operator ()(const Vec & x,Vec & fvec) const72 int lm_SRTRefine_functor::operator()( const Vec &x, Vec &fvec ) const
73 {
74   // convert x to rotation matrix and a translation vector and a Scale factor
75   // x = {tx,ty,tz,anglex,angley,anglez,S}
76   Vec3 transAdd = x.block<3, 1>( 0, 0 );
77   Vec3 rot = x.block<3, 1>( 3, 0 );
78   double Sadd = x( 6 );
79 
80   //Build the rotation matrix
81   Mat3 Rcor =
82     ( Eigen::AngleAxis<double>( rot( 0 ), Vec3::UnitX() )
83       * Eigen::AngleAxis<double>( rot( 1 ), Vec3::UnitY() )
84       * Eigen::AngleAxis<double>( rot( 2 ), Vec3::UnitZ() ) ).toRotationMatrix();
85 
86   const Mat3 nR  = R_ * Rcor;
87   const Vec3 nt = t_ + transAdd;
88   const double nS = S_ + Sadd;
89 
90   // Evaluate re-projection errors
91   Vec3 proj;
92   for ( Mat::Index i = 0; i < x1_.cols(); ++i )
93   {
94     proj = x2_.col( i ) -  ( nS *  nR * ( x1_.col( i ) ) + nt );
95     fvec[i * 3]   = proj( 0 );
96     fvec[i * 3 + 1] = proj( 1 );
97     fvec[i * 3 + 2] = proj( 2 );
98   }
99   return 0;
100 }
101 
102 
lm_RRefine_functor(int inputs,int values,const Mat & x1,const Mat & x2,const double & S,const Mat3 & R,const Vec & t)103 lm_RRefine_functor::lm_RRefine_functor( int inputs, int values,
104                     const Mat &x1, const Mat &x2,
105                     const double &S, const Mat3 & R, const Vec &t ): Functor<double>( inputs, values ),
106   x1_( x1 ), x2_( x2 ), t_( t ), R_( R ), S_( S ) { }
107 
operator ()(const Vec & x,Vec & fvec) const108 int lm_RRefine_functor::operator()( const Vec &x, Vec &fvec ) const
109 {
110   // convert x to rotation matrix
111   // x = {anglex,angley,anglez}
112   Vec3 rot = x.block<3, 1>( 0, 0 );
113 
114   //Build the rotation matrix
115   Mat3 Rcor =
116     ( Eigen::AngleAxis<double>( rot( 0 ), Vec3::UnitX() )
117       * Eigen::AngleAxis<double>( rot( 1 ), Vec3::UnitY() )
118       * Eigen::AngleAxis<double>( rot( 2 ), Vec3::UnitZ() ) ).toRotationMatrix();
119 
120   const Mat3 nR  = R_ * Rcor;
121   const Vec3 nt = t_;
122   const double nS = S_;
123 
124   // Evaluate re-projection errors
125   Vec3 proj;
126   for ( Mat::Index i = 0; i < x1_.cols(); ++i )
127   {
128     proj = x2_.col( i ) -  ( nS *  nR * ( x1_.col( i ) ) + nt );
129     fvec[i * 3]   = proj( 0 );
130     fvec[i * 3 + 1] = proj( 1 );
131     fvec[i * 3 + 2] = proj( 2 );
132   }
133   return 0;
134 }
135 
Refine_RTS(const Mat & x1,const Mat & x2,double * S,Vec3 * t,Mat3 * R)136 void Refine_RTS
137 (
138   const Mat &x1,
139   const Mat &x2,
140   double * S,
141   Vec3 * t,
142   Mat3 * R
143 )
144 {
145   {
146     lm_SRTRefine_functor functor( 7, 3 * x1.cols(), x1, x2, *S, *R, *t );
147 
148     Eigen::NumericalDiff<lm_SRTRefine_functor> numDiff( functor );
149 
150     Eigen::LevenbergMarquardt<Eigen::NumericalDiff<lm_SRTRefine_functor>> lm( numDiff );
151     lm.parameters.maxfev = 1000;
152     Vec xlm = Vec::Zero( 7 ); // The deviation vector {tx,ty,tz,rotX,rotY,rotZ,S}
153 
154     lm.minimize( xlm );
155 
156     const Vec3 transAdd = xlm.block<3, 1>( 0, 0 );
157     const Vec3 rot = xlm.block<3, 1>( 3, 0 );
158     const double SAdd = xlm( 6 );
159 
160     //Build the rotation matrix
161     const Mat3 Rcor =
162       ( Eigen::AngleAxis<double>( rot( 0 ), Vec3::UnitX() )
163         * Eigen::AngleAxis<double>( rot( 1 ), Vec3::UnitY() )
164         * Eigen::AngleAxis<double>( rot( 2 ), Vec3::UnitZ() ) ).toRotationMatrix();
165 
166     *R = ( *R ) * Rcor;
167     *t = ( *t ) + transAdd;
168     *S = ( *S ) + SAdd;
169   }
170 
171   // Refine rotation
172   {
173     lm_RRefine_functor functor( 3, 3 * x1.cols(), x1, x2, *S, *R, *t );
174 
175     Eigen::NumericalDiff<lm_RRefine_functor> numDiff( functor );
176 
177     Eigen::LevenbergMarquardt<Eigen::NumericalDiff<lm_RRefine_functor>> lm( numDiff );
178     lm.parameters.maxfev = 1000;
179     Vec xlm = Vec::Zero( 3 ); // The deviation vector {rotX,rotY,rotZ}
180 
181     lm.minimize( xlm );
182 
183     const Vec3 rot = xlm.block<3, 1>( 0, 0 );
184 
185     //Build the rotation matrix
186     const Mat3 Rcor =
187       ( Eigen::AngleAxis<double>( rot( 0 ), Vec3::UnitX() )
188         * Eigen::AngleAxis<double>( rot( 1 ), Vec3::UnitY() )
189         * Eigen::AngleAxis<double>( rot( 2 ), Vec3::UnitZ() ) ).toRotationMatrix();
190 
191     *R = ( *R ) * Rcor;
192   }
193 }
194 
195 } // namespace geometry
196 } // namespace openMVG
197