vil_gauss_reduce.hxx
Go to the documentation of this file.
1 // This is core/vil/algo/vil_gauss_reduce.hxx
2 #ifndef vil_gauss_reduce_hxx_
3 #define vil_gauss_reduce_hxx_
4 //:
5 // \file
6 // \brief Functions to smooth and sub-sample image in one direction
7 // \author Tim Cootes
8 
9 #include "vil_gauss_reduce.h"
10 #include <cassert>
11 #ifdef _MSC_VER
12 # include <vcl_msvc_warnings.h>
13 #endif
14 #include <vil/vil_bilin_interp.h>
15 #include <vil/vil_plane.h>
16 #include <vil/vil_convert.h>
17 
18 //: Smooth and subsample src_im to produce dest_im
19 // Applies filter in x and y, then samples every other pixel.
20 // work_im provides workspace
21 template<class T>
23  vil_image_view<T>& dest_im,
24  vil_image_view<T>& work_im)
25 {
26  unsigned ni = src_im.ni();
27  unsigned nj = src_im.nj();
28  unsigned n_planes = src_im.nplanes();
29 
30  // Output image size
31  unsigned ni2 = (ni+1)/2;
32  unsigned nj2 = (nj+1)/2;
33 
34  dest_im.set_size(ni2,nj2,n_planes);
35 
36  if (work_im.ni()<ni2 || work_im.nj()<nj)
37  work_im.set_size(ni2,nj);
38 
39  // Reduce plane-by-plane
40  for (unsigned int i=0;i<n_planes;++i)
41  {
42  // Smooth and subsample in x, result in work_im
43  vil_gauss_reduce_1plane(src_im.top_left_ptr()+i*src_im.planestep(),ni,nj,
44  src_im.istep(),src_im.jstep(),
45  work_im.top_left_ptr(),
46  work_im.istep(),work_im.jstep());
47 
48  // Smooth and subsample in y (by implicitly transposing work_im)
49  vil_gauss_reduce_1plane(work_im.top_left_ptr(),nj,ni2,
50  work_im.jstep(),work_im.istep(),
51  dest_im.top_left_ptr()+i*dest_im.planestep(),
52  dest_im.jstep(),dest_im.istep());
53  }
54 }
55 
56 //: Smooth and subsample src_im to produce dest_im (2/3 size)
57 // Applies filter in x and y, then samples every other pixel.
58 // work_im provides workspace
59 template<class T>
61  vil_image_view<T>& dest_im,
62  vil_image_view<T>& work_im)
63 {
64  unsigned ni = src_im.ni();
65  unsigned nj = src_im.nj();
66  unsigned n_planes = src_im.nplanes();
67 
68  // Output image size
69  unsigned ni2 = (2*ni+1)/3;
70  unsigned nj2 = (2*nj+1)/3;
71 
72  dest_im.set_size(ni2,nj2,n_planes);
73 
74  if (work_im.ni()<ni2 || work_im.nj()<nj)
75  work_im.set_size(ni2,nj);
76 
77  // Reduce plane-by-plane
78  for (unsigned int i=0;i<n_planes;++i)
79  {
80  // Smooth and subsample in x, result in work_im
81  vil_gauss_reduce_2_3_1plane(src_im.top_left_ptr()+i*src_im.planestep(),ni,nj,
82  src_im.istep(),src_im.jstep(),
83  work_im.top_left_ptr(),
84  work_im.istep(),work_im.jstep());
85 
86  // Smooth and subsample in y (by implicitly transposing work_im)
88  work_im.jstep(),work_im.istep(),
89  dest_im.top_left_ptr()+i*dest_im.planestep(),
90  dest_im.jstep(),dest_im.istep());
91  }
92 }
93 
94 //: Smooth and subsample src_im to produce dest_im
95 // Applies filter in x and y, then samples every other pixel.
96 template<class T>
98  vil_image_view<T>& dest_im)
99 {
100  unsigned int ni = src_im.ni();
101  unsigned int nj = src_im.nj();
102  unsigned int n_planes = src_im.nplanes();
103 
104  // Output image size
105  unsigned int ni2 = (ni+1)/2;
106  unsigned int nj2 = (nj+1)/2;
107 
108  dest_im.set_size(ni2,nj2,n_planes);
109 
110  // Reduce plane-by-plane
111  for (unsigned int i=0;i<n_planes;++i)
112  {
113  vil_gauss_reduce_121_1plane(src_im.top_left_ptr() + i*src_im.planestep(),
114  ni, nj,
115  src_im.istep(),src_im.jstep(),
116  dest_im.top_left_ptr() + i*dest_im.planestep(),
117  dest_im.istep(), dest_im.jstep());
118  }
119 }
120 
121 
122 //: An optimisable rounding function
123 inline unsigned char rl_round(double x, unsigned char )
124 { return (unsigned char) (x+0.5);}
125 
126 inline signed char rl_round(double x, signed char )
127 { return (signed char) (x+0.5);}
128 
129 inline unsigned short rl_round(double x, unsigned short )
130 { return (unsigned short) (x+0.5);}
131 
132 inline signed short rl_round(double x, signed short )
133 { return (signed short) (x+0.5);}
134 
135 inline unsigned int rl_round(double x, unsigned int )
136 { return (unsigned int) (x+0.5);}
137 
138 inline signed int rl_round(double x, signed int )
139 { return (signed int) (x+0.5);}
140 
141 inline unsigned long rl_round(double x, unsigned long )
142 { return (unsigned long) (x+0.5);}
143 
144 inline signed long rl_round(double x, signed long )
145 { return (signed long) (x+0.5);}
146 
147 inline double rl_round (double x, double )
148 { return x; }
149 
150 inline float rl_round (double x, float )
151 { return (float) x; }
152 
153 //=======================================================================
154 //: Smooth and subsample src_im to produce dest_im
155 // Applies 5 pin filter in x and y, then samples
156 // every other pixel.
157 // Assumes dest_im has sufficient data allocated
158 
159 template <class T>
161  vil_image_view<T>& dest,
162  vil_image_view<T>& worka,
163  vil_image_view<T>& workb,
164  const vil_gauss_reduce_params &params)
165 {
166  assert(src.ni() >= 5 && src.nj() >= 5);
167  // Convolve src with a 5 x 1 gaussian filter,
168  // placing result in worka
169 
170  // First perform horizontal smoothing
171  for (unsigned y=0;y<src.nj();y++)
172  {
173  unsigned ni2 = src.ni()-2;
174  for (unsigned x=2;x<ni2;++x)
175  worka(x,y) = rl_round( params.filt2() * (src(x-2,y) + src(x+2,y))
176  + params.filt1() * (src(x-1,y) + src(x+1,y))
177  + params.filt0() * src(x ,y),
178  T(0));
179 
180  // Now deal with edge effects :
181  worka(0,y) = rl_round( params.filt_edge0() * src(0,y)
182  + params.filt_edge1() * src(1,y)
183  + params.filt_edge2() * src(2,y), (T)0);
184 
185  worka(1,y) = rl_round( params.filt_pen_edge_n1() * src(0,y)
186  + params.filt_pen_edge0() * src(1,y)
187  + params.filt_pen_edge1() * src(2,y)
188  + params.filt_pen_edge2() * src(3,y), (T)0);
189 
190  worka(src.ni()-2,y) = rl_round( params.filt_pen_edge2() * src(src.ni()-4,y)
191  + params.filt_pen_edge1() * src(src.ni()-3,y)
192  + params.filt_pen_edge0() * src(src.ni()-2,y)
193  + params.filt_pen_edge_n1() * src(src.ni()-1,y), (T)0);
194 
195  worka(src.ni()-1,y) = rl_round( params.filt_edge2() * src(src.ni()-3,y)
196  + params.filt_edge1() * src(src.ni()-2,y)
197  + params.filt_edge0() * src(src.ni()-1,y), (T)0);
198  }
199 
200 // worka_.print_all(std::cout);
201  // Now perform vertical smoothing
202  for (unsigned int y=2;y+2<src.nj();++y)
203  {
204  for (unsigned int x=0; x<src.ni(); x++)
205  workb(x,y) = rl_round( params.filt2() *(worka(x,y-2) + worka(x,y+2))
206  + params.filt1() *(worka(x,y-1) + worka(x,y+1))
207  + params.filt0() * worka(x, y),
208  (T)0);
209  }
210 
211  // Now deal with edge effects :
212  //
213  for (unsigned x=0;x<src.ni();x++)
214  {
215  workb(x,src.nj()-1) = rl_round( params.filt_edge0() * worka(x,src.nj()-1)
216  + params.filt_edge1() * worka(x,src.nj()-2)
217  + params.filt_edge2() * worka(x,src.nj()-3), (T)0);
218 
219  workb(x,src.nj()-2) = rl_round( params.filt_pen_edge2() * worka(x,src.nj()-4)
220  + params.filt_pen_edge1() * worka(x,src.nj()-3)
221  + params.filt_pen_edge0() * worka(x,src.nj()-2)
222  + params.filt_pen_edge_n1() * worka(x,src.nj()-1), (T)0);
223 
224  workb(x,1) = rl_round( params.filt_pen_edge2() * worka(x,3)
225  + params.filt_pen_edge1() * worka(x,2)
226  + params.filt_pen_edge0() * worka(x,1)
227  + params.filt_pen_edge_n1() * worka(x,0), (T)0);
228 
229  workb(x,0) = rl_round( params.filt_edge2() * worka(x,2)
230  + params.filt_edge1() * worka(x,1)
231  + params.filt_edge0() * worka(x,0), (T)0);
232  }
233 
234 // workb_.print_all(std::cout);
235 
236 
237 // assert (dest_ni*scale_step() <= src.ni() && dest_nj*scale_step() <= src.nj());
238 
239  const double init_x = 0.5 * (src.ni()-1 - (dest.ni()-1)*params.scale_step());
240  double yd = 0.5 * (src.nj() -1 - (dest.nj()-1)*params.scale_step());
241  for (unsigned int yi=0; yi<dest.nj(); yi++)
242  {
243  double xd=init_x;
244  for (unsigned int xi=0; xi<dest.ni(); xi++)
245  {
246  dest(xi,yi) = rl_round (vil_bilin_interp_safe_extend(workb, xd, yd), T(0));
247  xd += params.scale_step();
248  }
249  yd+= params.scale_step();
250  }
251 }
252 
253 
254 template <class T>
256  vil_image_view<T>& dest,
257  vil_image_view<T>& worka,
258  vil_image_view<T>& workb,
259  const vil_gauss_reduce_params &params)
260 {
261  if (worka.ni() < src.ni() || worka.nj() < src.nj())
262  worka.set_size(src.ni(), src.nj());
263  if (workb.ni() < src.ni() || workb.nj() < src.nj())
264  workb.set_size(src.ni(), src.nj());
265  dest.set_size((unsigned) (src.ni()/params.scale_step()+0.5),
266  (unsigned) (src.nj()/params.scale_step()+0.5), src.nplanes());
267 
268  // Reduce plane-by-plane
269  for (unsigned p=0;p<src.nplanes();++p) {
270  vil_image_view<T> src_plane = vil_plane(src,p);
271  vil_image_view<T> dest_plane = vil_plane(dest,p);
272  vil_gauss_reduce_general_plane(src_plane, dest_plane, worka, workb, params);
273  }
274 #if 0
275  vsl_indent_inc(std::cout);
276  std::cout << vsl_indent() << "Work image B\n";
277  workb_.print_all(std::cout);
278  vsl_indent_dec(std::cout);
279 #endif
280 }
281 
282 
283 template <class T>
284 void vil_gauss_reduce_1plane(const T* src_im,
285  unsigned src_ni, unsigned src_nj,
286  std::ptrdiff_t s_x_step, std::ptrdiff_t s_y_step,
287  T* dest_im,
288  std::ptrdiff_t d_x_step, std::ptrdiff_t d_y_step)
289 {
290  T* d_row = dest_im;
291  const T* s_row = src_im;
292  std::ptrdiff_t sxs2 = s_x_step*2;
293  unsigned ni2 = (src_ni-3)/2;
295 
296  for (unsigned y=0;y<src_nj;++y)
297  {
298  // Set first element of row
299  double dsum = 0.071 * static_cast<double>(s_row[sxs2]) +
300  0.357 * static_cast<double>(s_row[s_x_step]) +
301  0.572 * static_cast<double>(s_row[0]);
302  rounder(dsum, *d_row);
303 
304  T* d = d_row + d_x_step;
305  const T* s = s_row + sxs2;
306  for (unsigned x=0;x<ni2;++x)
307  {
308  dsum = 0.05*static_cast<double>(s[-sxs2]) + 0.25*static_cast<double>(s[-s_x_step]) +
309  0.05*static_cast<double>(s[ sxs2]) + 0.25*static_cast<double>(s[ s_x_step]) +
310  0.4 *static_cast<double>(s[0]);
311  rounder(dsum, *d);
312 
313  d += d_x_step;
314  s += sxs2;
315  }
316  // Set last elements of row
317  dsum = 0.071 * static_cast<double>(s[-sxs2]) +
318  0.357 * static_cast<double>(s[-s_x_step]) +
319  0.572 * static_cast<double>(s[0]);
320  rounder(dsum, *d);
321 
322  d_row += d_y_step;
323  s_row += s_y_step;
324  }
325 }
326 
327 
328 //: Smooth and subsample single plane src_im in x to produce dest_im using 121 filter in x and y
329 // Smooths with a 3x3 filter and subsamples
330 template <class T>
331 void vil_gauss_reduce_121_1plane(const T* src_im,
332  unsigned src_ni, unsigned src_nj,
333  std::ptrdiff_t s_x_step, std::ptrdiff_t s_y_step,
334  T* dest_im,
335  std::ptrdiff_t d_x_step, std::ptrdiff_t d_y_step)
336 {
337  std::ptrdiff_t sxs2 = s_x_step*2;
338  std::ptrdiff_t sys2 = s_y_step*2;
339  T* d_row = dest_im+d_y_step;
340  const T* s_row1 = src_im + s_y_step;
341  const T* s_row2 = s_row1 + s_y_step;
342  const T* s_row3 = s_row2 + s_y_step;
343  unsigned ni2 = (src_ni-2)/2;
344  unsigned nj2 = (src_nj-2)/2;
346 
347  for (unsigned y=0;y<nj2;++y)
348  {
349  // Set first element of row
350  *d_row = *s_row2;
351  T * d = d_row + d_x_step;
352  const T* s1 = s_row1 + sxs2;
353  const T* s2 = s_row2 + sxs2;
354  const T* s3 = s_row3 + sxs2;
355  for (unsigned x=0;x<ni2;++x)
356  {
357  // The following is a little inefficient - could group terms to reduce arithmetic
358  double ds1 = 0.0625 * static_cast<double>(s1[-s_x_step])
359  + 0.125 * static_cast<double>(s1[0])
360  + 0.0625 * static_cast<double>(s1[s_x_step]),
361  ds2 = 0.1250 * static_cast<double>(s2[-s_x_step])
362  + 0.250 * static_cast<double>(s2[0])
363  + 0.1250 * static_cast<double>(s2[s_x_step]),
364  ds3 = 0.0625 * static_cast<double>(s3[-s_x_step])
365  + 0.125 * static_cast<double>(s3[0])
366  + 0.0625 * static_cast<double>(s3[s_x_step]);
367  rounder(ds1 + ds2 + ds3, *d);
368 
369  d += d_x_step;
370  s1 += sxs2;
371  s2 += sxs2;
372  s3 += sxs2;
373  }
374  // Set last elements of row
375  if (src_ni&1)
376  *d = *s2;
377 
378  d_row += d_y_step;
379  s_row1 += sys2;
380  s_row2 += sys2;
381  s_row3 += sys2;
382  }
383 
384  // Need to set first and last rows as well
385 
386  // Dest image should be (src_ni+1)/2 x (src_nj+1)/2
387  const T* s0 = src_im;
388  unsigned ni=(src_ni+1)/2;
389  for (unsigned i=0;i<ni;++i)
390  {
391  dest_im[i]= *s0;
392  s0+=sxs2;
393  }
394 
395  if (src_nj&1)
396  {
397  unsigned yhi = (src_nj-1)/2;
398  T* dest_last_row = dest_im + yhi*d_y_step;
399  const T* s_last = src_im + yhi*sys2;
400  for (unsigned i=0;i<ni;++i)
401  {
402  dest_last_row[i]= *s_last;
403  s_last+=sxs2;
404  }
405  }
406 }
407 
408 
409 template <class T>
410 void vil_gauss_reduce_2_3_1plane(const T* src_im,
411  unsigned src_ni, unsigned src_nj,
412  std::ptrdiff_t s_x_step, std::ptrdiff_t s_y_step,
413  T* dest_im, std::ptrdiff_t d_x_step, std::ptrdiff_t d_y_step)
414 {
415  T* d_row = dest_im;
416  const T* s_row = src_im;
417  std::ptrdiff_t sxs2 = s_x_step*2,sxs3 = s_x_step*3;
418  unsigned d_ni = (2*src_ni+1)/3;
419  unsigned d_ni2 = d_ni/2;
421 
422  for (unsigned y=0;y<src_nj;++y)
423  {
424  // Set first elements of row
425  // The 0.5 offset in the following ensures rounding
426  double drow0=0.75*static_cast<double>(s_row[0]) + 0.25*static_cast<double>(s_row[s_x_step]);
427  double drow_xs=0.5*static_cast<double>(s_row[s_x_step]) + 0.5*static_cast<double>(s_row[sxs2]);
428  rounder(drow0,d_row[0]);
429  rounder(drow_xs,d_row[d_x_step]);
430 
431  T* d = d_row + 2*d_x_step;
432  const T* s = s_row + sxs3;
433  for (unsigned x=1;x<d_ni2;++x)
434  {
435  double df= 0.2 * ( static_cast<double>(s[-s_x_step]) + static_cast<double>(s[s_x_step]) )
436  + 0.6 * static_cast<double>(s[0]) ;
437  rounder(df,*d);
438 
439  d += d_x_step;
440 
441  df = 0.5*(static_cast<double>(s[s_x_step]) + static_cast<double>(s[sxs2]) );
442  rounder(df,*d);
443 
444  d += d_x_step;
445  s += sxs3;
446  }
447  // Set last elements of row
448  if (src_ni%3==1)
449  {
450  double df=0.75*static_cast<double>(s[-s_x_step]) + 0.25*static_cast<double>(s[0]);
451  rounder(df,*d);
452  }
453  else if (src_ni%3==2)
454  {
455  double df = 0.2 * (static_cast<double>(s[-s_x_step]) + static_cast<double>(s[s_x_step]) )
456  + 0.6 * static_cast<double>(s[0]);
457  rounder(df,*d);
458  }
459  d_row += d_y_step;
460  s_row += s_y_step;
461  }
462 }
463 
464 
465 #undef VIL_GAUSS_REDUCE_INSTANTIATE
466 #define VIL_GAUSS_REDUCE_INSTANTIATE(T) \
467 template void vil_gauss_reduce(const vil_image_view<T >& src, \
468  vil_image_view<T >& dest, \
469  vil_image_view<T >& work_im); \
470 template void vil_gauss_reduce_2_3(const vil_image_view<T >& src, \
471  vil_image_view<T >& dest, \
472  vil_image_view<T >& work_im); \
473 template void vil_gauss_reduce_121(const vil_image_view<T >& src, \
474  vil_image_view<T >& dest); \
475 template void vil_gauss_reduce_general(const vil_image_view<T >& src_im, \
476  vil_image_view<T >& dest_im, \
477  vil_image_view<T >& worka, \
478  vil_image_view<T >& workb, \
479  const vil_gauss_reduce_params& params)
480 
481 #endif // vil_gauss_reduce_hxx_
double filt0() const
Filter tap value.
void vil_gauss_reduce_general(const vil_image_view< T > &src_im, vil_image_view< T > &dest_im, const vil_gauss_reduce_params &params)
Smooth and subsample src_im by an arbitrary factor to produce dest_im.
void vil_gauss_reduce_2_3_1plane(const T *src_im, unsigned src_ni, unsigned src_nj, std::ptrdiff_t s_x_step, std::ptrdiff_t s_y_step, T *dest_im, std::ptrdiff_t d_x_step, std::ptrdiff_t d_y_step)
Smooth and subsample single plane src_im in x, result is 2/3rd size.
void vil_gauss_reduce_121_1plane(const T *src_im, unsigned src_ni, unsigned src_nj, std::ptrdiff_t s_x_step, std::ptrdiff_t s_y_step, T *dest_im, std::ptrdiff_t d_x_step, std::ptrdiff_t d_y_step)
Smooth and subsample single plane src_im in x to produce dest_im using 121 filter in x and y.
Concrete view of image data of type T held in memory.
Definition: vil_fwd.h:13
double scale_step() const
the scale step between pyramid levels.
void vil_gauss_reduce_1plane(const T *src_im, unsigned src_ni, unsigned src_nj, std::ptrdiff_t s_x_step, std::ptrdiff_t s_y_step, T *dest_im, std::ptrdiff_t d_x_step, std::ptrdiff_t d_y_step)
Smooth and subsample single plane src_im in x to produce dest_im.
void vil_gauss_reduce_121(const vil_image_view< T > &src, vil_image_view< T > &dest)
Smooth and subsample src_im to produce dest_im.
double filt_pen_edge1() const
Filter tap value.
double filt_pen_edge0() const
Filter tap value.
void set_size(unsigned ni, unsigned nj) override
resize current planes to ni x nj.
void vil_gauss_reduce_2_3(const vil_image_view< T > &src_im, vil_image_view< T > &dest_im, vil_image_view< T > &work_im)
Smooth and subsample src_im to produce dest_im (2/3 size).
double filt_pen_edge_n1() const
Filter tap value.
Some standard conversion functions.
std::ptrdiff_t jstep() const
Add this to your pixel pointer to get next j pixel.
unsigned ni() const
Width.
double filt1() const
Filter tap value.
unsigned nj() const
Height.
std::ptrdiff_t planestep() const
Add this to your pixel pointer to get pixel on next plane.
Performs rounding between different pixel types.
Definition: vil_convert.h:317
double vil_bilin_interp_safe_extend(const vil_image_view< T > &view, double x, double y, unsigned p=0)
Compute bilinear interpolation at (x,y), with bound checks.
void vil_gauss_reduce(const vil_image_view< T > &src, vil_image_view< T > &dest, vil_image_view< T > &work_im)
Smooth and subsample src_im to produce dest_im.
double filt_edge2() const
Filter tap value.
T * top_left_ptr()
Pointer to the first (top left in plane 0) pixel.
vil_image_view< T > vil_plane(const vil_image_view< T > &im, unsigned p)
Return a view of im's plane p.
Definition: vil_plane.h:20
Functions to smooth and sub-sample image in one direction.
unsigned nplanes() const
Number of planes.
double filt2() const
Filter tap value.
double filt_pen_edge2() const
Filter tap value.
unsigned char rl_round(double x, unsigned char)
An optimisable rounding function.
std::ptrdiff_t istep() const
Add this to your pixel pointer to get next i pixel.
double filt_edge1() const
Filter tap value.
double filt_edge0() const
Filter tap value.
void vil_gauss_reduce_general_plane(const vil_image_view< T > &src, vil_image_view< T > &dest, vil_image_view< T > &worka, vil_image_view< T > &workb, const vil_gauss_reduce_params &params)
Smooth and subsample src_im to produce dest_im.
Bilinear interpolation functions for 2D images.