contrib/mul/mfpf/mfpf_hog_box_finder_builder.cxx
Go to the documentation of this file.
00001 #include "mfpf_hog_box_finder_builder.h"
00002 //:
00003 // \file
00004 // \brief Builder for mfpf_hog_box_finder objects.
00005 // \author Tim Cootes
00006 
00007 #include <mfpf/mfpf_hog_box_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 #if 0
00017 #include <mbl/mbl_exception.h>
00018 #endif
00019 
00020 #include <vil/vil_resample_bilin.h>
00021 #include <vil/vil_image_view.h>
00022 #include <vsl/vsl_vector_io.h>
00023 #include <vsl/vsl_indent.h>
00024 #include <vcl_algorithm.h>
00025 
00026 #include <mipa/mipa_orientation_histogram.h>
00027 #include <mipa/mipa_sample_histo_boxes.h>
00028 #include <mipa/mipa_identity_normaliser.h>
00029 #include <mipa/mipa_block_normaliser.h>
00030 #include <mipa/mipa_ms_block_normaliser.h>
00031 
00032 //: Divide elements of v by sum of last nA elements
00033 //  For histogram vectors these are the total sums
00034 inline void mfpf_norm_histo_vec(vnl_vector<double>& v, unsigned nA)
00035 {
00036   unsigned n=v.size();
00037   double sum = 0.0;
00038   for (unsigned i=1;i<=nA;++i) sum+=v[n-i];
00039   v/=sum;
00040 }
00041 
00042 //=======================================================================
00043 // Dflt ctor
00044 //=======================================================================
00045 
00046 mfpf_hog_box_finder_builder::mfpf_hog_box_finder_builder():normaliser_(mipa_identity_normaliser())
00047 {
00048   set_defaults();
00049 }
00050 
00051 //: Define default values
00052 void mfpf_hog_box_finder_builder::set_defaults()
00053 {
00054   step_size_=1.0;
00055   search_ni_=5;
00056   search_nj_=5;
00057   nA_=0;
00058   dA_=0.0;
00059 
00060   nA_bins_=8;
00061   full360_=true;
00062   nc_=2;
00063   ni_=0;
00064   nj_=0;
00065   ref_x_=0;
00066   ref_y_=0;
00067   //norm_method_=0;
00068   overlap_f_=1.0;
00069 }
00070 
00071 //=======================================================================
00072 // Destructor
00073 //=======================================================================
00074 
00075 mfpf_hog_box_finder_builder::~mfpf_hog_box_finder_builder()
00076 {
00077 }
00078 
00079 //: Create new mfpf_hog_box_finder on heap
00080 mfpf_point_finder* mfpf_hog_box_finder_builder::new_finder() const
00081 {
00082   return new mfpf_hog_box_finder();
00083 }
00084 
00085 void mfpf_hog_box_finder_builder::set_angle_bins(unsigned nA_bins,
00086                                                  bool full360, unsigned cell_size)
00087 {
00088   nA_bins_ = nA_bins;
00089   full360_  = full360;
00090   nc_      = cell_size;
00091   reconfigure_normaliser();
00092 }
00093 
00094 //: Define region size in world co-ordinates
00095 //  Sets up ROI to cover given box (with samples at step_size()),
00096 //  with ref point at centre.
00097 void mfpf_hog_box_finder_builder::set_region_size(double wi, double wj)
00098 {
00099   wi/=(2*nc_*step_size());
00100   wj/=(2*nc_*step_size());
00101   int ni = vcl_max(2,int(0.99+wi));
00102   int nj = vcl_max(2,int(0.99+wj));
00103   set_as_box(unsigned(ni),unsigned(nj),0.5*(ni-1),0.5*(nj-1));
00104 }
00105 
00106 
00107 //: Define model region as an ni x nj box
00108 void mfpf_hog_box_finder_builder::set_as_box(unsigned ni, unsigned nj,
00109                                              double ref_x, double ref_y,
00110                                              const mfpf_vec_cost_builder& builder)
00111 {
00112   set_as_box(ni,nj,ref_x,ref_y);
00113   cost_builder_ = builder.clone();
00114   reconfigure_normaliser();
00115 }
00116 
00117 //: Define model region as an ni x nj box
00118 void mfpf_hog_box_finder_builder::set_as_box(unsigned ni, unsigned nj,
00119                                              double ref_x, double ref_y)
00120 {
00121   ni_=ni; nj_=nj;
00122 
00123   ref_x_=ref_x;
00124   ref_y_=ref_y;
00125   reconfigure_normaliser();
00126 }
00127 
00128 
00129 //: Define model region as an ni x nj box
00130 void mfpf_hog_box_finder_builder::set_as_box(unsigned ni, unsigned nj,
00131                                              const mfpf_vec_cost_builder& builder)
00132 {
00133   set_as_box(ni,nj, 0.5*(ni-1),0.5*(nj-1), builder);
00134   reconfigure_normaliser();
00135 }
00136 
00137 
00138 //: Initialise building
00139 // Must be called before any calls to add_example(...)
00140 void mfpf_hog_box_finder_builder::clear(unsigned n_egs)
00141 {
00142   unsigned n_per_eg = (1+2*nA_);
00143   cost_builder().clear(n_egs*n_per_eg);
00144 }
00145 
00146 //: Add one example to the model
00147 void mfpf_hog_box_finder_builder::add_one_example(
00148                  const vimt_image_2d_of<float>& image,
00149                  const vgl_point_2d<double>& p,
00150                  const vgl_vector_2d<double>& u)
00151 {
00152   vgl_vector_2d<double> u1=step_size_*u;
00153   vgl_vector_2d<double> v1(-u1.y(),u1.x());
00154 
00155   assert(image.image().nplanes()==1);
00156 
00157   // Set up sample area with 1 unit border
00158   unsigned sni = 2+2*nc_*ni_;
00159   unsigned snj = 2+2*nc_*nj_;
00160   vil_image_view<float> sample(sni,snj);
00161 
00162   const vgl_point_2d<double> p0 = p-(1+nc_*ref_x_)*u1-(1+nc_*ref_y_)*v1;
00163 
00164   const vimt_transform_2d& s_w2i = image.world2im();
00165   vgl_point_2d<double> im_p0 = s_w2i(p0);
00166   vgl_vector_2d<double> im_u = s_w2i.delta(p0, u1);
00167   vgl_vector_2d<double> im_v = s_w2i.delta(p0, v1);
00168 
00169   vil_resample_bilin(image.image(),sample,
00170                      im_p0.x(),im_p0.y(),  im_u.x(),im_u.y(),
00171                      im_v.x(),im_v.y(),
00172                      sni,snj);
00173 
00174   vil_image_view<float> histo_im;
00175   mipa_orientation_histogram(sample,histo_im,nA_bins_,nc_,full360_);
00176 
00177   vnl_vector<double> v;
00178   mipa_sample_histo_boxes_3L(histo_im,0,0,v,ni_,nj_);
00179 
00180   normaliser_->normalise(v);
00181   //if (norm_method_==1) mfpf_norm_histo_vec(v,nA_bins_);
00182 
00183   cost_builder().add_example(v);
00184 }
00185 
00186 //: Add one example to the model
00187 void mfpf_hog_box_finder_builder::add_example(const vimt_image_2d_of<float>& image,
00188                                               const vgl_point_2d<double>& p,
00189                                               const vgl_vector_2d<double>& u)
00190 {
00191   if (nA_==0)
00192   {
00193     add_one_example(image,p,u);
00194     return;
00195   }
00196 
00197   vgl_vector_2d<double> v(-u.y(),u.x());
00198   for (int iA=-int(nA_);iA<=(int)nA_;++iA)
00199   {
00200     double A = iA*dA_;
00201     vgl_vector_2d<double> uA = u*vcl_cos(A)+v*vcl_sin(A);
00202     add_one_example(image,p,uA);
00203   }
00204 }
00205 
00206 //: Build this object from the data supplied in add_example()
00207 void mfpf_hog_box_finder_builder::build(mfpf_point_finder& pf)
00208 {
00209   assert(pf.is_a()=="mfpf_hog_box_finder");
00210   mfpf_hog_box_finder& rp = static_cast<mfpf_hog_box_finder&>(pf);
00211 
00212   mfpf_vec_cost *cost = cost_builder().new_cost();
00213 
00214   cost_builder().build(*cost);
00215 
00216   rp.set(nA_bins_,full360_,ni_,nj_,nc_,
00217          ref_x_,ref_y_,*cost,normaliser_);
00218   set_base_parameters(rp);
00219   rp.set_overlap_f(overlap_f_);
00220 
00221 vcl_cout<<"Model: "<<rp<<vcl_endl;
00222 
00223   // Tidy up
00224   delete cost;
00225 }
00226 
00227 
00228 //=======================================================================
00229 // Method: set_from_stream
00230 //=======================================================================
00231 //: Initialise from a string stream
00232 bool mfpf_hog_box_finder_builder::set_from_stream(vcl_istream &is)
00233 {
00234   // Cycle through string and produce a map of properties
00235   vcl_string s = mbl_parse_block(is);
00236   vcl_istringstream ss(s);
00237   mbl_read_props_type props = mbl_read_props_ws(ss);
00238 
00239   set_defaults();
00240 
00241   // Extract the properties
00242   parse_base_props(props);
00243 
00244   nc_=vul_string_atoi(props.get_optional_property("nc","2"));
00245   nA_bins_=vul_string_atoi(props.get_optional_property("nA_bins","8"));
00246   full360_=vul_string_to_bool(props.get_optional_property("full360","true"));
00247   ni_=vul_string_atoi(props.get_optional_property("ni","4"));
00248   nj_=vul_string_atoi(props.get_optional_property("nj","4"));
00249 
00250   bool reonfigureNormaliser=false;
00251   if (props.find("norm")!=props.end())
00252   {
00253     vcl_istringstream ss2(props["norm"]);
00254     mbl_read_props_type dummy_extra_props;
00255     vcl_auto_ptr<mipa_vector_normaliser> norm = mipa_vector_normaliser::new_normaliser_from_stream(ss2, dummy_extra_props);
00256     normaliser_=norm.release();
00257     reonfigureNormaliser=true;
00258 #if 0
00259     if (props["norm"]=="none") norm_method_=0;
00260     else
00261     if (props["norm"]=="linear") norm_method_=1;
00262     else throw mbl_exception_parse_error("Unknown norm: "+props["norm"]);
00263 #endif
00264 
00265     props.erase("norm");
00266   }
00267 
00268   overlap_f_=vul_string_atof(props.get_optional_property("overlap_f","1.0"));
00269 
00270   if (props.find("ref_x")!=props.end())
00271   {
00272     ref_x_=vul_string_atof(props["ref_x"]);
00273     props.erase("ref_x");
00274   }
00275   else ref_x_=0.5*(ni_-1);
00276 
00277   if (props.find("ref_y")!=props.end())
00278   {
00279     ref_y_=vul_string_atof(props["ref_y"]);
00280     props.erase("ref_y");
00281   }
00282   else ref_y_=0.5*(nj_-1);
00283 
00284   if (props.find("nA")!=props.end())
00285   {
00286     nA_=vul_string_atoi(props["nA"]);
00287     props.erase("nA");
00288   }
00289 
00290   if (props.find("dA")!=props.end())
00291   {
00292     dA_=vul_string_atof(props["dA"]);
00293     props.erase("dA");
00294   }
00295 
00296   if (props.find("cost_builder")!=props.end())
00297   {
00298     vcl_istringstream b_ss(props["cost_builder"]);
00299     vcl_auto_ptr<mfpf_vec_cost_builder> bb =
00300       mfpf_vec_cost_builder::create_from_stream(b_ss);
00301     cost_builder_ = bb->clone();
00302     props.erase("cost_builder");
00303   }
00304 
00305   //Some classes of normaliser may require reconfiguration (e.g. to pass on the region size)
00306   if (reonfigureNormaliser)
00307   {
00308     reconfigure_normaliser();
00309   }
00310   // Check for unused props
00311   mbl_read_props_look_for_unused_props(
00312       "mfpf_hog_box_finder_builder::set_from_stream", props, mbl_read_props_type());
00313   return true;
00314 }
00315 
00316 void mfpf_hog_box_finder_builder::reconfigure_normaliser()
00317 {
00318     mipa_vector_normaliser* pNormaliser=normaliser_.ptr();
00319     mipa_block_normaliser* pBlockNormaliser= dynamic_cast<mipa_block_normaliser*>(pNormaliser);
00320     if (pBlockNormaliser)
00321     {
00322       pBlockNormaliser->set_region(2*ni_,2*nj_);
00323       pBlockNormaliser->set_nbins(nA_bins_);
00324       //Also this builder always uses 2 SIFT scales and a final overall histogram
00325       mipa_ms_block_normaliser* pMSBlockNormaliser= dynamic_cast<mipa_ms_block_normaliser*>(pNormaliser);
00326       if (pMSBlockNormaliser)
00327       {
00328         pMSBlockNormaliser->set_nscales(2);
00329         pMSBlockNormaliser->set_include_overall_histogram(true);
00330       }
00331       else
00332       {
00333         vcl_cerr<<"WARNING from fpf_hog_box_finder_builder::reconfigure_normaliser...\n"
00334                 <<"The normaliser may not be multi-scale but this HOG Builder uses multi-scale histograms\n";
00335       }
00336     }
00337 }
00338 
00339 //=======================================================================
00340 // Method: is_a
00341 //=======================================================================
00342 
00343 vcl_string mfpf_hog_box_finder_builder::is_a() const
00344 {
00345   return vcl_string("mfpf_hog_box_finder_builder");
00346 }
00347 
00348 //: Create a copy on the heap and return base class pointer
00349 mfpf_point_finder_builder* mfpf_hog_box_finder_builder::clone() const
00350 {
00351   return new mfpf_hog_box_finder_builder(*this);
00352 }
00353 
00354 //=======================================================================
00355 // Method: print
00356 //=======================================================================
00357 
00358 void mfpf_hog_box_finder_builder::print_summary(vcl_ostream& os) const
00359 {
00360   os << "{ "<<'\n';
00361   vsl_indent_inc(os);
00362   os << vsl_indent()<<"size: " << ni_ << 'x' << nj_
00363      << " nc: " << nc_ <<" nA_bins: "<<nA_bins_
00364      << " ref_pt: (" << ref_x_ << ',' << ref_y_ << ')' <<'\n';
00365   if (full360_) os<<vsl_indent()<<"Angle range: 0-360"<<'\n';
00366   else          os<<vsl_indent()<<"Angle range: 0-180"<<'\n';
00367   vcl_cout<<"The HOG's normaliser is:"<<'\n';
00368   normaliser_->print_summary(os);
00369   //if (norm_method_==0) os<<vsl_indent()<<"norm: none"<<'\n';
00370   //else                 os<<vsl_indent()<<"norm: linear"<<'\n';
00371   os <<vsl_indent()<< "cost_builder: ";
00372   if (cost_builder_.ptr()==0) os << '-'<<'\n';
00373   else                       os << cost_builder_<<'\n';
00374   os <<vsl_indent()<< "nA: " << nA_ << " dA: " << dA_ << ' '<<'\n'
00375      <<vsl_indent();
00376   mfpf_point_finder_builder::print_summary(os);
00377   os <<'\n'
00378      <<vsl_indent()<<"overlap_f: "<<overlap_f_<<'\n';
00379   vsl_indent_dec(os);
00380   os <<vsl_indent()<< '}';
00381 }
00382 
00383 //: Version number for I/O
00384 short mfpf_hog_box_finder_builder::version_no() const
00385 {
00386   return 2;
00387 }
00388 
00389 void mfpf_hog_box_finder_builder::b_write(vsl_b_ostream& bfs) const
00390 {
00391   vsl_b_write(bfs,version_no());
00392   mfpf_point_finder_builder::b_write(bfs);  // Save base class
00393   vsl_b_write(bfs,nc_);
00394   vsl_b_write(bfs,ni_);
00395   vsl_b_write(bfs,nj_);
00396   vsl_b_write(bfs,nA_bins_);
00397   vsl_b_write(bfs,full360_);
00398   vsl_b_write(bfs,ref_x_);
00399   vsl_b_write(bfs,ref_y_);
00400   vsl_b_write(bfs,nA_);
00401   vsl_b_write(bfs,dA_);
00402   vsl_b_write(bfs,cost_builder_);
00403   vsl_b_write(bfs,normaliser_);
00404   vsl_b_write(bfs,overlap_f_);
00405 }
00406 
00407 //=======================================================================
00408 // Method: load
00409 //=======================================================================
00410 
00411 void mfpf_hog_box_finder_builder::b_read(vsl_b_istream& bfs)
00412 {
00413   if (!bfs) return;
00414   short version;
00415   vsl_b_read(bfs,version);
00416   switch (version)
00417   {
00418     case (1):
00419     case (2):
00420       mfpf_point_finder_builder::b_read(bfs);  // Load base class
00421       vsl_b_read(bfs,nc_);
00422       vsl_b_read(bfs,ni_);
00423       vsl_b_read(bfs,nj_);
00424       vsl_b_read(bfs,nA_bins_);
00425       vsl_b_read(bfs,full360_);
00426       vsl_b_read(bfs,ref_x_);
00427       vsl_b_read(bfs,ref_y_);
00428       vsl_b_read(bfs,nA_);
00429       vsl_b_read(bfs,dA_);
00430       vsl_b_read(bfs,cost_builder_);
00431       vsl_b_read(bfs,normaliser_);
00432       if (version==1) overlap_f_=1.0;
00433       else            vsl_b_read(bfs,overlap_f_);
00434       break;
00435     default:
00436       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&)\n"
00437                << "           Unknown version number "<< version << '\n';
00438       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00439       return;
00440   }
00441 }