vil_gauss_filter.hxx
Go to the documentation of this file.
1 // This is core/vil/algo/vil_gauss_filter.hxx
2 #ifndef vil_gauss_filter_hxx_
3 #define vil_gauss_filter_hxx_
4 //:
5 // \file
6 // \brief smooth images
7 // \author Ian Scott
8 
9 #include <iostream>
10 #include "vil_gauss_filter.h"
11 #include <vil/vil_transpose.h>
13 #ifdef _MSC_VER
14 # include <vcl_msvc_warnings.h>
15 #endif
16 #include <cassert>
17 
18 //=======================================================================
19 
20 // An optimisable rounding function
21 inline unsigned char vl_round(double x, unsigned char ) { return (unsigned char )(x<0?x-0.5:x+0.5);}
22 inline signed char vl_round(double x, signed char ) { return ( signed char )(x<0?x-0.5:x+0.5);}
23 inline unsigned short vl_round(double x, unsigned short ) { return (unsigned short)(x<0?x-0.5:x+0.5);}
24 inline signed short vl_round(double x, signed short ) { return ( signed short)(x<0?x-0.5:x+0.5);}
25 inline unsigned int vl_round(double x, unsigned int ) { return (unsigned int )(x<0?x-0.5:x+0.5);}
26 inline signed int vl_round(double x, signed int ) { return ( signed int )(x<0?x-0.5:x+0.5);}
27 inline unsigned long vl_round(double x, unsigned long ) { return (unsigned long )(x<0?x-0.5:x+0.5);}
28 inline signed long vl_round(double x, signed long ) { return ( signed long )(x<0?x-0.5:x+0.5);}
29 inline double vl_round(double x, double ) { return x; }
30 inline float vl_round(double x, float ) { return (float)x; }
31 
32 //=======================================================================
33 //: Smooth and subsample src_im to produce dest_im
34 // Applies 5 pin filter in x and y, then samples
35 // every other pixel.
36 // Assumes dest_im has sufficient data allocated
37 
38 template <class srcT, class destT>
39 void vil_gauss_filter_5tap(const srcT* src_im, std::ptrdiff_t src_istep, std::ptrdiff_t src_jstep,
40  destT* dest_im, std::ptrdiff_t dest_istep, std::ptrdiff_t dest_jstep,
41  unsigned nx, unsigned ny, const vil_gauss_filter_5tap_params& params,
42  destT* work, std::ptrdiff_t work_jstep)
43 {
44  assert (nx > 3 && ny > 3);
45  // Convolve src with a 5 x 1 Gaussian filter,
46  // placing result in work_
47  // First perform horizontal smoothing
48  for (unsigned int y=0;y<ny;y++)
49  {
50  destT* work_row = work + y*work_jstep;
51  const srcT* src_col3 = src_im + y*src_jstep;
52  const srcT* src_col2 = src_col3 - src_istep;
53  const srcT* src_col1 = src_col3 - 2 * src_istep;
54  const srcT* src_col4 = src_col3 + src_istep;
55  const srcT* src_col5 = src_col3 + 2 * src_istep;
56 
57  int x;
58  int nx2 = nx-2;
59  for (x=2;x<nx2;x++)
60  work_row[x] = vl_round( params.filt2() * src_col1[x*src_istep]
61  + params.filt1() * src_col2[x*src_istep]
62  + params.filt0() * src_col3[x*src_istep]
63  + params.filt1() * src_col4[x*src_istep]
64  + params.filt2() * src_col5[x*src_istep], (destT)0);
65 
66  // Now deal with edge effects :
67  work_row[0] = vl_round( params.filt_edge0() * src_col3[0]
68  + params.filt_edge1() * src_col4[0]
69  + params.filt_edge2() * src_col5[0], (destT)0);
70 
71  work_row[1] = vl_round( params.filt_pen_edge_n1() * src_col2[src_istep]
72  + params.filt_pen_edge0() * src_col3[src_istep]
73  + params.filt_pen_edge1() * src_col4[src_istep]
74  + params.filt_pen_edge2() * src_col5[src_istep], (destT)0);
75 
76  work_row[nx-2] = vl_round( params.filt_pen_edge2() * src_col1[(nx-2)*src_istep]
77  + params.filt_pen_edge1() * src_col2[(nx-2)*src_istep]
78  + params.filt_pen_edge0() * src_col3[(nx-2)*src_istep]
79  + params.filt_pen_edge_n1() * src_col4[(nx-2)*src_istep], (destT)0);
80 
81  work_row[nx-1] = vl_round( params.filt_edge2() * src_col1[(nx-1)*src_istep]
82  + params.filt_edge1() * src_col2[(nx-1)*src_istep]
83  + params.filt_edge0() * src_col3[(nx-1)*src_istep], (destT)0);
84  }
85 
86 // work_.print_all(std::cout);
87  // Now perform vertical smoothing
88  for (unsigned int y=2;y<ny-2;y++)
89  {
90  destT* dest_row = dest_im + y*dest_jstep;
91 
92  const destT* work_row3 = work + y*work_jstep;
93  const destT* work_row2 = work_row3 - work_jstep;
94  const destT* work_row1 = work_row3 - 2 * work_jstep;
95  const destT* work_row4 = work_row3 + work_jstep;
96  const destT* work_row5 = work_row3 + 2 * work_jstep;
97 
98  for (unsigned int x=0; x<nx; x++)
99  dest_row[x*dest_istep] = vl_round( params.filt2() * work_row1[x]
100  + params.filt1() * work_row2[x]
101  + params.filt0() * work_row3[x]
102  + params.filt1() * work_row4[x]
103  + params.filt2() * work_row5[x], (destT)0);
104  }
105 
106  // Now deal with edge effects :
107  //
108  const destT* work_row_bottom_1 = work;
109  const destT* work_row_bottom_2 = work_row_bottom_1 + work_jstep;
110  const destT* work_row_bottom_3 = work_row_bottom_1 + 2 * work_jstep;
111  const destT* work_row_bottom_4 = work_row_bottom_1 + 3 * work_jstep;
112 
113  const destT* work_row_top_5 = work + (ny-1) * work_jstep;
114  const destT* work_row_top_4 = work_row_top_5 - work_jstep;
115  const destT* work_row_top_3 = work_row_top_5 - 2 * work_jstep;
116  const destT* work_row_top_2 = work_row_top_5 - 3 * work_jstep;
117 
118  destT* dest_row_top = dest_im + (ny-1) * dest_jstep;
119  destT* dest_row_next_top = dest_row_top - dest_jstep;
120  destT* dest_row_bottom = dest_im;
121  destT* dest_row_next_bottom = dest_row_bottom + dest_jstep;
122 
123  for (unsigned int x=0;x<nx;x++)
124  {
125  dest_row_top[x*dest_istep] = vl_round( params.filt_edge0() * work_row_top_5[x]
126  + params.filt_edge1() * work_row_top_4[x]
127  + params.filt_edge2() * work_row_top_3[x], (destT)0);
128 
129  dest_row_next_top[x*dest_istep] = vl_round( params.filt_pen_edge2() * work_row_top_2[x]
130  + params.filt_pen_edge1() * work_row_top_3[x]
131  + params.filt_pen_edge0() * work_row_top_4[x]
132  + params.filt_pen_edge_n1()*work_row_top_5[x], (destT)0);
133 
134  dest_row_next_bottom[x*dest_istep] = vl_round( params.filt_pen_edge2() * work_row_bottom_4[x]
135  + params.filt_pen_edge1() * work_row_bottom_3[x]
136  + params.filt_pen_edge0() * work_row_bottom_2[x]
137  + params.filt_pen_edge_n1()*work_row_bottom_1[x], (destT)0);
138 
139  dest_row_bottom[x*dest_istep] = vl_round( params.filt_edge2() * work_row_bottom_3[x]
140  + params.filt_edge1() * work_row_bottom_2[x]
141  + params.filt_edge0() * work_row_bottom_1[x], (destT)0);
142  }
143 // dest_im.print_all(std::cout);
144 }
145 
146 template <class srcT, class destT>
148  vil_image_view<destT>& dest_im,
149  const vil_gauss_filter_5tap_params& params,
150  vil_image_view<destT>& work)
151 {
152  unsigned ni = src_im.ni();
153  unsigned nj = src_im.nj();
154  unsigned n_planes = src_im.nplanes();
155  dest_im.set_size(ni, nj, n_planes);
156  work.set_size(ni,nj,1);
157  assert (work.jstep() == (std::ptrdiff_t)ni);
158 
159  if (ni > 3 && nj > 3)
160  // Reduce plane-by-plane
161  for (unsigned p=0;p<n_planes;++p)
162  vil_gauss_filter_5tap(&src_im(0,0,p), src_im.istep(), src_im.jstep(),
163  &dest_im(0,0,p), dest_im.istep(), dest_im.jstep(), ni,nj,
164  params, work.top_left_ptr(), work.jstep());
165  else
166  {
167  if (ni==0 || nj==0) return;
168  if (ni==1)
169  {
170  for (unsigned p=0;p<n_planes;++p)
171  for (unsigned j=0;j<nj;++j)
172  work(0,j,p) = vl_round(src_im(0,j,p), destT());
173  }
174  else if (ni==2)
175  {
176  const double k0 = params.filt0()/(params.filt0() + params.filt1());
177  const double k1 = params.filt1()/(params.filt0() + params.filt1());
178  for (unsigned p=0;p<n_planes;++p)
179  for (unsigned j=0;j<nj;++j)
180  {
181  work(0,j,p) = vl_round(k0*src_im(0,j,p) + k1*src_im(1,j,p), destT());
182  work(1,j,p) = vl_round(k1*src_im(0,j,p) + k0*src_im(1,j,p), destT());
183  }
184  }
185  else if (ni==3)
186  {
187  const double ke0 = params.filt0()/(params.filt0() + params.filt1());
188  const double ke1 = params.filt1()/(params.filt0() + params.filt1());
189  const double k0 = params.filt0()/(params.filt0() + 2.0*params.filt1());
190  const double k1 = params.filt1()/(params.filt0() + 2.0*params.filt1());
191  for (unsigned p=0;p<n_planes;++p)
192  for (unsigned j=0;j<nj;++j)
193  {
194  work(0,j,p) = vl_round(ke0*src_im(0,j,p) + ke1*src_im(1,j,p) , destT());
195  work(1,j,p) = vl_round(k1 *src_im(0,j,p) + k0 *src_im(1,j,p) + k1 *src_im(2,j,p), destT());
196  work(2,j,p) = vl_round( ke1*src_im(1,j,p) + ke0*src_im(2,j,p), destT());
197  }
198  }
199  else if (ni==4)
200  {
201  const double ke0 = params.filt_edge0();
202  const double ke1 = params.filt_edge1();
203  const double ke2 = params.filt_edge2();
204  const double kpn1= params.filt_pen_edge_n1();
205  const double kp0 = params.filt_pen_edge0();
206  const double kp1 = params.filt_pen_edge1();
207  const double kp2 = params.filt_pen_edge2();
208  for (unsigned p=0;p<n_planes;++p)
209  for (unsigned j=0;j<nj;++j)
210  {
211  work(0,j,p) = vl_round(ke0 *src_im(0,j,p) + ke1*src_im(1,j,p) + ke2*src_im(2,j,p) , destT());
212  work(1,j,p) = vl_round(kpn1*src_im(0,j,p) + kp0*src_im(1,j,p) + kp1*src_im(2,j,p) + kp2 *src_im(3,j,p), destT());
213  work(2,j,p) = vl_round(kp2 *src_im(0,j,p) + kp1*src_im(1,j,p) + kp0*src_im(2,j,p) + kpn1*src_im(3,j,p), destT());
214  work(3,j,p) = vl_round( ke2*src_im(1,j,p) + ke1*src_im(2,j,p) + ke0 *src_im(3,j,p), destT());
215  }
216  }
217  else if (ni>4)
218  {
219  double kernel[5];
220  kernel[0] = kernel[4] = params.filt2();
221  kernel[1] = kernel[3] = params.filt1();
222  kernel[2] = params.filt0();
223  vil_convolve_1d(src_im,work, kernel+2, -2, +2,
225  }
226  if (nj==1)
227  dest_im=work;
228  else if (nj==2)
229  {
230  const double k0 = params.filt0()/(params.filt0() + params.filt1());
231  const double k1 = params.filt1()/(params.filt0() + params.filt1());
232  for (unsigned p=0;p<n_planes;++p)
233  for (unsigned i=0;i<ni;++i)
234  {
235  dest_im(i,0,p) = vl_round(k0 * work(i,0,p) + k1 * work(i,1,p), destT());
236  dest_im(i,1,p) = vl_round(k1 * work(i,0,p) + k0 * work(i,1,p), destT());
237  }
238  }
239  else if (nj==3)
240  {
241  const double ke0 = params.filt0()/(params.filt0() + params.filt1());
242  const double ke1 = params.filt1()/(params.filt0() + params.filt1());
243  const double k0 = params.filt0()/(params.filt0() + 2.0*params.filt1());
244  const double k1 = params.filt1()/(params.filt0() + 2.0*params.filt1());
245  for (unsigned p=0;p<n_planes;++p)
246  for (unsigned i=0;i<ni;++i)
247  {
248  dest_im(i,0,p) = vl_round(ke0*work(i,0,p) + ke1*work(i,1,p) , destT());
249  dest_im(i,1,p) = vl_round(k1 *work(i,0,p) + k0 *work(i,1,p) + k1 *work(i,2,p), destT());
250  dest_im(i,2,p) = vl_round( ke1*work(i,1,p) + ke0*work(i,2,p), destT());
251  }
252  }
253  else if (nj==4)
254  {
255  const double ke0 = params.filt_edge0();
256  const double ke1 = params.filt_edge1();
257  const double ke2 = params.filt_edge2();
258  const double kpn1= params.filt_pen_edge_n1();
259  const double kp0 = params.filt_pen_edge0();
260  const double kp1 = params.filt_pen_edge1();
261  const double kp2 = params.filt_pen_edge2();
262  for (unsigned p=0;p<n_planes;++p)
263  for (unsigned i=0;i<ni;++i)
264  {
265  dest_im(i,0,p) = vl_round(ke0 *work(i,0,p) + ke1*work(i,1,p) + ke2*work(i,2,p) , destT());
266  dest_im(i,1,p) = vl_round(kpn1*work(i,0,p) + kp0*work(i,1,p) + kp1*work(i,2,p) + kp2 *work(i,3,p), destT());
267  dest_im(i,2,p) = vl_round(kp2 *work(i,0,p) + kp1*work(i,1,p) + kp0*work(i,2,p) + kpn1*work(i,3,p), destT());
268  dest_im(i,3,p) = vl_round( ke2*work(i,1,p) + ke1*work(i,2,p) + ke0 *work(i,3,p), destT());
269  }
270  }
271  else if (nj>4)
272  {
273  double kernel[5];
274  kernel[0] = kernel[4] = params.filt2();
275  kernel[1] = kernel[3] = params.filt1();
276  kernel[2] = params.filt0();
277  vil_image_view<destT> rdest = vil_transpose(dest_im);
278  vil_convolve_1d(vil_transpose(work), rdest, kernel+2, -2, +2,
280  }
281  }
282 
283 #if 0
284  vsl_indent_inc(std::cout);
285  std::cout << vsl_indent() << "Work image B\n";
286  workb_.print_all(std::cout);
287  vsl_indent_dec(std::cout);
288 #endif
289 }
290 
291 #undef VIL_GAUSS_FILTER_INSTANTIATE
292 #define VIL_GAUSS_FILTER_INSTANTIATE(srcT, destT) \
293 template void vil_gauss_filter_5tap(const vil_image_view<srcT >& src_im, \
294  vil_image_view<destT >& dest_im, \
295  const vil_gauss_filter_5tap_params& params, \
296  vil_image_view<destT >& work)
297 
298 #endif // vil_gauss_filter_hxx_
double filt1() const
Filter tap value.
Smooths images.
Concrete view of image data of type T held in memory.
Definition: vil_fwd.h:13
double filt_edge0() const
Filter tap value.
double filt2() const
Filter tap value.
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.
unsigned ni() const
Width.
unsigned nj() const
Height.
double filt_pen_edge0() const
Filter tap value.
double filt_pen_edge2() const
Filter tap value.
Kernel is trimmed and reweighed, to allow convolution up to boundary.
double filt_edge1() const
Filter tap value.
double filt0() const
Filter tap value.
double filt_pen_edge1() const
Filter tap value.
double filt_pen_edge_n1() const
Filter tap value.
void vil_gauss_filter_5tap(const srcT *src_im, std::ptrdiff_t src_istep, std::ptrdiff_t src_jstep, destT *dest_im, std::ptrdiff_t dest_istep, std::ptrdiff_t dest_jstep, unsigned nx, unsigned ny, const vil_gauss_filter_5tap_params &params, destT *work, std::ptrdiff_t work_jstep)
Smooth and subsample src_im to produce dest_im.
T * top_left_ptr()
Pointer to the first (top left in plane 0) pixel.
void vil_convolve_1d(const vil_image_view< srcT > &src_im, vil_image_view< destT > &dest_im, const kernelT *kernel, std::ptrdiff_t k_lo, std::ptrdiff_t k_hi, accumT ac, vil_convolve_boundary_option start_option, vil_convolve_boundary_option end_option)
Convolve kernel[i] (i in [k_lo,k_hi]) with srcT in i-direction.
unsigned char vl_round(double x, unsigned char)
unsigned nplanes() const
Number of planes.
double filt_edge2() const
Filter tap value.
std::ptrdiff_t istep() const
Add this to your pixel pointer to get next i pixel.
1D Convolution with cunning boundary options
vil_image_view< T > vil_transpose(const vil_image_view< T > &v)
Create a view which appears as the transpose of this view.
Definition: vil_transpose.h:16