contrib/mul/mfpf/mfpf_grad_corr2d.cxx
Go to the documentation of this file.
00001 #include "mfpf_grad_corr2d.h"
00002 //:
00003 // \file
00004 // \brief Searches over a grid using normalised correlation
00005 // \author Tim Cootes
00006 
00007 #include <vsl/vsl_binary_loader.h>
00008 #include <vcl_cmath.h>
00009 #include <vcl_algorithm.h>
00010 #include <vcl_cassert.h>
00011 
00012 #include <vil/vil_resample_bilin.h>
00013 #include <vil/io/vil_io_image_view.h>
00014 #include <vil/vil_math.h>
00015 #include <vnl/vnl_math.h>
00016 #include <vgl/vgl_point_2d.h>
00017 #include <vgl/vgl_vector_2d.h>
00018 
00019 #include <vil/vil_convert.h>
00020 
00021 //=======================================================================
00022 // Dflt ctor
00023 //=======================================================================
00024 
00025 mfpf_grad_corr2d::mfpf_grad_corr2d()
00026 {
00027   set_defaults();
00028 }
00029 
00030 //: Define default values
00031 void mfpf_grad_corr2d::set_defaults()
00032 {
00033   step_size_=1.0;
00034   search_ni_=5;
00035   search_nj_=5;
00036   nA_=0; dA_=0.0;
00037   ns_=0; ds_=1.0;
00038   ref_x_ = 0.0;
00039   ref_y_ = 0.0;
00040   overlap_f_=1.0;
00041 }
00042 
00043 //=======================================================================
00044 // Destructor
00045 //=======================================================================
00046 
00047 mfpf_grad_corr2d::~mfpf_grad_corr2d()
00048 {
00049 }
00050 
00051 //: Define filter kernel to search with
00052 void mfpf_grad_corr2d::set(const vil_image_view<double>& kx,
00053                            const vil_image_view<double>& ky,
00054                            double ref_x, double ref_y)
00055 {
00056   // Copy, ensuring istep==1
00057   unsigned ni,nj;
00058   ni=kx.ni(); nj=kx.nj();
00059   kernel_x_.set_size(ni,nj);
00060   double sum_x=0.0,ss_x=0.0;
00061   for (unsigned j=0;j<nj;++j)
00062     for (unsigned i=0;i<ni;++i)
00063     {
00064       kernel_x_(i,j)=kx(i,j);
00065       sum_x+=kx(i,j); ss_x+=kx(i,j)*kx(i,j);
00066     }
00067 
00068   assert(!vnl_math_isnan(sum_x));
00069 
00070   // Normalise so that kernel_ has zero mean and unit sum of squares.
00071   double mean_x=sum_x/(ni*nj);
00072   ss_x-=(mean_x*mean_x*ni*nj);
00073   assert(ss_x>1e-6);  // If near zero, flat region - can't use correlation
00074   double s_x=1.0;
00075   if (ss_x>0) s_x = vcl_sqrt(1.0/ss_x);
00076   vil_math_scale_and_offset_values(kernel_x_,s_x,-s_x*mean_x);
00077 
00078   // repeat for y-image
00079   ni=ky.ni(); nj=ky.nj();
00080   kernel_y_.set_size(ni,nj);
00081   double sum_y=0.0,ss_y=0.0;
00082   for (unsigned j=0;j<nj;++j)
00083     for (unsigned i=0;i<ni;++i)
00084     {
00085       kernel_y_(i,j)=ky(i,j);
00086       sum_y+=ky(i,j); ss_y+=ky(i,j)*ky(i,j);
00087     }
00088 
00089   assert(!vnl_math_isnan(sum_y));
00090 
00091   // Normalise so that kernel_ has zero mean and unit sum of squares.
00092   double mean_y=sum_y/(ni*nj);
00093   ss_y-=(mean_y*mean_y*ni*nj);
00094   assert(ss_y>1e-6);  // If near zero, flat region - can't use correlation
00095   double s_y=1.0;
00096   if (ss_y>0) s_y = vcl_sqrt(1.0/ss_y);
00097   vil_math_scale_and_offset_values(kernel_y_,s_y,-s_y*mean_y);
00098 
00099   ref_x_ = ref_x;
00100   ref_y_ = ref_y;
00101 }
00102 
00103 //: Define filter kernel to search with
00104 void mfpf_grad_corr2d::set(const vil_image_view<double>& kx,
00105                            const vil_image_view<double>& ky)
00106 {
00107   set(kx, ky, 0.5*(kx.ni()-1.0), 0.5*(kx.nj()-1.0));
00108 }
00109 
00110 //: Define filter kernel to search with, expressed as a vector
00111 bool mfpf_grad_corr2d::set_model(const vcl_vector<double>& v)
00112 {
00113   // we assume that nplanes()==1
00114   assert( v.size() == model_dim() );
00115 
00116   // copy vector values into im_x, column by column
00117   vil_image_view<double> im_x( kernel_x_.ni(), kernel_x_.nj() );
00118   unsigned vec_ind=0;
00119   for (unsigned j=0;j<kernel_x_.nj();++j)
00120     for (unsigned i=0;i<kernel_x_.ni();++i,++vec_ind)
00121       im_x(i,j)=v[vec_ind];
00122 
00123   // copy vector values into im_y, column by column
00124   vil_image_view<double> im_y( kernel_y_.ni(), kernel_y_.nj() );
00125   for (unsigned j=0;j<kernel_y_.nj();++j)
00126     for (unsigned i=0;i<kernel_y_.nj();++i,++vec_ind)
00127       im_y(i,j)=v[vec_ind];
00128 
00129   // set kernel from created image
00130   set(im_x,im_y);
00131 
00132   return true;
00133 }
00134 
00135 //: Number of dimensions in the model
00136 unsigned mfpf_grad_corr2d::model_dim()
00137 {
00138   return kernel_x_.ni()*kernel_x_.nj() + kernel_y_.ni()*kernel_y_.nj();
00139 }
00140 
00141 //: Filter kernel to search with, expressed as a vector
00142 void mfpf_grad_corr2d::get_kernel_vector(vcl_vector<double>& v) const
00143 {
00144   // we assume that nplanes()==1
00145   v.resize( 2*kernel_x_.ni()*kernel_x_.nj() );
00146 
00147   // copy kernel values into v, column by column
00148   unsigned vec_ind=0;
00149   for (unsigned j=0;j<kernel_x_.nj();++j)
00150     for (unsigned i=0;i<kernel_x_.ni();++i,++vec_ind)
00151       v[vec_ind]=kernel_x_(i,j);
00152 
00153   for (unsigned j=0;j<kernel_y_.nj();++j)
00154     for (unsigned i=0;i<kernel_y_.ni();++i,++vec_ind)
00155       v[vec_ind]=kernel_y_(i,j);
00156 }
00157 
00158 //: Relative size of region used for estimating overlap
00159 //  If 0.5, then overlap requires pt inside central 50% of region.
00160 void mfpf_grad_corr2d::set_overlap_f(double f)
00161 {
00162   overlap_f_=f;
00163 }
00164 
00165 // Assumes im2[i] has zero mean and unit length as a vector
00166 // Assumes element (i,j) is im1[i+j*jstep1] etc
00167 inline double norm_corr(const double* im1, const double* im2,
00168                         vcl_ptrdiff_t jstep1, vcl_ptrdiff_t jstep2,
00169                         unsigned ni, unsigned nj)
00170 {
00171   double sum1=0.0,sum2=0.0,sum_sq=0.0;
00172   for (unsigned j=0;j<nj;++j,im1+=jstep1,im2+=jstep2)
00173     for (unsigned i=0;i<ni;++i)
00174     {
00175       sum1+=im1[i]*im2[i];
00176       sum2+=im1[i];
00177       sum_sq+=im1[i]*im1[i];
00178     }
00179   unsigned n=ni*nj;
00180   double mean = sum2/n;
00181   double ss = vcl_max(1e-6,sum_sq-n*mean*mean);
00182   double s = vcl_sqrt(ss);
00183 
00184   return sum1/s;
00185 }
00186 
00187 #if 0 // static (=local) function not used in this file
00188 static void normalize(vil_image_view<double>& im)
00189 {
00190   unsigned ni=im.ni(),nj=im.nj();
00191   double sum=0.0,ss=0.0;
00192   for (unsigned j=0;j<nj;++j)
00193     for (unsigned i=0;i<ni;++i)
00194     {
00195       sum+=im(i,j); ss+=im(i,j)*im(i,j);
00196     }
00197 
00198   assert(!vnl_math_isnan(sum));
00199 
00200   if (ss<1e-6)
00201   {
00202     vcl_cerr<<"Warning: Almost flat region in mfpf_grad_corr2d_builder\n"
00203             <<"         Size: "<<ni<<" x "<<nj<<vcl_endl;
00204   }
00205 
00206   // Normalise so that im has zero mean and unit sum of squares.
00207   double mean=sum/(ni*nj);
00208   ss-=(mean*mean*ni*nj);
00209   double s=1.0;
00210   if (ss>0) s = vcl_sqrt(1.0/ss);
00211   vil_math_scale_and_offset_values(im,s,-s*mean);
00212 }
00213 #endif // 0
00214 
00215 void mfpf_grad_corr2d::diff_image(const vimt_image_2d_of<float>& image,
00216                                   const vgl_point_2d<double>& p,
00217                                   const vgl_vector_2d<double>& u,
00218                                   vil_image_view<double>& grad_x,
00219                                   vil_image_view<double>& grad_y,
00220                                   int search_ni, int search_nj)
00221 {
00222   assert(image.image().size()>0);
00223 
00224   unsigned ni=kernel_x_.ni();
00225   unsigned nj=kernel_x_.nj();
00226   unsigned nsi = ni + 2*search_ni;
00227   unsigned nsj = nj + 2*search_nj;
00228 
00229   vgl_vector_2d<double> u1=step_size_*u;
00230   vgl_vector_2d<double> v1(-u1.y(),u1.x());
00231 
00232   const vimt_transform_2d& s_w2i = image.world2im();
00233   vgl_point_2d<double> p0, im_p0;
00234   vgl_vector_2d<double> im_u, im_v;
00235 
00236   vil_image_view<float> sample;
00237 
00238 
00239   // get raw image data first
00240   p0 = p - (ref_x_+search_ni+0.5)*u1 - (ref_y_+search_nj)*v1;
00241   im_p0 = s_w2i(p0);
00242   im_u = s_w2i.delta(p0, u1);
00243   im_v = s_w2i.delta(p0, v1);
00244   vil_resample_bilin(image.image(),sample, im_p0.x(),im_p0.y(),
00245                      im_u.x(),im_u.y(), im_v.x(),im_v.y(), nsi+1,nsj);
00246 
00247   // take differences across x
00248   grad_x.set_size( nsi,nsj );
00249 
00250   const float* s1 = sample.top_left_ptr();
00251   const float* s2 = sample.top_left_ptr()+sample.istep();
00252   vcl_ptrdiff_t s_jstep = sample.jstep();
00253 
00254   double* kx = grad_x.top_left_ptr();
00255   vcl_ptrdiff_t kx_jstep = grad_x.jstep();
00256 
00257   for (unsigned j=0;j<nsj;++j,kx+=kx_jstep,s1+=s_jstep,s2+=s_jstep)
00258     for (unsigned i=0;i<nsi;++i)
00259       *(kx+i) = *(s2+i) - *(s1+i);
00260 
00261 
00262   // get raw image data first
00263   p0 = p - (ref_x_+search_ni)*u1 - (ref_y_+search_nj+0.5)*v1;
00264   im_p0 = s_w2i(p0);
00265   im_u = s_w2i.delta(p0, u1);
00266   im_v = s_w2i.delta(p0, v1);
00267   vil_resample_bilin(image.image(),sample, im_p0.x(),im_p0.y(),
00268                      im_u.x(),im_u.y(), im_v.x(),im_v.y(), nsi,nsj+1);
00269 
00270   // take differences across y
00271   grad_y.set_size(nsi,nsj);
00272 
00273   s1 = sample.top_left_ptr();
00274   s2 = sample.top_left_ptr()+sample.jstep();
00275   s_jstep = sample.jstep();
00276 
00277   double* ky = grad_y.top_left_ptr();
00278   vcl_ptrdiff_t ky_jstep = grad_y.jstep();
00279 
00280   for (unsigned j=0;j<nsj;++j,ky+=ky_jstep,s1+=s_jstep,s2+=s_jstep)
00281     for (unsigned i=0;i<nsi;++i)
00282       *(ky+i) = *(s2+i) - *(s1+i);
00283 }
00284 
00285 //: Get sample of region around specified point in image
00286 void mfpf_grad_corr2d::get_sample_vector(const vimt_image_2d_of<float>& image,
00287                                          const vgl_point_2d<double>& p,
00288                                          const vgl_vector_2d<double>& u,
00289                                          vcl_vector<double>& v)
00290 {
00291   assert(image.image().size()>0);
00292 
00293   vil_image_view<double> grad_x, grad_y;
00294   diff_image(image,p,u,grad_x,grad_y);
00295 
00296   v.resize( model_dim() );
00297   unsigned v_ind=0;
00298 
00299   for (unsigned j=0;j<grad_x.nj();j++)
00300     for (unsigned i=0;i<grad_x.ni();i++,v_ind++)
00301       v[v_ind] = grad_x(i,j);
00302 
00303   for (unsigned j=0;j<grad_y.nj();j++)
00304     for (unsigned i=0;i<grad_y.ni();i++,v_ind++)
00305       v[v_ind] = grad_y(i,j);
00306 }
00307 
00308 //: Radius of circle containing modelled region
00309 double mfpf_grad_corr2d::radius() const
00310 {
00311   // Compute distance to each corner
00312   double wx = kernel_x_.ni()-1;
00313   double x2 = vcl_max(ref_x_*ref_x_,(ref_x_-wx)*(ref_x_-wx));
00314   double wy = kernel_x_.nj()-1;
00315   double y2 = vcl_max(ref_y_*ref_y_,(ref_y_-wy)*(ref_y_-wy));
00316   double r2 = x2+y2;
00317   if (r2<=1) return 1.0;
00318   return vcl_sqrt(r2);
00319 }
00320 
00321 //: Evaluate match at p, using u to define scale and orientation
00322 // Returns -1*edge strength at p along direction u
00323 double mfpf_grad_corr2d::evaluate(const vimt_image_2d_of<float>& image,
00324                                   const vgl_point_2d<double>& p,
00325                                   const vgl_vector_2d<double>& u)
00326 {
00327   vil_image_view<double> grad_x, grad_y;
00328   diff_image(image,p,u,grad_x,grad_y);
00329 
00330   return 2.0-norm_corr(grad_x.top_left_ptr(),kernel_x_.top_left_ptr(),
00331                        grad_x.jstep(),kernel_x_.jstep(),
00332                        kernel_x_.ni(),kernel_x_.nj())
00333             -norm_corr(grad_y.top_left_ptr(),kernel_y_.top_left_ptr(),
00334                        grad_y.jstep(),kernel_y_.jstep(),
00335                        kernel_y_.ni(),kernel_y_.nj());
00336 }
00337 
00338 //: Evaluate match at in a region around p
00339 // Returns a quality of fit at a set of positions.
00340 // response image (whose size and transform is set inside the
00341 // function), indicates the points at which the function was
00342 // evaluated.  response(i,j) is the fit at the point
00343 // response.world2im().inverse()(i,j).  The world2im() transformation
00344 // may be affine.
00345 void mfpf_grad_corr2d::evaluate_region(
00346                         const vimt_image_2d_of<float>& image,
00347                         const vgl_point_2d<double>& p,
00348                         const vgl_vector_2d<double>& u,
00349                         vimt_image_2d_of<double>& response)
00350 {
00351   assert(image.image().nplanes()==1);
00352 
00353   vil_image_view<double> grad_x, grad_y;
00354   diff_image(image,p,u,grad_x,grad_y,
00355              search_ni_,search_nj_);
00356 
00357   int ni=1+2*search_ni_;
00358   int nj=1+2*search_nj_;
00359   response.image().set_size(ni,nj);
00360 
00361   double* r = response.image().top_left_ptr();
00362   const double* kx = kernel_x_.top_left_ptr();
00363   const double* ky = kernel_y_.top_left_ptr();
00364   const double* sx = grad_x.top_left_ptr();
00365   const double* sy = grad_y.top_left_ptr();
00366   vcl_ptrdiff_t r_jstep = response.image().jstep();
00367   vcl_ptrdiff_t sx_jstep = grad_x.jstep();
00368   vcl_ptrdiff_t sy_jstep = grad_y.jstep();
00369   vcl_ptrdiff_t kx_jstep = kernel_x_.jstep();
00370   vcl_ptrdiff_t ky_jstep = kernel_y_.jstep();
00371 
00372   for (int j=0;j<nj;++j,r+=r_jstep,sx+=sx_jstep,sy+=sy_jstep)
00373   {
00374     for (int i=0;i<ni;++i)
00375     {
00376       r[i] = 2.0-norm_corr(sx+i,kx,sx_jstep,kx_jstep,
00377                            kernel_x_.ni(),kernel_x_.nj())
00378                 -norm_corr(sy+i,ky,sy_jstep,ky_jstep,
00379                            kernel_y_.ni(),kernel_y_.nj());
00380     }
00381   }
00382 
00383   // Set up transformation parameters
00384 
00385   // Point (i,j) in dest corresponds to p1+i.u+j.v,
00386   // an affine transformation for image to world
00387   vgl_vector_2d<double> u1=step_size_*u;
00388   vgl_vector_2d<double> v1(-u1.y(),u1.x());
00389   const vgl_point_2d<double> p1 = p-search_ni_*u1-search_nj_*v1;
00390 
00391   vimt_transform_2d i2w;
00392   i2w.set_similarity(vgl_point_2d<double>(u1.x(),u1.y()),p1);
00393   response.set_world2im(i2w.inverse());
00394 }
00395 
00396 //: Search given image around p, using u to define scale and angle
00397 //  On exit, new_p defines position of the best nearby match.
00398 //  Returns a quality of fit measure at that
00399 //  point (the smaller the better).
00400 double mfpf_grad_corr2d::search_one_pose(
00401                         const vimt_image_2d_of<float>& image,
00402                         const vgl_point_2d<double>& p,
00403                         const vgl_vector_2d<double>& u,
00404                         vgl_point_2d<double>& new_p)
00405 {
00406   assert(image.image().nplanes()==1);
00407 
00408   vil_image_view<double> grad_x, grad_y;
00409   diff_image(image,p,u,grad_x,grad_y,
00410              search_ni_,search_nj_);
00411 
00412   const double* kx = kernel_x_.top_left_ptr();
00413   const double* ky = kernel_y_.top_left_ptr();
00414   const double* sx = grad_x.top_left_ptr();
00415   const double* sy = grad_y.top_left_ptr();
00416   vcl_ptrdiff_t sx_jstep = grad_x.jstep();
00417   vcl_ptrdiff_t sy_jstep = grad_y.jstep();
00418   vcl_ptrdiff_t kx_jstep = kernel_x_.jstep();
00419   vcl_ptrdiff_t ky_jstep = kernel_y_.jstep();
00420 
00421   int ni=1+2*search_ni_;
00422   int nj=1+2*search_nj_;
00423 
00424   double best_r=-9e99;
00425   int best_i=-1,best_j=-1;
00426   for (int j=0;j<nj;++j,sx+=sx_jstep,sy+=sy_jstep)
00427   {
00428     for (int i=0;i<ni;++i)
00429     {
00430       double r = norm_corr(sx+i,kx,sx_jstep,kx_jstep,
00431                            kernel_x_.ni(),kernel_x_.nj())
00432                + norm_corr(sy+i,ky,sy_jstep,ky_jstep,
00433                            kernel_y_.ni(),kernel_y_.nj());
00434       if (r>best_r) { best_r=r; best_i=i; best_j=j; }
00435     }
00436   }
00437 
00438   // Compute position of best point
00439   vgl_vector_2d<double> u1=step_size_*u;
00440   vgl_vector_2d<double> v1(-u1.y(),u1.x());
00441   new_p = p+(best_i-search_ni_)*u1+(best_j-search_nj_)*v1;
00442   return 2.0 - best_r;
00443 }
00444 
00445 // Returns true if p is inside region at given pose
00446 bool mfpf_grad_corr2d::is_inside(const mfpf_pose& pose,
00447                                  const vgl_point_2d<double>& p,
00448                                  double f) const
00449 {
00450   // Set transform model frame -> World
00451   vimt_transform_2d t1;
00452   t1.set_similarity(step_size()*pose.u(),pose.p());
00453   // Compute position of p in model frame
00454   vgl_point_2d<double> q=t1.inverse()(p);
00455   q.x()/=f; q.y()/=f;  // To check that q in the central fraction f
00456   q.x()+=ref_x_;
00457   if (q.x()<0 || q.x()>(kernel_x_.ni()-1)) return false;
00458   q.y()+=ref_y_;
00459   if (q.y()<0 || q.y()>(kernel_x_.nj()-1)) return false;
00460   return true;
00461 }
00462 
00463 //: Return true if modelled regions at pose1 and pose2 overlap
00464 //  Checks if reference point of one is inside region of other
00465 bool mfpf_grad_corr2d::overlap(const mfpf_pose& pose1,
00466                                const mfpf_pose& pose2) const
00467 {
00468   if (is_inside(pose1,pose2.p(),overlap_f_)) return true;
00469   if (is_inside(pose2,pose1.p(),overlap_f_)) return true;
00470   return false;
00471 }
00472 
00473 //: Generate points in ref frame that represent boundary
00474 //  Points of a contour around the shape.
00475 //  Used for display purposes.
00476 void mfpf_grad_corr2d::get_outline(vcl_vector<vgl_point_2d<double> >& pts) const
00477 {
00478   pts.resize(7);
00479   int roi_ni=kernel_x_.ni();
00480   int roi_nj=kernel_x_.nj();
00481   vgl_vector_2d<double> r(ref_x_,ref_y_);
00482   pts[0]=vgl_point_2d<double>(0,roi_nj)-r;
00483   pts[1]=vgl_point_2d<double>(0,0);
00484   pts[2]=vgl_point_2d<double>(roi_ni,roi_nj)-r;
00485   pts[3]=vgl_point_2d<double>(0,roi_nj)-r;
00486   pts[4]=vgl_point_2d<double>(0,0)-r;
00487   pts[5]=vgl_point_2d<double>(roi_ni,0)-r;
00488   pts[6]=vgl_point_2d<double>(roi_ni,roi_nj)-r;
00489 }
00490 
00491 //: Return an image of the kernel
00492 void mfpf_grad_corr2d::get_image_of_model(vimt_image_2d_of<vxl_byte>& image) const
00493 {
00494   // compute magnitude of image gradient
00495   vil_image_view<double> kx_sqr,ky_sqr,kernel;
00496   vil_math_sum_sqr(kernel_x_,kx_sqr);
00497   vil_math_sum_sqr(kernel_y_,ky_sqr);
00498   vil_math_image_sum(kx_sqr,ky_sqr,kernel);
00499   vil_math_sqrt(kernel);
00500 
00501   vil_convert_stretch_range(kernel,image.image());
00502   vimt_transform_2d ref2im;
00503   ref2im.set_zoom_only(1.0/step_size_,ref_x_,ref_y_);
00504   image.set_world2im(ref2im);
00505 }
00506 
00507 //=======================================================================
00508 // Method: is_a
00509 //=======================================================================
00510 
00511 vcl_string mfpf_grad_corr2d::is_a() const
00512 {
00513   return vcl_string("mfpf_grad_corr2d");
00514 }
00515 
00516 //: Create a copy on the heap and return base class pointer
00517 mfpf_point_finder* mfpf_grad_corr2d::clone() const
00518 {
00519   return new mfpf_grad_corr2d(*this);
00520 }
00521 
00522 //=======================================================================
00523 // Method: print
00524 //=======================================================================
00525 
00526 void mfpf_grad_corr2d::print_summary(vcl_ostream& os) const
00527 {
00528   os << "{  size: " << kernel_x_.ni() << " x " << kernel_x_.nj();
00529   mfpf_point_finder::print_summary(os);
00530   os << " overlap_f: " << overlap_f_ << " }";
00531 }
00532 
00533 void mfpf_grad_corr2d::b_write(vsl_b_ostream& bfs) const
00534 {
00535   vsl_b_write(bfs,version_no());
00536   mfpf_point_finder::b_write(bfs);  // Save base class
00537   vsl_b_write(bfs,kernel_x_);
00538   vsl_b_write(bfs,kernel_y_);
00539   vsl_b_write(bfs,ref_x_);
00540   vsl_b_write(bfs,ref_y_);
00541   vsl_b_write(bfs,overlap_f_);
00542 }
00543 
00544 short mfpf_grad_corr2d::version_no() const
00545 {
00546   return 2;
00547 }
00548 
00549 //=======================================================================
00550 // Method: load
00551 //=======================================================================
00552 
00553 void mfpf_grad_corr2d::b_read(vsl_b_istream& bfs)
00554 {
00555   if (!bfs) return;
00556   short version;
00557   vsl_b_read(bfs,version);
00558   switch (version)
00559   {
00560     case (1):
00561     case (2):
00562       mfpf_point_finder::b_read(bfs);  // Load in base class
00563       vsl_b_read(bfs,kernel_x_);
00564       vsl_b_read(bfs,kernel_y_);
00565       vsl_b_read(bfs,ref_x_);
00566       vsl_b_read(bfs,ref_y_);
00567       if (version==1) overlap_f_=1.0;
00568       else            vsl_b_read(bfs,overlap_f_);
00569       break;
00570     default:
00571       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&)\n"
00572                << "           Unknown version number "<< version << vcl_endl;
00573       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00574       return;
00575   }
00576 }
00577 
00578 //: Test equality
00579 bool mfpf_grad_corr2d::operator==(const mfpf_grad_corr2d& nc) const
00580 {
00581   if (!base_equality(nc)) return false;
00582   if (kernel_x_.ni()!=nc.kernel_x_.ni()) return false;
00583   if (kernel_x_.nj()!=nc.kernel_x_.nj()) return false;
00584   if (kernel_y_.ni()!=nc.kernel_y_.ni()) return false;
00585   if (kernel_y_.nj()!=nc.kernel_y_.nj()) return false;
00586   if (vcl_fabs(ref_x_-nc.ref_x_)>1e-6) return false;
00587   if (vcl_fabs(ref_y_-nc.ref_y_)>1e-6) return false;
00588   if (kernel_x_.size()!=nc.kernel_x_.size()) return false;
00589   if (kernel_x_.size()==0) return true;  // ssd fails on empty
00590   if (kernel_y_.size()!=nc.kernel_y_.size()) return false;
00591   if (kernel_y_.size()==0) return true;  // ssd fails on empty
00592   if (vil_math_ssd(kernel_x_,nc.kernel_x_,double(0))>1e-4) return false;
00593   return (vil_math_ssd(kernel_y_,nc.kernel_y_,double(0))<1e-4);
00594 }
00595 
00596