core/vil/algo/vil_quad_distance_function.h
Go to the documentation of this file.
00001 #ifndef vil_quad_distance_function_h_
00002 #define vil_quad_distance_function_h_
00003 //:
00004 //  \file
00005 //  \brief Functions to compute quadratic distance functions
00006 //  \author Tim Cootes
00007 
00008 #include <vil/vil_image_view.h>
00009 #include <vcl_vector.h>
00010 #include <vcl_cassert.h>
00011 
00012 //: Add parabola y=y0+(x-x0)^2 to lower envelope defined by (x,y,z)
00013 //  Parabolas are  y' = y[i]+a(x'-x[i])^2
00014 //  Parabola i defines the envelope in the range (z[i],z[i+1]).
00015 inline void vil_update_parabola_set(vcl_vector<double>& x,
00016                                     vcl_vector<double>& y,
00017                                     vcl_vector<double>& z, double a,
00018                                     double x0, double y0, double n)
00019 {
00020   unsigned int k=x.size()-1;
00021   while (true)
00022   {
00023     // Compute intercept of parabola centred at x0 with that at v[k]
00024     // New parabola is below old for all x'>s
00025     double new_z = 0.5*((y0-y[k])/a + (x0*x0-x[k]*x[k]))/(x0-x[k]);
00026     if (new_z>n) return;  // Only useful outsize (0,n) so discard
00027     if (new_z>z[k])
00028     {
00029       x.push_back(x0);
00030       y.push_back(y0);
00031       z.push_back(new_z);
00032       return;
00033     }
00034     if (k==0)   // new_z<=0.0 since z[0]=0.0
00035     {
00036       // This parabola dominates in valid region so far
00037       x[0]=x0; y[0]=y0; z[0]=0.0;
00038       return;
00039     }
00040 
00041     // Remove parabola k and repeat
00042     x.erase(x.begin()+k);
00043     y.erase(y.begin()+k);
00044     z.erase(z.begin()+k);
00045     --k;
00046   }
00047 }
00048 
00049 //: Compute parabolas forming lower envelope from set over range [0,n)
00050 //  Set of parabolas  y=src[i*s_step]+a(x-i)^2    i=0..n-1
00051 //  Select those defining lower envelope in range [0,n)
00052 //  On exit, selected parabolas are  y' = y[i]+(x'-x[i])^2
00053 //  Parabola i defines the envelope in the range (z[i],z[i+1]).
00054 //  Thus z.size()==x.size()+1
00055 template<class srcT>
00056 inline void vil_quad_envelope(const srcT* src,vcl_ptrdiff_t s_step,
00057                               unsigned int n,
00058                               vcl_vector<double>& x,
00059                               vcl_vector<double>& y,
00060                               vcl_vector<double>& z, double a)
00061 {
00062   x.resize(1);  x[0]=0.0;
00063   y.resize(1);  y[0]=double(*src);
00064   z.resize(1);  z[0]=0.0;
00065   src+=s_step;
00066   for (unsigned int x0=1;x0<n;++x0,src+=s_step)
00067   {
00068     vil_update_parabola_set(x,y,z,a,x0,double(*src),double(n));
00069   }
00070   z.push_back(n);
00071 }
00072 
00073 //: Sample from lower envelope of a set of parabolas
00074 //  Parabolas are  y' = y[i]+a(x'-x[i])^2
00075 //  Parabola i defines the envelope in the range (z[i],z[i+1]).
00076 //  Thus z.size()==x.size()+1
00077 template<class destT>
00078 inline void vil_sample_quad_envelope(const vcl_vector<double>& x,
00079                                      const vcl_vector<double>& y,
00080                                      const vcl_vector<double>& z, double a,
00081                                      destT* dest, vcl_ptrdiff_t d_step,
00082                                      unsigned int n)
00083 {
00084   unsigned int k=0;
00085   for (unsigned int i=0;i<n;++i,dest+=d_step)
00086   {
00087     while (z[k+1]<i) ++k;  // Select relevant parabola
00088     *dest = destT(y[k] + a*(i-x[k])*(i-x[k]));
00089   }
00090 }
00091 
00092 //: Sample from lower envelope of a set of parabolas
00093 //  Parabolas are  y' = y[i]+a(x'-x[i])^2
00094 //  Parabola i defines the envelope in the range (z[i],z[i+1]).
00095 //  Thus z.size()==x.size()+1
00096 //
00097 //  iT assumed to be an integer type (vxl_byte,short, int etc)
00098 //  On exit, pos[i*p_step] gives the x position of the parabola
00099 //  used to compute the envelope at position i.
00100 template<class destT, class iT>
00101 inline void vil_sample_quad_envelope_with_pos(const vcl_vector<double>& x,
00102                                               const vcl_vector<double>& y,
00103                                               const vcl_vector<double>& z,
00104                                               double a,
00105                                               destT* dest, vcl_ptrdiff_t d_step,
00106                                               unsigned int n,
00107                                               iT* pos, vcl_ptrdiff_t p_step)
00108 {
00109   unsigned int k=0;
00110   for (unsigned int i=0;i<n;++i,dest+=d_step,pos+=p_step)
00111   {
00112     while (z[k+1]<i) ++k;  // Select relevant parabola
00113     *dest = destT(y[k] + a*(i-x[k])*(i-x[k]));
00114     *pos  = iT(x[k]+0.5);  // x[k] assumed positive, round to nearest integer
00115   }
00116 }
00117 
00118 //: Compute quadratic distance function for a 1D function
00119 //  On exit dest(x) = min_i src(x+i)+a(i^2)
00120 //  Implementation of Felzenszwalb and Huttenlocher's algorithm,
00121 //  as described in "Distance Transforms of Sampled Functions".
00122 //
00123 //  dest(x) = dest[x*d_step], src(x)=src[x*s_step]
00124 template<class srcT, class destT>
00125 inline void vil_quad_distance_function_1D(const srcT* src,vcl_ptrdiff_t s_step,
00126                                           unsigned int n,
00127                                           double a,
00128                                           destT* dest, vcl_ptrdiff_t d_step)
00129 {
00130   vcl_vector<double> x,y,z;
00131   vil_quad_envelope(src,s_step,n,x,y,z,a);
00132   vil_sample_quad_envelope(x,y,z,a,dest,d_step,n);
00133 }
00134 
00135 //: Compute quadratic distance function for a 1D function
00136 //  On exit dest(x) = min_i src(x+i)+a(i^2),
00137 //  pos(x) gives position (x+i) which leads to the minima
00138 //  Implementation of Felzenszwalb and Huttenlocher's algorithm,
00139 //  as described in "Distance Transforms of Sampled Functions".
00140 //
00141 //  dest(x) = dest[x*d_step], src(x)=src[x*s_step], pos(x)=pos[x*p_step]
00142 template<class srcT, class destT, class posT>
00143 inline void vil_quad_distance_function_1D(const srcT* src,vcl_ptrdiff_t s_step,
00144                                           unsigned int n,
00145                                           double a,
00146                                           destT* dest, vcl_ptrdiff_t d_step,
00147                                           posT* pos, vcl_ptrdiff_t p_step)
00148 {
00149   vcl_vector<double> x,y,z;
00150   vil_quad_envelope(src,s_step,n,x,y,z,a);
00151   vil_sample_quad_envelope_with_pos(x,y,z,a,dest,d_step,n, pos,p_step);
00152 }
00153 
00154 //: Apply quadratic distance transform along each row of src
00155 //
00156 //  $ dest(x,y)=min_i,j (src(x+i,y+j)+ai(i^2)+aj(j^2)) $
00157 // \relatesalso vil_image_view
00158 template<class srcT, class destT>
00159 inline void vil_quad_distance_function(const vil_image_view<srcT>& src,
00160                                        double ai, double aj,
00161                                        vil_image_view<destT>& dest)
00162 {
00163   assert(src.nplanes()==1);
00164   unsigned int ni=src.ni(),nj=src.nj();
00165   dest.set_size(ni,nj);
00166   vil_image_view<destT> tmp(ni,nj);  // Intermediate result
00167   vcl_ptrdiff_t s_istep = src.istep(),   s_jstep = src.jstep();
00168   vcl_ptrdiff_t t_istep = tmp.istep(),   t_jstep = tmp.jstep();
00169   vcl_ptrdiff_t d_istep = dest.istep(),  d_jstep = dest.jstep();
00170 
00171   const srcT* s_row = src.top_left_ptr();
00172   destT* t_row = tmp.top_left_ptr();
00173 
00174   vcl_vector<double> x,y,z;
00175 
00176   // Apply transform along i direction to get tmp
00177   for (unsigned int j=0;j<nj;++j, s_row+=s_jstep, t_row+=t_jstep)
00178   {
00179     vil_quad_envelope(s_row,s_istep,ni,x,y,z,ai);
00180     vil_sample_quad_envelope(x,y,z,ai,t_row,t_istep,ni);
00181   }
00182   // Apply transform along j direction to get dest
00183   destT* t_col = tmp.top_left_ptr();
00184   destT* d_col = dest.top_left_ptr();
00185   for (unsigned int i=0;i<ni;++i, d_col+=d_istep, t_col+=t_istep)
00186   {
00187     vil_quad_envelope(t_col,t_jstep,nj,x,y,z,aj);
00188     vil_sample_quad_envelope(x,y,z,aj,d_col,d_jstep,nj);
00189   }
00190 }
00191 
00192 //: Apply quadratic distance transform along each row of src
00193 //
00194 //  $ dest(x,y)=min_i,j (src(x+i,y+j)+ai(i^2)+aj(j^2)) $
00195 //  (pos(x,y,0),pos(x,y,1)) gives the position (x+i,y+j) leading to minima
00196 // \relatesalso vil_image_view
00197 template<class srcT, class destT, class posT>
00198 inline void vil_quad_distance_function(const vil_image_view<srcT>& src,
00199                                        double ai, double aj,
00200                                        vil_image_view<destT>& dest,
00201                                        vil_image_view<posT>& pos)
00202 {
00203   assert(src.nplanes()==1);
00204   unsigned int ni=src.ni(), nj=src.nj();
00205   dest.set_size(ni,nj);
00206   pos.set_size(ni,nj,2);
00207   vil_image_view<destT> tmp(ni,nj);  // Intermediate result
00208   vil_image_view<posT> tmp_pos(ni,nj); // Intermediate result
00209   vcl_ptrdiff_t  s_istep = src.istep(),      s_jstep = src.jstep();
00210   vcl_ptrdiff_t  t_istep = tmp.istep(),      t_jstep = tmp.jstep();
00211   vcl_ptrdiff_t  d_istep = dest.istep(),     d_jstep = dest.jstep();
00212   vcl_ptrdiff_t tp_istep = tmp_pos.istep(), tp_jstep = tmp_pos.jstep();
00213   vcl_ptrdiff_t  p_istep = pos.istep(),      p_jstep = pos.jstep();
00214 
00215   const srcT* s_row = src.top_left_ptr();
00216   destT* t_row = tmp.top_left_ptr();
00217   posT* tp_row  = tmp_pos.top_left_ptr();
00218 
00219   vcl_vector<double> x,y,z;
00220 
00221   // Apply transform along i direction to get tmp
00222   for (unsigned int j=0;j<nj;++j,s_row+=s_jstep,t_row+=t_jstep,tp_row+=tp_jstep)
00223   {
00224    vil_quad_envelope(s_row,s_istep,ni,x,y,z,ai);
00225    vil_sample_quad_envelope_with_pos(x,y,z,ai,t_row,t_istep,ni,tp_row,tp_istep);
00226   }
00227   // Apply transform along j direction to get dest
00228   destT* t_col = tmp.top_left_ptr();
00229   destT* d_col = dest.top_left_ptr();
00230   posT* pi_col = pos.top_left_ptr();
00231   posT* pj_col = pi_col + pos.planestep();
00232   posT* tp_col = tmp_pos.top_left_ptr();
00233 
00234   for (unsigned int i=0;i<ni;++i, d_col+=d_istep, t_col+=t_istep,
00235                                   pi_col+=p_istep, pj_col+=p_istep,
00236                                   tp_col+=tp_istep)
00237   {
00238     vil_quad_envelope(t_col,t_jstep,nj,x,y,z,aj);
00239     vil_sample_quad_envelope_with_pos(x,y,z,aj,d_col,d_jstep,nj,pj_col,p_jstep);
00240     // Now deduce the i position using the current offset
00241     for (unsigned int j=0;j<nj;++j)
00242     {
00243       pi_col[j*p_jstep] = tp_col[pj_col[j*p_jstep]*tp_jstep];
00244     }
00245   }
00246 }
00247 
00248 #endif