TuttleOFX  1
tps.tcc
Go to the documentation of this file.
00001 #include "../WarpPlugin.hpp"
00002 #include "../WarpProcess.hpp"
00003 #include "../WarpDefinitions.hpp"
00004 #include "tps.hpp"
00005 
00006 #include <terry/globals.hpp>
00007 #include <tuttle/plugin/global.hpp>
00008 
00009 #include <ofxsImageEffect.h>
00010 #include <ofxsMultiThread.h>
00011 
00012 #include <boost/gil/gil_all.hpp>
00013 #include <boost/numeric/conversion/cast.hpp>
00014 #include <boost/numeric/ublas/io.hpp>
00015 #include <boost/numeric/ublas/lu.hpp>
00016 #include <boost/numeric/ublas/matrix.hpp>
00017 #include <boost/numeric/ublas/matrix_proxy.hpp>
00018 #include <boost/numeric/ublas/vector.hpp>
00019 #include <boost/algorithm/string/predicate.hpp>
00020 #include <boost/math/special_functions/pow.hpp>
00021 #include <boost/foreach.hpp>
00022 
00023 #include <vector>
00024 #include <ostream>
00025 
00026 #define TPS_NORMALIZE_COORD
00027 
00028 namespace tuttle {
00029 namespace plugin {
00030 namespace warp {
00031 
00032 template<class Mat>
00033 inline std::ostream& coutMat( std::ostream& os, const Mat& m )
00034 {
00035         os << "[" << m.size1() << "," << m.size2() << "]\n";
00036         for( std::size_t y = 0; y < m.size1(); ++y )
00037         {
00038                 os << "(";
00039                 for( std::size_t x = 0; x < m.size2(); ++x )
00040                 {
00041                         os << boost::format("%4.3d") % m(y,x);
00042                         if( x < m.size2() - 1 )
00043                         {
00044                                 os << ",\t";
00045                         }
00046                 }
00047                 os << ")";
00048                 if( y < m.size1() - 1 )
00049                 {
00050                         os << ",";
00051                 }
00052                 os << "\n";
00053         }
00054         return os;
00055 }
00056 
00057 /**
00058  * @brief Radial basis function: Thin plate spline (a special polyharmonic spline).
00059  * 
00060  * @f[
00061  * r^2 * log(r)
00062  * @f[
00063  * 
00064  * @param r2 squared distance value
00065  * @return Computed weight value
00066  */
00067 inline double base_func( const double r2 )
00068 {
00069         using boost::math::pow;
00070         if( r2 == 0 )
00071                 return 0.0;
00072         static const double v = 1.0 / ( 2.0 * log(10.0) );
00073         return r2 * log( r2 ) * v;
00074 //      return r2 * log( std::sqrt(r2) ); // same as above solution
00075 
00076 //      return pow<2>(r2) * log( std::sqrt(r2) ); // r^4 * log(r)
00077 //      return pow<3>( std::sqrt(r2) ); // r^3
00078 //      return std::sqrt(r2); // r
00079 //      return r2; // r^2
00080 }
00081 
00082 template<typename SCALAR>
00083 TPS_Morpher<SCALAR>::TPS_Morpher()
00084 {}
00085 
00086 
00087 /**
00088  *
00089  * @param pIn
00090  * @param pOut
00091  * @param regularization Amount of "relaxation", 0.0 = exact interpolation
00092  * @param applyWarp
00093  * @param nbPoints
00094  */
00095 template<typename SCALAR>
00096 void TPS_Morpher<SCALAR>::setup( const std::vector< Point2 > pIn, const std::vector< Point2 > pOut, const double regularization, const bool applyWarp, const std::size_t width, const std::size_t height, const double transition )
00097 {
00098         using boost::math::pow;
00099 
00100         _width = width;
00101         _height = height;
00102         _transition = transition;
00103         
00104 #ifdef TPS_NORMALIZE_COORD
00105         _pIn.reserve( pIn.size() );
00106         _pOut.reserve( pOut.size() );
00107         BOOST_FOREACH( const Point2& p, pIn )
00108         {
00109                 Point2 np( p.x / _width, p.y / _height );
00110                 _pIn.push_back( np );
00111         }
00112         BOOST_FOREACH( const Point2& p, pOut )
00113         {
00114                 Point2 np( p.x / _width, p.y / _height );
00115                 _pOut.push_back( np );
00116         }
00117 #else
00118         _pIn = pIn;
00119         _pOut = pOut;
00120 #endif
00121         
00122         _nbPoints = _pIn.size();
00123         _mat_L.resize( _nbPoints + 3, _nbPoints + 3 );
00124         _mat_V.resize( _nbPoints + 3, 2 );
00125         _mat_K.resize( _nbPoints, _nbPoints );
00126         _activateWarp = applyWarp;
00127 
00128         //TUTTLE_TCOUT_VAR( _pIn.size() );
00129         //TUTTLE_TCOUT_VAR( _pOut.size() );
00130 
00131         BOOST_ASSERT( _pIn.size() == _pOut.size() );
00132 
00133         // Fill K and directly copy values into L
00134         // K = [ 0      U(r01) ...   U(r0n) ]
00135         //   = [ U(r10) 0      ...   U(r1n) ]
00136         //   = [ ...    ...    ...   ...    ]
00137         //   = [ U(rn0) U(rn1) ...   0      ]
00138         //
00139         // K.size = n x n
00140         for( std::size_t y = 0; y < _nbPoints; ++y )
00141         {
00142                 const Point2& point_i = _pIn[y];
00143                 for( std::size_t x = 0; x < _nbPoints; ++x )
00144                 {
00145                         if( y == x )
00146                         {
00147                                 _mat_L( y, x ) = _mat_K( y, x ) = regularization;
00148                                 // diagonal: reqularization parameters (lambda * a^2)
00149 //                              _mat_L( i, i ) = _mat_K( i, i ) = regularization /** (a*a)*/;
00150                         }
00151                         else
00152                         {
00153                                 const Point2& point_j = _pIn[x];
00154                                 const Scalar sum = pow<2>( point_i.x - point_j.x ) + pow<2>( point_i.y - point_j.y );
00155                                 _mat_L( y, x ) = _mat_K( y, x ) = base_func( sum );
00156                         }
00157                 }
00158         }
00159 
00160         // fill L with P and trans(P)
00161         //
00162         // P = [ 1   x1   y1  ]
00163         //     [ 1   x2   y2  ]
00164         //     [ ... ...  ... ]
00165         //     [ 1   xn   yn  ]
00166         //
00167         // P.size = n x 3
00168         for( std::size_t i = 0; i < _nbPoints; ++i )
00169         {
00170                 const Point2& pt = _pIn[i];
00171                 _mat_L( i, _nbPoints + 0 ) = 1.0;
00172                 _mat_L( i, _nbPoints + 1 ) = pt.x;
00173                 _mat_L( i, _nbPoints + 2 ) = pt.y;
00174 
00175                 _mat_L( _nbPoints + 0, i ) = 1.0;
00176                 _mat_L( _nbPoints + 1, i ) = pt.x;
00177                 _mat_L( _nbPoints + 2, i ) = pt.y;
00178         }
00179 
00180         // L = [ K        |  P  ]
00181         //     [          | 000 ]
00182         //     [ trans(P) | 000 ]
00183         //     [          | 000 ]
00184         //
00185         // L.size = (n+3) x (n+3)
00186         for( std::size_t i = 0; i < 3; ++i )
00187         {
00188                 for( std::size_t j = 0; j < 3; ++j )
00189                 {
00190                         _mat_L( _nbPoints+i, _nbPoints+j ) = 0.0;
00191                 }
00192         }
00193 
00194         // Fill V
00195         // V = [ x1' x2' ... xn' 000 ]
00196         //     [ y1' y2' ... yn' 000 ]
00197         //
00198         // V.size = 2 x (n+3)
00199         // here we manipulate trans(V)
00200         for( std::size_t i = 0; i < _nbPoints; ++i )
00201         {
00202                 const Point2& pIn = _pIn[i];
00203                 const Point2& pOut = _pOut[i];
00204                 _mat_V( i, 0 ) = pOut.x - pIn.x;
00205                 _mat_V( i, 1 ) = pOut.y - pIn.y;
00206                 //TUTTLE_TCOUT_VAR2( pOut.x, pIn.x );
00207                 //TUTTLE_TCOUT_VAR2( pOut.y, pIn.y );
00208         }
00209 
00210         _mat_V( _nbPoints + 0, 0 ) = _mat_V( _nbPoints + 1, 0 ) = _mat_V( _nbPoints + 2, 0 ) = 0.0;
00211         _mat_V( _nbPoints + 0, 1 ) = _mat_V( _nbPoints + 1, 1 ) = _mat_V( _nbPoints + 2, 1 ) = 0.0;
00212 
00213         //TUTTLE_TCOUT("");
00214         //TUTTLE_TCOUT( "mtx_v" );
00215         //coutMat( std::cout, _mat_V );
00216         //TUTTLE_TCOUT("");
00217         //TUTTLE_TCOUT( "mtx_orig_k" );
00218         //coutMat( std::cout, _mat_K );
00219         //TUTTLE_TCOUT("");
00220         //TUTTLE_TCOUT( "mtx_l" );
00221         //coutMat( std::cout, _mat_L );
00222         //TUTTLE_TCOUT("");
00223 
00224         // Solve the linear system "inplace"
00225         permutation_matrix<Scalar> P( _nbPoints + 3 );
00226 //      matrix<Scalar> x( _nbPoints + 3, 2 );
00227 
00228         lu_factorize( _mat_L, P );
00229         lu_substitute( _mat_L, P, _mat_V );
00230 
00231         //TUTTLE_TCOUT_X( 10, "-");
00232         //TUTTLE_TCOUT( "mtx_v" );
00233         //coutMat( std::cout, _mat_V );
00234         //TUTTLE_TCOUT("");
00235         //TUTTLE_TCOUT( "mtx_l" );
00236         //coutMat( std::cout, _mat_L );
00237         //TUTTLE_TCOUT("");
00238         //TUTTLE_TCOUT_X( 80, "_");
00239 }
00240 
00241 template<typename SCALAR>
00242 template<typename S2>
00243 typename TPS_Morpher<SCALAR>::Point2 TPS_Morpher<SCALAR>::operator( )( const point2<S2>& pt ) const
00244 {
00245         using boost::math::pow;
00246         if( !_activateWarp || _nbPoints <= 1 )
00247         {
00248                 return Point2( pt.x, pt.y );
00249         }
00250 #ifdef TPS_NORMALIZE_COORD
00251         const Point2 npt( pt.x / _width, pt.y / _height );
00252 #else
00253         const Point2 npt( pt.x, pt.y );
00254 #endif
00255 
00256         // Nombre de colonnes de la matrice K
00257         const std::size_t m = _mat_K.size2( );
00258 
00259         double dx = _mat_V( m + 0, 0 ) + _mat_V( m + 1, 0 ) * npt.x + _mat_V( m + 2, 0 ) * npt.y;
00260         double dy = _mat_V( m + 0, 1 ) + _mat_V( m + 1, 1 ) * npt.x + _mat_V( m + 2, 1 ) * npt.y;
00261 
00262         Const_Matrix_Col mat_Vx( _mat_V, 0 );
00263         Const_Matrix_Col mat_Vy( _mat_V, 1 );
00264 
00265         std::vector< point2<double> >::const_iterator it_out = _pOut.begin();
00266         typename Const_Matrix_Col::const_iterator it_Vx( mat_Vx.begin() );
00267         typename Const_Matrix_Col::const_iterator it_Vy( mat_Vy.begin() );
00268 
00269         //std::cout<<"DX fin "<<dx<<"  "<<dy<<std::endl;
00270 
00271         for( std::size_t i = 0;
00272              i < m;
00273              ++i, ++it_out, ++it_Vx, ++it_Vy )
00274         {
00275                 const double d = base_func( pow<2>( it_out->x - npt.x ) + pow<2>( it_out->y - npt.y ) );
00276                 dx += ( *it_Vx ) * d;
00277                 dy += ( *it_Vy ) * d;
00278         }
00279         dx *= _transition;
00280         dy *= _transition;
00281 #ifdef TPS_NORMALIZE_COORD
00282         return Point2( (npt.x+dx)*_width, (npt.y+dy)*_height );
00283 #else
00284         return Point2( npt.x+dx, npt.y+dy );
00285 #endif
00286 }
00287 
00288 }
00289 }
00290 }
00291 
00292