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

mr_find_matches.cxx

Go to the documentation of this file.
00001 //:
00002 // \file
00003 // \author Tim Cootes
00004 // \brief Example program using F&H method to locate matches on a pair of images
00005 
00006 #include <vul/vul_arg.h>
00007 #include <vimt/vimt_image_2d_of.h>
00008 #include <vimt/vimt_gaussian_pyramid_builder_2d.h>
00009 #include <vimt/vimt_image_pyramid.h>
00010 #include <vimt/vimt_crop.h>
00011 #include <vil/algo/vil_corners.h>
00012 #include <vil/algo/vil_find_peaks.h>
00013 #include <vil/vil_load.h>
00014 #include <vil/vil_save.h>
00015 #include <vil/vil_fill.h>
00016 #include <vil/vil_crop.h>
00017 #include <vil/vil_math.h>
00018 #include <vil/vil_convert.h>
00019 #include <vil/vil_resample_bilin.h>
00020 #include <mbl/mbl_index_sort.h>
00021 #include <vnl/vnl_math.h>
00022 #include <fhs/fhs_searcher.h>
00023 #include <vimt/algo/vimt_normalised_correlation_2d.h>
00024 #include <mbl/mbl_minimum_spanning_tree.h>
00025 #include <mbl/mbl_draw_line.h>
00026 
00027 void print_usage()
00028 {
00029   vcl_cout<<"find_matches -i1 image1.jpg -i2 image2.jpg -Llo 0 Lhi 2"<<vcl_endl
00030           <<"Loads in image1 and image2."<<vcl_endl
00031           <<"Locates a set of interesting features (corners) on levels of image1."<<vcl_endl
00032           <<"Constructs a tree model of their relative positions."<<vcl_endl
00033           <<"Points at level L are linked to the nearest points at level L+1"<<vcl_endl
00034           <<"Uses normalised correllation and this model to locate"<<vcl_endl
00035           <<"equivalent points on the second image."<<vcl_endl;
00036   vul_arg_display_usage_and_exit();
00037 }
00038 
00039 //: Write tree into the image
00040 // Draw disks at each point, and lines between linked points
00041 void draw_tree(vil_image_view<vxl_byte>& image,
00042                const vcl_vector<vgl_point_2d<double> >& pts,
00043                const vcl_vector<vcl_pair<int,int> >& pairs)
00044 {
00045   // Draw tree into image for display purposes
00046   for (unsigned i=0;i<pairs.size();++i)
00047     mbl_draw_line(image,
00048                   pts[pairs[i].first],
00049                   pts[pairs[i].second],vxl_byte(255));
00050 
00051   // Write position of selected points into the original image
00052   // for display purposes.
00053   for (unsigned i=0;i<pts.size();++i)
00054   {
00055     vil_fill_disk(image,pts[i].x(),pts[i].y(),4,vxl_byte(255));
00056   }
00057 }
00058 
00059 //: Apply corner operator to image and select strongest corners
00060 void get_strongest_corners(const vimt_image& image,
00061                            vcl_vector<vgl_point_2d<double> >& pts,
00062                            unsigned max_n)
00063 {
00064   const vimt_image_2d_of<vxl_byte>& byte_im =
00065                static_cast<const vimt_image_2d_of<vxl_byte>&>(image);
00066   vimt_image_2d_of<float> corner_im;
00067   corner_im.set_world2im(byte_im.world2im());
00068   vil_corners(byte_im.image(),corner_im.image());
00069 
00070   vcl_vector<unsigned> pi,pj;
00071   float threshold = 4.0f;
00072   vil_find_peaks_3x3(pi,pj,corner_im.image(),threshold);
00073 
00074   // Evaluate corner strength at each point (pi[i],pj[i])
00075   unsigned n = pi.size();
00076   vcl_vector<float> corner_str(n);
00077   for (unsigned i=0;i<n;++i)
00078     corner_str[i] = corner_im.image()(pi[i],pj[i]);
00079 
00080   // Sort and generate a list of image points and equivalent world points
00081   vcl_vector<unsigned> index;
00082   mbl_index_sort(corner_str,index);
00083 
00084   unsigned n_c = vcl_min(max_n,n);
00085   vcl_vector<vgl_point_2d<int> > im_pts(n_c);
00086   pts.resize(n_c);
00087   vimt_transform_2d im2w = byte_im.world2im().inverse();
00088   for (unsigned i=0;i<n_c;++i)
00089   {
00090     im_pts[i] = vgl_point_2d<int>(pi[index[n-1-i]],pj[index[n-1-i]]);
00091     pts[i]  = im2w(pi[index[n-1-i]],pj[index[n-1-i]]);
00092   }
00093 }
00094 
00095 //: Sample normalised patches around each point
00096 //  Extract patches with given half-width around each pts[i]
00097 //  When near an image edge, patch may be truncated
00098 //  ref_pts defines position of original point relative to the patch origin
00099 void extract_normalised_patches(const vimt_image& image,
00100                                 const vcl_vector<vgl_point_2d<double> >& pts,
00101                                 int half_width,
00102                                 vcl_vector<vil_image_view<float> >& patch,
00103                                 vcl_vector<vgl_point_2d<double> >& ref_pts)
00104 {
00105   const vimt_image_2d_of<vxl_byte>& byte_im =
00106                static_cast<const vimt_image_2d_of<vxl_byte>&>(image);
00107   int ni = byte_im.image().ni();
00108   int nj = byte_im.image().nj();
00109   unsigned n=pts.size();
00110   for (unsigned i=0;i<n;++i)
00111   {
00112     vgl_point_2d<double> im_p = byte_im.world2im()(pts[i]);
00113     int px = vnl_math_rnd(im_p.x()+0.5);
00114     int py = vnl_math_rnd(im_p.y()+0.5);
00115 
00116     // Select region around point, allowing for image edges.
00117     int ilo = vcl_max(0,px-half_width);
00118     int ihi = vcl_min(ni-1,px+half_width);
00119     int jlo = vcl_max(0,py-half_width);
00120     int jhi = vcl_min(nj-1,py+half_width);
00121 
00122     // Compute position of reference point relative to corner
00123     int kx = px-ilo;
00124     int ky = py-jlo;
00125     ref_pts.push_back(vgl_point_2d<double>(kx,ky));
00126     vil_image_view<float> patch1;
00127     vil_convert_cast(vil_crop(byte_im.image(),ilo,1+ihi-ilo, jlo,1+jhi-jlo),
00128                      patch1);
00129     vil_math_normalise(patch1);
00130     patch.push_back(patch1);
00131   }
00132 }
00133 
00134 //: Return index of closest point in pts[lo,hi]
00135 unsigned closest_pt_index(const vcl_vector<vgl_point_2d<double> >& pts,
00136                           unsigned lo, unsigned hi,
00137                           vgl_point_2d<double> p)
00138 {
00139   unsigned c=lo;
00140   double min_d = (pts[lo]-p).sqr_length();
00141   for (unsigned i=lo+1;i<=hi;++i)
00142   {
00143     double d = (pts[i]-p).sqr_length();
00144     if (d<min_d) {min_d=d; c=i;}
00145   }
00146   return c;
00147 }
00148 
00149 //: Generate a new image with resolution suitable for dest_L
00150 // Requires dest_L>src_L, else dest_im is a (shallow) copy of src_im
00151 void vimt_resample(const vimt_image_2d_of<float>& src_im, int src_L,
00152                    vimt_image_2d_of<float>& dest_im, int dest_L)
00153 {
00154   if (dest_L<=src_L)
00155   {
00156     dest_im=src_im;
00157     return;
00158   }
00159   unsigned ni = src_im.image().ni();
00160   unsigned nj = src_im.image().nj();
00161   unsigned s=1;
00162   for (int L=dest_L;L>=src_L;--L) s*=2;
00163 
00164   vil_resample_bilin(src_im.image(),dest_im.image(),1+s*(ni-1),1+s*(nj-1));
00165   vimt_transform_2d scale;
00166   scale.set_zoom_only(s,s,0.0,0.0);
00167   dest_im.set_world2im(scale*src_im.world2im());
00168 }
00169 
00170 int main( int argc, char* argv[] )
00171 {
00172   vul_arg<vcl_string> image1_path("-i1","Input image 1");
00173   vul_arg<vcl_string> image2_path("-i2","Input image 2");
00174   vul_arg<vcl_string> output_image1_path("-o1","Output image 1","output1.png");
00175   vul_arg<vcl_string> output_image2_path("-o2","Output image 1","output2.png");
00176   vul_arg<int> level_lo("-Llo","Image pyramid level to work on",0);
00177   vul_arg<int> level_hi("-Lhi","Image pyramid level to work on",2);
00178   vul_arg<unsigned> nc("-n","Number of points to select at coarse level",5);
00179   vul_arg<unsigned> w("-w","Half width of filters",7);
00180   vul_arg<double> f("-f","Relative strength of intensity vs shape",0.1);
00181 
00182   vul_arg_parse(argc, argv);
00183 
00184   if (image1_path()=="" || image2_path()=="")
00185   {
00186     print_usage();
00187     return 0;
00188   }
00189 
00190   // ============================================
00191   // Attempt to load in images
00192   // ============================================
00193 
00194   vimt_image_2d_of<vxl_byte> image1,image2;
00195   image1.image() = vil_load(image1_path().c_str());
00196   if (image1.image().size()==0)
00197   {
00198     vcl_cerr<<"Unable to read in image from "<<image1_path()<<vcl_endl;
00199     return 1;
00200   }
00201   image2.image() = vil_load(image2_path().c_str());
00202   if (image2.image().size()==0)
00203   {
00204     vcl_cerr<<"Unable to read in image from "<<image2_path()<<vcl_endl;
00205     return 1;
00206   }
00207 
00208   // ============================================
00209   // Build image pyramids and select chosen level
00210   // ============================================
00211   vimt_gaussian_pyramid_builder_2d<vxl_byte> pyr_builder;
00212   vimt_image_pyramid image_pyr1,image_pyr2;
00213   pyr_builder.build(image_pyr1,image1);
00214   pyr_builder.build(image_pyr2,image2);
00215 
00216   int max_L = vcl_min(image_pyr1.hi(),image_pyr2.hi());
00217   if (level_lo()<0  || level_hi()>max_L  || level_hi()<level_lo())
00218   {
00219     vcl_cerr<<"Levels must be in range [0,"<<max_L<<"], with lo<=hi\n";
00220     return 1;
00221   }
00222 
00223   // ====================================================================
00224   // Create model, consisting of patches and a tree of relative positions
00225   // ====================================================================
00226 
00227   vcl_vector<vgl_point_2d<double> > pts;
00228   vcl_vector<vil_image_view<float> > patch;
00229   vcl_vector<vgl_point_2d<double> > patch_ref;  // Reference point
00230   vcl_vector<vcl_pair<int,int> > pairs;
00231   vcl_vector<unsigned> im_level;  // Image level for each point
00232 
00233   // Search the coarsest image for corners
00234   get_strongest_corners(image_pyr1(level_hi()),pts,nc());
00235 
00236   // Construct tree structure for points
00237   mbl_minimum_spanning_tree(pts,pairs);
00238 
00239   // Extract patches for each point, pushing back onto patch,patch_ref
00240   extract_normalised_patches(image_pyr1(level_hi()),pts,w(),patch,patch_ref);
00241 
00242   for (unsigned i=0;i<pts.size();++i) im_level.push_back(level_hi());
00243 
00244   vcl_cout<<"Found "<<pts.size()<<" points at level "<<level_hi()<<vcl_endl;
00245 
00246   // Generate more points at each level
00247   int max_pts = nc();
00248   unsigned i0 = 0;          // Record index of first point at level above
00249   unsigned i1 = pts.size()-1; // Record index of last point at level above
00250   for (int L=level_hi()-1;L>=level_lo();--L)
00251   {
00252     max_pts*=3;
00253     vcl_vector<vgl_point_2d<double> > L_pts;
00254 
00255     // Search the coarsest image for corners
00256     get_strongest_corners(image_pyr1(L),L_pts,max_pts);
00257 
00258     // Extract patches for each point, pushing back onto patch,patch_ref
00259     extract_normalised_patches(image_pyr1(L),L_pts,w(),patch,patch_ref);
00260 
00261     // For each new point, find the closest point in the levels above
00262     // Change 0,i1 to i0,i1 to consider only level above
00263     for (unsigned i=0;i<L_pts.size();++i)
00264     {
00265       pts.push_back(L_pts[i]);
00266       vcl_pair<int,int> pair_i(pts.size()-1,
00267                                closest_pt_index(pts,0,i1,L_pts[i]));
00268       pairs.push_back(pair_i);
00269       im_level.push_back(L);
00270     }
00271 
00272     vcl_cout<<"Found "<<L_pts.size()<<" points at level "<<L<<vcl_endl;
00273 
00274     i0=i1+1;
00275     i1=pts.size()-1;
00276   }
00277 
00278 
00279   // Draw tree into image for display purposes
00280   draw_tree(image1.image(),pts,pairs);
00281 
00282   if (vil_save(image1.image(),output_image1_path().c_str()))
00283   {
00284     vcl_cout<<"Saved output image 1 to "<<output_image1_path()<<vcl_endl;
00285   }
00286 
00287   // =================================================
00288   // Construct the arc model from the points and pairs
00289   // =================================================
00290 
00291   vcl_vector<fhs_arc> arcs(pairs.size());
00292   int root_node = pairs[0].first;
00293   for (unsigned i=0;i<pairs.size();++i)
00294   {
00295     int i1 = pairs[i].first;
00296     int i2 = pairs[i].second;
00297     vgl_vector_2d<double> dp = pts[i2]-pts[i1];
00298     double sd_x = vcl_max(vcl_pow(2.0,double(im_level[i])),0.2*dp.length());
00299     double sd_y = vcl_max(vcl_pow(2.0,double(im_level[i])),0.2*dp.length());
00300     arcs[i]=fhs_arc(i1,i2,dp.x(),dp.y(),sd_x*sd_x,sd_y*sd_y);
00301   }
00302 
00303   // =================================================
00304   // Apply filters to image2 (initially to whole image)
00305   // =================================================
00306   vcl_vector<vimt_image_2d_of<float> > feature_response(pts.size());
00307   for (unsigned i=0;i<pts.size();++i)
00308   {
00309     const vimt_image_2d_of<vxl_byte>& byte_im =
00310        static_cast<const vimt_image_2d_of<vxl_byte>&>(image_pyr2(im_level[i]));
00311 
00312     // Compute region over which to search (20% of image, centered on point)
00313     int ni=byte_im.image().ni();
00314     int xw = ni/10+1+w();
00315     int nj=byte_im.image().nj();
00316     int yw = nj/10+1+w();
00317     vgl_point_2d<double> im_p = byte_im.world2im()(pts[i]);
00318     int xc = int(0.5+im_p.x());
00319     int yc = int(0.5+im_p.y());
00320     int ilo = vcl_max(0,xc-xw);
00321     int ihi = vcl_min(ni-1,xc+xw);
00322     int jlo = vcl_max(0,yc-yw);
00323     int jhi = vcl_min(nj-1,yc+yw);
00324     vimt_image_2d_of<vxl_byte> cropped_im = vimt_crop(byte_im,
00325                                                       ilo, 1+ihi-ilo,
00326                                                       jlo, 1+jhi-jlo);
00327 
00328     // Apply to whole image in first instance
00329     // Ideally would crop a region around expected position
00330     vimt_normalised_correlation_2d(cropped_im,feature_response[i],
00331                                    patch[i],patch_ref[i],float());
00332     if (im_level[i]!=level_lo())
00333     {
00334       // Resample the feature response at resolution of image level_lo()
00335       vimt_image_2d_of<float> fr;
00336       fr.deep_copy(feature_response[i]);
00337       vimt_resample(fr,im_level[i], feature_response[i],level_lo());
00338     }
00339     // Need good values to be small, not large, so apply -ve factor
00340     vil_math_scale_values(feature_response[i].image(),-f());
00341   }
00342 
00343   // ======================================================
00344   // Use fhs_searcher to locate equivalent points on image2
00345   // ======================================================
00346 
00347   fhs_searcher searcher;
00348   searcher.set_tree(arcs,root_node);
00349   searcher.search(feature_response);
00350   vcl_vector<vgl_point_2d<double> > pts2;
00351   searcher.best_points(pts2);
00352 
00353   // Draw tree into image for display purposes
00354   draw_tree(image2.image(),pts2,pairs);
00355 
00356   if (vil_save(image2.image(),output_image2_path().c_str()))
00357   {
00358     vcl_cout<<"Saved output image 2 to "<<output_image2_path()<<vcl_endl;
00359   }
00360 
00361 
00362   return 0;
00363 }

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