vil_suppress_non_max_edges.hxx
Go to the documentation of this file.
1 // This is core/vil/algo/vil_suppress_non_max_edges.hxx
2 #ifndef vil_suppress_non_max_edges_hxx_
3 #define vil_suppress_non_max_edges_hxx_
4 //:
5 // \file
6 // \brief Given gradient image, compute magnitude and zero any non-maximal values
7 // \author Tim Cootes
8 
9 #include <cmath>
11 #include <vil/vil_bilin_interp.h>
12 #include <vil/vil_fill.h>
13 #ifdef _MSC_VER
14 # include <vcl_msvc_warnings.h>
15 #endif
16 #include <cassert>
17 
18 //: Given gradient images, computes magnitude image containing maximal edges
19 // Computes magnitude image. Zeros any below a threshold.
20 // Points with magnitude above a threshold are tested against gradient
21 // along normal to the edge and retained only if they are higher than
22 // their neighbours.
23 //
24 // Note: Currently assumes single plane only.
25 // 2 pixel border around output set to zero
26 template<class srcT, class destT>
28  const vil_image_view<srcT>& grad_j,
29  double grad_mag_threshold,
30  vil_image_view<destT>& grad_mag)
31 {
32  assert(grad_i.nplanes()==grad_j.nplanes());
33  assert(grad_i.nplanes()==1);
34  unsigned ni = grad_i.ni(), nj = grad_i.nj();
35  assert(ni>2 && nj>2);
36  assert(grad_j.ni()==ni && grad_j.nj()==nj);
37  grad_mag.set_size(ni,nj,1);
38 
39  // Fill 2 pixel border with zero
40  vil_fill_col(grad_mag,0,destT(0));
41  vil_fill_col(grad_mag,1,destT(0));
42  vil_fill_col(grad_mag,ni-1,destT(0));
43  vil_fill_col(grad_mag,ni-2,destT(0));
44  vil_fill_row(grad_mag,0,destT(0));
45  vil_fill_row(grad_mag,1,destT(0));
46  vil_fill_row(grad_mag,nj-1,destT(0));
47  vil_fill_row(grad_mag,nj-2,destT(0));
48 
49  const std::ptrdiff_t gi_istep = grad_i.istep(), gi_jstep = grad_i.jstep();
50  const std::ptrdiff_t gj_istep = grad_j.istep(), gj_jstep = grad_j.jstep();
51  const std::ptrdiff_t gm_istep = grad_mag.istep(), gm_jstep = grad_mag.jstep();
52 
53  const srcT * gi_data = &grad_i(0,0);
54  const srcT * gj_data = &grad_j(0,0);
55  const srcT * gi_row = &grad_i(2,2);
56  const srcT * gj_row = &grad_j(2,2);
57  destT * gm_row = &grad_mag(2,2);
58  unsigned ihi=ni-3;
59  unsigned jhi=nj-3;
60 
61  for (unsigned j=2; j<=jhi; ++j, gi_row+=gi_jstep, gj_row+=gj_jstep,
62  gm_row+=gm_jstep)
63  {
64  const srcT* pgi = gi_row;
65  const srcT* pgj = gj_row;
66  destT *pgm = gm_row;
67  for (unsigned i=2; i<=ihi; ++i, pgi+=gi_istep, pgj+=gj_istep,
68  pgm+=gm_istep)
69  {
70  double gmag=std::sqrt(double(pgi[0]*pgi[0] + pgj[0]*pgj[0]));
71  if (gmag<grad_mag_threshold) *pgm=0;
72  else
73  {
74  double dx=pgi[0]/gmag;
75  double dy=pgj[0]/gmag;
76  // Evaluate gradient along direction (dx,dy) at point (i+dx,j+dy)
77  double gx1=vil_bilin_interp_unsafe(i+dx,j+dy,gi_data,gi_istep,gi_jstep);
78  double gy1=vil_bilin_interp_unsafe(i+dx,j+dy,gj_data,gj_istep,gj_jstep);
79  if (dx*gx1+dy*gy1>gmag) *pgm=0;
80  else
81  {
82  // Evaluate gradient along direction (dx,dy) at point (i-dx,j-dy)
83  double gx2=vil_bilin_interp_unsafe(i-dx,j-dy,gi_data,gi_istep,gi_jstep);
84  double gy2=vil_bilin_interp_unsafe(i-dx,j-dy,gj_data,gj_istep,gj_jstep);
85  if (dx*gx2+dy*gy2>gmag) *pgm=0;
86  else
87  *pgm = destT(gmag); // This is a maximal edge!
88  }
89  }
90  }
91  }
92 }
93 
94 namespace {
95  //: Parabolic interpolation of 3 points \p y_0, \p y_1, \p y_2
96  // \returns the peak value by reference in \p y_peak
97  // \returns the peak location offset from the x of \p y_0
98  double interpolate_parabola(double y_1, double y_0, double y_2,
99  double& y_peak)
100  {
101  y_peak = y_0; // initial peak
102  double diff1 = y_2 - y_1; // first derivative
103  double diff2 = 2 * y_0 - y_1 - y_2; // second derivative
104  // handle special case of zero offset
105  if (diff2 == 0.0)
106  return 0.0;
107  y_peak += diff1 * diff1 / (8 * diff2); // interpolate y as max/min
108  return diff1 / (2 * diff2); // interpolate x offset
109  }
110 }
111 
112 
113 //: Given gradient images, computes a subpixel edgemap with magnitudes and orientations
114 // Computes magnitude image. Zeros any below a threshold.
115 // Points with magnitude above a threshold are tested against gradient
116 // along normal to the edge and retained only if they are higher than
117 // their neighbours. The magnitude of retained points is revised using
118 // parabolic interpolation in the normal direction. The same interpolation
119 // provides a subpixel offset from the integral pixel location.
120 //
121 // This algorithm returns a 3-plane image where the planes are:
122 // - 0 - The interpolated peak magnitude
123 // - 1 - The orientation (in radians)
124 // - 2 - The offset to the subpixel peak in the gradient direction
125 // All non-maximal edge pixel have the value zero in all three planes.
126 // \sa vil_orientations_at_edges
127 //
128 // The subpixel location for pixel (i,j) is computed as
129 // \code
130 // double theta = grad_mag_orient_offset(i,j,1);
131 // double offset = grad_mag_orient_offset(i,j,2);
132 // double x = i + std::cos(theta)*offset;
133 // double y = j + std::sin(theta)*offset;
134 // \endcode
135 //
136 // Note: Currently assumes single plane only.
137 // 2 pixel border around output set to zero.
138 // If two neighbouring edges have exactly the same strength, it retains
139 // both (ie an edge is eliminated if it is strictly lower than a neighbour,
140 // but not if it is the same as two neighbours).
141 template<class srcT, class destT>
143  const vil_image_view<srcT>& grad_j,
144  double grad_mag_threshold,
145  vil_image_view<destT>& grad_moo)
146 {
147  assert(grad_i.nplanes()==grad_j.nplanes());
148  assert(grad_i.nplanes()==1);
149  unsigned ni = grad_i.ni(), nj = grad_i.nj();
150  assert(ni>2 && nj>2);
151  assert(grad_j.ni()==ni && grad_j.nj()==nj);
152  grad_moo.set_size(ni,nj,3);
153 
154  // Fill 2 pixel border with zero
155  vil_fill_col(grad_moo,0,destT(0));
156  vil_fill_col(grad_moo,1,destT(0));
157  vil_fill_col(grad_moo,ni-1,destT(0));
158  vil_fill_col(grad_moo,ni-2,destT(0));
159  vil_fill_row(grad_moo,0,destT(0));
160  vil_fill_row(grad_moo,1,destT(0));
161  vil_fill_row(grad_moo,nj-1,destT(0));
162  vil_fill_row(grad_moo,nj-2,destT(0));
163 
164  const std::ptrdiff_t gi_istep = grad_i.istep(), gi_jstep = grad_i.jstep();
165  const std::ptrdiff_t gj_istep = grad_j.istep(), gj_jstep = grad_j.jstep();
166  const std::ptrdiff_t gm_istep = grad_moo.istep(), gm_jstep = grad_moo.jstep();
167  const std::ptrdiff_t gm_pstep = grad_moo.planestep();
168 
169  const srcT * gi_data = &grad_i(0,0);
170  const srcT * gj_data = &grad_j(0,0);
171  const srcT * gi_row = &grad_i(2,2);
172  const srcT * gj_row = &grad_j(2,2);
173  destT * gm_row = &grad_moo(2,2);
174  unsigned ihi=ni-3;
175  unsigned jhi=nj-3;
176 
177  for (unsigned j=2; j<=jhi; ++j, gi_row+=gi_jstep, gj_row+=gj_jstep,
178  gm_row+=gm_jstep)
179  {
180  const srcT* pgi = gi_row;
181  const srcT* pgj = gj_row;
182  destT *pgm = gm_row;
183  for (unsigned i=2; i<=ihi; ++i, pgi+=gi_istep, pgj+=gj_istep,
184  pgm+=gm_istep)
185  {
186  double gmag=std::sqrt(double(pgi[0]*pgi[0] + pgj[0]*pgj[0]));
187  if (gmag<grad_mag_threshold){
188  *pgm=0;
189  *(pgm+gm_pstep)=0;
190  *(pgm+2*gm_pstep)=0;
191  }
192  else
193  {
194  double dx=pgi[0]/gmag;
195  double dy=pgj[0]/gmag;
196  // Evaluate gradient along direction (dx,dy) at point (i+dx,j+dy)
197  double gx1=vil_bilin_interp_unsafe(i+dx,j+dy,gi_data,gi_istep,gi_jstep);
198  double gy1=vil_bilin_interp_unsafe(i+dx,j+dy,gj_data,gj_istep,gj_jstep);
199  double g1mag = dx*gx1+dy*gy1;
200  if (g1mag>gmag){
201  *pgm=0;
202  *(pgm+gm_pstep)=0;
203  *(pgm+2*gm_pstep)=0;
204  }
205  else
206  {
207  // Evaluate gradient along direction (dx,dy) at point (i-dx,j-dy)
208  double gx2=vil_bilin_interp_unsafe(i-dx,j-dy,gi_data,gi_istep,gi_jstep);
209  double gy2=vil_bilin_interp_unsafe(i-dx,j-dy,gj_data,gj_istep,gj_jstep);
210  double g2mag = dx*gx2+dy*gy2;
211  if (g2mag>gmag){
212  *pgm=0;
213  *(pgm+gm_pstep)=0;
214  *(pgm+2*gm_pstep)=0;
215  }
216  else
217  {
218  // This is a maximal edge!
219  double peak;
220  double offset = interpolate_parabola(g2mag, gmag, g1mag, peak);
221  *pgm = destT(peak);
222  *(pgm+gm_pstep) = destT(std::atan2(dy,dx));
223  *(pgm+2*gm_pstep) = destT(offset);
224  }
225  }
226  }
227  }
228  }
229 }
230 
231 #undef VIL_SUPPRESS_NON_MAX_EDGES_INSTANTIATE
232 #define VIL_SUPPRESS_NON_MAX_EDGES_INSTANTIATE(srcT, destT) \
233 template void vil_suppress_non_max_edges(const vil_image_view<srcT >& grad_i,\
234  const vil_image_view<srcT >& grad_j,\
235  double grad_mag_threshold,\
236  vil_image_view<destT >& grad_mag);\
237 template void vil_suppress_non_max_edges_subpixel(const vil_image_view<srcT >& grad_i,\
238  const vil_image_view<srcT >& grad_j,\
239  double grad_mag_threshold,\
240  vil_image_view<destT >& grad_moo)
241 
242 #endif // vil_suppress_non_max_edges_hxx_
Various functions for manipulating image views.
Concrete view of image data of type T held in memory.
Definition: vil_fwd.h:13
void set_size(unsigned ni, unsigned nj) override
resize current planes to ni x nj.
std::ptrdiff_t jstep() const
Add this to your pixel pointer to get next j pixel.
void vil_fill_col(vil_image_view< T > &view, unsigned i, T value)
Fill column i in view with given value.
Definition: vil_fill.h:148
unsigned ni() const
Width.
unsigned nj() const
Height.
std::ptrdiff_t planestep() const
Add this to your pixel pointer to get pixel on next plane.
void vil_suppress_non_max_edges(const vil_image_view< srcT > &grad_i, const vil_image_view< srcT > &grad_j, double grad_mag_threshold, vil_image_view< destT > &grad_mag)
Given gradient images, computes magnitude image containing maximal edges.
unsigned nplanes() const
Number of planes.
void vil_suppress_non_max_edges_subpixel(const vil_image_view< srcT > &grad_i, const vil_image_view< srcT > &grad_j, double grad_mag_threshold, vil_image_view< destT > &grad_mag_orient_offset)
Given gradient images, computes a subpixel edgemap with magnitudes and orientations.
double vil_bilin_interp_unsafe(double x, double y, const T *data, std::ptrdiff_t xstep, std::ptrdiff_t ystep)
Compute bilinear interpolation at (x,y), no bound checks. Requires 0<x<ni-2, 0<y<nj-2.
std::ptrdiff_t istep() const
Add this to your pixel pointer to get next i pixel.
void vil_fill_row(vil_image_view< T > &view, unsigned j, T value)
Fill row j in view with given value.
Definition: vil_fill.h:133
Given gradient image, compute magnitude and zero any non-maximal values.
Bilinear interpolation functions for 2D images.