contrib/mul/fhs/fhs_searcher.cxx

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

Generated on Mon Mar 8 05:17:23 2010 for contrib/mul/fhs by  doxygen 1.5.1