contrib/mul/mfpf/mfpf_region_pdf.cxx
Go to the documentation of this file.
00001 #include "mfpf_region_pdf.h"
00002 //:
00003 // \file
00004 // \brief Searches with a PDF of an arbitrary region
00005 // \author Tim Cootes
00006 
00007 #include <vsl/vsl_binary_loader.h>
00008 #include <vcl_cmath.h>
00009 #include <vcl_cassert.h>
00010 
00011 #include <vil/vil_resample_bilin.h>
00012 #include <vil/io/vil_io_image_view.h>
00013 #include <vsl/vsl_vector_io.h>
00014 #include <vsl/vsl_indent.h>
00015 #include <vnl/vnl_vector_ref.h>
00016 
00017 #include <vgl/vgl_point_2d.h>
00018 #include <vgl/vgl_vector_2d.h>
00019 #include <mfpf/mfpf_sample_region.h>
00020 #include <mfpf/mfpf_norm_vec.h>
00021 
00022 //=======================================================================
00023 // Dflt ctor
00024 //=======================================================================
00025 
00026 mfpf_region_pdf::mfpf_region_pdf()
00027 {
00028   set_defaults();
00029 }
00030 
00031 //: Define default values
00032 void mfpf_region_pdf::set_defaults()
00033 {
00034   step_size_=1.0;
00035   search_ni_=5;
00036   search_nj_=5;
00037   nA_=0; dA_=0.0;
00038   ns_=0; ds_=1.0;
00039   n_pixels_=0;
00040   roi_.resize(0);
00041   roi_ni_=0;
00042   roi_nj_=0;
00043   ref_x_=0;
00044   ref_y_=0;
00045   norm_method_=1;
00046   overlap_f_=1.0;
00047 }
00048 
00049 //=======================================================================
00050 // Destructor
00051 //=======================================================================
00052 
00053 mfpf_region_pdf::~mfpf_region_pdf()
00054 {
00055 }
00056 
00057 //: Define region and PDF of region
00058 void mfpf_region_pdf::set(const vcl_vector<mbl_chord>& roi,
00059                           double ref_x, double ref_y,
00060                           const vpdfl_pdf_base& pdf,
00061                           short norm_method)
00062 {
00063   pdf_ = pdf.clone();
00064   ref_x_ = ref_x;
00065   ref_y_ = ref_y;
00066 
00067   // Check bounding box
00068   if (roi.size()==0) { roi_ni_=0; roi_nj_=0; return; }
00069   int ilo=roi[0].start_x(), ihi=roi[0].end_x();
00070   int jlo=roi[0].y(), jhi=roi[0].y();
00071 
00072   for (unsigned k=1;k<roi.size();++k)
00073   {
00074     if (roi[k].start_x()<ilo) ilo=roi[k].start_x();
00075     if (roi[k].end_x()>ihi)   ihi=roi[k].end_x();
00076     if (roi[k].y()<jlo) jlo=roi[k].y();
00077     if (roi[k].y()>jhi) jhi=roi[k].y();
00078   }
00079   roi_ni_=1+ihi-ilo;
00080   roi_nj_=1+jhi-jlo;
00081 
00082   // Apply offset of (-ilo,-jlo) to ensure bounding box is +ive
00083   ref_x_-=ilo; ref_y_-=jlo;
00084   roi_.resize(roi.size());
00085   n_pixels_=0;
00086   for (unsigned k=0;k<roi.size();++k)
00087   {
00088     roi_[k]= mbl_chord(roi[k].start_x()-ilo,
00089                        roi[k].end_x()-ilo,   roi[k].y()-jlo);
00090     n_pixels_+=1+roi[k].end_x()-roi[k].start_x();
00091   }
00092 
00093   assert(norm_method>=0 && norm_method<=1);
00094   norm_method_ = norm_method;
00095 }
00096 
00097 //: Relative size of region used for estimating overlap
00098 //  If 0.5, then overlap requires pt inside central 50% of region.
00099 void mfpf_region_pdf::set_overlap_f(double f)
00100 {
00101   overlap_f_=f;
00102 }
00103 
00104 
00105 //: Radius of circle containing modelled region
00106 double mfpf_region_pdf::radius() const
00107 {
00108   // Compute distance to each corner
00109   double wx = roi_ni_-1;
00110   double x2 = vcl_max(ref_x_*ref_x_,(ref_x_-wx)*(ref_x_-wx));
00111   double wy = roi_nj_-1;
00112   double y2 = vcl_max(ref_y_*ref_y_,(ref_y_-wy)*(ref_y_-wy));
00113   double r2 = x2+y2;
00114   if (r2<=1) return 1.0;
00115   return vcl_sqrt(r2);
00116 }
00117 
00118 
00119 //: Evaluate match at p, using u to define scale and orientation
00120 // Returns -1*edge strength at p along direction u
00121 double mfpf_region_pdf::evaluate(const vimt_image_2d_of<float>& image,
00122                                  const vgl_point_2d<double>& p,
00123                                  const vgl_vector_2d<double>& u)
00124 {
00125   vgl_vector_2d<double> u1=step_size_*u;
00126   vgl_vector_2d<double> v1(-u1.y(),u1.x());
00127 
00128   unsigned np=image.image().nplanes();
00129   // Set up sample area with interleaved planes (ie planestep==1)
00130   vil_image_view<float> sample(roi_ni_,roi_nj_,1,np);
00131 
00132   const vgl_point_2d<double> p0 = p-ref_x_*u1-ref_y_*v1;
00133 
00134   const vimt_transform_2d& s_w2i = image.world2im();
00135   vgl_point_2d<double> im_p0 = s_w2i(p0);
00136   vgl_vector_2d<double> im_u = s_w2i.delta(p0, u1);
00137   vgl_vector_2d<double> im_v = s_w2i.delta(p0, v1);
00138 
00139   vil_resample_bilin(image.image(),sample,
00140                      im_p0.x(),im_p0.y(),  im_u.x(),im_u.y(),
00141                      im_v.x(),im_v.y(),
00142                      roi_ni_,roi_nj_);
00143 
00144   vnl_vector<double> v(n_pixels_*sample.nplanes());
00145   mfpf_sample_region(sample.top_left_ptr(),sample.jstep(),
00146                      np,roi_,v);
00147 
00148   if (norm_method_==1) mfpf_norm_vec(v);
00149   return -1*pdf().log_p(v);
00150 }
00151 
00152 //: Evaluate match at in a region around p
00153 // Returns a quality of fit at a set of positions.
00154 // response image (whose size and transform is set inside the
00155 // function), indicates the points at which the function was
00156 // evaluated.  response(i,j) is the fit at the point
00157 // response.world2im().inverse()(i,j).  The world2im() transformation
00158 // may be affine.
00159 void mfpf_region_pdf::evaluate_region(
00160                         const vimt_image_2d_of<float>& image,
00161                         const vgl_point_2d<double>& p,
00162                         const vgl_vector_2d<double>& u,
00163                         vimt_image_2d_of<double>& response)
00164 {
00165   int ni=1+2*search_ni_;
00166   int nj=1+2*search_nj_;
00167   int nsi = 2*search_ni_ + roi_ni_;
00168   int nsj = 2*search_nj_ + roi_nj_;
00169 
00170   unsigned np=image.image().nplanes();
00171   // Set up sample area with interleaved planes (ie planestep==1)
00172   vil_image_view<float> sample(nsi,nsj,1,np);
00173   vgl_vector_2d<double> u1=step_size_*u;
00174   vgl_vector_2d<double> v1(-u1.y(),u1.x());
00175   const vgl_point_2d<double> p0 = p-(search_ni_+ref_x_)*u1
00176                                    -(search_nj_+ref_y_)*v1;
00177 
00178   const vimt_transform_2d& s_w2i = image.world2im();
00179   vgl_point_2d<double> im_p0 = s_w2i(p0);
00180   vgl_vector_2d<double> im_u = s_w2i.delta(p0, u1);
00181   vgl_vector_2d<double> im_v = s_w2i.delta(p0, v1);
00182 
00183   vil_resample_bilin(image.image(),sample,
00184                      im_p0.x(),im_p0.y(),  im_u.x(),im_u.y(),
00185                      im_v.x(),im_v.y(),
00186                      nsi,nsj);
00187 
00188   vnl_vector<double> v(n_pixels_*np);
00189 
00190   response.image().set_size(ni,nj);
00191   double* r = response.image().top_left_ptr();
00192   const float* s = sample.top_left_ptr();
00193   vcl_ptrdiff_t r_jstep = response.image().jstep();
00194   vcl_ptrdiff_t s_jstep = sample.jstep();
00195 
00196   for (unsigned j=0;j<(unsigned)nj;++j,r+=r_jstep,s+=s_jstep)
00197   {
00198     for (int i=0;i<ni;++i)
00199     {
00200       mfpf_sample_region(s+i*np,s_jstep,np,roi_,v);
00201       if (norm_method_==1) mfpf_norm_vec(v);
00202       r[i] = -1*pdf().log_p(v);
00203     }
00204   }
00205 
00206   // Set up transformation parameters
00207 
00208   // Point (i,j) in dest corresponds to p1+i.u+j.v,
00209   // an affine transformation for image to world
00210   const vgl_point_2d<double> p1 = p-search_ni_*u1-search_nj_*v1;
00211 
00212   vimt_transform_2d i2w;
00213   i2w.set_similarity(vgl_point_2d<double>(u1.x(),u1.y()),p1);
00214   response.set_world2im(i2w.inverse());
00215 }
00216 
00217    //: Search given image around p, using u to define scale and orientation
00218    //  On exit, new_p and new_u define position, scale and orientation of
00219    //  the best nearby match.  Returns a quality of fit measure at that
00220    //  point (the smaller the better).
00221 double mfpf_region_pdf::search_one_pose(const vimt_image_2d_of<float>& image,
00222                                         const vgl_point_2d<double>& p,
00223                                         const vgl_vector_2d<double>& u,
00224                                         vgl_point_2d<double>& new_p)
00225 {
00226   int ni=1+2*search_ni_;
00227   int nj=1+2*search_nj_;
00228   int nsi = 2*search_ni_ + roi_ni_;
00229   int nsj = 2*search_nj_ + roi_nj_;
00230 
00231   unsigned np=image.image().nplanes();
00232   // Set up sample area with interleaved planes (ie planestep==1)
00233   vil_image_view<float> sample(nsi,nsj,1,np);
00234   vgl_vector_2d<double> u1=step_size_*u;
00235   vgl_vector_2d<double> v1(-u1.y(),u1.x());
00236   const vgl_point_2d<double> p0 = p-(search_ni_+ref_x_)*u1
00237                                    -(search_nj_+ref_y_)*v1;
00238 
00239   const vimt_transform_2d& s_w2i = image.world2im();
00240   vgl_point_2d<double> im_p0 = s_w2i(p0);
00241   vgl_vector_2d<double> im_u = s_w2i.delta(p0, u1);
00242   vgl_vector_2d<double> im_v = s_w2i.delta(p0, v1);
00243 
00244   vil_resample_bilin(image.image(),sample,
00245                      im_p0.x(),im_p0.y(),  im_u.x(),im_u.y(),
00246                      im_v.x(),im_v.y(),
00247                      nsi,nsj);
00248 
00249   vnl_vector<double> v(n_pixels_*np);
00250 
00251   const float* s = sample.top_left_ptr();
00252   vcl_ptrdiff_t s_jstep = sample.jstep();
00253 
00254   double best_r=9.99e9;
00255   int best_i=0,best_j=0;
00256   for (unsigned j=0;j<(unsigned)nj;++j,s+=s_jstep)
00257   {
00258     for (int i=0;i<ni;++i)
00259     {
00260       mfpf_sample_region(s+i*np,s_jstep,np,roi_,v);
00261       if (norm_method_==1) mfpf_norm_vec(v);
00262       double r = -1*pdf().log_p(v);
00263       if (r<best_r) { best_r=r; best_i=i; best_j=j; }
00264     }
00265   }
00266 
00267   // Compute position of best point
00268   new_p = p+(best_i-search_ni_)*u1+(best_j-search_nj_)*v1;
00269   return best_r;
00270 }
00271 
00272 // Returns true if p is inside region at given pose
00273 bool mfpf_region_pdf::is_inside(const mfpf_pose& pose,
00274                                 const vgl_point_2d<double>& p,
00275                                 double f) const
00276 {
00277   // Set transform model frame -> World
00278   vimt_transform_2d t1;
00279   t1.set_similarity(step_size()*pose.u(),pose.p());
00280   // Compute position of p in model frame
00281   vgl_point_2d<double> q=t1.inverse()(p);
00282   q.x()/=f; q.y()/=f;  // To check that q in the central fraction f
00283   q.x()+=ref_x_;
00284   if (q.x()<0 || q.x()>(roi_ni_-1)) return false;
00285   q.y()+=ref_y_;
00286   if (q.y()<0 || q.y()>(roi_nj_-1)) return false;
00287   return true;
00288 }
00289 
00290 //: Return true if modelled regions at pose1 and pose2 overlap
00291 //  Checks if reference point of one is inside region of other
00292 bool mfpf_region_pdf::overlap(const mfpf_pose& pose1,
00293                               const mfpf_pose& pose2) const
00294 {
00295   if (is_inside(pose1,pose2.p(),overlap_f_)) return true;
00296   if (is_inside(pose2,pose1.p(),overlap_f_)) return true;
00297   return false;
00298 }
00299 
00300 //: Generate points in ref frame that represent boundary
00301 //  Points of a contour around the shape.
00302 //  Used for display purposes.
00303 void mfpf_region_pdf::get_outline(vcl_vector<vgl_point_2d<double> >& pts) const
00304 {
00305   pts.resize(7);
00306   vgl_vector_2d<double> r(ref_x_,ref_y_);
00307   pts[0]=vgl_point_2d<double>(0,roi_nj_)-r;
00308   pts[1]=vgl_point_2d<double>(0,0);
00309   pts[2]=vgl_point_2d<double>(roi_ni_,roi_nj_)-r;
00310   pts[3]=vgl_point_2d<double>(0,roi_nj_)-r;
00311   pts[4]=vgl_point_2d<double>(0,0)-r;
00312   pts[5]=vgl_point_2d<double>(roi_ni_,0)-r;
00313   pts[6]=vgl_point_2d<double>(roi_ni_,roi_nj_)-r;
00314 }
00315 
00316 //: Return an image of the region of interest
00317 void mfpf_region_pdf::get_image_of_model(vimt_image_2d_of<vxl_byte>& image) const
00318 {
00319   vnl_vector<double> mean = pdf().mean();
00320 
00321   assert(mean.size()>=n_pixels_);
00322 
00323   // Just copy first plane
00324   vnl_vector_ref<double> mean1(n_pixels_,mean.data_block());
00325   double min1=mean1.min_value();
00326   double max1=mean1.max_value();
00327   double s =255/(max1-min1);
00328   image.image().set_size(roi_ni_,roi_nj_);
00329   image.image().fill(0);
00330   unsigned q=0;
00331   for (unsigned k=0;k<roi_.size();++k)
00332   {
00333     for (int i=roi_[k].start_x();i<=roi_[k].end_x();++i,++q)
00334       image.image()(i,roi_[k].y())=vxl_byte(s*(mean[q]-min1));
00335   }
00336   vimt_transform_2d ref2im;
00337   ref2im.set_zoom_only(1.0/step_size_,ref_x_,ref_y_);
00338   image.set_world2im(ref2im);
00339 }
00340 
00341 //=======================================================================
00342 // Method: is_a
00343 //=======================================================================
00344 
00345 vcl_string mfpf_region_pdf::is_a() const
00346 {
00347   return vcl_string("mfpf_region_pdf");
00348 }
00349 
00350 //: Create a copy on the heap and return base class pointer
00351 mfpf_point_finder* mfpf_region_pdf::clone() const
00352 {
00353   return new mfpf_region_pdf(*this);
00354 }
00355 
00356 //=======================================================================
00357 // Method: print
00358 //=======================================================================
00359 
00360 void mfpf_region_pdf::print_summary(vcl_ostream& os) const
00361 {
00362   os << "{  size: "<<roi_ni_<<" x "<<roi_nj_
00363      << " n_pixels: "<<n_pixels_
00364      << " ref_pt: ("<<ref_x_<<','<<ref_y_<<')'<<'\n';
00365   vsl_indent_inc(os);
00366   if (norm_method_==0) os<<vsl_indent()<<"norm: none"<<'\n';
00367   else                 os<<vsl_indent()<<"norm: linear"<<'\n';
00368   os <<vsl_indent()<< "PDF: ";
00369   if (pdf_.ptr()==0) os << "--"<<vcl_endl; else os << pdf_<<'\n';
00370   os<<vsl_indent();
00371   mfpf_point_finder::print_summary(os);
00372   os <<vcl_endl <<vsl_indent()<<"overlap_f: "<<overlap_f_<<'\n';
00373   vsl_indent_dec(os);
00374   os<<vsl_indent()<<'}';
00375 }
00376 
00377 void mfpf_region_pdf::print_shape(vcl_ostream& os) const
00378 {
00379   vil_image_view<vxl_byte> im(roi_ni_,roi_nj_);
00380   im.fill(0);
00381   for (unsigned k=0;k<roi_.size();++k)
00382     for (int i=roi_[k].start_x();i<=roi_[k].end_x();++i)
00383       im(i,roi_[k].y())=1;
00384   for (unsigned j=0;j<im.nj();++j)
00385   {
00386     for (unsigned i=0;i<im.ni();++i)
00387       if (im(i,j)==0) os<<' ';
00388       else            os<<'X';
00389     os<<'\n';
00390   }
00391 }
00392 
00393 short mfpf_region_pdf::version_no() const
00394 {
00395   return 1;
00396 }
00397 
00398 
00399 void mfpf_region_pdf::b_write(vsl_b_ostream& bfs) const
00400 {
00401   vsl_b_write(bfs,version_no());
00402   mfpf_point_finder::b_write(bfs);  // Save base class
00403   vsl_b_write(bfs,roi_);
00404   vsl_b_write(bfs,roi_ni_);
00405   vsl_b_write(bfs,roi_nj_);
00406   vsl_b_write(bfs,n_pixels_);
00407   vsl_b_write(bfs,pdf_);
00408   vsl_b_write(bfs,ref_x_);
00409   vsl_b_write(bfs,ref_y_);
00410   vsl_b_write(bfs,norm_method_);
00411   vsl_b_write(bfs,overlap_f_);
00412 }
00413 
00414 //=======================================================================
00415 // Method: load
00416 //=======================================================================
00417 
00418 void mfpf_region_pdf::b_read(vsl_b_istream& bfs)
00419 {
00420   if (!bfs) return;
00421   short version;
00422   vsl_b_read(bfs,version);
00423   switch (version)
00424   {
00425     case (1):
00426     case (2):
00427       mfpf_point_finder::b_read(bfs);  // Load in base class
00428       vsl_b_read(bfs,roi_);
00429       vsl_b_read(bfs,roi_ni_);
00430       vsl_b_read(bfs,roi_nj_);
00431       vsl_b_read(bfs,n_pixels_);
00432       vsl_b_read(bfs,pdf_);
00433       vsl_b_read(bfs,ref_x_);
00434       vsl_b_read(bfs,ref_y_);
00435       vsl_b_read(bfs,norm_method_);
00436       if (version==1) overlap_f_=1.0;
00437       else            vsl_b_read(bfs,overlap_f_);
00438       break;
00439     default:
00440       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&)\n"
00441                << "           Unknown version number "<< version << vcl_endl;
00442       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00443       return;
00444   }
00445 }
00446 
00447 //: Test equality
00448 bool mfpf_region_pdf::operator==(const mfpf_region_pdf& nc) const
00449 {
00450   if (!base_equality(nc)) return false;
00451   if (roi_ni_!=nc.roi_ni_) return false;
00452   if (roi_nj_!=nc.roi_nj_) return false;
00453   if (norm_method_!=nc.norm_method_) return false;
00454   if (n_pixels_!=nc.n_pixels_) return false;
00455   if (vcl_fabs(ref_x_-nc.ref_x_)>1e-6) return false;
00456   if (vcl_fabs(ref_y_-nc.ref_y_)>1e-6) return false;
00457   // Strictly should compare PDFs
00458   return true;
00459 }
00460 
00461