contrib/mul/mfpf/mfpf_region_finder_builder.cxx
Go to the documentation of this file.
00001 #include "mfpf_region_finder_builder.h"
00002 //:
00003 // \file
00004 // \brief Builder for mfpf_region_finder objects.
00005 // \author Tim Cootes
00006 
00007 #include <mfpf/mfpf_region_finder.h>
00008 #include <vsl/vsl_binary_loader.h>
00009 #include <vul/vul_string.h>
00010 #include <vcl_cmath.h>
00011 #include <vcl_sstream.h>
00012 #include <vcl_cassert.h>
00013 
00014 #include <mbl/mbl_parse_block.h>
00015 #include <mbl/mbl_read_props.h>
00016 #include <mbl/mbl_exception.h>
00017 
00018 #include <vil/vil_resample_bilin.h>
00019 #include <vsl/vsl_vector_io.h>
00020 #include <vsl/vsl_indent.h>
00021 
00022 #include <vgl/vgl_point_2d.h>
00023 #include <vgl/vgl_vector_2d.h>
00024 #include <mfpf/mfpf_sample_region.h>
00025 #include <mfpf/mfpf_norm_vec.h>
00026 
00027 //=======================================================================
00028 // Dflt ctor
00029 //=======================================================================
00030 
00031 mfpf_region_finder_builder::mfpf_region_finder_builder()
00032 {
00033   set_defaults();
00034 }
00035 
00036 //: Define default values
00037 void mfpf_region_finder_builder::set_defaults()
00038 {
00039   step_size_=1.0;
00040   search_ni_=5;
00041   search_nj_=5;
00042   n_pixels_=0;
00043   roi_.resize(0);
00044   roi_ni_=0;
00045   roi_nj_=0;
00046   ref_x_=0;
00047   ref_y_=0;
00048   nA_=0;
00049   dA_=0.0;
00050   norm_method_=1;
00051   overlap_f_=1.0;
00052   var_min_ = 1.0E-6;
00053   tvar_min_=1.0E30;
00054   num_examples_=0;
00055   estimate_var_min_=true;
00056 }
00057 
00058 //=======================================================================
00059 // Destructor
00060 //=======================================================================
00061 
00062 mfpf_region_finder_builder::~mfpf_region_finder_builder()
00063 {
00064 }
00065 
00066 //: Create new mfpf_region_finder on heap
00067 mfpf_point_finder* mfpf_region_finder_builder::new_finder() const
00068 {
00069   return new mfpf_region_finder();
00070 }
00071 
00072 //: Define model region using description in form
00073 //  Assumes form defined in world-coords.
00074 //  Assumes step_size() pixel units (ie dimensions
00075 //  are divided by step_size() to map to reference frame).
00076 void mfpf_region_finder_builder::set_region(const mfpf_region_form& form)
00077 {
00078   step_size_ = form.pose().scale();
00079 
00080   if (form.form()=="box")
00081   {
00082     int ni = vcl_max(1,int(0.99+form.wi()));
00083     int nj = vcl_max(1,int(0.99+form.wj()));
00084     set_as_box(unsigned(ni),unsigned(nj),0.5*ni,0.5*nj);
00085   }
00086   else
00087   if (form.form()=="ellipse")
00088   {
00089     double ri = vcl_max(1.0,form.wi());
00090     double rj = vcl_max(1.0,form.wj());
00091     set_as_ellipse(ri,rj);
00092   }
00093   else
00094   {
00095     vcl_cerr<<"mfpf_region_finder_builder::set_region : Unknown form: "<<form<<vcl_endl;
00096     vcl_abort();
00097   }
00098 }
00099 
00100 //: Define region size in world co-ordinates
00101 //  Sets up ROI to cover given box (with samples at step_size()),
00102 //  with ref point at centre.
00103 void mfpf_region_finder_builder::set_region_size(double wi, double wj)
00104 {
00105   wi/=step_size();
00106   wj/=step_size();
00107   int ni = vcl_max(1,int(0.99+wi));
00108   int nj = vcl_max(1,int(0.99+wj));
00109   set_as_box(unsigned(ni),unsigned(nj),0.5*(ni-1),0.5*(nj-1));
00110 }
00111 
00112 
00113 //: Define model region as an ni x nj box
00114 void mfpf_region_finder_builder::set_as_box(unsigned ni, unsigned nj,
00115                                             double ref_x, double ref_y,
00116                                             const mfpf_vec_cost_builder& builder)
00117 {
00118   set_as_box(ni,nj,ref_x,ref_y);
00119   cost_builder_ = builder.clone();
00120 }
00121 
00122 //: Define model region as an ni x nj box
00123 void mfpf_region_finder_builder::set_as_box(unsigned ni, unsigned nj,
00124                                             double ref_x, double ref_y)
00125 {
00126   roi_ni_=ni; roi_nj_=nj;
00127   n_pixels_ = ni*nj;
00128 
00129   // Set ROI to be a box
00130   roi_.resize(nj);
00131   for (unsigned j=0;j<nj;++j) roi_[j]=mbl_chord(0,ni-1,j);
00132 
00133   ref_x_=ref_x;
00134   ref_y_=ref_y;
00135 }
00136 
00137 
00138 //: Define model region as an ni x nj box
00139 void mfpf_region_finder_builder::set_as_box(unsigned ni, unsigned nj,
00140                                             const mfpf_vec_cost_builder& builder)
00141 {
00142   set_as_box(ni,nj, 0.5*(ni-1),0.5*(nj-1), builder);
00143 }
00144 
00145 //: Define model region as an ellipse with radii ri, rj
00146 //  Ref. point in centre.
00147 void mfpf_region_finder_builder::set_as_ellipse(double ri, double rj,
00148                                                 const mfpf_vec_cost_builder& builder)
00149 {
00150   set_as_ellipse(ri,rj);
00151   cost_builder_ = builder.clone();
00152 }
00153 
00154 //: Define model region as an ellipse with radii ri, rj
00155 //  Ref. point in centre.
00156 void mfpf_region_finder_builder::set_as_ellipse(double ri, double rj)
00157 {
00158   ri+=1e-6; rj+=1e-6;
00159   int ni=int(ri);
00160   int nj=int(rj);
00161   roi_.resize(0);
00162   n_pixels_=0;
00163   for (int j = -nj;j<=nj;++j)
00164   {
00165     // Find start and end of line of pixels inside disk
00166     int x = int(ri*vcl_sqrt(1.0-j*j/(rj*rj)));
00167     roi_.push_back(mbl_chord(ni-x,ni+x,nj+j));
00168     n_pixels_+=2*x+1;
00169   }
00170 
00171   ref_x_=ni;
00172   ref_y_=nj;
00173   roi_ni_=2*ni+1;
00174   roi_nj_=2*nj+1;
00175 }
00176 
00177 //: Number of dimensions in the model
00178 unsigned mfpf_region_finder_builder::model_dim()
00179 {
00180   return n_pixels_;
00181 }
00182 
00183 //: Initialise building
00184 // Must be called before any calls to add_example(...)
00185 void mfpf_region_finder_builder::clear(unsigned n_egs)
00186 {
00187   unsigned n_per_eg = (1+2*nA_);
00188   cost_builder().clear(n_egs*n_per_eg);
00189 }
00190 
00191 //: Add one example to the model
00192 void mfpf_region_finder_builder::add_one_example(
00193                  const vimt_image_2d_of<float>& image,
00194                  const vgl_point_2d<double>& p,
00195                  const vgl_vector_2d<double>& u)
00196 {
00197   vgl_vector_2d<double> u1=step_size_*u;
00198   vgl_vector_2d<double> v1(-u1.y(),u1.x());
00199 
00200   unsigned np=image.image().nplanes();
00201   // Set up sample area with interleaved planes (ie planestep==1)
00202   vil_image_view<float> sample(roi_ni_,roi_nj_,1,np);
00203 
00204   const vgl_point_2d<double> p0 = p-ref_x_*u1-ref_y_*v1;
00205 
00206   const vimt_transform_2d& s_w2i = image.world2im();
00207   vgl_point_2d<double> im_p0 = s_w2i(p0);
00208   vgl_vector_2d<double> im_u = s_w2i.delta(p0, u1);
00209   vgl_vector_2d<double> im_v = s_w2i.delta(p0, v1);
00210 
00211   vil_resample_bilin(image.image(),sample,
00212                      im_p0.x(),im_p0.y(),  im_u.x(),im_u.y(),
00213                      im_v.x(),im_v.y(),roi_ni_,roi_nj_);
00214 
00215   vnl_vector<double> v(n_pixels_*sample.nplanes());
00216   mfpf_sample_region(sample.top_left_ptr(),sample.jstep(),
00217                      np,roi_,v);
00218 
00219   if (norm_method_==1)
00220   {
00221       double var=var_min_;
00222       mfpf_norm_vec(v,var_min_,&var);
00223       if (var<tvar_min_)
00224           tvar_min_ = var;
00225   }
00226   cost_builder().add_example(v);
00227   ++num_examples_;
00228 }
00229 
00230 //: Add one example to the model
00231 void mfpf_region_finder_builder::add_example(const vimt_image_2d_of<float>& image,
00232                                              const vgl_point_2d<double>& p,
00233                                              const vgl_vector_2d<double>& u)
00234 {
00235   if (nA_==0)
00236   {
00237     add_one_example(image,p,u);
00238     return;
00239   }
00240 
00241   vgl_vector_2d<double> v(-u.y(),u.x());
00242   for (int iA=-int(nA_);iA<=(int)nA_;++iA)
00243   {
00244     double A = iA*dA_;
00245     vgl_vector_2d<double> uA = u*vcl_cos(A)+v*vcl_sin(A);
00246     add_one_example(image,p,uA);
00247   }
00248 }
00249 
00250 //: Build this object from the data supplied in add_example()
00251 void mfpf_region_finder_builder::build(mfpf_point_finder& pf)
00252 {
00253   assert(pf.is_a()=="mfpf_region_finder");
00254   mfpf_region_finder& rp = static_cast<mfpf_region_finder&>(pf);
00255 
00256   mfpf_vec_cost *cost = cost_builder().new_cost();
00257 
00258   cost_builder().build(*cost);
00259 
00260   rp.set(roi_,ref_x_,ref_y_,*cost,norm_method_);
00261   set_base_parameters(rp);
00262   rp.set_overlap_f(overlap_f_);
00263 
00264   if (estimate_var_min_ && norm_method_==1 && num_examples_>0)
00265   {
00266     //Assume applied var_min is r* min in training set, where r->1 as n->infinity
00267     //Set r=0.98 for n around 50
00268     double dn=double(num_examples_);
00269     if (dn>0.0)
00270     {
00271       double r=0.925; //so r attains 0.98 around n=50
00272       double alpha=1.0;
00273       if (dn>50.0)
00274         alpha=0.98;
00275       else
00276       {
00277         alpha=1-vcl_pow(r,dn);
00278         alpha=vcl_min(alpha,0.98);
00279       }
00280       tvar_min_ *= alpha;
00281       var_min_ = vcl_max(var_min_,tvar_min_);
00282     }
00283     rp.set_var_min(var_min_);
00284   }
00285 
00286   // Tidy up
00287   delete cost;
00288 }
00289 
00290 //: Parse stream to set up as a box shape.
00291 // Expects: "{ ni: 3 nj: 5 ref_x: 1.0 ref_y: 2.0 }
00292 void mfpf_region_finder_builder::config_as_box(vcl_istream &is)
00293 {
00294   // Cycle through string and produce a map of properties
00295   vcl_string s = mbl_parse_block(is);
00296   vcl_istringstream ss(s);
00297   mbl_read_props_type props = mbl_read_props_ws(ss);
00298 
00299   unsigned ni=5,nj=5;
00300 
00301   if (props.find("ni")!=props.end())
00302   {
00303     ni=vul_string_atoi(props["ni"]);
00304     props.erase("ni");
00305   }
00306   if (props.find("nj")!=props.end())
00307   {
00308     nj=vul_string_atoi(props["nj"]);
00309     props.erase("nj");
00310   }
00311 
00312   if (props.find("ref_x")!=props.end())
00313   {
00314     ref_x_=vul_string_atof(props["ref_x"]);
00315     props.erase("ref_x");
00316   }
00317   else ref_x_=0.5*(ni-1.0);
00318 
00319   if (props.find("ref_y")!=props.end())
00320   {
00321     ref_y_=vul_string_atof(props["ref_y"]);
00322     props.erase("ref_y");
00323   }
00324   else ref_y_=0.5*(nj-1.0);
00325 
00326   // Check for unused props
00327   mbl_read_props_look_for_unused_props(
00328       "mfpf_region_finder_builder::config_as_box",
00329       props, mbl_read_props_type());
00330 
00331   set_as_box(ni,nj,ref_x_,ref_y_);
00332 }
00333 
00334 //: Parse stream to set up as an ellipse shape.
00335 // Expects: "{ ri: 2.1 rj: 5.2 }
00336 void mfpf_region_finder_builder::config_as_ellipse(vcl_istream &is)
00337 {
00338   // Cycle through string and produce a map of properties
00339   vcl_string s = mbl_parse_block(is);
00340   vcl_istringstream ss(s);
00341   mbl_read_props_type props = mbl_read_props_ws(ss);
00342 
00343   double ri=3.1,rj=3.1;
00344   if (props.find("ri")!=props.end())
00345   {
00346     ri=vul_string_atof(props["ri"]);
00347     props.erase("ri");
00348   }
00349 
00350   if (props.find("rj")!=props.end())
00351   {
00352     rj=vul_string_atof(props["rj"]);
00353     props.erase("rj");
00354   }
00355 
00356   // Check for unused props
00357   mbl_read_props_look_for_unused_props(
00358       "mfpf_region_finder_builder::config_as_ellipse",
00359       props, mbl_read_props_type());
00360 
00361   set_as_ellipse(ri,rj);
00362 }
00363 
00364 
00365 //=======================================================================
00366 // Method: set_from_stream
00367 //=======================================================================
00368 //: Initialise from a string stream
00369 bool mfpf_region_finder_builder::set_from_stream(vcl_istream &is)
00370 {
00371   // Cycle through string and produce a map of properties
00372   vcl_string s = mbl_parse_block(is);
00373   vcl_istringstream ss(s);
00374   mbl_read_props_type props = mbl_read_props_ws(ss);
00375 
00376   set_defaults();
00377 
00378   // Extract the properties
00379   parse_base_props(props);
00380 
00381   if (props.find("shape")!=props.end())
00382   {
00383     vcl_istringstream shape_s(props["shape"]);
00384     shape_s>>shape_;
00385     if (shape_=="box")
00386     {
00387       // Parse parameters after box
00388       config_as_box(shape_s);
00389     }
00390     else
00391     if (shape_=="ellipse")
00392     {
00393       // Parse parameters after ellipse
00394       config_as_ellipse(shape_s);
00395     }
00396     else throw mbl_exception_parse_error("Unknown shape: "+shape_);
00397 
00398     props.erase("shape");
00399   }
00400 
00401   if (props.find("norm")!=props.end())
00402   {
00403     if (props["norm"]=="none") norm_method_=0;
00404     else
00405     if (props["norm"]=="linear") norm_method_=1;
00406     else throw mbl_exception_parse_error("Unknown norm: "+props["norm"]);
00407 
00408     props.erase("norm");
00409   }
00410   if (props.find("estimate_var_min") !=props.end())
00411   {
00412     vcl_string strEstimate=props["estimate_var_min"];
00413     if (strEstimate[0]=='f' || strEstimate[0]=='F' || strEstimate[0]=='0')
00414         estimate_var_min_=false;
00415     else
00416         estimate_var_min_=true;
00417 
00418     props.erase("estimate_var_min");
00419   }
00420 
00421   overlap_f_=vul_string_atof(props.get_optional_property("overlap_f",
00422                                                          "1.0"));
00423 
00424   if (props.find("nA")!=props.end())
00425   {
00426     nA_=vul_string_atoi(props["nA"]);
00427     props.erase("nA");
00428   }
00429 
00430   if (props.find("dA")!=props.end())
00431   {
00432     dA_=vul_string_atof(props["dA"]);
00433     props.erase("dA");
00434   }
00435 
00436   if (props.find("cost_builder")!=props.end())
00437   {
00438     vcl_istringstream b_ss(props["cost_builder"]);
00439     vcl_auto_ptr<mfpf_vec_cost_builder> bb =
00440          mfpf_vec_cost_builder::create_from_stream(b_ss);
00441     cost_builder_ = bb->clone();
00442     props.erase("cost_builder");
00443   }
00444 
00445   // Check for unused props
00446   mbl_read_props_look_for_unused_props(
00447       "mfpf_region_finder_builder::set_from_stream", props, mbl_read_props_type());
00448   return true;
00449 }
00450 
00451 //=======================================================================
00452 // Method: is_a
00453 //=======================================================================
00454 
00455 vcl_string mfpf_region_finder_builder::is_a() const
00456 {
00457   return vcl_string("mfpf_region_finder_builder");
00458 }
00459 
00460 //: Create a copy on the heap and return base class pointer
00461 mfpf_point_finder_builder* mfpf_region_finder_builder::clone() const
00462 {
00463   return new mfpf_region_finder_builder(*this);
00464 }
00465 
00466 //=======================================================================
00467 // Method: print
00468 //=======================================================================
00469 
00470 void mfpf_region_finder_builder::print_summary(vcl_ostream& os) const
00471 {
00472   os << "{ size: " << roi_ni_ << 'x' << roi_nj_
00473      << " n_pixels: " << n_pixels_
00474      << " ref_pt: (" << ref_x_ << ',' << ref_y_ << ')' <<'\n';
00475   vsl_indent_inc(os);
00476   if (norm_method_==0) os<<vsl_indent()<<"norm: none"<<'\n';
00477   else                 os<<vsl_indent()<<"norm: linear"<<'\n';
00478   os <<vsl_indent()<< "cost_builder: ";
00479   if (cost_builder_.ptr()==0) os << '-'<<'\n';
00480   else                       os << cost_builder_<<'\n';
00481   os <<vsl_indent()<< "nA: " << nA_ << " dA: " << dA_ << ' '<<'\n'
00482      <<vsl_indent();
00483   mfpf_point_finder_builder::print_summary(os);
00484   os <<'\n' <<vsl_indent()<<"overlap_f: "<<overlap_f_<<'\n';
00485   vsl_indent_dec(os);
00486   os <<vsl_indent()<< '}';
00487 }
00488 
00489 void mfpf_region_finder_builder::print_shape(vcl_ostream& os) const
00490 {
00491   vil_image_view<vxl_byte> im(roi_ni_,roi_nj_);
00492   im.fill(0);
00493   for (unsigned k=0;k<roi_.size();++k)
00494     for (int i=roi_[k].start_x();i<=roi_[k].end_x();++i)
00495       im(i,roi_[k].y())=1;
00496   for (unsigned j=0;j<im.nj();++j)
00497   {
00498     for (unsigned i=0;i<im.ni();++i)
00499       if (im(i,j)==0) os<<' ';
00500       else            os<<'X';
00501     os<<'\n';
00502   }
00503 }
00504 
00505 //: Version number for I/O
00506 short mfpf_region_finder_builder::version_no() const
00507 {
00508   return 3;
00509 }
00510 
00511 void mfpf_region_finder_builder::b_write(vsl_b_ostream& bfs) const
00512 {
00513   vsl_b_write(bfs,version_no());
00514   mfpf_point_finder_builder::b_write(bfs);  // Save base class
00515   vsl_b_write(bfs,roi_);
00516   vsl_b_write(bfs,roi_ni_);
00517   vsl_b_write(bfs,roi_nj_);
00518   vsl_b_write(bfs,n_pixels_);
00519   vsl_b_write(bfs,ref_x_);
00520   vsl_b_write(bfs,ref_y_);
00521   vsl_b_write(bfs,nA_);
00522   vsl_b_write(bfs,dA_);
00523   vsl_b_write(bfs,cost_builder_);
00524   vsl_b_write(bfs,norm_method_);
00525   vsl_b_write(bfs,overlap_f_);
00526   if (version_no()>2)
00527       vsl_b_write(bfs,var_min_);
00528 }
00529 
00530 //=======================================================================
00531 // Method: load
00532 //=======================================================================
00533 
00534 void mfpf_region_finder_builder::b_read(vsl_b_istream& bfs)
00535 {
00536   if (!bfs) return;
00537   short version;
00538   vsl_b_read(bfs,version);
00539   switch (version)
00540   {
00541     case (1):
00542     case (2):
00543     case (3):
00544       mfpf_point_finder_builder::b_read(bfs);  // Load base class
00545       vsl_b_read(bfs,roi_);
00546       vsl_b_read(bfs,roi_ni_);
00547       vsl_b_read(bfs,roi_nj_);
00548       vsl_b_read(bfs,n_pixels_);
00549       vsl_b_read(bfs,ref_x_);
00550       vsl_b_read(bfs,ref_y_);
00551       vsl_b_read(bfs,nA_);
00552       vsl_b_read(bfs,dA_);
00553       vsl_b_read(bfs,cost_builder_);
00554       vsl_b_read(bfs,norm_method_);
00555       if (version==1)
00556           overlap_f_=1.0;
00557       else
00558           vsl_b_read(bfs,overlap_f_);
00559       if (version>2)
00560           vsl_b_read(bfs,var_min_);
00561       else
00562           var_min_=1.0E-6;
00563       break;
00564     default:
00565       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&)\n"
00566                << "           Unknown version number "<< version << vcl_endl;
00567       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00568       return;
00569   }
00570 }