contrib/mul/mfpf/mfpf_mr_point_finder.cxx
Go to the documentation of this file.
00001 #include "mfpf_mr_point_finder.h"
00002 //:
00003 // \file
00004 // \author Tim Cootes
00005 // \brief Multi-res point finder.  Searches at range of scales.
00006 
00007 #include <mfpf/mfpf_prune_overlaps.h>
00008 
00009 #include <vimt/vimt_image_pyramid.h>
00010 #include <vil/vil_save.h>
00011 #include <vcl_cmath.h>
00012 #include <vcl_cassert.h>
00013 #include <vcl_cstdlib.h> // for std::abort()
00014 #include <vcl_sstream.h>
00015 
00016 #include <vsl/vsl_indent.h>
00017 #include <vsl/vsl_binary_loader.h>
00018 #include <vsl/vsl_vector_io.h>
00019 
00020 //=======================================================================
00021 // Dflt ctor
00022 //=======================================================================
00023 
00024 mfpf_mr_point_finder::mfpf_mr_point_finder()
00025   : max_after_pruning_(0)
00026 {
00027 }
00028 
00029 
00030 //=======================================================================
00031 // Destructor
00032 //=======================================================================
00033 
00034 mfpf_mr_point_finder::~mfpf_mr_point_finder()
00035 {
00036 }
00037 
00038 //: Maximum number of candidates to retain during multi_search_and_prune
00039 //  If zero, then refine all.
00040 void mfpf_mr_point_finder::set_max_after_pruning(unsigned max_n)
00041 {
00042   max_after_pruning_=max_n;
00043 }
00044 
00045 //: Define point finders.  Clone of each taken
00046 void mfpf_mr_point_finder::set(const vcl_vector<mfpf_point_finder*>& finders)
00047 {
00048   finders_.resize(finders.size());
00049   for (unsigned i=0;i<finders.size();++i)
00050     finders_[i]=*finders[i];  // Clone taken by copy operator
00051 }
00052 
00053 //: Select best level for searching around pose with finder i
00054 //  Selects pyramid level with pixel sizes best matching
00055 //  the model pixel size at given pose.
00056 unsigned mfpf_mr_point_finder::image_level(
00057                       unsigned i, const mfpf_pose& pose,
00058                       const vimt_image_pyramid& im_pyr) const
00059 {
00060   return finder(i).image_level(pose,im_pyr);
00061 }
00062 
00063 // Find non-empty image in pyramid closest to given level
00064 static unsigned nearest_valid_level(const vimt_image_pyramid& im_pyr,
00065                                     unsigned level)
00066 {
00067   int L0=int(level);
00068   int bestL=0;
00069   int min_d2=999;
00070   for (int L=0;L<=im_pyr.hi();++L)
00071   {
00072     if (im_pyr(L).image_size()[0]>0)  // This level is not empty
00073     {
00074       int d2 = (L-L0)*(L-L0);
00075       if (d2<min_d2) { min_d2=d2; bestL=L; }
00076     }
00077   }
00078   return unsigned(bestL);
00079 }
00080 
00081 //: Get sample image at specified point for level L of the point_finder hierarchy
00082 void mfpf_mr_point_finder::get_sample_vector(
00083                         const vimt_image_pyramid& image_pyr,
00084                         const vgl_point_2d<double>& p,
00085                         const vgl_vector_2d<double>& u,
00086                         unsigned L,
00087                         vcl_vector<double>& v)
00088 {
00089   assert( L<finders_.size() );
00090 
00091   unsigned im_L = image_level(L,mfpf_pose(p,u),image_pyr);
00092 
00093   if (image_pyr(im_L).image_size()[0]==0)
00094   {
00095     vcl_cerr<<"Image at level "<<im_L<<" in pyramid has not been set up.\n"
00096             <<"This is required for level "<<L<<" of the mfpf model.\n"
00097             <<"Check range for which pyramid is defined.\n";
00098 
00099     im_L=nearest_valid_level(image_pyr,im_L);
00100     if (image_pyr(im_L).image_size()[0]==0)
00101     {
00102        vcl_cerr << "No image pyramid levels set up.\n";
00103        vcl_abort();
00104     }
00105   }
00106 
00107   assert(image_pyr(im_L).is_a()=="vimt_image_2d_of<float>");
00108   const vimt_image_2d_of<float>& image
00109     = static_cast<const vimt_image_2d_of<float>&>(image_pyr(im_L));
00110 
00111   finders_[L]->get_sample_vector(image,p,u,v);
00112 }
00113 
00114 //: Searches around given pose, starting at coarsest model.
00115 //  Searches with coarsest model, and feeds best result into
00116 //  search for next model.  Result can be further improved
00117 //  by a call to refine()
00118 double mfpf_mr_point_finder::search(const vimt_image_pyramid& im_pyr,
00119                                     const mfpf_pose& pose0,
00120                                     mfpf_pose& best_pose)
00121 {
00122   mfpf_pose pose=pose0;
00123   double fit = 9e99; // initialize to a "bad" value; in case iteration is empty
00124 
00125   // First search at coarsest level
00126   for (int L = size()-1; L>=0;--L) // use int 'cos unsigned always>0!
00127   {
00128     unsigned im_L = image_level(L,pose0,im_pyr);
00129     assert(im_pyr(im_L).is_a()=="vimt_image_2d_of<float>");
00130     const vimt_image_2d_of<float>& image
00131       = static_cast<const vimt_image_2d_of<float>&>(im_pyr(im_L));
00132     fit = finder(L).search_with_opt(image,pose.p(),pose.u(),
00133                            best_pose.p(),best_pose.u());
00134     pose=best_pose;
00135   }
00136 
00137   return fit;
00138 }
00139 
00140 //: Searches around given pose, starting at coarsest model.
00141 //  Searches with finder(L_hi) and feeds best result into
00142 //  search for next model, until level L_lo.
00143 //  Result can be further improved by a call to refine_match()
00144 double mfpf_mr_point_finder::mr_search(
00145                    const vimt_image_pyramid& im_pyr,
00146                    mfpf_pose& pose, int L_lo, int L_hi)
00147 {
00148   mfpf_pose pose0=pose;
00149   double fit = 9e99; // initialize to a "bad" value; in case iteration is empty
00150 
00151   assert(L_hi>=L_lo);
00152 
00153   // First search at coarsest level
00154   for (int L = L_hi; L>=L_lo;--L) // use int 'cos unsigned is always >= 0!
00155   {
00156     unsigned im_L = image_level(L,pose0,im_pyr);
00157     assert(im_pyr(im_L).is_a()=="vimt_image_2d_of<float>");
00158     const vimt_image_2d_of<float>& image
00159       = static_cast<const vimt_image_2d_of<float>&>(im_pyr(im_L));
00160     fit = finder(L).search_with_opt(image,pose0.p(),pose0.u(),
00161                                     pose.p(),pose.u());
00162     pose0=pose;
00163   }
00164 
00165   return fit;
00166 }
00167 
00168 
00169 //: Perform local optimisation to refine position,scale and angle
00170 //  Uses finder(L) to do refinement.
00171 void mfpf_mr_point_finder::refine_match(
00172                   const vimt_image_pyramid& im_pyr,
00173                   mfpf_pose& pose, double& fit, unsigned L)
00174 {
00175   unsigned im_L = image_level(L,pose,im_pyr);
00176   assert(im_pyr(im_L).is_a()=="vimt_image_2d_of<float>");
00177   const vimt_image_2d_of<float>& image
00178     = static_cast<const vimt_image_2d_of<float>&>(im_pyr(im_L));
00179   finder(L).refine_match(image,pose.p(),pose.u(),fit);
00180 }
00181 
00182 //: Find all local optima at coarsest scale and search around each
00183 //  Runs search at coarsest resolution, to find all local optima.
00184 //  If multiple angles/scales considered, the there may be many
00185 //  nearby responses.
00186 //  Each candidate is then localised by searching at finer and
00187 //  finer resolutions.
00188 //  Final responses may be further improved with refine_match()
00189 void mfpf_mr_point_finder::multi_search(
00190                   const vimt_image_pyramid& im_pyr,
00191                   const mfpf_pose& pose0,
00192                   vcl_vector<mfpf_pose>& poses,
00193                   vcl_vector<double>& fits)
00194 {
00195   poses.resize(0); fits.resize(0);
00196 
00197   // Search for multiple responses at coarsest scale
00198   int L=size()-1;
00199   unsigned im_L = image_level(L,pose0,im_pyr);
00200   assert(im_pyr(im_L).is_a()=="vimt_image_2d_of<float>");
00201   const vimt_image_2d_of<float>& image
00202     = static_cast<const vimt_image_2d_of<float>&>(im_pyr(im_L));
00203   finder(L).multi_search(image,pose0.p(),pose0.u(),poses,fits);
00204 
00205   if (L==0) return;
00206 
00207   // Now search around each one
00208   for (unsigned i=0;i<poses.size();++i)
00209   {
00210     fits[i] = mr_search(im_pyr,poses[i],0,L-1);
00211   }
00212 }
00213 
00214 //: Find all non-overlapping local optima.
00215 //  Runs search at coarsest resolution, to find all local optima.
00216 //  If multiple angles/scales considered, the there may be many
00217 //  nearby responses.
00218 //  Each candidate is then localised by searching at finer and
00219 //  finer resolutions.
00220 //  After searching to level prune_level, overlapping responses
00221 //  are pruned (best match in any overlapping group is retained).
00222 //  prune_level is defined modulo size(), so -1 is equivalent to
00223 //  pruning at the coarsest level (size()-1).
00224 //  Final responses may be further improved with refine_match().
00225 void mfpf_mr_point_finder::multi_search_and_prune(
00226                     const vimt_image_pyramid& im_pyr,
00227                     const mfpf_pose& pose0,
00228                     vcl_vector<mfpf_pose>& poses,
00229                     vcl_vector<double>& fits,
00230                     int prune_level)
00231 {
00232   poses.resize(0); fits.resize(0);
00233 
00234   // Force prune_level into range [0,size()]
00235   prune_level=(prune_level+size())%size();
00236   if (prune_level<0) prune_level+=size();
00237 
00238   // Search for multiple responses at coarsest scale
00239   int L0=size()-1;
00240   unsigned im_L = image_level(L0,pose0,im_pyr);
00241   assert(im_pyr(im_L).is_a()=="vimt_image_2d_of<float>");
00242   const vimt_image_2d_of<float>& image
00243     = static_cast<const vimt_image_2d_of<float>&>(im_pyr(im_L));
00244   finder(L0).multi_search(image,pose0.p(),pose0.u(),poses,fits);
00245 
00246   if (poses.size()==0)
00247   {
00248     vcl_cerr<<"Warning: No poses returned by mfpf_point_finder\n";
00249     // Perform search to find single good point
00250     vgl_point_2d<double> new_p;
00251     double f = finder(L0).search_one_pose(image,pose0.p(),pose0.u(),new_p);
00252     poses.resize(1); poses[0]=mfpf_pose(new_p,pose0.u());
00253     fits.resize(1); fits[0]=f;
00254   }
00255 
00256   if (L0==prune_level)
00257     mfpf_prune_and_sort_overlaps(finder(L0),poses,fits,max_after_pruning_);
00258 //    mfpf_prune_overlaps(finder(L0),poses,fits);
00259 
00260   if (L0==0) return;
00261 
00262   for (int L=L0-1;L>=0;--L)
00263   {
00264     // Perform local search one each pose
00265     for (unsigned i=0;i<poses.size();++i)
00266     {
00267       fits[i] = mr_search(im_pyr,poses[i],L,L);
00268     }
00269 
00270     // Remove overlaps if we are at prune_level
00271     if (L==prune_level)
00272     {
00273       mfpf_prune_and_sort_overlaps(finder(L),poses,fits,max_after_pruning_);
00274 //      mfpf_prune_overlaps(finder(L),poses,fits);
00275     }
00276   }
00277 }
00278 
00279 //: Save an image summarising each model in the hierarchy
00280 //  Saves images to basepath_L0.png, basepath_L1.png ...
00281 void mfpf_mr_point_finder::save_images_of_models(const vcl_string& basepath) const
00282 {
00283   for (unsigned L=0;L<size();++L)
00284   {
00285     vcl_stringstream s;
00286     s<<basepath<<"_L"<<L<<".png";
00287     vimt_image_2d_of<vxl_byte> image;
00288     finder(L).get_image_of_model(image);
00289     if (vil_save(image.image(),s.str().c_str()))
00290       vcl_cout<<"Saved image to "<<s.str()<<vcl_endl;
00291     else
00292       vcl_cout<<"Failed to save image to "<<s.str()<<vcl_endl;
00293   }
00294 }
00295 
00296 
00297 //=======================================================================
00298 // Method: version_no
00299 //=======================================================================
00300 
00301 short mfpf_mr_point_finder::version_no() const
00302 {
00303   return 2;
00304 }
00305 
00306 
00307 //=======================================================================
00308 // Method: is_a
00309 //=======================================================================
00310 
00311 vcl_string mfpf_mr_point_finder::is_a() const
00312 {
00313   return vcl_string("mfpf_mr_point_finder");
00314 }
00315 
00316 //: Print class to os
00317 void mfpf_mr_point_finder::print_summary(vcl_ostream& os) const
00318 {
00319   os<<'\n';
00320   unsigned n=finders_.size();
00321   os<<vsl_indent()<<"n_finders: "<<n<<'\n';
00322   vsl_indent_inc(os);
00323   for (unsigned i=0;i<n;i++)
00324   {
00325     os<<vsl_indent()<<i<<") ";
00326     vsl_indent_inc(os);
00327     os<<finders_[i]<<'\n';
00328     vsl_indent_dec(os);
00329   }
00330   vsl_indent_dec(os);
00331 }
00332 
00333 //=======================================================================
00334 // Method: save
00335 //=======================================================================
00336 
00337 void mfpf_mr_point_finder::b_write(vsl_b_ostream& bfs) const
00338 {
00339   vsl_b_write(bfs,version_no());
00340   vsl_b_write(bfs,finders_.size());
00341   for (unsigned i=0;i<finders_.size();++i)
00342     vsl_b_write(bfs,finders_[i]);
00343   vsl_b_write(bfs,max_after_pruning_);
00344 }
00345 
00346 //=======================================================================
00347 // Method: load
00348 //=======================================================================
00349 
00350 void mfpf_mr_point_finder::b_read(vsl_b_istream& bfs)
00351 {
00352   if (!bfs) return;
00353   short version;
00354   vsl_b_read(bfs,version);
00355   unsigned n;
00356   switch (version)
00357   {
00358     case (1):
00359     case (2):
00360       vsl_b_read(bfs,n);
00361       finders_.resize(n);
00362       for (unsigned i=0;i<n;++i) vsl_b_read(bfs,finders_[i]);
00363       if (version==1) max_after_pruning_=0;
00364       else vsl_b_read(bfs,max_after_pruning_);
00365       break;
00366     default:
00367       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&)\n"
00368                << "           Unknown version number "<< version << vcl_endl;
00369       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00370       return;
00371   }
00372 }
00373 
00374 //=======================================================================
00375 // Associated function: operator<<
00376 //=======================================================================
00377 
00378 vcl_ostream& operator<<(vcl_ostream& os,const mfpf_mr_point_finder& b)
00379 {
00380   os << b.is_a() << ": ";
00381   vsl_indent_inc(os);
00382   b.print_summary(os);
00383   vsl_indent_dec(os);
00384   return os;
00385 }
00386 
00387 //: Binary file stream output operator for class reference
00388 void vsl_b_write(vsl_b_ostream& bfs, const mfpf_mr_point_finder& b)
00389 {
00390   b.b_write(bfs);
00391 }
00392 
00393 //: Binary file stream input operator for class reference
00394 void vsl_b_read(vsl_b_istream& bfs, mfpf_mr_point_finder& b)
00395 {
00396   b.b_read(bfs);
00397 }
00398 
00399