CVD 0.8
cvd/esm.h
00001 #ifndef CVD_ESM_H_
00002 #define CVD_ESM_H_
00003 
00004 #include <TooN/wls.h>
00005 #include <TooN/so3.h>
00006 
00007 #include <cvd/image.h>
00008 #include <cvd/image_io.h>
00009 #include <cvd/vector_image_ref.h>
00010 #include <cvd/vision.h>
00011 
00012 #include <vector>
00013 #include <sstream>
00014 #include <iomanip>
00015 
00016 namespace CVD {
00017 
00018 static int DEBUG_ESM = 0;
00019 
00054 
00057 struct ESMResult {
00058     double error;   
00059     double delta;   
00060     int pixels;     
00061     int iterations; 
00062     
00064     double RMSE() const { return std::sqrt(error/std::max(1,pixels)); }
00065 };
00066 
00067 inline std::ostream & operator<<(std::ostream & out, const ESMResult & r ){
00068     out << r.error << "\t" << r.pixels << "\t" << r.RMSE() << "\t" << "\t" << r.delta << "\t" << r.iterations;
00069     return out;
00070 }
00071 
00072 namespace Internal { // forward declaration of some internal functions
00073     // intersects the line AB with the rectangle rect, returns the interval for the parameter t
00074     // for X = A + (B-A)*t
00075     // A, B in homogeneous coordinates, yielding the correct perspective interpolation for t
00076     inline TooN::Vector<2> getBounds(const TooN::Vector<2> & rect, const TooN::Vector<3> & A, const TooN::Vector<3> & B);
00077     inline std::vector<TooN::Vector<2,int> > getBounds( const TooN::Vector<2> & rect, const ImageRef & in, const TooN::Matrix<3> & H );
00078     inline std::vector<TooN::Vector<2,int> > erode( const std::vector<TooN::Vector<2,int> > & in );
00079 
00080     // H takes pixels in out to pixels in in as a 2D homography (3x3 matrix)
00081     template <typename T> inline std::vector<TooN::Vector<2,int> > transform_perspective( SubImage<T> & out, const SubImage<T> & in, const TooN::Matrix<3> & H);
00082     // H takes pixels in out to pixels in in as a 2D affine transformation (3x3 matrix)
00083     template <typename T> inline std::vector<TooN::Vector<2,int> > transform_affine( SubImage<T> & out, const SubImage<T> & in, const TooN::Matrix<3> & H);
00084     // H takes pixels in out to pixels in in as a 2D translation only (stored in the 3x3 matrix)
00085     // This is extra optimized to use the constant mixing factors in the bi-linear interpolation
00086     template <typename T> inline std::vector<TooN::Vector<2,int> > transform_translation( SubImage<T> & out, const SubImage<T> & in, const TooN::Matrix<3> & H);
00087     // automatically selects the right transform implementation based on the properties of H
00088     template <typename T> inline std::vector<TooN::Vector<2,int> > transform( SubImage<T> & out, const SubImage<T> & in, const TooN::Matrix<3> & H);
00089     
00090     // calculates the gradient of a one component image within the given bounds only. The 
00091     // function uses central differences, but does not divide by 2!
00092     template<typename GRADIENT, typename IMAGE> inline Image<GRADIENT> gradient( const SubImage<IMAGE> & img, const std::vector<TooN::Vector<2,int> > & bounds );
00093     // calculates the gradient of a one component image directly. The 
00094     // function uses central differences, but does not divide by 2!
00095     template<typename GRADIENT, typename IMAGE> inline Image<GRADIENT> gradient( const SubImage<IMAGE> & img );
00096     
00109     template <typename TRANSFORM, typename APPEARANCE, typename IMAGE, typename GRADIENT>
00110     inline ESMResult esm_opt( TRANSFORM & T, APPEARANCE & A, const SubImage<IMAGE> & templateImage, const SubImage<GRADIENT> & templateGradient, const SubImage<IMAGE> & target, const int max_iterations = 40, const double min_delta = 1e-8, const double max_RMSE = 1.0 );                
00111 } // namespace Internal
00112 
00125 template<int PARAMS>
00126 class Homography {
00127 public:
00128     static const int dimensions = PARAMS;
00129     
00130     Homography() : H(TooN::Identity) {}
00131     
00132     template <int R, int C, typename P, typename B>
00133     Homography( const TooN::Matrix<R, C, P, B> & h) : H(h) {}
00134     
00135     const TooN::Matrix<3> & get_matrix() const { return H; }
00136     TooN::Matrix<3> & get_matrix() { return H; }
00137     const TooN::Vector<PARAMS> & get_jacobian( const TooN::Vector<2> & p, const TooN::Vector<2> & grad ) const {
00138         switch(PARAMS){
00139         case 8:
00140             // 0 0 p[1]
00141             J[7] = -p[1]*(grad*p); // TooN::makeVector(-p[1]*p[0], -p[1]*p[1]);
00142         case 7:
00143             // 0 0 p[0]
00144             J[6] = -p[0]*(grad*p); // TooN::makeVector(-p[0]*p[0], -p[0]*p[1]);
00145         case 6:
00146             // p[1] p[0] 0 
00147             J[5] = grad[0]*p[1] + grad[1]*p[0];// TooN::makeVector(p[1], p[0]);
00148         case 5:
00149             // p[0] -p[1] 0 
00150             J[4] = grad[0]*p[0] - grad[1]*p[1]; // TooN::makeVector(p[0], -p[1]);
00151         case 4:
00152             // p[0] p[1] -2 
00153             J[3] = 3 * (grad * p); //TooN::makeVector(3*p[0], 3*p[1]);
00154         case 3:
00155             // -p[1] p[0] 0 
00156             J[2] = - grad[0]*p[1] + grad[1]*p[0] ; // TooN::makeVector(-p[1], p[0]);
00157         case 2:
00158             J[1] = grad[1]; // TooN::makeVector(0, 1);
00159         case 1:
00160             J[0] = grad[0]; // TooN::makeVector(1, 0);
00161             break;
00162         default: 
00163             assert(false);
00164         }
00165         return J;
00166     }
00167     
00168     void update( const TooN::Vector<PARAMS> & v ){
00169         TooN::Matrix<3> G = TooN::Zeros;
00170         switch(PARAMS){
00171         case 8:
00172             G(2,1) = v[7];
00173         case 7:
00174             G(2,0) = v[6];
00175         case 6:
00176             G(0,1) = v[5];
00177             G(1,0) = v[5];
00178         case 5:
00179             G(0,0) = v[4];
00180             G(1,1) = -v[4];
00181         case 4:
00182             G(0,0) += v[3];
00183             G(1,1) += v[3];
00184             G(2,2) += -2*v[3];
00185         case 3:
00186             G(0,1) -= v[2];
00187             G(1,0) += v[2];
00188         case 2:
00189             G(1,2) = v[1];
00190         case 1:
00191             G(0,2) = v[0];
00192             break;
00193         default: assert(false);
00194         }
00195         H = H * TooN::exp(-G);
00196     }
00197 
00198 protected:
00199     TooN::Matrix<3> H;
00200     mutable TooN::Vector<PARAMS> J;
00201 };
00202 
00209 template<int PARAMS>
00210 class HomographyPrefix : public Homography<PARAMS> {
00211 public:
00212     static const int dimensions = PARAMS;
00213     
00214     HomographyPrefix() : Pre(TooN::Identity) {}
00215 
00216     template <int R, int C, typename P, typename B>
00217     HomographyPrefix( const TooN::Matrix<R, C, P, B> & p ) : Pre(p) {
00218         const TooN::Matrix<3> id = TooN::Identity;
00219         H = TooN::gaussian_elimination(Pre, id);
00220     }
00221     
00222     template <int R, int C, typename P, typename B, int R2, int C2, typename P2, typename B2>
00223     HomographyPrefix( const TooN::Matrix<R, C, P, B> & h, const TooN::Matrix<R2, C2, P2, B2> & p) : Pre(p) {
00224         const TooN::Matrix<3> id = TooN::Identity;
00225         H = h * TooN::gaussian_elimination(Pre, id);
00226     }
00227     
00228     const TooN::Matrix<3> get_matrix() const { return H * Pre; }
00229     
00230     const TooN::Vector<PARAMS> & get_jacobian( const TooN::Vector<2> & x, const TooN::Vector<2> & grad ) const {
00231         if( PARAMS > 2){
00232             const TooN::Vector<2> p = TooN::project(Pre * TooN::unproject(x));
00233             return Homography<PARAMS>::get_jacobian(p, grad);
00234         }
00235         return Homography<PARAMS>::get_jacobian(x, grad);
00236     }
00237 
00238 protected:
00239     using Homography<PARAMS>::H;
00240     TooN::Matrix<3> Pre;
00241 };
00242 
00247 class CameraRotation {
00248 public:
00249     static const int dimensions = 3;
00250 
00251     CameraRotation() {}
00252     
00253     template <typename P, typename B>
00254     CameraRotation( const TooN::Vector<4, P, B> & camera_params, const TooN::SO3<> & init = TooN::SO3<>() ) 
00255     : R(init) 
00256     {
00257         set_camera(camera_params);
00258     }
00259     
00260     template <typename P, typename B>
00261     void set_camera( const TooN::Vector<4, P, B> & camera_params ){
00262         k = camera_params;
00263         K = TooN::Identity;
00264         K(0,0) = camera_params[0];
00265         K(1,1) = camera_params[1];
00266         K(0,2) = camera_params[2];
00267         K(1,2) = camera_params[3];
00268         
00269         kinv = TooN::makeVector( 1/camera_params[0], 1/camera_params[1],  -camera_params[2]/camera_params[0], -camera_params[3]/camera_params[1]);
00270         Kinv = TooN::Identity;
00271         Kinv(0,0) = kinv[0];
00272         Kinv(1,1) = kinv[1];
00273         Kinv(0,2) = kinv[2];
00274         Kinv(1,2) = kinv[3];
00275     }
00276     
00277     const TooN::Matrix<3> get_matrix() const { return K*R*Kinv; }
00278     
00279     const TooN::Vector<dimensions> & get_jacobian( const TooN::Vector<2> & p, const TooN::Vector<2> & grad ) const {
00280         // this implements grad * proj * K * G_i * Kinv * p;
00281         const TooN::Vector<3> pW = TooN::makeVector(kinv[0] * p[0] + kinv[2], kinv[1] * p[1] + kinv[3], 1);
00282         const TooN::Vector<3> J_pK = TooN::makeVector(grad[0] * k[0], grad[1] * k[1], grad * (k.slice<2,2>() - p));
00283         J[0] = J_pK * TooN::SO3<>::generator_field(0, pW);
00284         J[1] = J_pK * TooN::SO3<>::generator_field(1, pW);
00285         J[2] = J_pK * TooN::SO3<>::generator_field(2, pW);
00286         return J;
00287     }
00288     
00289     void update( const TooN::Vector<dimensions> & v ){
00290         R = R * TooN::SO3<>::exp(-v);
00291     }
00292 
00293     const TooN::SO3<> & get_rotation() const { return R; }
00294     TooN::SO3<> & get_rotation() { return R; }
00295 
00296 protected:
00297     TooN::Matrix<3> K, Kinv; 
00298     TooN::Vector<4> k, kinv; 
00299     TooN::SO3<> R;           
00300     mutable TooN::Vector<dimensions> J; 
00301 };
00302 
00305 class StaticAppearance {
00306 public:
00307     static const int dimensions = 0;
00308     template <typename PIXEL> double difference( const PIXEL & warped, const PIXEL & templatePixel ) const { 
00309         return warped - templatePixel;
00310     }
00311     template <typename PIXEL> std::pair<double, TooN::Vector<dimensions> > difference_jacobian( const PIXEL & warped, const PIXEL & templatePixel ) const {
00312         return std::make_pair( difference(warped, templatePixel), TooN::Vector<dimensions>());
00313     }
00314     TooN::Vector<2> image_jacobian( const TooN::Vector<2> & gradWarped, const TooN::Vector<2> & gradTemplate ) const { 
00315         return (gradWarped + gradTemplate) * 0.25;
00316     }
00317     void update( const TooN::Vector<dimensions> & d ) {}
00318 };
00319 
00323 class OffsetAppearance {
00324 public:
00325     static const int dimensions = 1;
00326     template <typename PIXEL> double difference( const PIXEL & warped, const PIXEL & templatePixel ) const { 
00327         return warped - templatePixel - offset; 
00328     }
00329     template <typename PIXEL> std::pair<double, TooN::Vector<dimensions> > difference_jacobian( const PIXEL & warped, const PIXEL & templatePixel ) const {
00330         return std::make_pair( difference(warped, templatePixel), TooN::makeVector(1));
00331     }
00332     TooN::Vector<2> image_jacobian( const TooN::Vector<2> & gradWarped, const TooN::Vector<2> & gradTemplate ) const {
00333         return (gradWarped + gradTemplate) * 0.25; 
00334     }
00335     void update( const TooN::Vector<dimensions> & d ) { 
00336         offset += d[0]; 
00337     }
00338     
00339     OffsetAppearance() : offset(0) {}
00340     double offset;
00341 };
00342 
00347 class BlurAppearance {
00348 public:
00349     static const int dimensions = 0;
00350     template <typename PIXEL> double difference( const PIXEL & warped, const PIXEL & templatePixel ) const { 
00351         return warped - templatePixel; 
00352     }
00353     template <typename PIXEL> std::pair<double, TooN::Vector<dimensions> > difference_jacobian( const PIXEL & warped, const PIXEL & templatePixel ) const {
00354         return std::make_pair( difference(warped, templatePixel), TooN::Vector<dimensions>());
00355     }
00356     TooN::Vector<2> image_jacobian( const TooN::Vector<2> & gradWarped, const TooN::Vector<2> & gradTemplate ) const { 
00357         return (gradWarped + 0.5*gradTemplate) * 0.25; 
00358     }
00359     void update( const TooN::Vector<dimensions> & d ) { 
00360     }
00361 };
00362 
00363 #if 0
00364 
00365 class LinearAppearance {
00366     static const int dimensions = 2;
00367 
00368     template <typename PIXEL> double difference( const PIXEL & templatePixel, const PIXEL & warped ) const { return warped - templatePixel*scale - offset; }
00369     Vector<2> image_jacobian( const Vector<2> & gradTemplate, const Vector<2> & gradImage ) const { return gradTemplate + gradImage; }
00370     Vector<dimensions> get_jacobian( const Vector<2> & point ) { return TooN::makeVector(1.0); }
00371     void update( const Vector<dimensions> & d ) { offset += d[0]; }
00372     
00373     double offset;
00374     double scale;
00375 };
00376 #endif
00377 
00378 template <typename P>
00379 inline TooN::Matrix<3,3,P> scaleHomography( const TooN::Matrix<3,3,P> & H, const P & f ){
00380     const TooN::Vector<3,P> s = TooN::makeVector<P>(1/f, 1/f, 1);
00381     const TooN::Vector<3,P> is = TooN::makeVector<P>(f, f, 1);
00382     return is.as_diagonal() * H * s.as_diagonal();
00383 }
00384 
00385 #if 0
00386     
00389 template<int PARAMS, typename APPEARANCE, typename IMAGE>
00390 inline TooN::Matrix<3> inter_frame_homography( const SubImage<IMAGE> & from, const SubImage<IMAGE> & to, const TooN::Matrix<3> & init = TooN::Identity, ESMResult * const res = NULL){    
00391     TooN::Matrix<3> prefix = TooN::Identity;
00392 #if 1
00393     prefix(0,2) = -from.size().x/2;
00394     prefix(1,2) = -from.size().y/2;
00395 #else
00396     prefix(0,0) = 2.0/from.size().x;
00397     prefix(1,1) = 2.0/from.size().y;
00398     prefix(0,2) = -1;
00399     prefix(1,2) = -1;
00400     cout << prefix << endl;
00401 #endif
00402     
00403     HomographyPrefix<PARAMS> H(init, prefix);
00404     APPEARANCE appearance;
00405     ESMTransform<HomographyPrefix<PARAMS>, APPEARANCE> T(H, appearance);
00406         
00407     ESMResult r = esm_opt(T, from, to, 40, 1e-2);
00408     
00409     if(res != NULL)
00410         *res = r;
00411     return T.transform.get_matrix();
00412 }
00413 
00414 #endif
00415 
00419 template <typename TRANSFORM, typename APPEARANCE, typename IMAGE = CVD::byte, typename GRADIENT = TooN::Vector<2, typename Pixel::traits<IMAGE>::wider_type> >
00420 class ESMEstimator {
00421 public:
00422     TRANSFORM transform;
00423     APPEARANCE appearance;
00424         
00425     Image<IMAGE> templ;
00426     Image<GRADIENT> templGradient;
00427     
00428     int max_iterations;
00429     double min_delta;
00430     double max_RMSE;
00431     
00432     ESMResult result;
00433     
00434     ESMEstimator() : max_iterations(40), min_delta(1e-5), max_RMSE(1e-2) {}
00435     ESMEstimator( const Image<IMAGE> & t, const Image<GRADIENT> & g ) : templ(t), templGradient(g), max_iterations(40), min_delta(1e-5), max_RMSE(1e-2) {}
00436     ESMEstimator( const Image<IMAGE> & t) : templ(t), max_iterations(40), min_delta(1e-5), max_RMSE(1e-2) {
00437         templGradient = Internal::gradient<IMAGE, GRADIENT>(templ);
00438     }
00439     ESMEstimator( const SubImage<IMAGE> & t) : max_iterations(40), min_delta(1e-5), max_RMSE(1e-2) {
00440         templ.copy_from(t);
00441         templGradient = Internal::gradient<IMAGE, GRADIENT>(templ);
00442     }
00443     
00444     void set_image( const Image<IMAGE> & t, const Image<GRADIENT> & g ){
00445         assert(t.size() == g.size());
00446         templ = t;
00447         templGradient = g;
00448     }
00449 
00450     void set_image( const Image<IMAGE> & t ){
00451         templ = t;
00452         templGradient = Internal::gradient<GRADIENT>(templ);
00453     }
00454 
00455     void set_image( const SubImage<IMAGE> & t ){
00456         Image<IMAGE> temp;
00457         temp.copy_from(t);
00458         set_image(temp);
00459     }
00460     
00461     void reset() { 
00462         transform = TRANSFORM();
00463         appearance = APPEARANCE();
00464     }
00465 
00466     const ESMResult & optimize( const SubImage<IMAGE> & to ){
00467         return result = Internal::esm_opt( transform, appearance, templ, templGradient, to, max_iterations, min_delta, max_RMSE);
00468     }
00469 
00470     const ESMResult & optimize( const SubImage<IMAGE> & from, const SubImage<IMAGE> & to ){
00471         Image<GRADIENT> fromGradient = Internal::gradient<IMAGE, GRADIENT>(from);
00472         return optimize( from, fromGradient, to );
00473     }
00474 
00475     const ESMResult & optimize( const SubImage<IMAGE> & from, const SubImage<GRADIENT> & fromGradient, const SubImage<IMAGE> & to ){
00476         return result = Internal::esm_opt( transform, appearance, from, fromGradient, to, max_iterations, min_delta, max_RMSE);
00477     }
00478 
00479     const ESMResult & get_result() const { return result; }
00480 };
00481 
00484 template <typename APPEARANCE, typename IMAGE = CVD::byte, typename GRADIENT = TooN::Vector<2, typename Pixel::traits<IMAGE>::wider_type> >
00485 class RotationEstimator : public ESMEstimator<CameraRotation, APPEARANCE, IMAGE, GRADIENT> {
00486 public:
00487     using ESMEstimator<CameraRotation, APPEARANCE, IMAGE, GRADIENT>::transform;
00488     using ESMEstimator<CameraRotation, APPEARANCE, IMAGE, GRADIENT>::appearance;
00489 
00490     RotationEstimator( const TooN::Vector<4> & cam_params, const TooN::SO3<> & init = TooN::SO3<>() ) {
00491         transform.set_camera( cam_params );
00492         transform.get_rotation() = init;
00493     }
00494     
00495     void reset() { 
00496         transform.get_rotation() = TooN::SO3<>(); 
00497         appearance = APPEARANCE();
00498     }
00499 
00500 };
00501 
00502     namespace Internal {
00503         
00504         // intersects the line AB with the rectangle rect, returns the interval for the parameter t
00505         // for X = A + (B-A)*t
00506         // A, B in homogeneous coordinates, yielding the correct perspective interpolation for t
00507         inline TooN::Vector<2> getBounds(const TooN::Vector<2> & rect, const TooN::Vector<3> & A, const TooN::Vector<3> & B){
00508             const TooN::Vector<3> dir = A - B;
00509             TooN::Vector<2> interval = TooN::makeVector(0,1);
00510             
00511             TooN::Matrix<4,3> lines;
00512             lines[0] = TooN::makeVector(1,0,0);
00513             lines[1] = TooN::makeVector(-1,0,rect[0]);
00514             lines[2] = TooN::makeVector(0,1,0);
00515             lines[3] = TooN::makeVector(0,-1,rect[1]);
00516             
00517             const TooN::Vector<4> hitA = lines * A;
00518             const TooN::Vector<4> hitB = lines * B;
00519             
00520             for(int i = 0; i < 4; ++i){
00521                 if(hitA[i] <=0 && hitB[i] <= 0)
00522                     return TooN::makeVector(0,0);
00523                 if(hitA[i] * hitB[i] < 0){
00524                     const double t = hitA[i] / (lines[i] * dir);
00525                     if(hitA[i] < 0){
00526                         interval[0] = std::max(t, interval[0]);
00527                     } else {
00528                         interval[1] = std::min(t, interval[1]);
00529                     }
00530                 }
00531             }
00532             
00533             if(interval[0] > interval[1])
00534                 return TooN::makeVector(0,0);
00535             return interval;
00536         }
00537         
00538         inline std::vector<TooN::Vector<2,int> > getBounds( const TooN::Vector<2> & rect, const ImageRef & in, const TooN::Matrix<3> & H ){
00539             std::vector<TooN::Vector<2,int> > bounds(in.y);
00540             for(int y = 0; y < in.y; ++y){
00541                 // map the current scan line
00542                 const TooN::Vector<3> A = H * TooN::makeVector(0,y,1);
00543                 const TooN::Vector<3> B = H * TooN::makeVector(in.x-1, y, 1);
00544                 // determine valid sample interval
00545                 const TooN::Vector<2> b = getBounds(rect, A, B) * (in.x-1);
00546                 bounds[y] = TooN::makeVector<int>(ceil(b[0]), floor(b[1])+1);
00547             }
00548             return bounds;
00549         }
00550         
00551         inline std::vector<TooN::Vector<2,int> > erode( const std::vector<TooN::Vector<2,int> > & in ){
00552             using std::min; using std::max;
00553             std::vector<TooN::Vector<2,int> > out(in.size());
00554             out.front() = out.back() = TooN::makeVector(0, 0);
00555             for(unsigned i = 1; i < in.size()-1; ++i){
00556                 out[i][0] = max(in[i-1][0], max(in[i][0] + 1, in[i+1][0]));  // the right most of all three 
00557                 out[i][1] = min(in[i-1][1], min(in[i][1] - 1, in[i+1][1]));  // the left most of all three
00558                 if(out[i][0] > out[i][1])                               // if its empty, then mark as (0,0)
00559                     out[i][0] = out[i][1] = 0;
00560             }
00561             return out;
00562         }
00563         
00564         // H takes pixels in out to pixels in in as a 2D homography (3x3 matrix)
00565         template <typename T>
00566         inline std::vector<TooN::Vector<2,int> > transform_perspective( SubImage<T> & out, const SubImage<T> & in, const TooN::Matrix<3> & H){
00567             const ImageRef & size = out.size();
00568             const TooN::Vector<2> insize = vec(in.size() - ImageRef(1,1));
00569             const TooN::Vector<3> across = H.T()[0];
00570             const TooN::Vector<3> down = H.T()[1];
00571             TooN::Vector<3> base = H.T()[2];
00572             
00573             const std::vector<TooN::Vector<2, int> > bounds = getBounds(insize, size, H);
00574             
00575             for(int y = 0; y < size.y; ++y){
00576                 TooN::Vector<3> X = base + bounds[y][0] * across;
00577                 T * data = out[y] + bounds[y][0];
00578                 for(int x = bounds[y][0]; x < bounds[y][1]; ++x, X += across, ++data){
00579                     const TooN::DefaultPrecision inv = 1/X[2];
00580                     sample(in, X[0] * inv, X[1] * inv, *data );
00581                 }
00582                 base += down;                                                           // next line
00583             }
00584             
00585             return bounds;
00586         }
00587         
00588         // H takes pixels in out to pixels in in as a 2D affine transformation (3x3 matrix)
00589         template <typename T>
00590         inline std::vector<TooN::Vector<2,int> > transform_affine( SubImage<T> & out, const SubImage<T> & in, const TooN::Matrix<3> & H){
00591             const ImageRef & size = out.size();
00592             const TooN::Vector<2> insize = vec(in.size() - ImageRef(1,1));
00593             const TooN::Vector<2> across = H.T()[0].slice<0,2>();
00594             const TooN::Vector<2> down = H.T()[1].slice<0,2>();
00595             TooN::Vector<2> base = H.T()[2].slice<0,2>();
00596             
00597             const std::vector<TooN::Vector<2, int> > bounds = getBounds(insize, size, H);
00598 
00599             for(int y = 0; y < size.y; ++y){
00600                 TooN::Vector<2> X = base + bounds[y][0] * across;
00601                 T * data = out[y] + bounds[y][0];
00602                 for(int x = bounds[y][0]; x < bounds[y][1]; ++x, X += across, ++data){
00603                     sample(in, X[0], X[1], *data );
00604                 }
00605                 base += down;                                                           // next line
00606             }
00607 
00608             return bounds;
00609         }
00610         
00611         // H takes pixels in out to pixels in in as a 2D translation only (stored in the 3x3 matrix)
00612         // This is extra optimized to use the constant mixing factors in the bi-linear interpolation
00613         template <typename T>
00614         inline std::vector<TooN::Vector<2,int> > transform_translation( SubImage<T> & out, const SubImage<T> & in, const TooN::Matrix<3> & H){
00615             const ImageRef & size = out.size();
00616             const TooN::Vector<2> insize = vec(in.size() - ImageRef(1,1));
00617             const TooN::Vector<2> across = H.T()[0].slice<0,2>();
00618             const TooN::Vector<2> down = H.T()[1].slice<0,2>();
00619             TooN::Vector<2> base = H.T()[2].slice<0,2>();
00620             
00621             std::vector<TooN::Vector<2, int> > bounds = getBounds(insize, size, H);
00622             
00623             const double fx = H(0,2) - floor(H(0,2));
00624             const double fy = H(1,2) - floor(H(1,2));
00625             const double ul = fx * fy;
00626             const double ur = (1-fx) * fy;
00627             const double ll = fx * (1-fy);
00628             const double lr = (1-fx)*(1-fy);
00629             
00630             for(int y = 0; y < size.y; ++y){
00631                 const TooN::Vector<2> & interval = bounds[y];                           // determine valid sample interval
00632                 int x = interval[0];
00633                 T * data = out[y] + x;                                                  
00634                 TooN::Vector<2> X = base + x * across;                                  // then sample
00635                 for(; x < interval[1]; ++x, ++data, X += across){
00636                     sample(in, X[0], X[1], *data );
00637                 }
00638                 base += down;                                                           // next line
00639             }
00640             
00641             return bounds;
00642         }
00643         
00644         template <typename T>
00645         inline std::vector<TooN::Vector<2,int> > transform( SubImage<T> & out, const SubImage<T> & in, const TooN::Matrix<3> & H){
00646             const double perspective = H(2,0)*H(2,0) + H(2,1)*H(2,1);
00647             if(perspective < 1e-10)
00648                 return transform_affine(out, in, H);
00649             return transform_perspective(out, in, H);
00650         }
00651         
00652         template<typename GRADIENT, typename IMAGE>
00653         inline Image<GRADIENT> gradient( const SubImage<IMAGE> & img, const std::vector<TooN::Vector<2,int> > & bounds ){
00654             assert(bounds.size() == unsigned(img.size().y));
00655             Image<GRADIENT> grad(img.size());
00656             for(int y = 1; y < img.size().y-1; ++y){
00657                 for(int x = bounds[y][0]; x < bounds[y][1]; ++x){
00658                     grad[y][x][0] = img[y][x+1] - img[y][x-1];
00659                     grad[y][x][1] = img[y+1][x] - img[y-1][x];
00660                 }
00661             }
00662             return grad;
00663         }
00664         
00665         template<typename GRADIENT, typename IMAGE>
00666         inline Image<GRADIENT> gradient( const SubImage<IMAGE> & img ){
00667             Image<GRADIENT> grad(img.size());
00668             for(int y = 1; y < img.size().y-1; ++y){
00669                 for(int x = 1; x < img.size().x-1; ++x){
00670                     grad[y][x][0] = img[y][x+1] - img[y][x-1];
00671                     grad[y][x][1] = img[y+1][x] - img[y-1][x];
00672                 }
00673             }
00674             return grad;
00675         }
00676         
00677         template <typename TRANSFORM, typename APPEARANCE, typename IMAGE, typename GRADIENT>
00678         inline ESMResult esm_opt( TRANSFORM & T, APPEARANCE & A, const SubImage<IMAGE> & templateImage, const SubImage<GRADIENT> & templateGradient, const SubImage<IMAGE> & target, const int max_iterations = 40, const double min_delta = 1e-8, const double max_RMSE = 1.0 ){
00679             assert(templateImage.size() == templateGradient.size());
00680             
00681             const int dimensions = TRANSFORM::dimensions+APPEARANCE::dimensions;
00682             
00683             Image<IMAGE> warped(templateImage.size());
00684             
00685             TooN::WLS<dimensions> wls;
00686         
00687             ESMResult result = {1e100, 1e100, 0, 0};
00688         
00689             // first warp here to be able to compare error before and after
00690             std::vector<TooN::Vector<2,int> > mask = Internal::transform(warped, target, T.get_matrix());
00691             mask = Internal::erode(mask);
00692         
00693             do {
00694                 // get the gradient for the warped image
00695                 Image<GRADIENT> grad_warped = Internal::gradient<GRADIENT>(warped, mask);
00696         
00697                 // create the least squares system
00698                 wls.clear();
00699                 TooN::Vector<dimensions> J;
00700                 for(unsigned y = 0; y < mask.size(); ++y){
00701                     for(int x = mask[y][0]; x < mask[y][1]; ++x){
00702                         std::pair<double, TooN::Vector<APPEARANCE::dimensions> > da = A.difference_jacobian( warped[y][x], templateImage[y][x]);
00703                         J.template slice<0,TRANSFORM::dimensions>() = T.get_jacobian(TooN::makeVector(x,y), A.image_jacobian(grad_warped[y][x], templateGradient[y][x]));
00704                         J.template slice<TRANSFORM::dimensions, APPEARANCE::dimensions>() = da.second;
00705                         wls.add_mJ(da.first, J);
00706                     }
00707                 }
00708         
00709                 // solve and update
00710                 wls.compute();
00711                 T.update(wls.get_mu().template slice<0,TRANSFORM::dimensions>());
00712                 A.update(wls.get_mu().template slice<TRANSFORM::dimensions, APPEARANCE::dimensions>());
00713                 ++result.iterations;
00714         
00715                 // compute new warp to calculate new error
00716                 mask = Internal::transform(warped, target, T.get_matrix());
00717                 mask = Internal::erode(mask);
00718         
00719                 // compute error after update
00720                 result.error = 0;
00721                 // and number of pixels for RMS computation
00722                 result.pixels = 0;
00723                 for(unsigned y = 0; y < mask.size(); ++y){
00724                     result.pixels += mask[y][1] - mask[y][0];
00725                     for(int x = mask[y][0]; x < mask[y][1]; ++x){
00726                         const double d = A.difference(warped[y][x], templateImage[y][x]);
00727                         result.error += d*d;
00728                     }
00729                 }
00730         
00731                 // store results
00732                 result.delta = norm(wls.get_mu());
00733 
00734 #if 0                
00735                 if(DEBUG_ESM){
00736                     ostringstream sout;
00737                     sout << "debug_" << setw(4) << setfill('0') << DEBUG_ESM << "_" << result.iterations << ".jpg";
00738                     Image<byte> warped(templateImage.size());
00739                     Internal::transform(warped, target, T.get_matrix());
00740                     img_save(warped, sout.str());
00741                     
00742                 }
00743 #endif
00744 
00745             } while(result.iterations < max_iterations && result.delta > min_delta && result.RMSE() > max_RMSE);
00746         
00747             return result;
00748         }
00749                 
00750     } // namespace Internal
00751 
00752 template <int PARAMS>
00753 inline std::ostream & operator<<(std::ostream & out, const Homography<PARAMS> & t ){
00754     out << t.H[0] << "\t" << t.H[1] << "\t" << t.H[2];
00755     return out;
00756 }
00757 
00758 inline std::ostream & operator<<(std::ostream & out, const StaticAppearance & t ){
00759     return out;
00760 }
00761 
00762 inline std::ostream & operator<<(std::ostream & out, const OffsetAppearance & t ){
00763     out << t.offset;
00764     return out;
00765 }
00766 
00767 inline std::ostream & operator<<(std::ostream & out, const BlurAppearance & t ){
00768     return out;
00769 }
00770 
00771 } // namespace CVD
00772 
00773 #endif // CVD_ESM_H