core/vil/algo/vil_find_peaks.h
Go to the documentation of this file.
00001 // This is core/vil/algo/vil_find_peaks.h
00002 #ifndef vil_find_peaks_h_
00003 #define vil_find_peaks_h_
00004 //:
00005 // \file
00006 // \brief Find peaks in image
00007 // \author Tim Cootes
00008 
00009 #include <vil/vil_image_view.h>
00010 #include <vcl_vector.h>
00011 
00012 //: True if pixel at *im is strictly above 8 neighbours.
00013 // \sa vil_is_plateau_3x3()
00014 template <class T>
00015 inline bool vil_is_peak_3x3(const T* im, vcl_ptrdiff_t i_step, vcl_ptrdiff_t j_step)
00016 {
00017   T v = *im;
00018   return v > im[i_step]
00019       && v > im[-i_step]
00020       && v > im[j_step]
00021       && v > im[-j_step]
00022       && v > im[i_step+j_step]
00023       && v > im[i_step-j_step]
00024       && v > im[j_step-i_step]
00025       && v > im[-i_step-j_step];
00026 }
00027 
00028 //: Return (pi,pj) for all points in image strictly above their 8 neighbours
00029 //  Compute position of all local peaks (pi[k],pj[k]) above given threshold value.
00030 // \param clear_list  If true (the default) then empty lists before adding new examples
00031 // \sa vil_find_plateaus_3x3()
00032 // \relatesalso vil_image_view
00033 template <class T>
00034 inline void vil_find_peaks_3x3(vcl_vector<unsigned>& pi,
00035                                vcl_vector<unsigned>& pj,
00036                                const vil_image_view<T>& image,
00037                                const T& min_thresh,
00038                                bool clear_list=true)
00039 {
00040   if (clear_list) {
00041     pi.resize(0);
00042     pj.resize(0);
00043   }
00044   const unsigned ni1=image.ni()-1,nj1=image.nj()-1;
00045   const vcl_ptrdiff_t istep = image.istep(),jstep=image.jstep();
00046   const T* row = image.top_left_ptr()+istep+jstep;
00047   for (unsigned j=1;j<nj1;++j,row+=jstep)
00048   {
00049     const T* pixel = row;
00050     for (unsigned i=1;i<ni1;++i,pixel+=istep)
00051       if (*pixel>=min_thresh && vil_is_peak_3x3(pixel,istep,jstep))
00052       { pi.push_back(i); pj.push_back(j); }
00053   }
00054 }
00055 
00056 
00057 //: Fit a paraboloid to a pixel and its 8 neighbors to interpolate the peak.
00058 // \return true if the neighborhood produces a proper peak (not a saddle)
00059 // return by reference the sub-pixel offsets from the pixel center
00060 // \a dx and \a dy as well as the interpolated peak value \a val.
00061 template<class T>
00062 bool vil_interpolate_peak(const T* pixel,
00063                           vcl_ptrdiff_t istep, vcl_ptrdiff_t jstep,
00064                           double& dx, double& dy, double& val)
00065 {
00066   dx=dy=0;
00067   //extract the neighborhood
00068   // +-----+-----+-----+
00069   // | p00 | p10 | p20 |
00070   // +-----+-----+-----+
00071   // | p01 | p11 | p21 |
00072   // +-----+-----+-----+
00073   // | p02 | p12 | p22 |
00074   // +-----+-----+-----+
00075   const T& p11 = *pixel;
00076   const T& p01 = *(pixel-istep);
00077   const T& p21 = *(pixel+istep);
00078   const T& p10 = *(pixel-jstep);
00079   const T& p12 = *(pixel+jstep);
00080   const T& p00 = *(&p10-istep);
00081   const T& p20 = *(&p10+istep);
00082   const T& p02 = *(&p12-istep);
00083   const T& p22 = *(&p12+istep);
00084 
00085   //Compute the 2nd order quadratic coefficients
00086   //      1/6 * [ -1  0 +1 ]
00087   // Ix =       [ -1  0 +1 ]
00088   //            [ -1  0 +1 ]
00089   double Ix =(-p00-p01-p02 +p20+p21+p22)/6.0;
00090   //      1/6 * [ -1 -1 -1 ]
00091   // Iy =       [  0  0  0 ]
00092   //            [ +1 +1 +1 ]
00093   double Iy =(-p00-p10-p20 +p02+p12+p22)/6.0;
00094   //      1/3 * [ +1 -2 +1 ]
00095   // Ixx =      [ +1 -2 +1 ]
00096   //            [ +1 -2 +1 ]
00097   double Ixx = ((p00+p01+p02 +p20+p21+p22)-2.0*(p10+p11+p12))/3.0;
00098   //      1/4 * [ +1  0 -1 ]
00099   // Ixy =      [  0  0  0 ]
00100   //            [ -1  0 +1 ]
00101   double Ixy = (p00+p22 -p02-p20)/4.0;
00102   //      1/3 * [ +1 +1 +1 ]
00103   // Iyy =      [ -2 -2 -2 ]
00104   //            [ +1 +1 +1 ]
00105   double Iyy = ((p00+p10+p20 +p02+p12+p22)-2.0*(p01+p11+p21))/3.0;
00106 
00107   //
00108   // The next bit is to find the extremum of the fitted surface by setting its
00109   // partial derivatives to zero. We need to solve the following linear system :
00110   // Given the fitted surface is
00111   // I(x,y) = Io + Ix x + Iy y + 1/2 Ixx x^2 + Ixy x y + 1/2 Iyy y^2
00112   // we solve for the maximum (x,y),
00113   //
00114   //  [ Ixx Ixy ] [ dx ] + [ Ix ] = [ 0 ]      (dI/dx = 0)
00115   //  [ Ixy Iyy ] [ dy ]   [ Iy ]   [ 0 ]      (dI/dy = 0)
00116   //
00117   double det = Ixx*Iyy - Ixy*Ixy;
00118   // det>0 corresponds to a true local extremum otherwise a saddle point
00119   if (det<=0)
00120     return false;
00121 
00122   dx = (Iy*Ixy - Ix*Iyy) / det;
00123   dy = (Ix*Ixy - Iy*Ixx) / det;
00124   // more than one pixel away
00125   if (dx > 1.0 || dx < -1.0 || dy > 1.0 || dy < -1.0)
00126     return false;
00127 
00128   double Io =(p00+p01+p02 +p10+p11+p12 +p20+p21+p22)/9.0;
00129 
00130   val = Io + (Ix + 0.5*Ixx*dx + Ixy*dy)*dx + (Iy + 0.5*Iyy*dy)*dy;
00131 
00132   return true;
00133 }
00134 
00135 
00136 //: Return sub-pixel (px,py,val) for all points in image strictly above their 8 neighbours
00137 //  Interpolation sub-pixel position of all local peaks (px[k],py[k])
00138 //  above given threshold value by fitting a paraboloid.
00139 //  Interpolated peak values are returned in \a val.
00140 // \param clear_list  If true (the default) then empty lists before adding new examples.
00141 // \relatesalso vil_image_view
00142 template <class T>
00143 inline void vil_find_peaks_3x3_subpixel(vcl_vector<double>& px,
00144                                         vcl_vector<double>& py,
00145                                         vcl_vector<double>& val,
00146                                         const vil_image_view<T>& image,
00147                                         const T& min_thresh,
00148                                         bool clear_list=true)
00149 {
00150   if (clear_list) {
00151     px.resize(0);
00152     py.resize(0);
00153     val.resize(0);
00154   }
00155   const unsigned ni1=image.ni()-1,nj1=image.nj()-1;
00156   const vcl_ptrdiff_t istep = image.istep(),jstep=image.jstep();
00157   const T* row = image.top_left_ptr()+istep+jstep;
00158   double dx,dy,v;
00159   for (unsigned j=1;j<nj1;++j,row+=jstep)
00160   {
00161     const T* pixel = row;
00162     for (unsigned i=1;i<ni1;++i,pixel+=istep)
00163       if (*pixel>=min_thresh && vil_is_peak_3x3(pixel,istep,jstep) &&
00164           vil_interpolate_peak(pixel,istep,jstep,dx,dy,v))
00165       {
00166         px.push_back(i+dx);
00167         py.push_back(j+dy);
00168         val.push_back(v);
00169       }
00170   }
00171 }
00172 
00173 #endif // vil_find_peaks_h_