00001 #include "mfpf_point_finder.h"
00002
00003
00004
00005
00006
00007 #include <vsl/vsl_indent.h>
00008 #include <vsl/vsl_binary_loader.h>
00009
00010 #include <vcl_cmath.h>
00011 #include <vimt/algo/vimt_find_troughs.h>
00012 #include <vimt/vimt_image_pyramid.h>
00013 #include <vnl/vnl_cost_function.h>
00014 #include <vnl/algo/vnl_amoeba.h>
00015 #include <vnl/algo/vnl_powell.h>
00016 #include <vnl/vnl_math.h>
00017 #include <vgl/vgl_point_2d.h>
00018 #include <vil/vil_print.h>
00019
00020
00021
00022
00023
00024
00025 inline double parabolic_min(double fa, double fb, double fc)
00026 {
00027 double df1=fa-fb;
00028 double df2=fc-fb;
00029 double d=df1+df2;
00030 if (vcl_fabs(d)<1e-6) return 0.0;
00031 else return 0.5*(df1-df2)/d;
00032 }
00033
00034 class mfpf_pf_cost : public vnl_cost_function
00035 {
00036 private:
00037 mfpf_point_finder* pf_;
00038 const vimt_image_2d_of<float>& image_;
00039 vgl_point_2d<double> p_;
00040 vgl_vector_2d<double> u_;
00041 vgl_vector_2d<double> v_;
00042 double ds_,dA_;
00043 public:
00044 virtual ~mfpf_pf_cost() {}
00045 mfpf_pf_cost(mfpf_point_finder& pf,
00046 const vimt_image_2d_of<float>& image,
00047 const vgl_point_2d<double>& p0,
00048 const vgl_vector_2d<double>& u0,
00049 double ds, double dA)
00050 : vnl_cost_function(4),
00051 pf_(&pf),image_(image),p_(p0),
00052 u_(u0),v_(-u_.y(),u_.x()),
00053 ds_(ds),dA_(dA) {}
00054
00055
00056 virtual double f(const vnl_vector<double>& params);
00057
00058 void get_pose(const vnl_vector<double>& params,
00059 vgl_point_2d<double>& p,
00060 vgl_vector_2d<double>& u) const;
00061
00062
00063
00064
00065
00066 bool check_and_refine_minima(vnl_vector<double>& params,
00067 double &fit,
00068 unsigned index, double dx);
00069 };
00070
00071 double mfpf_pf_cost::f(const vnl_vector<double>& params)
00072 {
00073 vgl_point_2d<double> p;
00074 vgl_vector_2d<double> u;
00075 get_pose(params,p,u);
00076 return pf_->evaluate(image_,p,u);
00077 }
00078
00079 void mfpf_pf_cost::get_pose(const vnl_vector<double>& v,
00080 vgl_point_2d<double>& p,
00081 vgl_vector_2d<double>& u) const
00082 {
00083 p = p_+pf_->step_size()*(v[0]*u_+v[1]*v_);
00084 double s = vcl_pow(ds_,v[2]);
00085 double A = dA_*v[3];
00086 u = s*(u_*vcl_cos(A)+v_*vcl_sin(A));
00087 }
00088
00089
00090
00091
00092
00093 bool mfpf_pf_cost::check_and_refine_minima(vnl_vector<double>& params,
00094 double &fit,
00095 unsigned index, double dx)
00096 {
00097 double p0 = params[index];
00098 params[index]=p0-dx;
00099 double f1=f(params);
00100 if (f1<fit) { fit=f1; return false; }
00101
00102 params[index]=p0+dx;
00103 double f2=f(params);
00104 if (f2<fit) { fit=f2; return false; }
00105
00106 double a = parabolic_min(f1,fit,f2);
00107 params[index]=p0+a*dx;
00108 double f3=f(params);
00109 if (f3<fit) { fit=f3; return true; }
00110
00111
00112 params[index]=p0;
00113 return true;
00114 }
00115
00116
00117
00118
00119
00120 mfpf_point_finder::mfpf_point_finder()
00121 : step_size_(1.0),
00122 search_ni_(5),search_nj_(0),
00123 nA_(0),dA_(0.0),ns_(0),ds_(1.0)
00124 {
00125 }
00126
00127
00128
00129
00130
00131 mfpf_point_finder::~mfpf_point_finder()
00132 {
00133 }
00134
00135
00136 void mfpf_point_finder::set_step_size(double s)
00137 {
00138 step_size_=s;
00139 }
00140
00141
00142
00143
00144 void mfpf_point_finder::set_search_area(unsigned ni, unsigned nj)
00145 {
00146 search_ni_=ni;
00147 search_nj_=nj;
00148 }
00149
00150
00151 void mfpf_point_finder::set_angle_range(unsigned nA, double dA)
00152 {
00153 nA_=nA;
00154 dA_=dA;
00155 }
00156
00157
00158 void mfpf_point_finder::set_scale_range(unsigned ns, double ds)
00159 {
00160 ns_=ns;
00161 ds_=ds;
00162 }
00163
00164
00165 bool mfpf_point_finder::set_model(const vcl_vector<double>& )
00166 {
00167 return false;
00168 }
00169
00170
00171 unsigned mfpf_point_finder::model_dim()
00172 {
00173 return 0;
00174 }
00175
00176
00177 void mfpf_point_finder::get_sample_vector(const vimt_image_2d_of<float>& image,
00178 const vgl_point_2d<double>& p,
00179 const vgl_vector_2d<double>& u,
00180 vcl_vector<double>& v)
00181 {
00182
00183 v.resize(0);
00184 }
00185
00186
00187
00188
00189
00190
00191
00192
00193 double mfpf_point_finder::search_one_pose_with_opt(
00194 const vimt_image_2d_of<float>& image,
00195 const vgl_point_2d<double>& p0,
00196 const vgl_vector_2d<double>& u,
00197 vgl_point_2d<double>& new_p)
00198 {
00199 vimt_image_2d_of<double> response_im;
00200 evaluate_region(image,p0,u,response_im);
00201
00202 vimt_transform_2d im2w = response_im.world2im().inverse();
00203 const vil_image_view<double>& r_im = response_im.image();
00204
00205
00206 unsigned x=0,y=0;
00207 double f0 = r_im(x,y);
00208 for (unsigned j=0;j<r_im.nj();++j)
00209 for (unsigned i=0;i<r_im.ni();++i)
00210 {
00211 if (r_im(i,j)<f0) { f0=r_im(i,j); x=i; y=j; }
00212 }
00213
00214
00215 double dx=0.0;
00216 if (x>0 && x+1<r_im.ni())
00217 dx = parabolic_min(r_im(x-1,y ),f0,r_im(x+1,y ));
00218
00219 double dy=0.0;
00220 if (y>0 && y+1<r_im.nj())
00221 dy = parabolic_min(r_im(x ,y-1),f0,r_im(x ,y+1));
00222
00223
00224 new_p=im2w(x+dx,y+dy);
00225
00226 if (dx==0.0 && dy==0.0) return f0;
00227
00228 double f1=evaluate(image,new_p,u);
00229 if (f1<f0) return f1;
00230
00231
00232 new_p=p0;
00233 return f0;
00234 }
00235
00236
00237
00238
00239
00240
00241 double mfpf_point_finder::search(const vimt_image_2d_of<float>& image,
00242 const vgl_point_2d<double>& p,
00243 const vgl_vector_2d<double>& u,
00244 vgl_point_2d<double>& new_p,
00245 vgl_vector_2d<double>& new_u)
00246 {
00247 if (nA_==0 && ns_==0)
00248 {
00249
00250 new_u = u;
00251 return search_one_pose(image,p,u,new_p);
00252 }
00253
00254 double best_fit=9e99;
00255 vgl_point_2d<double> p1;
00256
00257 vgl_vector_2d<double> v(-u.y(),u.x());
00258
00259 for (int is=-int(ns_);is<=int(ns_);++is)
00260 {
00261 double s = vcl_pow(ds_,is);
00262 for (int iA=-int(nA_);iA<=int(nA_);++iA)
00263 {
00264 double A = iA*dA_;
00265 vgl_vector_2d<double> uA = s*(u*vcl_cos(A)+v*vcl_sin(A));
00266
00267 double f = search_one_pose(image,p,uA,p1);
00268 if (f<best_fit)
00269 {
00270 best_fit = f;
00271 new_u = uA;
00272 new_p = p1;
00273 }
00274 }
00275 }
00276 return best_fit;
00277 }
00278
00279
00280
00281
00282
00283 double mfpf_point_finder::search_with_opt(
00284 const vimt_image_2d_of<float>& image,
00285 const vgl_point_2d<double>& p,
00286 const vgl_vector_2d<double>& u,
00287 vgl_point_2d<double>& new_p,
00288 vgl_vector_2d<double>& new_u)
00289 {
00290 if (nA_==0 && ns_==0)
00291 {
00292
00293 new_u = u;
00294 return search_one_pose_with_opt(image,p,u,new_p);
00295 }
00296
00297 double best_fit=1e99;
00298 vgl_point_2d<double> p1;
00299
00300 vgl_vector_2d<double> v(-u.y(),u.x());
00301
00302 for (int is=-int(ns_);is<=int(ns_);++is)
00303 {
00304 double s = vcl_pow(ds_,is);
00305 for (int iA=-int(nA_);iA<=int(nA_);++iA)
00306 {
00307 double A = iA*dA_;
00308 vgl_vector_2d<double> uA = s*(u*vcl_cos(A)+v*vcl_sin(A));
00309
00310 double f = search_one_pose_with_opt(image,p,uA,p1);
00311 if (f<best_fit)
00312 {
00313 best_fit = f;
00314 new_u = uA;
00315 new_p = p1;
00316 }
00317 }
00318 }
00319
00320 mfpf_pf_cost pf_cost(*this,image,new_p,new_u,1+0.7*(ds_-1),0.7*dA_);
00321 vnl_vector<double> params(4,0.0);
00322
00323 pf_cost.check_and_refine_minima(params,best_fit,2,1.0);
00324
00325 pf_cost.check_and_refine_minima(params,best_fit,3,1.0);
00326
00327
00328 pf_cost.get_pose(params,new_p,new_u);
00329
00330 return best_fit;
00331 }
00332
00333
00334
00335
00336
00337 void mfpf_point_finder::grid_search_one_pose(
00338 const vimt_image_2d_of<float>& image,
00339 const vgl_point_2d<double>& p,
00340 const vgl_vector_2d<double>& u,
00341 vcl_vector<mfpf_pose>& pts,
00342 vcl_vector<double>& fit)
00343 {
00344 vimt_image_2d_of<double> response_im;
00345 evaluate_region(image,p,u,response_im);
00346
00347 vcl_vector<vgl_point_2d<unsigned> > t_pts;
00348 vcl_vector<double> t_value;
00349 vimt_find_image_troughs_3x3(t_pts,t_value,response_im.image());
00350 vimt_transform_2d im2w = response_im.world2im().inverse();
00351
00352 for (unsigned i=0;i<t_pts.size();++i)
00353 {
00354 unsigned x =t_pts[i].x(), y=t_pts[i].y();
00355 fit.push_back(t_value[i]);
00356 pts.push_back(mfpf_pose(im2w(x,y),u));
00357 }
00358 }
00359
00360
00361
00362
00363
00364
00365
00366
00367 void mfpf_point_finder::multi_search_one_pose(
00368 const vimt_image_2d_of<float>& image,
00369 const vgl_point_2d<double>& p0,
00370 const vgl_vector_2d<double>& u,
00371 vcl_vector<mfpf_pose>& pts,
00372 vcl_vector<double>& fit)
00373 {
00374 vimt_image_2d_of<double> response_im;
00375 evaluate_region(image,p0,u,response_im);
00376
00377 const vil_image_view<double>& r_im = response_im.image();
00378
00379 vcl_vector<vgl_point_2d<unsigned> > t_pts;
00380 vimt_find_image_troughs_3x3(t_pts,r_im);
00381 vimt_transform_2d im2w = response_im.world2im().inverse();
00382
00383 for (unsigned i=0;i<t_pts.size();++i)
00384 {
00385 unsigned x =t_pts[i].x(), y=t_pts[i].y();
00386 double f0 = r_im(x,y);
00387
00388 if (vnl_math_isnan(f0))
00389 {
00390 vcl_cerr<<"mfpf_point_finder::multi_search_one_pose()\n"
00391 <<"Response was a NaN at "<<x<<','<<y<<vcl_endl
00392 <<"Reponse image: "<<response_im.image()<<vcl_endl;
00393 vil_print_all(vcl_cout,response_im.image());
00394 vcl_abort();
00395 }
00396
00397
00398 double dx=parabolic_min(r_im(x-1,y ),f0,r_im(x+1,y ));
00399 double dy=parabolic_min(r_im(x ,y-1),f0,r_im(x ,y+1));
00400
00401
00402 vgl_point_2d<double> p1=im2w(x+dx,y+dy);
00403 vgl_vector_2d<double> u1=u;
00404 double f1=evaluate(image,p1,u);
00405 if (f1>=f0)
00406 {
00407
00408 f1=f0;
00409 p1=im2w(x,y);
00410 }
00411
00412
00413
00414 mfpf_pf_cost pf_cost(*this,image,p1,u1,ds_,dA_);
00415 vnl_vector<double> params(4,0.0);
00416
00417 if (vcl_fabs(ds_-1.0)>1e-6)
00418 {
00419
00420 pf_cost.check_and_refine_minima(params,f1,2,0.7);
00421
00422
00423
00424
00425 }
00426
00427
00428 if (dA_!=0.0)
00429 {
00430
00431 pf_cost.check_and_refine_minima(params,f1,3,0.7);
00432 }
00433
00434
00435 pf_cost.get_pose(params,p1,u1);
00436
00437 fit.push_back(f1);
00438 pts.push_back(mfpf_pose(p1,u1));
00439 }
00440 }
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452 void mfpf_point_finder::grid_search(const vimt_image_2d_of<float>& image,
00453 const vgl_point_2d<double>& p,
00454 const vgl_vector_2d<double>& u,
00455 vcl_vector<mfpf_pose>& pts,
00456 vcl_vector<double>& fit)
00457 {
00458 pts.resize(0);
00459 fit.resize(0);
00460
00461 if (nA_==0 && ns_==0)
00462 {
00463
00464 return grid_search_one_pose(image,p,u,pts,fit);
00465 }
00466
00467 vgl_vector_2d<double> v(-u.y(),u.x());
00468
00469 for (int is=-int(ns_);is<=int(ns_);++is)
00470 {
00471 double s = vcl_pow(ds_,is);
00472 for (int iA=-int(nA_);iA<=int(nA_);++iA)
00473 {
00474 double A = iA*dA_;
00475 vgl_vector_2d<double> uA = s*(u*vcl_cos(A)+v*vcl_sin(A));
00476
00477 grid_search_one_pose(image,p,uA,pts,fit);
00478 }
00479 }
00480 }
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493 void mfpf_point_finder::multi_search(const vimt_image_2d_of<float>& image,
00494 const vgl_point_2d<double>& p,
00495 const vgl_vector_2d<double>& u,
00496 vcl_vector<mfpf_pose>& poses,
00497 vcl_vector<double>& fits)
00498 {
00499 poses.resize(0);
00500 fits.resize(0);
00501
00502 if (nA_==0 && ns_==0)
00503 {
00504
00505 return multi_search_one_pose(image,p,u,poses,fits);
00506 }
00507
00508 vgl_vector_2d<double> v(-u.y(),u.x());
00509
00510 for (int is=-int(ns_);is<=int(ns_);++is)
00511 {
00512 double s = vcl_pow(ds_,is);
00513 for (int iA=-int(nA_);iA<=int(nA_);++iA)
00514 {
00515 double A = iA*dA_;
00516 vgl_vector_2d<double> uA = s*(u*vcl_cos(A)+v*vcl_sin(A));
00517
00518 multi_search_one_pose(image,p,uA,poses,fits);
00519 }
00520 }
00521 }
00522
00523
00524
00525
00526 void mfpf_point_finder::refine_match(const vimt_image_2d_of<float>& image,
00527 vgl_point_2d<double>& p,
00528 vgl_vector_2d<double>& u,
00529 double& fit)
00530 {
00531
00532
00533 double ds=1.5;
00534 double dA=0.5;
00535 mfpf_pf_cost cost_fn(*this,image,p,u,ds,dA);
00536
00537 vnl_vector<double> initial_dv(4,0.5), v(4,0.0);
00538
00539 vnl_amoeba amoeba(cost_fn);
00540
00541
00542 amoeba.set_x_tolerance(0.1);
00543 amoeba.set_f_tolerance(9e9);
00544
00545
00546
00547 amoeba.minimize(v, initial_dv);
00548
00549 #if 0
00550
00551
00552 vnl_powell powell(&cost_fn);
00553 powell.set_initial_step(0.5);
00554 powell.set_linmin_xtol(0.1);
00555 powell.minimize(v);
00556 #endif // 0
00557
00558 fit = cost_fn.f(v);
00559 cost_fn.get_pose(v,p,u);
00560 }
00561
00562
00563
00564
00565
00566 void mfpf_point_finder::get_image_of_model(vimt_image_2d_of<vxl_byte>& image) const
00567 {
00568
00569 image.image().set_size(0,0);
00570 }
00571
00572
00573
00574
00575 unsigned mfpf_point_finder::image_level(const mfpf_pose& pose,
00576 const vimt_image_pyramid& im_pyr) const
00577 {
00578 double model_pixel_size = step_size()*pose.scale();
00579 double rel_size0 = model_pixel_size/im_pyr.base_pixel_width();
00580
00581 double log_step = vcl_log(im_pyr.scale_step());
00582
00583
00584 int level = int(vcl_log(rel_size0)/log_step);
00585 if (level<im_pyr.lo()) return im_pyr.lo();
00586 else if (level>im_pyr.hi()) return im_pyr.hi();
00587 else return level;
00588 }
00589
00590
00591 bool mfpf_point_finder::overlap(const mfpf_pose& pose1,
00592 const mfpf_pose& pose2) const
00593 {
00594 return false;
00595 }
00596
00597
00598
00599
00600 void mfpf_point_finder::aligned_bounding_box(const mfpf_pose& pose,
00601 mfpf_pose& box_pose,
00602 double& wi, double& wj) const
00603 {
00604
00605 vcl_vector<vgl_point_2d<double> > pts;
00606 get_outline(pts);
00607 double xlo=pts[0].x(), xhi=xlo;
00608 double ylo=pts[0].y(), yhi=ylo;
00609 for (unsigned i=1;i<pts.size();++i)
00610 {
00611 if (pts[i].x()<xlo) xlo=pts[i].x();
00612 else if (pts[i].x()>xhi) xhi=pts[i].x();
00613 if (pts[i].y()<ylo) ylo=pts[i].y();
00614 else if (pts[i].y()>yhi) yhi=pts[i].y();
00615 }
00616
00617
00618 mfpf_pose trans(0.5*(xlo+xhi),0.5*(ylo+yhi),step_size(),0);
00619 box_pose = pose*trans;
00620
00621 wi=xhi-xlo;
00622 wj=yhi-ylo;
00623 }
00624
00625
00626
00627 bool mfpf_point_finder::base_equality(const mfpf_point_finder& pf) const
00628 {
00629 return
00630 search_ni_==pf.search_ni_ &&
00631 search_nj_==pf.search_nj_ &&
00632 nA_==pf.nA_ &&
00633 ns_==pf.ns_ &&
00634 vcl_fabs(dA_-pf.dA_)<=1e-6 &&
00635 vcl_fabs(ds_-pf.ds_)<=1e-6 &&
00636 vcl_fabs(step_size_-pf.step_size_)<=1e-6;
00637 }
00638
00639
00640
00641
00642
00643
00644 void mfpf_point_finder::print_summary(vcl_ostream& os) const
00645 {
00646 os<<" step_size: "<<step_size_
00647 <<" search_ni: "<<search_ni_
00648 <<" search_nj: "<<search_nj_
00649 <<" nA: "<<nA_<<" dA: "<<dA_
00650 <<" ns: "<<ns_<<" ds: "<<ds_<<' ';
00651 }
00652
00653
00654
00655
00656
00657 short mfpf_point_finder::version_no() const
00658 {
00659 return 1;
00660 }
00661
00662
00663
00664
00665
00666
00667 void mfpf_point_finder::b_write(vsl_b_ostream& bfs) const
00668 {
00669 vsl_b_write(bfs,version_no());
00670 vsl_b_write(bfs,step_size_);
00671 vsl_b_write(bfs,search_ni_);
00672 vsl_b_write(bfs,search_nj_);
00673 vsl_b_write(bfs,nA_);
00674 vsl_b_write(bfs,dA_);
00675 vsl_b_write(bfs,ns_);
00676 vsl_b_write(bfs,ds_);
00677 }
00678
00679
00680
00681
00682
00683 void mfpf_point_finder::b_read(vsl_b_istream& bfs)
00684 {
00685 if (!bfs) return;
00686 short version;
00687 vsl_b_read(bfs,version);
00688 switch (version)
00689 {
00690 case 1:
00691 vsl_b_read(bfs,step_size_);
00692 vsl_b_read(bfs,search_ni_);
00693 vsl_b_read(bfs,search_nj_);
00694 vsl_b_read(bfs,nA_);
00695 vsl_b_read(bfs,dA_);
00696 vsl_b_read(bfs,ns_);
00697 vsl_b_read(bfs,ds_);
00698 break;
00699 default:
00700 vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&)\n"
00701 << " Unknown version number "<< version << vcl_endl;
00702 bfs.is().clear(vcl_ios::badbit);
00703 break;
00704 }
00705 }
00706
00707
00708
00709
00710
00711 vcl_string mfpf_point_finder::is_a() const
00712 {
00713 return vcl_string("mfpf_point_finder");
00714 }
00715
00716
00717 void vsl_add_to_binary_loader(const mfpf_point_finder& b)
00718 {
00719 vsl_binary_loader<mfpf_point_finder>::instance().add(b);
00720 }
00721
00722
00723
00724
00725
00726 void vsl_b_write(vsl_b_ostream& bfs, const mfpf_point_finder& b)
00727 {
00728 b.b_write(bfs);
00729 }
00730
00731
00732
00733
00734
00735 void vsl_b_read(vsl_b_istream& bfs, mfpf_point_finder& b)
00736 {
00737 b.b_read(bfs);
00738 }
00739
00740
00741
00742
00743
00744 vcl_ostream& operator<<(vcl_ostream& os,const mfpf_point_finder& b)
00745 {
00746 os << b.is_a() << ": ";
00747 vsl_indent_inc(os);
00748 b.print_summary(os);
00749 vsl_indent_dec(os);
00750 return os;
00751 }
00752
00753
00754
00755
00756
00757 vcl_ostream& operator<<(vcl_ostream& os,const mfpf_point_finder* b)
00758 {
00759 if (b)
00760 return os << *b;
00761 else
00762 return os << "No mfpf_point_finder defined.";
00763 }