Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members

fhs_searcher.cxx

Go to the documentation of this file.
00001 //:
00002 // \file
00003 // \author Tim Cootes
00004 // \brief Use F&H's DP style algorithm to search for global solutions
00005 
00006 #include <fhs/fhs_searcher.h>
00007 #include <vcl_cassert.h>
00008 #include <vcl_cmath.h>
00009 #include <vcl_cstdlib.h> // for std::abort()
00010 #include <vnl/vnl_math.h>
00011 #include <vil/vil_bilin_interp.h>
00012 #include <vil/algo/vil_quad_distance_function.h>
00013 #include <vimt/vimt_bilin_interp.h>
00014 
00015 //: Default constructor
00016 fhs_searcher::fhs_searcher()
00017 {
00018 }
00019 
00020 //: Set tree defining relationships between features
00021 //  Input arcs define neighbour relationships in any order.
00022 //  root_node defines which feature to be used as the root
00023 void fhs_searcher::set_tree(const vcl_vector<fhs_arc>& arcs,
00024                             unsigned root_node)
00025 {
00026   if (!fhs_order_tree_from_root(arcs,arc_,children_,root_node))
00027   {
00028     vcl_cerr<<"fhs_searcher::set_tree() Failed to set up the tree.\n";
00029     return;
00030   }
00031 
00032   // Fill in elements of arc_to_j_
00033   arc_to_j_.resize(arc_.size()+1);
00034   for (unsigned i=0;i<arc_.size();++i)
00035     arc_to_j_[arc_[i].j()]=i;
00036 }
00037 
00038 //: Index of root node (set by last call to set_tree()
00039 unsigned fhs_searcher::root_node() const
00040 {
00041   assert(arc_.size()>0);
00042   return arc_[0].i();
00043 }
00044 
00045 //: Combine responses for image im_index
00046 void fhs_searcher::combine_responses(unsigned im_index,
00047                                      const vimt_image_2d_of<float>& feature_response)
00048 {
00049   sum_im_[im_index].deep_copy(feature_response);
00050 
00051   for (unsigned ci = 0; ci<children_[im_index].size();++ci)
00052   {
00053     unsigned child_node = children_[im_index][ci];
00054     const fhs_arc& arc_to_c = arc_[arc_to_j_[child_node]];
00055 
00056     // Add offset version of child_node information to sum_im_
00057 
00058     // An inefficient initial implementation, assuming a
00059     // large cost for points sampled outside any response image
00060     vimt_transform_2d i2w = sum_im_[im_index].world2im().inverse();
00061     vimt_transform_2d c_w2i =  sum_im_[child_node].world2im();
00062 
00063     vgl_vector_2d<double> dp(arc_to_c.dx(),arc_to_c.dy());
00064 
00065     // Check if transformation between images is an integer multiple
00066     vgl_point_2d<double> p0 = c_w2i(i2w(0,0)+dp);
00067     vgl_point_2d<double> p1 = c_w2i(i2w(1,0)+dp);
00068     vgl_point_2d<double> p2 = c_w2i(i2w(0,1)+dp);
00069     vgl_vector_2d<double> dx = p1-p0;
00070     vgl_vector_2d<double> dy = p2-p0;
00071     // Check that dx=(0,1) and dy=(1,0)
00072     bool is_int_transform = (vcl_fabs(dx.x()-1)<1e-4) &&
00073                             (vcl_fabs(dx.y()  )<1e-4) &&
00074                             (vcl_fabs(dy.x()  )<1e-4) &&
00075                             (vcl_fabs(dy.y()-1)<1e-4);
00076 
00077     vil_image_view<float>& sum_image = sum_im_[im_index].image();
00078     unsigned ni = sum_image.ni();
00079     unsigned nj = sum_image.nj();
00080     float* s_data = sum_image.top_left_ptr();
00081     vcl_ptrdiff_t s_istep = sum_image.istep();
00082     vcl_ptrdiff_t s_jstep = sum_image.jstep();
00083     unsigned cni = dist_im_[child_node].image().ni();
00084     unsigned cnj = dist_im_[child_node].image().nj();
00085     const float* c_data = dist_im_[child_node].image().top_left_ptr();
00086     vcl_ptrdiff_t c_istep = dist_im_[child_node].image().istep();
00087     vcl_ptrdiff_t c_jstep = dist_im_[child_node].image().jstep();
00088 
00089     if (is_int_transform)
00090     {
00091       // Use fast pointer based approach to performing the addition
00092       int ci0 = vnl_math_rnd(p0.x());
00093       int cj  = vnl_math_rnd(p0.y());
00094       const float * c_row = c_data + cj*c_jstep + ci0*c_istep;
00095       float *s_row = s_data;
00096       for (unsigned j=0;j<nj;++j,++cj,c_row+=c_jstep,s_row+=s_jstep)
00097       {
00098         int ci = ci0;
00099         const float *c_pix = c_row;
00100         float * s_pix = s_row;
00101 
00102         if (cj<0 || cj>=(cnj-1))  // Whole row is illegal
00103         {
00104           for (unsigned i=0;i<ni;++i, s_pix+=s_istep)
00105             *s_pix += 9999;  // Illegal if off the image
00106         }
00107         else
00108         {
00109           for (unsigned i=0;i<ni;++i,++ci,c_pix+=c_istep, s_pix+=s_istep)
00110           {
00111             if (ci<0 ||ci>=(cni-1))
00112               *s_pix += 9999;  // Illegal if off the image
00113             else
00114               *s_pix += *c_pix;
00115           }
00116         }
00117       }
00118     }
00119     else
00120     {
00121       for (unsigned j=0;j<nj;++j)
00122         for (unsigned i=0;i<ni;++i)
00123         {
00124           // Compute point in target image (in image co-ords)
00125           vgl_point_2d<double> p = c_w2i(i2w(i,j)+dp);
00126 
00127           if (p.x()<0 || p.y()<0 || p.x()>=(cni-1) || p.y()>=(cnj-1))
00128             sum_image(i,j) += 9999;  // Illegal if off the image
00129           else
00130             sum_image(i,j) += vil_bilin_interp_unsafe(p.x(),p.y(),
00131                                                       c_data,c_istep,c_jstep);
00132         }
00133     }
00134   }
00135 }
00136 
00137 //: Perform global search
00138 //  Images of feature response supplied.  The transformation
00139 //  (world2im()) for each image can be used to indicate regions
00140 //  which don't necessarily overlap.  However, effective displacements
00141 //  are assumed to be in pixel sized steps.
00142 //
00143 //  After calling search(), results can be obtained using
00144 //  points() and best_points() etc
00145 void fhs_searcher::search(const vcl_vector<vimt_image_2d_of<float> >& feature_response)
00146 {
00147   assert(feature_response.size()==n_points());
00148 
00149   sum_im_.resize(n_points());
00150   dist_im_.resize(n_points());
00151   pos_im_.resize(n_points());
00152 
00153   // Work from leaves back to root
00154   for (int a=int(arc_.size())-1; a>=0; --a)
00155   {
00156     // arc_[a].j() is unique, and is only visited after its children
00157     unsigned node_a = arc_[a].j();
00158 
00159     // Set sum_im_[node_a] to be the feature response image
00160     // combined with the suitably offset summed responses from
00161     // the children of node_a
00162     combine_responses(node_a,feature_response[node_a]);
00163 
00164     // Compute scaling applied to x and y axes
00165     const vimt_transform_2d& w2im = feature_response[node_a].world2im();
00166     double sx = w2im.delta(vgl_point_2d<double>(0,0),
00167                        vgl_vector_2d<double>(1,0)).length();
00168     double sy = w2im.delta(vgl_point_2d<double>(0,0),
00169                        vgl_vector_2d<double>(0,1)).length();
00170 
00171 
00172     vil_quad_distance_function(sum_im_[node_a].image(),
00173                                1.0/(sx*sx*arc_[a].var_x()),
00174                                1.0/(sy*sy*arc_[a].var_y()),
00175                                dist_im_[node_a].image(),
00176                                pos_im_[node_a].image());
00177     dist_im_[node_a].set_world2im(sum_im_[node_a].world2im());
00178     pos_im_[node_a].set_world2im(sum_im_[node_a].world2im());
00179   }
00180 
00181   // Now combine children of root node
00182   unsigned root_node = arc_[0].i();
00183   combine_responses(root_node,feature_response[root_node]);
00184 
00185   // sum_im_[root_node] now contains quality of fit result for solution
00186 }
00187 
00188 //: Compute optimal position of all points given position of root
00189 //  Assumes search() has been called first
00190 void fhs_searcher::points_from_root(const vgl_point_2d<double>& root_pt,
00191                         vcl_vector<vgl_point_2d<double> >& pts) const
00192 {
00193   if (n_points()<2)
00194   {
00195     vcl_cerr<<"fhs_searcher::points_from_root() Not initialised.\n";
00196     vcl_abort();
00197   }
00198 
00199   pts.resize(n_points());
00200   pts[root_node()]=root_pt;
00201 
00202   // Propogate solution through the tree
00203   // arc_ ordered so that parents always precede leaves
00204   for (vcl_vector<fhs_arc>::const_iterator a=arc_.begin();a!=arc_.end();++a)
00205   {
00206     // Compute mean predicted position for leaf point
00207     vgl_point_2d<double> p_j0 = pts[a->i()]
00208                                  +vgl_vector_2d<double>(a->dx(),a->dy());
00209 
00210     // Look up position allowing for offset (in image coords)
00211     double px = vimt_bilin_interp_safe(pos_im_[a->j()],p_j0,0);
00212     double py = vimt_bilin_interp_safe(pos_im_[a->j()],p_j0,1);
00213     // Project back to world co-ords
00214     pts[a->j()] = pos_im_[a->j()].world2im().inverse()(px,py);
00215   }
00216 }
00217 
00218 static vgl_point_2d<int> min_image_point(const vil_image_view<float>& image)
00219 {
00220   const unsigned ni=image.ni(),nj=image.nj();
00221   const vcl_ptrdiff_t istep = image.istep(),jstep=image.jstep();
00222   const float* row = image.top_left_ptr();
00223   unsigned best_i=0,best_j=0;
00224   float min_val = *row;
00225   for (unsigned j=0;j<nj;++j,row+=jstep)
00226   {
00227     const float* pixel = row;
00228     for (unsigned i=0;i<ni;++i,pixel+=istep)
00229       if (*pixel<min_val) {min_val=*pixel, best_i=i; best_j=j; }
00230   }
00231 
00232   return vgl_point_2d<int>(best_i,best_j);
00233 }
00234 
00235 //: Compute optimal position of all points
00236 //  Assumes search() has been called first
00237 double fhs_searcher::best_points(vcl_vector<vgl_point_2d<double> >& pts) const
00238 {
00239   // Find position of global minima in root quality image
00240   vgl_point_2d<int> p_im = min_image_point(sum_im_[root_node()].image());
00241   vgl_point_2d<double> root_pt = sum_im_[root_node()].world2im().inverse()(p_im.x(),p_im.y());
00242   points_from_root(root_pt,pts);
00243 
00244   return sum_im_[root_node()].image()(p_im.x(),p_im.y());
00245 }
00246 
00247 //: Return final total cost image for root
00248 const vimt_image_2d_of<float>& fhs_searcher::root_cost_image() const
00249 {
00250   return sum_im_[root_node()];
00251 }

Generated on Thu Jan 10 14:44:46 2008 for contrib/mul/fhs by  doxygen 1.4.4