contrib/mul/mfpf/mfpf_pose_predictor.cxx
Go to the documentation of this file.
00001 #include "mfpf_pose_predictor.h"
00002 //:
00003 // \file
00004 // \brief Uses regression to predict new pose from current sample.
00005 // \author Tim Cootes
00006 
00007 #include <mfpf/mfpf_sample_region.h>
00008 #include <mfpf/mfpf_norm_vec.h>
00009 #include <vil/vil_resample_bilin.h>
00010 #include <vsl/vsl_binary_loader.h>
00011 #include <vsl/vsl_vector_io.h>
00012 #include <vsl/vsl_indent.h>
00013 #include <vgl/vgl_point_2d.h>
00014 #include <vgl/vgl_vector_2d.h>
00015 #include <vnl/vnl_vector.h>
00016 #include <vnl/io/vnl_io_vector.h>
00017 #include <vnl/io/vnl_io_matrix.h>
00018 #include <vcl_cmath.h>
00019 #include <vcl_cassert.h>
00020 
00021 //=======================================================================
00022 // Dflt ctor
00023 //=======================================================================
00024 
00025 mfpf_pose_predictor::mfpf_pose_predictor()
00026 {
00027   set_defaults();
00028 }
00029 
00030 //: Define default values
00031 void mfpf_pose_predictor::set_defaults()
00032 {
00033   step_size_=1.0;
00034   pose_type_=translation;
00035   n_pixels_=0;
00036   roi_.resize(0);
00037   roi_ni_=0;
00038   roi_nj_=0;
00039   ref_x_=0;
00040   ref_y_=0;
00041   norm_method_=1;
00042   var_min_ = 1.0E-6;
00043 }
00044 
00045 //: Size of step between sample points
00046 void mfpf_pose_predictor::set_step_size(double s)
00047 {
00048   step_size_=s;
00049 }
00050 
00051 //: Initialise as a rectangle with ref. in centre.
00052 void mfpf_pose_predictor::set_as_box(unsigned ni, unsigned nj,
00053                                      short norm_method)
00054 {
00055   // Set ROI to be a box
00056   vcl_vector<mbl_chord> roi(nj);
00057   for (unsigned j=0;j<nj;++j) roi[j]=mbl_chord(0,ni-1,j);
00058 
00059   set(roi,0.5*(ni-1),0.5*(nj-1),norm_method);
00060 }
00061 
00062 //: Define model region as an ellipse with radii ri, rj
00063 //  Ref. point in centre.
00064 void mfpf_pose_predictor::set_as_ellipse(double ri, double rj,
00065                                          short norm_method)
00066 {
00067   vcl_vector<mbl_chord> roi;
00068 
00069   int ni=int(ri+1e-6);
00070   int nj=int(rj+1e-6);
00071   for (int j = -nj;j<=nj;++j)
00072   {
00073     // Find start and end of line of pixels inside disk
00074     int x = int(ri*vcl_sqrt(1.0-j*j/(rj*rj)));
00075     roi.push_back(mbl_chord(ni-x,ni+x,nj+j));
00076   }
00077 
00078   set(roi,ni,nj,norm_method);
00079 }
00080 
00081 void mfpf_pose_predictor::set_pose_type(const mfpf_pose_type& pt)
00082 {
00083   pose_type_=pt;
00084 }
00085 //=======================================================================
00086 // Destructor
00087 //=======================================================================
00088 
00089 mfpf_pose_predictor::~mfpf_pose_predictor()
00090 {
00091 }
00092 
00093 //: Define region and cost of region
00094 void mfpf_pose_predictor::set(const vcl_vector<mbl_chord>& roi,
00095                               double ref_x, double ref_y,
00096                               short norm_method)
00097 {
00098   ref_x_ = ref_x;
00099   ref_y_ = ref_y;
00100 
00101   // Check bounding box
00102   if (roi.size()==0) { roi_ni_=0; roi_nj_=0; return; }
00103   int ilo=roi[0].start_x(), ihi=roi[0].end_x();
00104   int jlo=roi[0].y(), jhi=roi[0].y();
00105 
00106   for (unsigned k=1;k<roi.size();++k)
00107   {
00108     if (roi[k].start_x()<ilo) ilo=roi[k].start_x();
00109     if (roi[k].end_x()>ihi)   ihi=roi[k].end_x();
00110     if (roi[k].y()<jlo) jlo=roi[k].y();
00111     if (roi[k].y()>jhi) jhi=roi[k].y();
00112   }
00113   roi_ni_=1+ihi-ilo;
00114   roi_nj_=1+jhi-jlo;
00115 
00116   // Apply offset of (-ilo,-jlo) to ensure bounding box is +ive
00117   ref_x_-=ilo; ref_y_-=jlo;
00118   roi_.resize(roi.size());
00119   n_pixels_=0;
00120   for (unsigned k=0;k<roi.size();++k)
00121   {
00122     roi_[k]= mbl_chord(roi[k].start_x()-ilo,
00123                        roi[k].end_x()-ilo,   roi[k].y()-jlo);
00124     n_pixels_+=1+roi[k].end_x()-roi[k].start_x();
00125   }
00126 
00127   assert(norm_method>=0 && norm_method<=1);
00128   norm_method_ = norm_method;
00129 }
00130 
00131 //: Set regression matrices
00132 void mfpf_pose_predictor::set_predictor(const vnl_matrix<double>& R,
00133                                         const vnl_vector<double>& dp0)
00134 {
00135   assert(R.rows()==dp0.size());
00136   R_=R;
00137   dp0_=dp0;
00138 }
00139 
00140 //: Radius of circle containing modelled region
00141 double mfpf_pose_predictor::radius() const
00142 {
00143   // Compute distance to each corner
00144   double wx = roi_ni_-1;
00145   double x2 = vcl_max(ref_x_*ref_x_,(ref_x_-wx)*(ref_x_-wx));
00146   double wy = roi_nj_-1;
00147   double y2 = vcl_max(ref_y_*ref_y_,(ref_y_-wy)*(ref_y_-wy));
00148   double r2 = x2+y2;
00149   if (r2<=1) return 1.0;
00150   return vcl_sqrt(r2);
00151 }
00152 
00153 
00154 //: Get sample of region around specified point in image
00155 void mfpf_pose_predictor::get_sample_vector(const vimt_image_2d_of<float>& image,
00156                                             const vgl_point_2d<double>& p,
00157                                             const vgl_vector_2d<double>& u,
00158                                             vnl_vector<double>& vec)
00159 {
00160   vgl_vector_2d<double> u1=step_size_*u;
00161   vgl_vector_2d<double> v1(-u1.y(),u1.x());
00162 
00163   unsigned np=image.image().nplanes();
00164   // Set up sample area with interleaved planes (ie planestep==1)
00165   vil_image_view<float> sample(roi_ni_,roi_nj_,1,np);
00166 
00167   const vgl_point_2d<double> p0 = p-ref_x_*u1-ref_y_*v1;
00168 
00169   const vimt_transform_2d& s_w2i = image.world2im();
00170   vgl_point_2d<double> im_p0 = s_w2i(p0);
00171   vgl_vector_2d<double> im_u = s_w2i.delta(p0, u1);
00172   vgl_vector_2d<double> im_v = s_w2i.delta(p0, v1);
00173 
00174   vil_resample_bilin(image.image(),sample,
00175                      im_p0.x(),im_p0.y(),
00176                      im_u.x(),im_u.y(),
00177                      im_v.x(),im_v.y(),
00178                      roi_ni_,roi_nj_);
00179 
00180   vec.set_size(n_pixels_*sample.nplanes());
00181   mfpf_sample_region(sample.top_left_ptr(),sample.jstep(),
00182                      np,roi_,vec);
00183 
00184   if (norm_method_==1) mfpf_norm_vec(vec,var_min_);
00185 }
00186 
00187 //: Sample at pose0 and predict a better pose (new_pose)
00188 void mfpf_pose_predictor::new_pose(const vimt_image_2d_of<float>& image,
00189                                    const mfpf_pose& pose0,
00190                                    mfpf_pose& new_pose)
00191 {
00192   vnl_vector<double> v;
00193   get_sample_vector(image,pose0.p(),pose0.u(),v);
00194   vnl_vector<double> dp = R_*v+dp0_;
00195 
00196   // Compute pose to update pose0
00197   mfpf_pose dpose;
00198   double s;
00199 
00200   switch (pose_type_)
00201   {
00202     case translation:
00203       dpose.p().set(dp[0],dp[1]);
00204       break;
00205     case rigid:
00206       dpose.p().set(dp[0],dp[1]);
00207       dpose.u().set(vcl_cos(dp[2]),vcl_sin(dp[2]));
00208       break;
00209     case zoom:
00210       dpose.p().set(dp[0],dp[1]);
00211       dpose.u().set(vcl_exp(dp[2]),0);
00212       break;
00213     case similarity:
00214       dpose.p().set(dp[0],dp[1]);
00215       s=vcl_exp(dp[2]);
00216       dpose.u().set(s*vcl_cos(dp[3]),s*vcl_sin(dp[3]));
00217       break;
00218     default: assert(!"Invalid pose_type_"); break;
00219   }
00220 
00221   new_pose=pose0*dpose.inverse(); // i.e., apply inverse of dpose, then pose0
00222 }
00223 
00224 
00225 //: Generate points in ref frame that represent boundary
00226 //  Points of a contour around the shape.
00227 //  Used for display purposes.
00228 void mfpf_pose_predictor::get_outline(vcl_vector<vgl_point_2d<double> >& pts) const
00229 {
00230   pts.resize(7);
00231   vgl_vector_2d<double> r(ref_x_,ref_y_);
00232   pts[0]=vgl_point_2d<double>(0,roi_nj_)-r;
00233   pts[1]=vgl_point_2d<double>(0,0);
00234   pts[2]=vgl_point_2d<double>(roi_ni_,roi_nj_)-r;
00235   pts[3]=vgl_point_2d<double>(0,roi_nj_)-r;
00236   pts[4]=vgl_point_2d<double>(0,0)-r;
00237   pts[5]=vgl_point_2d<double>(roi_ni_,0)-r;
00238   pts[6]=vgl_point_2d<double>(roi_ni_,roi_nj_)-r;
00239 }
00240 
00241 
00242 //=======================================================================
00243 // Method: is_a
00244 //=======================================================================
00245 
00246 vcl_string mfpf_pose_predictor::is_a() const
00247 {
00248   return vcl_string("mfpf_pose_predictor");
00249 }
00250 
00251 //: Create a copy on the heap and return base class pointer
00252 mfpf_pose_predictor* mfpf_pose_predictor::clone() const
00253 {
00254   return new mfpf_pose_predictor(*this);
00255 }
00256 
00257 //=======================================================================
00258 // Method: print
00259 //=======================================================================
00260 
00261 void mfpf_pose_predictor::print_summary(vcl_ostream& os) const
00262 {
00263   os << "{  step_size: "<<step_size_
00264      <<" pose_type: "<<pose_type_
00265      <<" size: "<<roi_ni_<<" x "<<roi_nj_
00266      << " n_pixels: "<<n_pixels_
00267      << " ref_pt: ("<<ref_x_<<','<<ref_y_<<')'<<'\n';
00268   vsl_indent_inc(os);
00269   if (norm_method_==0) os<<vsl_indent()<<"norm: none ";
00270   else                 os<<vsl_indent()<<"norm: linear ";
00271   os << "n_params: "<<R_.rows()<<'\n';
00272   vsl_indent_dec(os);
00273   os<<vsl_indent()<<'}';
00274 }
00275 
00276 void mfpf_pose_predictor::print_shape(vcl_ostream& os) const
00277 {
00278   vil_image_view<vxl_byte> im(roi_ni_,roi_nj_);
00279   im.fill(0);
00280   for (unsigned k=0;k<roi_.size();++k)
00281     for (int i=roi_[k].start_x();i<=roi_[k].end_x();++i)
00282       im(i,roi_[k].y())=1;
00283   for (unsigned j=0;j<im.nj();++j)
00284   {
00285     for (unsigned i=0;i<im.ni();++i)
00286       if (im(i,j)==0) os<<' ';
00287       else            os<<'X';
00288     os<<'\n';
00289   }
00290 }
00291 
00292 short mfpf_pose_predictor::version_no() const
00293 {
00294   return 1;
00295 }
00296 
00297 
00298 void mfpf_pose_predictor::b_write(vsl_b_ostream& bfs) const
00299 {
00300   vsl_b_write(bfs,version_no());
00301   vsl_b_write(bfs,step_size_);
00302   vsl_b_write(bfs,unsigned(pose_type_));
00303   vsl_b_write(bfs,roi_);
00304   vsl_b_write(bfs,roi_ni_);
00305   vsl_b_write(bfs,roi_nj_);
00306   vsl_b_write(bfs,n_pixels_);
00307   vsl_b_write(bfs,ref_x_);
00308   vsl_b_write(bfs,ref_y_);
00309   vsl_b_write(bfs,norm_method_);
00310   vsl_b_write(bfs,R_);
00311   vsl_b_write(bfs,dp0_);
00312 }
00313 
00314 //=======================================================================
00315 // Method: load
00316 //=======================================================================
00317 
00318 void mfpf_pose_predictor::b_read(vsl_b_istream& bfs)
00319 {
00320   if (!bfs) return;
00321   short version;
00322   unsigned i;
00323   vsl_b_read(bfs,version);
00324   switch (version)
00325   {
00326     case 1:
00327       vsl_b_read(bfs,step_size_);
00328       vsl_b_read(bfs,i); pose_type_=mfpf_pose_type(i);
00329       vsl_b_read(bfs,roi_);
00330       vsl_b_read(bfs,roi_ni_);
00331       vsl_b_read(bfs,roi_nj_);
00332       vsl_b_read(bfs,n_pixels_);
00333       vsl_b_read(bfs,ref_x_);
00334       vsl_b_read(bfs,ref_y_);
00335       vsl_b_read(bfs,norm_method_);
00336       vsl_b_read(bfs,R_);
00337       vsl_b_read(bfs,dp0_);
00338       break;
00339     default:
00340       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&)\n"
00341                << "           Unknown version number "<< version << vcl_endl;
00342       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00343       return;
00344   }
00345 }
00346 
00347 //: Test equality
00348 bool mfpf_pose_predictor::operator==(const mfpf_pose_predictor& nc) const
00349 {
00350   if (vcl_fabs(step_size_-nc.step_size_)>1e-6) return false;
00351   if (pose_type_!=nc.pose_type_) return false;
00352   if (roi_ni_!=nc.roi_ni_) return false;
00353   if (roi_nj_!=nc.roi_nj_) return false;
00354   if (norm_method_!=nc.norm_method_) return false;
00355   if (n_pixels_!=nc.n_pixels_) return false;
00356   if (vcl_fabs(ref_x_-nc.ref_x_)>1e-6) return false;
00357   if (vcl_fabs(ref_y_-nc.ref_y_)>1e-6) return false;
00358   return true;
00359 }
00360 
00361 //=======================================================================
00362 // Associated function: operator<<
00363 //=======================================================================
00364 
00365 void vsl_b_write(vsl_b_ostream& bfs, const mfpf_pose_predictor& b)
00366 {
00367     b.b_write(bfs);
00368 }
00369 
00370 //=======================================================================
00371 // Associated function: operator>>
00372 //=======================================================================
00373 
00374 void vsl_b_read(vsl_b_istream& bfs, mfpf_pose_predictor& b)
00375 {
00376     b.b_read(bfs);
00377 }
00378 
00379 //=======================================================================
00380 // Associated function: operator<<
00381 //=======================================================================
00382 
00383 vcl_ostream& operator<<(vcl_ostream& os,const mfpf_pose_predictor& b)
00384 {
00385   os << b.is_a() << ": ";
00386   vsl_indent_inc(os);
00387   b.print_summary(os);
00388   vsl_indent_dec(os);
00389   return os;
00390 }
00391 
00392 vcl_ostream& operator<<(vcl_ostream& os ,const mfpf_pose_type& pt)
00393 {
00394   switch (pt)
00395   {
00396     case translation: os<<"translation"; break;
00397     case rigid: os<<"rigid"; break;
00398     case zoom: os<<"zoom"; break;
00399     case similarity: os<<"similarity"; break;
00400     default: os<<"Invalid"; break;
00401   }
00402   return os;
00403 }
00404