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