contrib/mul/fhs/tools/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 <vcl_cassert.h>
00007 #include <vul/vul_arg.h>
00008 #include <vimt/vimt_image_2d_of.h>
00009 #include <vimt/vimt_gaussian_pyramid_builder_2d.h>
00010 #include <vimt/vimt_image_pyramid.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 <vgl/vgl_point_2d.h>
00020 #include <vgl/vgl_vector_2d.h>
00021 #include <mbl/mbl_index_sort.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 -L 2\n"
00030           <<"Loads in image1 and image2.\n"
00031           <<"Locates a set of interesting features (corners) on level L of image1.\n"
00032           <<"Constructs a model of their relative positions.\n"
00033           <<"Uses normalised correlation and this model to locate\n"
00034           <<"equivalent points on the same level of the second image."<<vcl_endl;
00035   vul_arg_display_usage_and_exit();
00036 }
00037 
00038 //: Write tree into the image
00039 // Draw disks at each point, and lines between linked points
00040 void draw_tree(vil_image_view<vxl_byte>& image,
00041                const vcl_vector<vgl_point_2d<double> >& pts,
00042                const vcl_vector<vcl_pair<int,int> >& pairs)
00043 {
00044   // Draw tree into image for display purposes
00045   for (unsigned i=0;i<pairs.size();++i)
00046     mbl_draw_line(image,
00047                   pts[pairs[i].first],
00048                   pts[pairs[i].second],vxl_byte(255));
00049 
00050   // Write position of selected points into the original image
00051   // for display purposes.
00052   for (unsigned i=0;i<pts.size();++i)
00053   {
00054     vil_fill_disk(image,pts[i].x(),pts[i].y(),4,vxl_byte(255));
00055   }
00056 }
00057 
00058 int main( int argc, char* argv[] )
00059 {
00060   vul_arg<vcl_string> image1_path("-i1","Input image 1");
00061   vul_arg<vcl_string> image2_path("-i2","Input image 2");
00062   vul_arg<vcl_string> output_image1_path("-o1","Output image 1","output1.png");
00063   vul_arg<vcl_string> output_image2_path("-o2","Output image 1","output2.png");
00064   vul_arg<unsigned> level("-L","Image pyramid level to work on",2);
00065   vul_arg<unsigned> nc("-n","Number of points to select",10);
00066   vul_arg<unsigned> w("-w","Half width of filters",7);
00067   vul_arg<double> f("-f","Relative strength of intensity vs shape",0.1);
00068 
00069   vul_arg_parse(argc, argv);
00070 
00071   if (image1_path()=="" || image2_path()=="")
00072   {
00073     print_usage();
00074     return 0;
00075   }
00076 
00077   // ============================================
00078   // Attempt to load in images
00079   // ============================================
00080 
00081   vimt_image_2d_of<vxl_byte> image1,image2;
00082   image1.image() = vil_load(image1_path().c_str());
00083   if (image1.image().size()==0)
00084   {
00085     vcl_cerr<<"Unable to read in image from "<<image1_path()<<vcl_endl;
00086     return 1;
00087   }
00088   image2.image() = vil_load(image2_path().c_str());
00089   if (image2.image().size()==0)
00090   {
00091     vcl_cerr<<"Unable to read in image from "<<image2_path()<<vcl_endl;
00092     return 1;
00093   }
00094 
00095   // ============================================
00096   // Build image pyramids and select chosen level
00097   // ============================================
00098   vimt_gaussian_pyramid_builder_2d<vxl_byte> pyr_builder;
00099   vimt_image_pyramid image_pyr1,image_pyr2;
00100   pyr_builder.build(image_pyr1,image1);
00101   pyr_builder.build(image_pyr2,image2);
00102 
00103   const vimt_image_2d_of<vxl_byte>& image1_L = static_cast<const vimt_image_2d_of<vxl_byte>&>(image_pyr1(level()));
00104   const vimt_image_2d_of<vxl_byte>& image2_L = static_cast<const vimt_image_2d_of<vxl_byte>&>(image_pyr2(level()));
00105 
00106   // ====================================================
00107   // Apply corner operator to image1_L and select corners
00108   // ====================================================
00109 
00110   vimt_image_2d_of<float> corner_im;
00111   corner_im.set_world2im(image1_L.world2im());
00112   vil_corners(image1_L.image(),corner_im.image());
00113 
00114   vcl_vector<unsigned> pi,pj;
00115   float threshold = 4.0f;
00116   vil_find_peaks_3x3(pi,pj,corner_im.image(),threshold);
00117 
00118   // Evaluate corner strength at each point (pi[i],pj[i])
00119   unsigned n = pi.size();
00120   vcl_vector<float> corner_str(n);
00121   for (unsigned i=0;i<n;++i)
00122     corner_str[i] = corner_im.image()(pi[i],pj[i]);
00123 
00124   // Sort and generate a list of image points and equivalent world points
00125   vcl_vector<unsigned> index;
00126   mbl_index_sort(corner_str,index);
00127 
00128   unsigned n_c = vcl_min(nc(),n);
00129   vcl_vector<vgl_point_2d<int> > im_pts(n_c);
00130   vcl_vector<vgl_point_2d<double> > w_pts(n_c);
00131   vimt_transform_2d im2w = image1_L.world2im().inverse();
00132   for (unsigned i=0;i<n_c;++i)
00133   {
00134     im_pts[i] = vgl_point_2d<int>(pi[index[n-1-i]],pj[index[n-1-i]]);
00135     w_pts[i]  = im2w(pi[index[n-1-i]],pj[index[n-1-i]]);
00136   }
00137 
00138 
00139   // ========================================================
00140   // Extract patches around each selected point and normalise
00141   // ========================================================
00142   vcl_vector<vil_image_view<float> > patch(n_c);
00143   vcl_vector<vgl_point_2d<double> > patch_ref(n_c);  // Reference point
00144   int ni = image1.image().ni();
00145   int nj = image1.image().nj();
00146   for (unsigned i=0;i<n_c;++i)
00147   {
00148     // Select region around point, allowing for image edges.
00149     int ilo = vcl_max(0,int(im_pts[i].x()-w()));
00150     int ihi = vcl_min(ni-1,int(im_pts[i].x()+w()));
00151     int jlo = vcl_max(0,int(im_pts[i].y()-w()));
00152     int jhi = vcl_min(nj-1,int(im_pts[i].y()+w()));
00153 
00154     // Compute position of reference point relative to corner
00155     int kx = im_pts[i].x()-ilo;
00156     int ky = im_pts[i].y()-jlo;
00157     patch_ref[i] =vgl_point_2d<double>(kx,ky);
00158     vil_convert_cast(vil_crop(image1_L.image(),ilo,1+ihi-ilo, jlo,1+jhi-jlo),
00159                      patch[i]);
00160     vil_math_normalise(patch[i]);
00161   }
00162 
00163   // Construct tree structure for points
00164   vcl_vector<vcl_pair<int,int> > pairs;
00165   mbl_minimum_spanning_tree(w_pts,pairs);
00166 
00167   assert(pairs.size()==n_c-1);
00168 
00169   // Draw tree into image for display purposes
00170   draw_tree(image1.image(),w_pts,pairs);
00171 
00172   if (vil_save(image1.image(),output_image1_path().c_str()))
00173   {
00174     vcl_cout<<"Saved output image 1 to "<<output_image1_path()<<vcl_endl;
00175   }
00176 
00177   // =================================================
00178   // Construct the arc model from the points and pairs
00179   // =================================================
00180 
00181   vcl_vector<fhs_arc> arcs(n_c-1);
00182   int root_node = pairs[0].first;
00183   for (unsigned i=0;i<pairs.size();++i)
00184   {
00185     int i1 = pairs[i].first;
00186     int i2 = pairs[i].second;
00187     vgl_vector_2d<double> dp = w_pts[i2]-w_pts[i1];
00188     double sd_x = vcl_max(0.1*ni,0.2*dp.length());
00189     double sd_y = vcl_max(0.1*nj,0.2*dp.length());
00190     arcs[i]=fhs_arc(i1,i2,dp.x(),dp.y(),sd_x*sd_x,sd_y*sd_y);
00191   }
00192 
00193   // =================================================
00194   // Apply filters to image2 (initially to whole image)
00195   // =================================================
00196   vcl_vector<vimt_image_2d_of<float> > feature_response(n_c);
00197   for (unsigned i=0;i<n_c;++i)
00198   {
00199     // Apply to whole image in first instance
00200     // Ideally would crop a region around expected position
00201     vimt_normalised_correlation_2d(image2_L,feature_response[i],
00202                                    patch[i],patch_ref[i],float());
00203 
00204     // Need good values to be small, not large, so apply -ve factor
00205     vil_math_scale_values(feature_response[i].image(),-f());
00206   }
00207 
00208   // ======================================================
00209   // Use fhs_searcher to locate equivalent points on image2
00210   // ======================================================
00211 
00212   fhs_searcher searcher;
00213   searcher.set_tree(arcs,root_node);
00214   searcher.search(feature_response);
00215   vcl_vector<vgl_point_2d<double> > pts2;
00216   searcher.best_points(pts2);
00217 
00218   // Draw tree into image for display purposes
00219   draw_tree(image2.image(),pts2,pairs);
00220 
00221   if (vil_save(image2.image(),output_image2_path().c_str()))
00222   {
00223     vcl_cout<<"Saved output image 2 to "<<output_image2_path()<<vcl_endl;
00224   }
00225 
00226   return 0;
00227 }