core/vil/algo/vil_suppress_non_max_edges.txx
Go to the documentation of this file.
00001 // This is core/vil/algo/vil_suppress_non_max_edges.txx
00002 #ifndef vil_suppress_non_max_edges_txx_
00003 #define vil_suppress_non_max_edges_txx_
00004 //:
00005 // \file
00006 // \brief Given gradient image, compute magnitude and zero any non-maximal values
00007 // \author Tim Cootes
00008 
00009 #include "vil_suppress_non_max_edges.h"
00010 #include <vil/vil_bilin_interp.h>
00011 #include <vil/vil_fill.h>
00012 #include <vcl_cmath.h>
00013 #include <vcl_cassert.h>
00014 
00015 //: Given gradient images, computes magnitude image containing maximal edges
00016 //  Computes magnitude image.  Zeros any below a threshold.
00017 //  Points with magnitude above a threshold are tested against gradient
00018 //  along normal to the edge and retained only if they are higher than
00019 //  their neighbours.
00020 //
00021 //  Note: Currently assumes single plane only.
00022 //  2 pixel border around output set to zero
00023 template<class srcT, class destT>
00024 void vil_suppress_non_max_edges(const vil_image_view<srcT>& grad_i,
00025                                 const vil_image_view<srcT>& grad_j,
00026                                 double grad_mag_threshold,
00027                                 vil_image_view<destT>& grad_mag)
00028 {
00029   assert(grad_i.nplanes()==grad_j.nplanes());
00030   assert(grad_i.nplanes()==1);
00031   unsigned ni = grad_i.ni(), nj = grad_i.nj();
00032   assert(ni>2 && nj>2);
00033   assert(grad_j.ni()==ni && grad_j.nj()==nj);
00034   grad_mag.set_size(ni,nj,1);
00035 
00036   // Fill 2 pixel border with zero
00037   vil_fill_col(grad_mag,0,destT(0));
00038   vil_fill_col(grad_mag,1,destT(0));
00039   vil_fill_col(grad_mag,ni-1,destT(0));
00040   vil_fill_col(grad_mag,ni-2,destT(0));
00041   vil_fill_row(grad_mag,0,destT(0));
00042   vil_fill_row(grad_mag,1,destT(0));
00043   vil_fill_row(grad_mag,nj-1,destT(0));
00044   vil_fill_row(grad_mag,nj-2,destT(0));
00045 
00046   const vcl_ptrdiff_t gi_istep = grad_i.istep(), gi_jstep = grad_i.jstep();
00047   const vcl_ptrdiff_t gj_istep = grad_j.istep(), gj_jstep = grad_j.jstep();
00048   const vcl_ptrdiff_t gm_istep = grad_mag.istep(), gm_jstep = grad_mag.jstep();
00049 
00050   const srcT * gi_data = &grad_i(0,0);
00051   const srcT * gj_data = &grad_j(0,0);
00052   const srcT * gi_row = &grad_i(2,2);
00053   const srcT * gj_row = &grad_j(2,2);
00054   destT * gm_row = &grad_mag(2,2);
00055   unsigned ihi=ni-3;
00056   unsigned jhi=nj-3;
00057 
00058   for (unsigned j=2; j<=jhi; ++j, gi_row+=gi_jstep, gj_row+=gj_jstep,
00059                                   gm_row+=gm_jstep)
00060   {
00061     const srcT* pgi = gi_row;
00062     const srcT* pgj = gj_row;
00063     destT *pgm = gm_row;
00064     for (unsigned i=2; i<=ihi; ++i, pgi+=gi_istep, pgj+=gj_istep,
00065                                     pgm+=gm_istep)
00066     {
00067       double gmag=vcl_sqrt(double(pgi[0]*pgi[0] + pgj[0]*pgj[0]));
00068       if (gmag<grad_mag_threshold) *pgm=0;
00069       else
00070       {
00071         double dx=pgi[0]/gmag;
00072         double dy=pgj[0]/gmag;
00073         // Evaluate gradient along direction (dx,dy) at point (i+dx,j+dy)
00074         double gx1=vil_bilin_interp_unsafe(i+dx,j+dy,gi_data,gi_istep,gi_jstep);
00075         double gy1=vil_bilin_interp_unsafe(i+dx,j+dy,gj_data,gj_istep,gj_jstep);
00076         if (dx*gx1+dy*gy1>gmag) *pgm=0;
00077         else
00078         {
00079           // Evaluate gradient along direction (dx,dy) at point (i-dx,j-dy)
00080           double gx2=vil_bilin_interp_unsafe(i-dx,j-dy,gi_data,gi_istep,gi_jstep);
00081           double gy2=vil_bilin_interp_unsafe(i-dx,j-dy,gj_data,gj_istep,gj_jstep);
00082           if (dx*gx2+dy*gy2>gmag) *pgm=0;
00083           else
00084             *pgm = destT(gmag);  // This is a maximal edge!
00085         }
00086       }
00087     }
00088   }
00089 }
00090 
00091 namespace {
00092   //: Parabolic interpolation of 3 points \p y_0, \p y_1, \p y_2
00093   //  \returns the peak value by reference in \p y_peak
00094   //  \returns the peak location offset from the x of \p y_0
00095   double interpolate_parabola(double y_1, double y_0, double y_2,
00096                               double& y_peak)
00097   {
00098     double diff1 = y_2 - y_1;      // first derivative
00099     double diff2 = 2 * y_0 - y_1 - y_2; // second derivative
00100     y_peak = y_0 + diff1 * diff1 / (8 * diff2);        // interpolate y as max/min
00101     return diff1 / (2 * diff2);   // interpolate x offset
00102   }
00103 }
00104 
00105 
00106 //: Given gradient images, computes a subpixel edgemap with magnitudes and orientations
00107 //  Computes magnitude image.  Zeros any below a threshold.
00108 //  Points with magnitude above a threshold are tested against gradient
00109 //  along normal to the edge and retained only if they are higher than
00110 //  their neighbours.  The magnitude of retained points is revised using
00111 //  parabolic interpolation in the normal direction.  The same interpolation
00112 //  provides a subpixel offset from the integral pixel location.
00113 //
00114 //  This algorithm returns a 3-plane image where the planes are:
00115 //  - 0 - The interpolated peak magnitude
00116 //  - 1 - The orientation (in radians)
00117 //  - 2 - The offset to the subpixel peak in the gradient direction
00118 //  All non-maximal edge pixel have the value zero in all three planes.
00119 //  \sa vil_orientations_at_edges
00120 //
00121 //  The subpixel location for pixel (i,j) is computed as
00122 //  \code
00123 //    double theta = grad_mag_orient_offset(i,j,1);
00124 //    double offset = grad_mag_orient_offset(i,j,2);
00125 //    double x = i + vcl_cos(theta)*offset;
00126 //    double y = j + vcl_sin(theta)*offset;
00127 //  \endcode
00128 //
00129 //  Note: Currently assumes single plane only.
00130 //  2 pixel border around output set to zero.
00131 //  If two neighbouring edges have exactly the same strength, it retains
00132 //  both (ie an edge is eliminated if it is strictly lower than a neighbour,
00133 //  but not if it is the same as two neighbours).
00134 template<class srcT, class destT>
00135 void vil_suppress_non_max_edges_subpixel(const vil_image_view<srcT>& grad_i,
00136                                          const vil_image_view<srcT>& grad_j,
00137                                          double grad_mag_threshold,
00138                                          vil_image_view<destT>& grad_moo)
00139 {
00140   assert(grad_i.nplanes()==grad_j.nplanes());
00141   assert(grad_i.nplanes()==1);
00142   unsigned ni = grad_i.ni(), nj = grad_i.nj();
00143   assert(ni>2 && nj>2);
00144   assert(grad_j.ni()==ni && grad_j.nj()==nj);
00145   grad_moo.set_size(ni,nj,3);
00146 
00147   // Fill 2 pixel border with zero
00148   vil_fill_col(grad_moo,0,destT(0));
00149   vil_fill_col(grad_moo,1,destT(0));
00150   vil_fill_col(grad_moo,ni-1,destT(0));
00151   vil_fill_col(grad_moo,ni-2,destT(0));
00152   vil_fill_row(grad_moo,0,destT(0));
00153   vil_fill_row(grad_moo,1,destT(0));
00154   vil_fill_row(grad_moo,nj-1,destT(0));
00155   vil_fill_row(grad_moo,nj-2,destT(0));
00156 
00157   const vcl_ptrdiff_t gi_istep = grad_i.istep(), gi_jstep = grad_i.jstep();
00158   const vcl_ptrdiff_t gj_istep = grad_j.istep(), gj_jstep = grad_j.jstep();
00159   const vcl_ptrdiff_t gm_istep = grad_moo.istep(), gm_jstep = grad_moo.jstep();
00160   const vcl_ptrdiff_t gm_pstep = grad_moo.planestep();
00161 
00162   const srcT * gi_data = &grad_i(0,0);
00163   const srcT * gj_data = &grad_j(0,0);
00164   const srcT * gi_row = &grad_i(2,2);
00165   const srcT * gj_row = &grad_j(2,2);
00166   destT * gm_row = &grad_moo(2,2);
00167   unsigned ihi=ni-3;
00168   unsigned jhi=nj-3;
00169 
00170   for (unsigned j=2; j<=jhi; ++j, gi_row+=gi_jstep, gj_row+=gj_jstep,
00171                                   gm_row+=gm_jstep)
00172   {
00173     const srcT* pgi = gi_row;
00174     const srcT* pgj = gj_row;
00175     destT *pgm = gm_row;
00176     for (unsigned i=2; i<=ihi; ++i, pgi+=gi_istep, pgj+=gj_istep,
00177                                     pgm+=gm_istep)
00178     {
00179       double gmag=vcl_sqrt(double(pgi[0]*pgi[0] + pgj[0]*pgj[0]));
00180       if (gmag<grad_mag_threshold){
00181         *pgm=0;
00182         *(pgm+gm_pstep)=0;
00183         *(pgm+2*gm_pstep)=0;
00184       }
00185       else
00186       {
00187         double dx=pgi[0]/gmag;
00188         double dy=pgj[0]/gmag;
00189         // Evaluate gradient along direction (dx,dy) at point (i+dx,j+dy)
00190         double gx1=vil_bilin_interp_unsafe(i+dx,j+dy,gi_data,gi_istep,gi_jstep);
00191         double gy1=vil_bilin_interp_unsafe(i+dx,j+dy,gj_data,gj_istep,gj_jstep);
00192         double g1mag = dx*gx1+dy*gy1;
00193         if (g1mag>gmag){
00194           *pgm=0;
00195           *(pgm+gm_pstep)=0;
00196           *(pgm+2*gm_pstep)=0;
00197         }
00198         else
00199         {
00200           // Evaluate gradient along direction (dx,dy) at point (i-dx,j-dy)
00201           double gx2=vil_bilin_interp_unsafe(i-dx,j-dy,gi_data,gi_istep,gi_jstep);
00202           double gy2=vil_bilin_interp_unsafe(i-dx,j-dy,gj_data,gj_istep,gj_jstep);
00203           double g2mag = dx*gx2+dy*gy2;
00204           if (g2mag>gmag){
00205             *pgm=0;
00206             *(pgm+gm_pstep)=0;
00207             *(pgm+2*gm_pstep)=0;
00208           }
00209           else
00210           {
00211             // This is a maximal edge!
00212             double peak;
00213             double offset = interpolate_parabola(g2mag, gmag, g1mag, peak);
00214             *pgm = destT(peak);
00215             *(pgm+gm_pstep) = destT(vcl_atan2(dy,dx));
00216             *(pgm+2*gm_pstep) = destT(offset);
00217           }
00218         }
00219       }
00220     }
00221   }
00222 }
00223 
00224 #undef VIL_SUPPRESS_NON_MAX_EDGES_INSTANTIATE
00225 #define VIL_SUPPRESS_NON_MAX_EDGES_INSTANTIATE(srcT, destT) \
00226 template void vil_suppress_non_max_edges(const vil_image_view<srcT >& grad_i,\
00227                                          const vil_image_view<srcT >& grad_j,\
00228                                          double grad_mag_threshold,\
00229                                          vil_image_view<destT >& grad_mag);\
00230 template void vil_suppress_non_max_edges_subpixel(const vil_image_view<srcT >& grad_i,\
00231                                                   const vil_image_view<srcT >& grad_j,\
00232                                                   double grad_mag_threshold,\
00233                                                   vil_image_view<destT >& grad_moo)
00234 
00235 #endif // vil_suppress_non_max_edges_txx_