contrib/mul/mfpf/mfpf_point_finder.cxx
Go to the documentation of this file.
00001 #include "mfpf_point_finder.h"
00002 //:
00003 // \file
00004 // \brief Base for classes which locate feature points
00005 // \author Tim Cootes
00006 
00007 #include <vsl/vsl_indent.h>
00008 #include <vsl/vsl_binary_loader.h>
00009 
00010 #include <vcl_cmath.h>
00011 #include <vimt/algo/vimt_find_troughs.h>
00012 #include <vimt/vimt_image_pyramid.h>
00013 #include <vnl/vnl_cost_function.h>
00014 #include <vnl/algo/vnl_amoeba.h>
00015 #include <vnl/algo/vnl_powell.h>
00016 #include <vnl/vnl_math.h>
00017 #include <vgl/vgl_point_2d.h>
00018 #include <vil/vil_print.h>
00019 
00020 //=======================================================================
00021 //=== Utility functions for optimisation
00022 //=======================================================================
00023 
00024 //: Return position of minima of parabola fit to (-1,fa),(0,fb), (1,fc)
00025 inline double parabolic_min(double fa, double fb, double fc)
00026 {
00027   double df1=fa-fb;
00028   double df2=fc-fb;
00029   double d=df1+df2;
00030   if (vcl_fabs(d)<1e-6) return 0.0;
00031   else                  return 0.5*(df1-df2)/d;
00032 }
00033 
00034 class mfpf_pf_cost : public vnl_cost_function
00035 {
00036  private:
00037   mfpf_point_finder* pf_;
00038   const vimt_image_2d_of<float>& image_;
00039   vgl_point_2d<double> p_;
00040   vgl_vector_2d<double> u_;
00041   vgl_vector_2d<double> v_;
00042   double ds_,dA_;
00043  public:
00044   virtual ~mfpf_pf_cost() {}
00045   mfpf_pf_cost(mfpf_point_finder& pf,
00046                const vimt_image_2d_of<float>& image,
00047                const vgl_point_2d<double>& p0,
00048                const vgl_vector_2d<double>& u0,
00049                double ds, double dA)
00050     : vnl_cost_function(4),
00051       pf_(&pf),image_(image),p_(p0),
00052       u_(u0),v_(-u_.y(),u_.x()),
00053       ds_(ds),dA_(dA) {}
00054 
00055     //:  Given the parameter vector x, compute the value of f(x).
00056   virtual double f(const vnl_vector<double>& params);
00057 
00058   void get_pose(const vnl_vector<double>& params,
00059                 vgl_point_2d<double>& p,
00060                 vgl_vector_2d<double>& u) const;
00061 
00062   //: Checks if parameter index is at minima by displacing by +/-dx
00063   //  If it is, then uses parabolic fit to attempt to improved it.
00064   //  fit is f(params).  On exit params contains new
00065   //  values, and fit contains new fit at that position.
00066   bool check_and_refine_minima(vnl_vector<double>& params,
00067                                double &fit,
00068                                unsigned index, double dx);
00069 };
00070 
00071 double mfpf_pf_cost::f(const vnl_vector<double>& params)
00072 {
00073   vgl_point_2d<double> p;
00074   vgl_vector_2d<double> u;
00075   get_pose(params,p,u);
00076   return pf_->evaluate(image_,p,u);
00077 }
00078 
00079 void mfpf_pf_cost::get_pose(const vnl_vector<double>& v,
00080                             vgl_point_2d<double>& p,
00081                             vgl_vector_2d<double>& u) const
00082 {
00083   p = p_+pf_->step_size()*(v[0]*u_+v[1]*v_);
00084   double s = vcl_pow(ds_,v[2]);
00085   double A = dA_*v[3];
00086   u = s*(u_*vcl_cos(A)+v_*vcl_sin(A));
00087 }
00088 
00089 //: Checks if parameter index is at minima by displacing by +/-dx
00090 //  If it is, then uses parabolic fit to attempt to improved it.
00091 //  fit is f(params).  On exit params contains new
00092 //  values, and fit contains new fit at that position.
00093 bool mfpf_pf_cost::check_and_refine_minima(vnl_vector<double>& params,
00094                                            double &fit,
00095                                            unsigned index, double dx)
00096 {
00097   double p0 = params[index];
00098   params[index]=p0-dx;
00099   double f1=f(params);
00100   if (f1<fit) { fit=f1; return false; }
00101 
00102   params[index]=p0+dx;
00103   double f2=f(params);
00104   if (f2<fit) { fit=f2; return false; }
00105 
00106   double a = parabolic_min(f1,fit,f2);
00107   params[index]=p0+a*dx;
00108   double f3=f(params);
00109   if (f3<fit) { fit=f3; return true; }
00110 
00111   // Optimisation no better, so revert
00112   params[index]=p0;
00113   return true;
00114 }
00115 
00116 //=======================================================================
00117 // Dflt ctor
00118 //=======================================================================
00119 
00120 mfpf_point_finder::mfpf_point_finder()
00121   : step_size_(1.0),
00122     search_ni_(5),search_nj_(0),
00123     nA_(0),dA_(0.0),ns_(0),ds_(1.0)
00124 {
00125 }
00126 
00127 //=======================================================================
00128 // Destructor
00129 //=======================================================================
00130 
00131 mfpf_point_finder::~mfpf_point_finder()
00132 {
00133 }
00134 
00135 //: Size of step between sample points
00136 void mfpf_point_finder::set_step_size(double s)
00137 {
00138   step_size_=s;
00139 }
00140 
00141 //: Define search region size
00142 //  During search, samples at points on grid [-ni,ni]x[-nj,nj],
00143 //  with axes defined by u.
00144 void mfpf_point_finder::set_search_area(unsigned ni, unsigned nj)
00145 {
00146   search_ni_=ni;
00147   search_nj_=nj;
00148 }
00149 
00150 //: Define angle search parameters
00151 void mfpf_point_finder::set_angle_range(unsigned nA, double dA)
00152 {
00153   nA_=nA;
00154   dA_=dA;
00155 }
00156 
00157 //: Define scale search parameters
00158 void mfpf_point_finder::set_scale_range(unsigned ns, double ds)
00159 {
00160   ns_=ns;
00161   ds_=ds;
00162 }
00163 
00164 //: Define model parameters (return false by default)
00165 bool mfpf_point_finder::set_model(const vcl_vector<double>& /*v*/)
00166 {
00167   return false;
00168 }
00169 
00170 //: Number of dimensions in the model (return 0 by default)
00171 unsigned mfpf_point_finder::model_dim()
00172 {
00173   return 0;
00174 }
00175 
00176 //: Get sample of region around specified point in image
00177 void mfpf_point_finder::get_sample_vector(const vimt_image_2d_of<float>& image,
00178                                           const vgl_point_2d<double>& p,
00179                                           const vgl_vector_2d<double>& u,
00180                                           vcl_vector<double>& v)
00181 {
00182   // Return empty vector
00183   v.resize(0);
00184 }
00185 
00186 //: Search given image around p, using u to define scale and angle
00187 //  Evaluates responses on a grid, finds the best point on the
00188 //  grid, then optimises its position by fitting a parabola.
00189 //
00190 //  On exit, new_p defines position of the best nearby match.
00191 //  Returns a quality of fit measure at that
00192 //  point (the smaller the better).
00193 double mfpf_point_finder::search_one_pose_with_opt(
00194                       const vimt_image_2d_of<float>& image,
00195                       const vgl_point_2d<double>& p0,
00196                       const vgl_vector_2d<double>& u,
00197                       vgl_point_2d<double>& new_p)
00198 {
00199   vimt_image_2d_of<double> response_im;
00200   evaluate_region(image,p0,u,response_im);
00201 
00202   vimt_transform_2d im2w = response_im.world2im().inverse();
00203   const vil_image_view<double>& r_im =  response_im.image();
00204 
00205   // Find position of lowest point in response image
00206   unsigned x=0,y=0;
00207   double f0 = r_im(x,y);
00208   for (unsigned j=0;j<r_im.nj();++j)
00209     for (unsigned i=0;i<r_im.ni();++i)
00210     {
00211       if (r_im(i,j)<f0) { f0=r_im(i,j); x=i; y=j; }
00212     }
00213 
00214   // Perform local prediction of minima
00215   double dx=0.0;
00216   if (x>0 && x+1<r_im.ni())
00217     dx = parabolic_min(r_im(x-1,y  ),f0,r_im(x+1,y  ));
00218 
00219   double dy=0.0;
00220   if (y>0 && y+1<r_im.nj())
00221     dy = parabolic_min(r_im(x  ,y-1),f0,r_im(x  ,y+1));
00222 
00223   // Check the estimate is better
00224   new_p=im2w(x+dx,y+dy);
00225 
00226   if (dx==0.0 && dy==0.0) return f0;
00227 
00228   double f1=evaluate(image,new_p,u);
00229   if (f1<f0) return f1;
00230 
00231   // new_p not an improvement
00232   new_p=p0;
00233   return f0;
00234 }
00235 
00236 
00237 //: Search given image around p, using u to define scale and orientation
00238 //  On exit, new_p and new_u define position, scale and orientation of
00239 //  the best nearby match.  Returns a quality of fit measure at that
00240 //  point (the smaller the better).
00241 double mfpf_point_finder::search(const vimt_image_2d_of<float>& image,
00242                                  const vgl_point_2d<double>& p,
00243                                  const vgl_vector_2d<double>& u,
00244                                  vgl_point_2d<double>& new_p,
00245                                  vgl_vector_2d<double>& new_u)
00246 {
00247   if (nA_==0 && ns_==0)
00248   {
00249     // Only search at one scale/orientation
00250     new_u = u;
00251     return search_one_pose(image,p,u,new_p);
00252   }
00253 
00254   double best_fit=9e99;
00255   vgl_point_2d<double> p1;
00256 
00257   vgl_vector_2d<double> v(-u.y(),u.x());
00258 
00259   for (int is=-int(ns_);is<=int(ns_);++is)
00260   {
00261     double s = vcl_pow(ds_,is);
00262     for (int iA=-int(nA_);iA<=int(nA_);++iA)
00263     {
00264       double A = iA*dA_;
00265       vgl_vector_2d<double> uA = s*(u*vcl_cos(A)+v*vcl_sin(A));
00266 
00267       double f = search_one_pose(image,p,uA,p1);
00268       if (f<best_fit)
00269       {
00270         best_fit = f;
00271         new_u = uA;
00272         new_p = p1;
00273       }
00274     }
00275   }
00276   return best_fit;
00277 }
00278 
00279 //: Search given image around p, using u to define scale and orientation
00280 //  On exit, new_p and new_u define position, scale and orientation of
00281 //  the best nearby match.  Returns a quality of fit measure at that
00282 //  point (the smaller the better).
00283 double mfpf_point_finder::search_with_opt(
00284                         const vimt_image_2d_of<float>& image,
00285                         const vgl_point_2d<double>& p,
00286                         const vgl_vector_2d<double>& u,
00287                         vgl_point_2d<double>& new_p,
00288                         vgl_vector_2d<double>& new_u)
00289 {
00290   if (nA_==0 && ns_==0)
00291   {
00292     // Only search at one scale/orientation
00293     new_u = u;
00294     return search_one_pose_with_opt(image,p,u,new_p);
00295   }
00296 
00297   double best_fit=1e99; // initialise to high value; will become min(f)
00298   vgl_point_2d<double> p1;
00299 
00300   vgl_vector_2d<double> v(-u.y(),u.x());
00301 
00302   for (int is=-int(ns_);is<=int(ns_);++is)
00303   {
00304     double s = vcl_pow(ds_,is);
00305     for (int iA=-int(nA_);iA<=int(nA_);++iA)
00306     {
00307       double A = iA*dA_;
00308       vgl_vector_2d<double> uA = s*(u*vcl_cos(A)+v*vcl_sin(A));
00309 
00310       double f = search_one_pose_with_opt(image,p,uA,p1);
00311       if (f<best_fit)
00312       {
00313         best_fit = f;
00314         new_u = uA;
00315         new_p = p1;
00316       }
00317     }
00318   }
00319 
00320   mfpf_pf_cost pf_cost(*this,image,new_p,new_u,1+0.7*(ds_-1),0.7*dA_);
00321   vnl_vector<double> params(4,0.0);
00322   // Attempt to optimise scale
00323   pf_cost.check_and_refine_minima(params,best_fit,2,1.0);
00324   // Attempt to optimise orientation
00325   pf_cost.check_and_refine_minima(params,best_fit,3,1.0);
00326 
00327   // Compute actual pose for these optimal parameters
00328   pf_cost.get_pose(params,new_p,new_u);
00329 
00330   return best_fit;
00331 }
00332 
00333 
00334 //: Search for local optima around given point/scale/angle
00335 //  Search in a grid around p (defined by search_ni and search_nj).
00336 //  Find local minima on this grid and return them in pts.
00337 void mfpf_point_finder::grid_search_one_pose(
00338                            const vimt_image_2d_of<float>& image,
00339                            const vgl_point_2d<double>& p,
00340                            const vgl_vector_2d<double>& u,
00341                            vcl_vector<mfpf_pose>& pts,
00342                            vcl_vector<double>& fit)
00343 {
00344   vimt_image_2d_of<double> response_im;
00345   evaluate_region(image,p,u,response_im);
00346 
00347   vcl_vector<vgl_point_2d<unsigned> > t_pts;
00348   vcl_vector<double> t_value;
00349   vimt_find_image_troughs_3x3(t_pts,t_value,response_im.image());
00350   vimt_transform_2d im2w = response_im.world2im().inverse();
00351 
00352   for (unsigned i=0;i<t_pts.size();++i)
00353   {
00354     unsigned x =t_pts[i].x(), y=t_pts[i].y();
00355     fit.push_back(t_value[i]);
00356     pts.push_back(mfpf_pose(im2w(x,y),u));
00357   }
00358 }
00359 
00360 //: Search for local optima around given point/scale/angle
00361 //  Search in a grid around p (defined by search_ni and search_nj).
00362 //  Find local minima on this grid.
00363 //  Perform single sub-grid optimisation by fitting a parabola
00364 //  in x and y and testing resulting point.
00365 //  Append each to pts.
00366 //  Note: pts is not resized, so empty beforehand if necessary.
00367 void mfpf_point_finder::multi_search_one_pose(
00368                           const vimt_image_2d_of<float>& image,
00369                           const vgl_point_2d<double>& p0,
00370                           const vgl_vector_2d<double>& u,
00371                           vcl_vector<mfpf_pose>& pts,
00372                           vcl_vector<double>& fit)
00373 {
00374   vimt_image_2d_of<double> response_im;
00375   evaluate_region(image,p0,u,response_im);
00376 
00377   const vil_image_view<double>& r_im =  response_im.image();
00378 
00379   vcl_vector<vgl_point_2d<unsigned> > t_pts;
00380   vimt_find_image_troughs_3x3(t_pts,r_im);
00381   vimt_transform_2d im2w = response_im.world2im().inverse();
00382 
00383   for (unsigned i=0;i<t_pts.size();++i)
00384   {
00385     unsigned x =t_pts[i].x(), y=t_pts[i].y();
00386     double f0 = r_im(x,y);  // Value at minima on grid
00387 
00388     if (vnl_math_isnan(f0))
00389     {
00390       vcl_cerr<<"mfpf_point_finder::multi_search_one_pose()\n"
00391               <<"Response was a NaN at "<<x<<','<<y<<vcl_endl
00392               <<"Reponse image: "<<response_im.image()<<vcl_endl;
00393       vil_print_all(vcl_cout,response_im.image());
00394       vcl_abort();
00395     }
00396 
00397     // Perform local prediction of minima
00398     double dx=parabolic_min(r_im(x-1,y  ),f0,r_im(x+1,y  ));
00399     double dy=parabolic_min(r_im(x  ,y-1),f0,r_im(x  ,y+1));
00400 
00401     // Check the estimate is better
00402     vgl_point_2d<double> p1=im2w(x+dx,y+dy);
00403     vgl_vector_2d<double> u1=u;
00404     double f1=evaluate(image,p1,u);
00405     if (f1>=f0)
00406     {
00407       // Refined minima is no better so revert to grid position
00408       f1=f0;
00409       p1=im2w(x,y);
00410     }
00411 
00412     // Check if locally optimal in scale and orientation
00413     // Only add in if it is.
00414     mfpf_pf_cost pf_cost(*this,image,p1,u1,ds_,dA_);
00415     vnl_vector<double> params(4,0.0);
00416     // Attempt to optimise scale
00417     if (vcl_fabs(ds_-1.0)>1e-6)
00418     {
00419       // Do parabolic fit if local minima
00420       pf_cost.check_and_refine_minima(params,f1,2,0.7);
00421 
00422       // Note that above fn returns false if not a local minima
00423       // However, safest not to discard this until understand
00424       // shape of fit surface better.
00425     }
00426 
00427     // Attempt to optimise orientation
00428     if (dA_!=0.0)
00429     {
00430       // Do parabolic fit if local minima
00431       pf_cost.check_and_refine_minima(params,f1,3,0.7);
00432     }
00433 
00434     // Compute actual pose for these optimal parameters
00435     pf_cost.get_pose(params,p1,u1);
00436 
00437     fit.push_back(f1);
00438     pts.push_back(mfpf_pose(p1,u1));
00439   }
00440 }
00441 
00442 
00443 //: Search for local optima around given point/scale/angle
00444 //  For each angle and scale (defined by internal nA,dA,ns,ds)
00445 //  search in a grid around p (defined by search_ni and search_nj).
00446 //  Find local minima on this grid and return them in pts.
00447 //
00448 //  Note that an object in an image may lead to multiple responses,
00449 //  one at each scale and angle near to the optima.  Thus the
00450 //  poses defined in pts should be further refined to eliminate
00451 //  such multiple responses.
00452 void mfpf_point_finder::grid_search(const vimt_image_2d_of<float>& image,
00453                                     const vgl_point_2d<double>& p,
00454                                     const vgl_vector_2d<double>& u,
00455                                     vcl_vector<mfpf_pose>& pts,
00456                                     vcl_vector<double>& fit)
00457 {
00458   pts.resize(0);
00459   fit.resize(0);
00460 
00461   if (nA_==0 && ns_==0)
00462   {
00463     // Only search at one scale/orientation
00464     return grid_search_one_pose(image,p,u,pts,fit);
00465   }
00466 
00467   vgl_vector_2d<double> v(-u.y(),u.x());
00468 
00469   for (int is=-int(ns_);is<=int(ns_);++is)
00470   {
00471     double s = vcl_pow(ds_,is);
00472     for (int iA=-int(nA_);iA<=int(nA_);++iA)
00473     {
00474       double A = iA*dA_;
00475       vgl_vector_2d<double> uA = s*(u*vcl_cos(A)+v*vcl_sin(A));
00476 
00477       grid_search_one_pose(image,p,uA,pts,fit);
00478     }
00479   }
00480 }
00481 
00482 //: Search for local optima around given point/scale/angle
00483 //  For each angle and scale (defined by internal nA,dA,ns,ds)
00484 //  search in a grid around p (defined by search_ni and search_nj).
00485 //  Find local minima on this grid.
00486 //  Sub-grid estimation using parabolic fitting included.
00487 //  poses[i] defines result i, with corresponding fit fits[i]
00488 //
00489 //  Note that an object in an image may lead to multiple responses,
00490 //  one at each scale and angle near to the optima.  Thus the
00491 //  poses defined in pts should be further refined to eliminate
00492 //  such multiple responses.
00493 void mfpf_point_finder::multi_search(const vimt_image_2d_of<float>& image,
00494                                      const vgl_point_2d<double>& p,
00495                                      const vgl_vector_2d<double>& u,
00496                                      vcl_vector<mfpf_pose>& poses,
00497                                      vcl_vector<double>& fits)
00498 {
00499   poses.resize(0);
00500   fits.resize(0);
00501 
00502   if (nA_==0 && ns_==0)
00503   {
00504     // Only search at one scale/orientation
00505     return multi_search_one_pose(image,p,u,poses,fits);
00506   }
00507 
00508   vgl_vector_2d<double> v(-u.y(),u.x());
00509 
00510   for (int is=-int(ns_);is<=int(ns_);++is)
00511   {
00512     double s = vcl_pow(ds_,is);
00513     for (int iA=-int(nA_);iA<=int(nA_);++iA)
00514     {
00515       double A = iA*dA_;
00516       vgl_vector_2d<double> uA = s*(u*vcl_cos(A)+v*vcl_sin(A));
00517 
00518       multi_search_one_pose(image,p,uA,poses,fits);
00519     }
00520   }
00521 }
00522 
00523 //: Perform local optimisation to refine position,scale and angle
00524 //  On input fit is match at p,u.  On exit p,u and fit are updated.
00525 //  Baseclass implementation uses simplex optimisation.
00526 void mfpf_point_finder::refine_match(const vimt_image_2d_of<float>& image,
00527                                      vgl_point_2d<double>& p,
00528                                      vgl_vector_2d<double>& u,
00529                                      double& fit)
00530 {
00531   // Strictly should set up scale and orientation tolerances
00532   // based on extent of object
00533   double ds=1.5;
00534   double dA=0.5;
00535   mfpf_pf_cost cost_fn(*this,image,p,u,ds,dA);
00536 
00537   vnl_vector<double> initial_dv(4,0.5), v(4,0.0);
00538 
00539   vnl_amoeba amoeba(cost_fn);
00540 
00541   // One unit in v moves p by 1, scale by pow(ds,1) and A by dA
00542   amoeba.set_x_tolerance(0.1);  // Default 1e-8
00543   amoeba.set_f_tolerance(9e9);
00544   // amoeba continues until both x and f tolerances are satisfied
00545   // Setting large f tolerance ensures x is satisfied
00546 
00547   amoeba.minimize(v, initial_dv);
00548 
00549 #if 0
00550   // Powell minimizer seems to require a huge number of iterations
00551   // Stick with Simplex for the moment.
00552   vnl_powell powell(&cost_fn);
00553   powell.set_initial_step(0.5);
00554   powell.set_linmin_xtol(0.1);
00555   powell.minimize(v);
00556 #endif // 0
00557 
00558   fit = cost_fn.f(v);
00559   cost_fn.get_pose(v,p,u);
00560 }
00561 
00562 //: Create an image summarising the average model (where possible)
00563 //  For instance, creates an image of the mean template used for
00564 //  search.
00565 //  Default implementation does nothing - returns an empty image.
00566 void mfpf_point_finder::get_image_of_model(vimt_image_2d_of<vxl_byte>& image) const
00567 {
00568   // Return empty image.
00569   image.image().set_size(0,0);
00570 }
00571 
00572 //: Select best level for searching around pose with finder
00573 //  Selects pyramid level with pixel sizes best matching
00574 //  the model pixel size at given pose.
00575 unsigned mfpf_point_finder::image_level(const mfpf_pose& pose,
00576                                         const vimt_image_pyramid& im_pyr) const
00577 {
00578   double model_pixel_size = step_size()*pose.scale();
00579   double rel_size0 = model_pixel_size/im_pyr.base_pixel_width();
00580 
00581   double log_step = vcl_log(im_pyr.scale_step());
00582 
00583   // Round level down, to work with slightly higher res. image.
00584   int level = int(vcl_log(rel_size0)/log_step);
00585   if      (level<im_pyr.lo()) return im_pyr.lo();
00586   else if (level>im_pyr.hi()) return im_pyr.hi();
00587   else                        return level;
00588 }
00589 
00590 //: Return true if modelled regions at pose1 and pose2 overlap
00591 bool mfpf_point_finder::overlap(const mfpf_pose& pose1,
00592                                 const mfpf_pose& pose2) const
00593 {
00594   return false;
00595 }
00596 
00597 //: Computes the aligned bounding box for feature with given pose
00598 //  On exit box_pose.p() gives the centre, corners are given by
00599 //  box_pose(+/-0.5*wi, +/-0.5*wj).
00600 void mfpf_point_finder::aligned_bounding_box(const mfpf_pose& pose,
00601                                              mfpf_pose& box_pose,
00602                                              double& wi, double& wj) const
00603 {
00604   // Compute the bounding box of the outline (in ref frame)
00605   vcl_vector<vgl_point_2d<double> > pts;
00606   get_outline(pts);
00607   double xlo=pts[0].x(), xhi=xlo;
00608   double ylo=pts[0].y(), yhi=ylo;
00609   for (unsigned i=1;i<pts.size();++i)
00610   {
00611     if (pts[i].x()<xlo) xlo=pts[i].x();
00612     else if (pts[i].x()>xhi) xhi=pts[i].x();
00613     if (pts[i].y()<ylo) ylo=pts[i].y();
00614     else if (pts[i].y()>yhi) yhi=pts[i].y();
00615   }
00616   // Mapping required to transform a box centred on the origin to
00617   // given bounding box, then scaling by model scale step
00618   mfpf_pose trans(0.5*(xlo+xhi),0.5*(ylo+yhi),step_size(),0);
00619   box_pose = pose*trans;
00620 
00621   wi=xhi-xlo;
00622   wj=yhi-ylo;
00623 }
00624 
00625 
00626 //: Return true if base class parameters are the same in pf
00627 bool mfpf_point_finder::base_equality(const mfpf_point_finder& pf) const
00628 {
00629   return
00630     search_ni_==pf.search_ni_ &&
00631     search_nj_==pf.search_nj_ &&
00632     nA_==pf.nA_ &&
00633     ns_==pf.ns_ &&
00634     vcl_fabs(dA_-pf.dA_)<=1e-6 &&
00635     vcl_fabs(ds_-pf.ds_)<=1e-6 &&
00636     vcl_fabs(step_size_-pf.step_size_)<=1e-6;
00637 }
00638 
00639 
00640 //=======================================================================
00641 // Method: print
00642 //=======================================================================
00643 
00644 void mfpf_point_finder::print_summary(vcl_ostream& os) const
00645 {
00646   os<<" step_size: "<<step_size_
00647     <<" search_ni: "<<search_ni_
00648     <<" search_nj: "<<search_nj_
00649     <<" nA: "<<nA_<<" dA: "<<dA_
00650     <<" ns: "<<ns_<<" ds: "<<ds_<<' ';
00651 }
00652 
00653 //=======================================================================
00654 // Method: version_no
00655 //=======================================================================
00656 
00657 short mfpf_point_finder::version_no() const
00658 {
00659   return 1;
00660 }
00661 
00662 //=======================================================================
00663 // Binary I/O
00664 //=======================================================================
00665 
00666 
00667 void mfpf_point_finder::b_write(vsl_b_ostream& bfs) const
00668 {
00669   vsl_b_write(bfs,version_no());
00670   vsl_b_write(bfs,step_size_);
00671   vsl_b_write(bfs,search_ni_);
00672   vsl_b_write(bfs,search_nj_);
00673   vsl_b_write(bfs,nA_);
00674   vsl_b_write(bfs,dA_);
00675   vsl_b_write(bfs,ns_);
00676   vsl_b_write(bfs,ds_);
00677 }
00678 
00679 //=======================================================================
00680 // Method: load
00681 //=======================================================================
00682 
00683 void mfpf_point_finder::b_read(vsl_b_istream& bfs)
00684 {
00685   if (!bfs) return;
00686   short version;
00687   vsl_b_read(bfs,version);
00688   switch (version)
00689   {
00690     case 1:
00691       vsl_b_read(bfs,step_size_);
00692       vsl_b_read(bfs,search_ni_);
00693       vsl_b_read(bfs,search_nj_);
00694       vsl_b_read(bfs,nA_);
00695       vsl_b_read(bfs,dA_);
00696       vsl_b_read(bfs,ns_);
00697       vsl_b_read(bfs,ds_);
00698       break;
00699     default:
00700       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&)\n"
00701                << "           Unknown version number "<< version << vcl_endl;
00702       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00703       break;
00704   }
00705 }
00706 
00707 //=======================================================================
00708 // Method: is_a
00709 //=======================================================================
00710 
00711 vcl_string mfpf_point_finder::is_a() const
00712 {
00713   return vcl_string("mfpf_point_finder");
00714 }
00715 
00716 //: Allows derived class to be loaded by base-class pointer
00717 void vsl_add_to_binary_loader(const mfpf_point_finder& b)
00718 {
00719   vsl_binary_loader<mfpf_point_finder>::instance().add(b);
00720 }
00721 
00722 //=======================================================================
00723 // Associated function: operator<<
00724 //=======================================================================
00725 
00726 void vsl_b_write(vsl_b_ostream& bfs, const mfpf_point_finder& b)
00727 {
00728   b.b_write(bfs);
00729 }
00730 
00731 //=======================================================================
00732 // Associated function: operator>>
00733 //=======================================================================
00734 
00735 void vsl_b_read(vsl_b_istream& bfs, mfpf_point_finder& b)
00736 {
00737   b.b_read(bfs);
00738 }
00739 
00740 //=======================================================================
00741 // Associated function: operator<<
00742 //=======================================================================
00743 
00744 vcl_ostream& operator<<(vcl_ostream& os,const mfpf_point_finder& b)
00745 {
00746   os << b.is_a() << ": ";
00747   vsl_indent_inc(os);
00748   b.print_summary(os);
00749   vsl_indent_dec(os);
00750   return os;
00751 }
00752 
00753 //=======================================================================
00754 // Associated function: operator<<
00755 //=======================================================================
00756 
00757 vcl_ostream& operator<<(vcl_ostream& os,const mfpf_point_finder* b)
00758 {
00759   if (b)
00760     return os << *b;
00761   else
00762     return os << "No mfpf_point_finder defined.";
00763 }