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 <vgl/vgl_point_2d.h>
00023 #include <vgl/vgl_vector_2d.h>
00024 #include <fhs/fhs_searcher.h>
00025 #include <vimt/algo/vimt_normalised_correlation_2d.h>
00026 #include <mbl/mbl_minimum_spanning_tree.h>
00027 #include <mbl/mbl_draw_line.h>
00028
00029 void print_usage()
00030 {
00031 vcl_cout<<"find_matches -i1 image1.jpg -i2 image2.jpg -Llo 0 Lhi 2\n"
00032 <<"Loads in image1 and image2.\n"
00033 <<"Locates a set of interesting features (corners) on levels of image1.\n"
00034 <<"Constructs a tree model of their relative positions.\n"
00035 <<"Points at level L are linked to the nearest points at level L+1\n"
00036 <<"Uses normalised correlation and this model to locate\n"
00037 <<"equivalent points on the second image."<<vcl_endl;
00038 vul_arg_display_usage_and_exit();
00039 }
00040
00041
00042
00043 void draw_tree(vil_image_view<vxl_byte>& image,
00044 const vcl_vector<vgl_point_2d<double> >& pts,
00045 const vcl_vector<vcl_pair<int,int> >& pairs)
00046 {
00047
00048 for (unsigned i=0;i<pairs.size();++i)
00049 mbl_draw_line(image,
00050 pts[pairs[i].first],
00051 pts[pairs[i].second],vxl_byte(255));
00052
00053
00054
00055 for (unsigned i=0;i<pts.size();++i)
00056 {
00057 vil_fill_disk(image,pts[i].x(),pts[i].y(),4,vxl_byte(255));
00058 }
00059 }
00060
00061
00062 void get_strongest_corners(const vimt_image& image,
00063 vcl_vector<vgl_point_2d<double> >& pts,
00064 unsigned max_n)
00065 {
00066 const vimt_image_2d_of<vxl_byte>& byte_im =
00067 static_cast<const vimt_image_2d_of<vxl_byte>&>(image);
00068 vimt_image_2d_of<float> corner_im;
00069 corner_im.set_world2im(byte_im.world2im());
00070 vil_corners(byte_im.image(),corner_im.image());
00071
00072 vcl_vector<unsigned> pi,pj;
00073 float threshold = 4.0f;
00074 vil_find_peaks_3x3(pi,pj,corner_im.image(),threshold);
00075
00076
00077 unsigned n = pi.size();
00078 vcl_vector<float> corner_str(n);
00079 for (unsigned i=0;i<n;++i)
00080 corner_str[i] = corner_im.image()(pi[i],pj[i]);
00081
00082
00083 vcl_vector<unsigned> index;
00084 mbl_index_sort(corner_str,index);
00085
00086 unsigned n_c = vcl_min(max_n,n);
00087 vcl_vector<vgl_point_2d<int> > im_pts(n_c);
00088 pts.resize(n_c);
00089 vimt_transform_2d im2w = byte_im.world2im().inverse();
00090 for (unsigned i=0;i<n_c;++i)
00091 {
00092 im_pts[i] = vgl_point_2d<int>(pi[index[n-1-i]],pj[index[n-1-i]]);
00093 pts[i] = im2w(pi[index[n-1-i]],pj[index[n-1-i]]);
00094 }
00095 }
00096
00097
00098
00099
00100
00101 void extract_normalised_patches(const vimt_image& image,
00102 const vcl_vector<vgl_point_2d<double> >& pts,
00103 int half_width,
00104 vcl_vector<vil_image_view<float> >& patch,
00105 vcl_vector<vgl_point_2d<double> >& ref_pts)
00106 {
00107 const vimt_image_2d_of<vxl_byte>& byte_im =
00108 static_cast<const vimt_image_2d_of<vxl_byte>&>(image);
00109 int ni = byte_im.image().ni();
00110 int nj = byte_im.image().nj();
00111 unsigned n=pts.size();
00112 for (unsigned i=0;i<n;++i)
00113 {
00114 vgl_point_2d<double> im_p = byte_im.world2im()(pts[i]);
00115 int px = vnl_math_rnd(im_p.x()+0.5);
00116 int py = vnl_math_rnd(im_p.y()+0.5);
00117
00118
00119 int ilo = vcl_max(0,px-half_width);
00120 int ihi = vcl_min(ni-1,px+half_width);
00121 int jlo = vcl_max(0,py-half_width);
00122 int jhi = vcl_min(nj-1,py+half_width);
00123
00124
00125 int kx = px-ilo;
00126 int ky = py-jlo;
00127 ref_pts.push_back(vgl_point_2d<double>(kx,ky));
00128 vil_image_view<float> patch1;
00129 vil_convert_cast(vil_crop(byte_im.image(),ilo,1+ihi-ilo, jlo,1+jhi-jlo),
00130 patch1);
00131 vil_math_normalise(patch1);
00132 patch.push_back(patch1);
00133 }
00134 }
00135
00136
00137 unsigned closest_pt_index(const vcl_vector<vgl_point_2d<double> >& pts,
00138 unsigned lo, unsigned hi,
00139 vgl_point_2d<double> p)
00140 {
00141 unsigned c=lo;
00142 double min_d = (pts[lo]-p).sqr_length();
00143 for (unsigned i=lo+1;i<=hi;++i)
00144 {
00145 double d = (pts[i]-p).sqr_length();
00146 if (d<min_d) {min_d=d; c=i;}
00147 }
00148 return c;
00149 }
00150
00151
00152
00153 void vimt_resample(const vimt_image_2d_of<float>& src_im, int src_L,
00154 vimt_image_2d_of<float>& dest_im, int dest_L)
00155 {
00156 if (dest_L<=src_L)
00157 {
00158 dest_im=src_im;
00159 return;
00160 }
00161 unsigned ni = src_im.image().ni();
00162 unsigned nj = src_im.image().nj();
00163 unsigned s=1;
00164 for (int L=dest_L;L>=src_L;--L) s*=2;
00165
00166 vil_resample_bilin(src_im.image(),dest_im.image(),1+s*(ni-1),1+s*(nj-1));
00167 vimt_transform_2d scale;
00168 scale.set_zoom_only(s,s,0.0,0.0);
00169 dest_im.set_world2im(scale*src_im.world2im());
00170 }
00171
00172 int main( int argc, char* argv[] )
00173 {
00174 vul_arg<vcl_string> image1_path("-i1","Input image 1");
00175 vul_arg<vcl_string> image2_path("-i2","Input image 2");
00176 vul_arg<vcl_string> output_image1_path("-o1","Output image 1","output1.png");
00177 vul_arg<vcl_string> output_image2_path("-o2","Output image 1","output2.png");
00178 vul_arg<int> level_lo("-Llo","Image pyramid level to work on",0);
00179 vul_arg<int> level_hi("-Lhi","Image pyramid level to work on",2);
00180 vul_arg<unsigned> nc("-n","Number of points to select at coarse level",5);
00181 vul_arg<unsigned> w("-w","Half width of filters",7);
00182 vul_arg<double> f("-f","Relative strength of intensity vs shape",0.1);
00183
00184 vul_arg_parse(argc, argv);
00185
00186 if (image1_path()=="" || image2_path()=="")
00187 {
00188 print_usage();
00189 return 0;
00190 }
00191
00192
00193
00194
00195
00196 vimt_image_2d_of<vxl_byte> image1,image2;
00197 image1.image() = vil_load(image1_path().c_str());
00198 if (image1.image().size()==0)
00199 {
00200 vcl_cerr<<"Unable to read in image from "<<image1_path()<<'\n';
00201 return 1;
00202 }
00203 image2.image() = vil_load(image2_path().c_str());
00204 if (image2.image().size()==0)
00205 {
00206 vcl_cerr<<"Unable to read in image from "<<image2_path()<<'\n';
00207 return 1;
00208 }
00209
00210
00211
00212
00213 vimt_gaussian_pyramid_builder_2d<vxl_byte> pyr_builder;
00214 vimt_image_pyramid image_pyr1,image_pyr2;
00215 pyr_builder.build(image_pyr1,image1);
00216 pyr_builder.build(image_pyr2,image2);
00217
00218 int max_L = vcl_min(image_pyr1.hi(),image_pyr2.hi());
00219 if (level_lo()<0 || level_hi()>max_L || level_hi()<level_lo())
00220 {
00221 vcl_cerr<<"Levels must be in range [0,"<<max_L<<"], with lo<=hi\n";
00222 return 1;
00223 }
00224
00225
00226
00227
00228
00229 vcl_vector<vgl_point_2d<double> > pts;
00230 vcl_vector<vil_image_view<float> > patch;
00231 vcl_vector<vgl_point_2d<double> > patch_ref;
00232 vcl_vector<vcl_pair<int,int> > pairs;
00233 vcl_vector<unsigned> im_level;
00234
00235
00236 get_strongest_corners(image_pyr1(level_hi()),pts,nc());
00237
00238
00239 mbl_minimum_spanning_tree(pts,pairs);
00240
00241
00242 extract_normalised_patches(image_pyr1(level_hi()),pts,w(),patch,patch_ref);
00243
00244 for (unsigned i=0;i<pts.size();++i) im_level.push_back(level_hi());
00245
00246 vcl_cout<<"Found "<<pts.size()<<" points at level "<<level_hi()<<vcl_endl;
00247
00248
00249 int max_pts = nc();
00250 unsigned i0 = 0;
00251 unsigned i1 = pts.size()-1;
00252 for (int L=level_hi()-1;L>=level_lo();--L)
00253 {
00254 max_pts*=3;
00255 vcl_vector<vgl_point_2d<double> > L_pts;
00256
00257
00258 get_strongest_corners(image_pyr1(L),L_pts,max_pts);
00259
00260
00261 extract_normalised_patches(image_pyr1(L),L_pts,w(),patch,patch_ref);
00262
00263
00264
00265 for (unsigned i=0;i<L_pts.size();++i)
00266 {
00267 pts.push_back(L_pts[i]);
00268 vcl_pair<int,int> pair_i(pts.size()-1,
00269 closest_pt_index(pts,0,i1,L_pts[i]));
00270 pairs.push_back(pair_i);
00271 im_level.push_back(L);
00272 }
00273
00274 vcl_cout<<"Found "<<L_pts.size()<<" points at level "<<L<<vcl_endl;
00275
00276 i0=i1+1;
00277 i1=pts.size()-1;
00278 }
00279
00280
00281
00282 draw_tree(image1.image(),pts,pairs);
00283
00284 if (vil_save(image1.image(),output_image1_path().c_str()))
00285 {
00286 vcl_cout<<"Saved output image 1 to "<<output_image1_path()<<vcl_endl;
00287 }
00288
00289
00290
00291
00292
00293 vcl_vector<fhs_arc> arcs(pairs.size());
00294 int root_node = pairs[0].first;
00295 for (unsigned i=0;i<pairs.size();++i)
00296 {
00297 int i1 = pairs[i].first;
00298 int i2 = pairs[i].second;
00299 vgl_vector_2d<double> dp = pts[i2]-pts[i1];
00300 double sd_x = vcl_max(double(1<<im_level[i]),0.2*dp.length());
00301 double sd_y = vcl_max(double(1<<im_level[i]),0.2*dp.length());
00302 arcs[i]=fhs_arc(i1,i2,dp.x(),dp.y(),sd_x*sd_x,sd_y*sd_y);
00303 }
00304
00305
00306
00307
00308 vcl_vector<vimt_image_2d_of<float> > feature_response(pts.size());
00309 for (unsigned i=0;i<pts.size();++i)
00310 {
00311 const vimt_image_2d_of<vxl_byte>& byte_im =
00312 static_cast<const vimt_image_2d_of<vxl_byte>&>(image_pyr2(im_level[i]));
00313
00314
00315 int ni=byte_im.image().ni();
00316 int xw = ni/10+1+w();
00317 int nj=byte_im.image().nj();
00318 int yw = nj/10+1+w();
00319 vgl_point_2d<double> im_p = byte_im.world2im()(pts[i]);
00320 int xc = int(0.5+im_p.x());
00321 int yc = int(0.5+im_p.y());
00322 int ilo = vcl_max(0,xc-xw);
00323 int ihi = vcl_min(ni-1,xc+xw);
00324 int jlo = vcl_max(0,yc-yw);
00325 int jhi = vcl_min(nj-1,yc+yw);
00326 vimt_image_2d_of<vxl_byte> cropped_im = vimt_crop(byte_im,
00327 ilo, 1+ihi-ilo,
00328 jlo, 1+jhi-jlo);
00329
00330
00331
00332 vimt_normalised_correlation_2d(cropped_im,feature_response[i],
00333 patch[i],patch_ref[i],float());
00334 if (im_level[i]!=(unsigned int)level_lo())
00335 {
00336
00337 vimt_image_2d_of<float> fr;
00338 fr.deep_copy(feature_response[i]);
00339 vimt_resample(fr,im_level[i], feature_response[i],level_lo());
00340 }
00341
00342 vil_math_scale_values(feature_response[i].image(),-f());
00343 }
00344
00345
00346
00347
00348
00349 fhs_searcher searcher;
00350 searcher.set_tree(arcs,root_node);
00351 searcher.search(feature_response);
00352 vcl_vector<vgl_point_2d<double> > pts2;
00353 searcher.best_points(pts2);
00354
00355
00356 draw_tree(image2.image(),pts2,pairs);
00357
00358 if (vil_save(image2.image(),output_image2_path().c_str()))
00359 {
00360 vcl_cout<<"Saved output image 2 to "<<output_image2_path()<<vcl_endl;
00361 }
00362
00363
00364 return 0;
00365 }