00001
00002
00003
00004
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
00040
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
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
00052
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
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
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
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
00096
00097
00098
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
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
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
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
00150
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
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
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
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;
00230 vcl_vector<vcl_pair<int,int> > pairs;
00231 vcl_vector<unsigned> im_level;
00232
00233
00234 get_strongest_corners(image_pyr1(level_hi()),pts,nc());
00235
00236
00237 mbl_minimum_spanning_tree(pts,pairs);
00238
00239
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
00247 int max_pts = nc();
00248 unsigned i0 = 0;
00249 unsigned i1 = pts.size()-1;
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
00256 get_strongest_corners(image_pyr1(L),L_pts,max_pts);
00257
00258
00259 extract_normalised_patches(image_pyr1(L),L_pts,w(),patch,patch_ref);
00260
00261
00262
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
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
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
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
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
00329
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
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
00340 vil_math_scale_values(feature_response[i].image(),-f());
00341 }
00342
00343
00344
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
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 }