iLab Neuromorphic Robotics Toolkit  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
LowPassImpl.H
Go to the documentation of this file.
1 /*! @file
2  @author Laurent Itti
3  @copyright GNU Public License (GPL v3)
4  @section License
5  @verbatim
6  // ////////////////////////////////////////////////////////////////////////
7  // The iLab Neuromorphic Robotics Toolkit (NRT) //
8  // Copyright 2010-2012 by the University of Southern California (USC) //
9  // and the iLab at USC. //
10  // //
11  // iLab - University of Southern California //
12  // Hedco Neurociences Building, Room HNB-10 //
13  // Los Angeles, Ca 90089-2520 - USA //
14  // //
15  // See http://ilab.usc.edu for information about this project. //
16  // ////////////////////////////////////////////////////////////////////////
17  // This file is part of The iLab Neuromorphic Robotics Toolkit. //
18  // //
19  // The iLab Neuromorphic Robotics Toolkit is free software: you can //
20  // redistribute it and/or modify it under the terms of the GNU General //
21  // Public License as published by the Free Software Foundation, either //
22  // version 3 of the License, or (at your option) any later version. //
23  // //
24  // The iLab Neuromorphic Robotics Toolkit is distributed in the hope //
25  // that it will be useful, but WITHOUT ANY WARRANTY; without even the //
26  // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //
27  // PURPOSE. See the GNU General Public License for more details. //
28  // //
29  // You should have received a copy of the GNU General Public License //
30  // along with The iLab Neuromorphic Robotics Toolkit. If not, see //
31  // <http://www.gnu.org/licenses/>. //
32  // ////////////////////////////////////////////////////////////////////////
33  @endverbatim */
34 
35 
36 #ifndef INCLUDE_NRT_IMAGEPROC_FILTERING_DETAILS_LOWPASSIMPL_H
37 #define INCLUDE_NRT_IMAGEPROC_FILTERING_DETAILS_LOWPASSIMPL_H
38 
39 // ######################################################################
40 template <NRT_PROMOTE_PIX_NO_DEFAULT> inline
42 {
43  int const w = src.width(), h = src.height();
44  // promote the source image to DestType if necessary, so that we do the promotion only once for all, rather than many
45  // times as we access the pixels of the image; if no promotion is necessary, "source" will just point to the original
46  // data of "src" through the copy-on-write/ref-counting behavior of Image:
47  nrt::Image<DestType> const source = nrt::Image<DestType>(src);
48  if (w < 2) return source; // nothing to smooth
49 
50  nrt::Image<DestType> result(source.dims());
51  typename nrt::Image<DestType>::const_iterator sptr = source.const_begin();
52  typename nrt::Image<DestType>::iterator dptr = result.begin();
53 
54  // Unfolded version (all particular cases treated) for max speed. do not use access overload since can access the C
55  // array directly. the bet is: float computations are faster than int (true for most float copros), and int2float
56  // converts fast->do not have to divide by the coeff sum since we directly use float coeffs. notations: in () is the
57  // position of dest ptr, and ^ is src ptr
58 
59  // ########## horizontal pass
60  for (int j = 0; j < h; ++ j)
61  {
62  // leftmost point [ (2^) 1 ] / 3
63  *dptr++ = sptr[0] * (2.0F / 3.0F) + sptr[1] * (1.0F / 3.0F);
64 
65  // rest of the line except last point [ 1^ (2) 1 ] / 4
66  for (int i = 0; i < w - 2; ++ i)
67  {
68  *dptr++ = (sptr[0] + sptr[2]) * 0.25F + sptr[1] * 0.5F;
69  ++ sptr;
70  }
71 
72  // last point [ 1^ (2) ] / 3
73  *dptr++ = sptr[0] * (1.0F / 3.0F) + sptr[1] * (2.0F / 3.0F);
74  sptr += 2; // sptr back to same position as dptr
75  }
76 
77  return result;
78 }
79 
80 // ######################################################################
81 namespace
82 {
83  template <typename promo>
84  struct lowpass3x_visitor : public boost::static_visitor<nrt::GenericImage>
85  {
86  template <class PixType>
87  nrt::GenericImage operator()(nrt::Image<PixType> const im) const
88  { return nrt::GenericImage(nrt::lowPass3x<promo>(im)); }
89  };
90 }
91 
92 template <typename promo> inline
94 { return src.apply_visitor(lowpass3x_visitor<promo>()); }
95 
96 // ######################################################################
97 template <NRT_PROMOTE_PIX_NO_DEFAULT> inline
99 {
100  int const w = src.width(), h = src.height();
101  // promote the source image to float if necessary, so that we do the promotion only once for all, rather than many
102  // times as we access the pixels of the image; if no promotion is necessary, "source" will just point to the original
103  // data of "src" through the copy-on-write/ref-counting behavior of Image:
104  nrt::Image<DestType> const source = nrt::Image<DestType>(src);
105  if (h < 2) return source; // nothing to smooth
106 
107  nrt::Image<DestType> result(source.dims());
108  typename nrt::Image<DestType>::const_iterator sptr = source.const_begin();
109  typename nrt::Image<DestType>::iterator dptr = result.begin();
110  int const w2 = w * 2; // speedup
111 
112  // ########## vertical pass (even though we scan horiz for speedup)
113  // topmost points ( [ (2^) 1 ] / 3 )^T
114  for (int i = 0; i < w; ++ i)
115  {
116  *dptr++ = sptr[0] * (2.0F / 3.0F) + sptr[w] * (1.0F / 3.0F);
117  ++ sptr;
118  }
119  sptr -= w; // go back to top-left
120 
121  // rest of the column except last point ( [ 1^ (2) 1 ] / 4.0 )^T
122  for (int j = 0; j < h - 2; ++ j)
123  for (int i = 0; i < w; ++ i) {
124  *dptr++ = (sptr[0] + sptr[w2]) * 0.25F + sptr[w] * 0.5F;
125  ++ sptr;
126  }
127 
128  // last points ( [ 1^ (2) ] / 3 )^T
129  for (int i = 0; i < w; ++ i)
130  {
131  *dptr++ = sptr[0] * (1.0F / 3.0F) + sptr[w] * (2.0F / 3.0F);
132  ++ sptr;
133  }
134 
135  // finished. here, sptr is one line before the end of array.
136  return result;
137 }
138 
139 // ######################################################################
140 namespace
141 {
142  template <typename promo>
143  struct lowpass3y_visitor : public boost::static_visitor<nrt::GenericImage>
144  {
145  template <class PixType>
146  nrt::GenericImage operator()(nrt::Image<PixType> const im) const
147  { return nrt::GenericImage(nrt::lowPass3y<promo>(im)); }
148  };
149 }
150 
151 template <typename promo> inline
153 { return src.apply_visitor(lowpass3y_visitor<promo>()); }
154 
155 // ######################################################################
156 template <NRT_PROMOTE_PIX_NO_DEFAULT> inline
158 {
159  return nrt::lowPass3y<promo>(nrt::lowPass3x<promo>(src));
160 }
161 
162 // ######################################################################
163 namespace
164 {
165  template <typename promo>
166  struct lowpass3_visitor : public boost::static_visitor<nrt::GenericImage>
167  {
168  template <class PixType>
169  nrt::GenericImage operator()(nrt::Image<PixType> const im) const
170  { return nrt::GenericImage(nrt::lowPass3<promo>(im)); }
171  };
172 }
173 
174 template <typename promo> inline
176 { return src.apply_visitor(lowpass3_visitor<promo>()); }
177 
178 // ######################################################################
179 template <NRT_PROMOTE_PIX_NO_DEFAULT> inline
181 {
182  int const w = src.width(), h = src.height();
183  // promote the source image to float if necessary, so that we do the promotion only once for all, rather than many
184  // times as we access the pixels of the image; if no promotion is necessary, "source" will just point to the original
185  // data of "src" through the copy-on-write/ref-counting behavior of Image:
187  if (w < 2) return source; // nothing to smooth
189  typename nrt::Image<DestType>::const_iterator sptr = source.const_begin();
190  typename nrt::Image<DestType>::iterator dptr = result.begin();
191 
192  if (w == 2) //////////////////////////////////////////////////
193  for (int j = 0; j < h; ++ j)
194  {
195  // leftmost point [ (6^) 4 ] / 10
196  *dptr++ = sptr[0] * (6.0F / 10.0F) + sptr[1] * (4.0F / 10.0F);
197 
198  // rightmost point [ 4^ (6) ] / 10
199  *dptr++ = sptr[0] * (4.0F / 10.0F) + sptr[1] * (6.0F / 10.0F);
200 
201  sptr += 2; // sptr back to same position as dptr
202  }
203  else if (w == 3) //////////////////////////////////////////////////
204  for (int j = 0; j < h; ++ j)
205  {
206  // leftmost point [ (6^) 4 1 ] / 11
207  *dptr++ = sptr[0] * (6.0F / 11.0F) +
208  sptr[1] * (4.0F / 11.0F) +
209  sptr[2] * (1.0F / 11.0F);
210 
211  // middle point [ 4^ (6) 4 ] / 14
212  *dptr++ = (sptr[0] + sptr[2]) * (4.0F / 14.0F) +
213  sptr[1] * (6.0F / 14.0F);
214 
215  // rightmost point [ 1^ 4 (6) ] / 11
216  *dptr++ = sptr[0] * (1.0F / 11.0F) +
217  sptr[1] * (4.0F / 11.0F) +
218  sptr[2] * (6.0F / 11.0F);
219 
220  sptr += 3; // sptr back to same position as dptr
221  }
222  else ////////////////////////////// general case for width() >= 4
223  // *** unfolded version (all particular cases treated) for max speed. do not use access overloading since can
224  // access the C array directly. the bet is: float computations are faster than int (true for most float
225  // copros), and int2float convert is fast -> do not have to divide by the coeff sum since we directly use float
226  // coeffs.
227  // notations: in () is the position of dest ptr, and ^ is src ptr
228  // ########## horizontal pass
229  for (int j = 0; j < h; ++ j)
230  {
231  // leftmost point [ (6^) 4 1 ] / 11
232  *dptr++ = sptr[0] * (6.0F / 11.0F) +
233  sptr[1] * (4.0F / 11.0F) +
234  sptr[2] * (1.0F / 11.0F);
235 
236  // second point [ 4^ (6) 4 1 ] / 15
237  *dptr++ = (sptr[0] + sptr[2]) * (4.0F / 15.0F) +
238  sptr[1] * (6.0F / 15.0F) +
239  sptr[3] * (1.0F / 15.0F);
240 
241  // rest of the line except last 2 points [ 1^ 4 (6) 4 1 ] / 16.0
242  for (int i = 0; i < w - 4; ++ i)
243  {
244  *dptr++ = (sptr[0] + sptr[4]) * (1.0F / 16.0F) +
245  (sptr[1] + sptr[3]) * (4.0F / 16.0F) +
246  sptr[2] * (6.0F / 16.0F);
247  ++ sptr;
248  }
249 
250  // before last point [ 1^ 4 (6) 4 ] / 15
251  *dptr++ = sptr[0] * (1.0F / 15.0F) +
252  (sptr[1] + sptr[3]) * (4.0F / 15.0F) +
253  sptr[2] * (6.0F / 15.0F);
254  ++ sptr;
255 
256  // last point [ 1^ 4 (6) ] / 11
257  *dptr++ = sptr[0] * (1.0F / 11.0F) +
258  sptr[1] * (4.0F / 11.0F) +
259  sptr[2] * (6.0F / 11.0F);
260  sptr += 3; // sptr back to same position as dptr
261  }
262  return result;
263 }
264 
265 
266 // ######################################################################
267 // Anderson's separable kernel: 1/16 * [1 4 6 4 1]
268 template <NRT_PROMOTE_PIX_NO_DEFAULT> inline
270 {
271  int const w = src.width(), h = src.height();
272  // promote the source image to float if necessary, so that we do the promotion only once for all, rather than many
273  // times as we access the pixels of the image; if no promotion is necessary, "source" will just point to the original
274  // data of "src" through the copy-on-write/ref-counting behavior of Image:
276  if (h < 2) return source; // nothing to smooth
278  typename nrt::Image<DestType>::const_iterator sptr = source.const_begin();
279  typename nrt::Image<DestType>::iterator dptr = result.begin();
280 
281  // ########## vertical pass (even though we scan horiz for speedup)
282  int const w2 = w * 2, w3 = w * 3, w4 = w * 4; // speedup
283 
284  if (h == 2) //////////////////////////////////////////////////
285  {
286  // topmost points ( [ (6^) 4 ] / 10 )^T
287  for (int i = 0; i < w; ++ i)
288  {
289  *dptr++ = sptr[0] * (6.0F / 10.0F) +
290  sptr[w] * (4.0F / 10.0F);
291  ++ sptr;
292  }
293  sptr -= w; // go back to top-left
294 
295  // bottommost points ( [ 4^ (6) ] / 10 )^T
296  for (int i = 0; i < w; i ++)
297  {
298  *dptr++ = sptr[0] * (4.0F / 10.0F) +
299  sptr[w] * (6.0F / 10.0F);
300  ++ sptr;
301  }
302  }
303  else if (h == 3) //////////////////////////////////////////////////
304  {
305  // topmost points ( [ (6^) 4 1 ] / 11 )^T
306  for (int i = 0; i < w; ++ i)
307  {
308  *dptr++ = sptr[ 0] * (6.0F / 11.0F) +
309  sptr[ w] * (4.0F / 11.0F) +
310  sptr[w2] * (1.0F / 11.0F);
311  ++ sptr;
312  }
313  sptr -= w; // go back to top-left
314 
315  // middle points ( [ 4^ (6) 4 ] / 14 )^T
316  for (int i = 0; i < w; ++ i)
317  {
318  *dptr++ = (sptr[ 0] + sptr[w2]) * (4.0F / 14.0F) +
319  sptr[ w] * (6.0F / 14.0F);
320  ++ sptr;
321  }
322  sptr -= w; // go back to top-left
323 
324  // bottommost points ( [ 1^ 4 (6) ] / 11 )^T
325  for (int i = 0; i < w; ++ i)
326  {
327  *dptr++ = sptr[ 0] * (1.0F / 11.0F) +
328  sptr[ w] * (4.0F / 11.0F) +
329  sptr[w2] * (6.0F / 11.0F);
330  ++ sptr;
331  }
332  }
333  else ///////////////////////////////// general case for height >= 4
334  {
335  // topmost points ( [ (6^) 4 1 ] / 11 )^T
336  for (int i = 0; i < w; ++ i)
337  {
338  *dptr++ = sptr[ 0] * (6.0F / 11.0F) +
339  sptr[ w] * (4.0F / 11.0F) +
340  sptr[w2] * (1.0F / 11.0F);
341  ++ sptr;
342  }
343  sptr -= w; // go back to top-left
344 
345  // second topmost points ( [ 4^ (6) 4 1 ] / 15 )^T
346  for (int i = 0; i < w; ++ i)
347  {
348  *dptr++ = (sptr[ 0] + sptr[w2]) * (4.0F / 15.0F) +
349  sptr[ w] * (6.0F / 15.0F) +
350  sptr[w3] * (1.0F / 15.0F);
351  ++ sptr;
352  }
353  sptr -= w; // go back to top-left
354 
355  // rest of the column except last 2 points ( [ 1^ 4 (6) 4 1 ] / 16 )T
356  for (int j = 0; j < h - 4; ++ j)
357  for (int i = 0; i < w; ++ i)
358  {
359  *dptr++ = (sptr[ 0] + sptr[w4]) * (1.0F / 16.0F) +
360  (sptr[ w] + sptr[w3]) * (4.0F / 16.0F) +
361  sptr[w2] * (6.0F / 16.0F);
362  ++ sptr;
363  }
364 
365  // before last points ( [ 1^ 4 (6) 4 ] / 15 )T
366  for (int i = 0; i < w; ++ i)
367  {
368  *dptr++ = sptr[ 0] * (1.0F / 15.0F) +
369  (sptr[ w] + sptr[w3]) * (4.0F / 15.0F) +
370  sptr[w2] * (6.0F / 15.0F);
371  ++ sptr;
372  }
373 
374  // last points ( [ 1^ 4 (6) ] / 11 )T
375  for (int i = 0; i < w; ++ i)
376  {
377  *dptr++ = sptr[ 0] * (1.0F / 11.0F) +
378  sptr[ w] * (4.0F / 11.0F) +
379  sptr[w2] * (6.0F / 11.0F);
380  ++ sptr;
381  }
382  // finished. here, sptr is two lines before the end of array.
383  }
384  return result;
385 }
386 
387 
388 // ######################################################################
389 template <NRT_PROMOTE_PIX_NO_DEFAULT> inline
391 {
392  int const w = src.width(), h = src.height();
393  // promote the source image to DestType if necessary, so that we do the promotion only once for all, rather than many
394  // times as we access the pixels of the image; if no promotion is necessary, "source" will just point to the original
395  // data of "src" through the copy-on-write/ref-counting behavior of Image:
397  if (w < 2) return source; // nothing to smooth
399  typename nrt::Image<DestType>::const_iterator sptr = source.const_begin();
400  typename nrt::Image<DestType>::iterator dptr = result.begin();
401 
402  if (w < 9) // use inefficient implementation for small images
403  {
404  nrt::Image<DestType> const kernel =
405  { { DestType(1.0F / 256.0F), DestType(8.0F / 256.0F), DestType(28.0F / 256.0F),
406  DestType(56.0F / 256.0F), DestType(70.0F / 256.0F), DestType(56.0F / 256.0F),
407  DestType(28.0F / 256.0F), DestType(8.0F / 256.0F), DestType(1.0F / 256.0F) } };
408 
409  nrt::Image<DestType> empty;
410 
411  return nrt::separableFilter<DestType>(src, kernel, empty, nrt::ConvolutionBoundaryStrategy::CLEAN);
412  }
413 
414  // boundary conditions: truncated filter
415  for (int j = 0; j < h; j ++)
416  {
417  // leftmost points
418  *dptr++ = sptr[0] * (70.0F / 163.0F) +
419  sptr[1] * (56.0F / 163.0F) +
420  sptr[2] * (28.0F / 163.0F) +
421  sptr[3] * ( 8.0F / 163.0F) +
422  sptr[4] * ( 1.0F / 163.0F);
423  *dptr++ = (sptr[0] + sptr[2]) * (56.0F / 219.0F) +
424  sptr[1] * (70.0F / 219.0F) +
425  sptr[3] * (28.0F / 219.0F) +
426  sptr[4] * ( 8.0F / 219.0F) +
427  sptr[5] * ( 1.0F / 219.0F);
428  *dptr++ = (sptr[0] + sptr[4]) * (28.0F / 247.0F) +
429  (sptr[1] + sptr[3]) * (56.0F / 247.0F) +
430  sptr[2] * (70.0F / 247.0F) +
431  sptr[5] * ( 8.0F / 247.0F) +
432  sptr[6] * ( 1.0F / 247.0F);
433  *dptr++ = (sptr[0] + sptr[6]) * ( 8.0F / 255.0F) +
434  (sptr[1] + sptr[5]) * (28.0F / 255.0F) +
435  (sptr[2] + sptr[4]) * (56.0F / 255.0F) +
436  sptr[3] * (70.0F / 255.0F) +
437  sptr[7] * ( 1.0F / 255.0F);
438 
439  // far from the borders
440  for (int i = 0; i < w - 8; i ++)
441  {
442  *dptr++ = (sptr[0] + sptr[8]) * ( 1.0F / 256.0F) +
443  (sptr[1] + sptr[7]) * ( 8.0F / 256.0F) +
444  (sptr[2] + sptr[6]) * (28.0F / 256.0F) +
445  (sptr[3] + sptr[5]) * (56.0F / 256.0F) +
446  sptr[4] * (70.0F / 256.0F);
447  sptr ++;
448  }
449 
450  // rightmost points
451  *dptr++ = sptr[0] * ( 1.0F / 255.0F) +
452  (sptr[1] + sptr[7]) * ( 8.0F / 255.0F) +
453  (sptr[2] + sptr[6]) * (28.0F / 255.0F) +
454  (sptr[3] + sptr[5]) * (56.0F / 255.0F) +
455  sptr[4] * (70.0F / 255.0F);
456  sptr ++;
457  *dptr++ = sptr[0] * ( 1.0F / 247.0F) +
458  sptr[1] * ( 8.0F / 247.0F) +
459  (sptr[2] + sptr[6]) * (28.0F / 247.0F) +
460  (sptr[3] + sptr[5]) * (56.0F / 247.0F) +
461  sptr[4] * (70.0F / 247.0F);
462  sptr ++;
463  *dptr++ = sptr[0] * ( 1.0F / 219.0F) +
464  sptr[1] * ( 8.0F / 219.0F) +
465  sptr[2] * (28.0F / 219.0F) +
466  (sptr[3] + sptr[5]) * (56.0F / 219.0F) +
467  sptr[4] * (70.0F / 219.0F);
468  sptr ++;
469  *dptr++ = sptr[0] * ( 1.0F / 163.0F) +
470  sptr[1] * ( 8.0F / 163.0F) +
471  sptr[2] * (28.0F / 163.0F) +
472  sptr[3] * (56.0F / 163.0F) +
473  sptr[4] * (70.0F / 163.0F);
474  sptr += 5; // sptr back to same as dptr (start of next line)
475  }
476  return result;
477 }
478 
479 // ######################################################################
480 template <NRT_PROMOTE_PIX_NO_DEFAULT> inline
482 {
483  int const w = src.width(), h = src.height();
484  // promote the source image to float if necessary, so that we do the promotion only once for all, rather than many
485  // times as we access the pixels of the image; if no promotion is necessary, "source" will just point to the original
486  // data of "src" through the copy-on-write/ref-counting behavior of Image:
488  if (h < 2) return source; // nothing to smooth
490  typename nrt::Image<DestType>::const_iterator sptr = source.const_begin();
491  typename nrt::Image<DestType>::iterator dptr = result.begin();
492 
493  if (h < 9) // use inefficient implementation for small images
494  {
495  nrt::Image<DestType> kernel =
496  { { DestType(1.0F / 256.0F) }, { DestType(8.0F / 256.0F) }, { DestType(28.0F / 256.0F) },
497  { DestType(56.0F / 256.0F) }, { DestType(70.0F / 256.0F) }, { DestType(56.0F / 256.0F) },
498  { DestType(28.0F / 256.0F) }, { DestType(8.0F / 256.0F) }, { DestType(1.0F / 256.0F) } };
499  nrt::Image<DestType> empty;
500  return nrt::separableFilter<DestType>(src, empty, kernel, nrt::ConvolutionBoundaryStrategy::CLEAN);
501  }
502 
503  // *** vertical pass ***
504  int const w2 = w + w, w3 = w2 + w, w4 = w3 + w, w5 = w4 + w, w6 = w5 + w, w7 = w6 + w, w8 = w7 + w;
505  for (int i = 0; i < w; i ++)
506  {
507  *dptr++ = sptr[ 0] * (70.0F / 163.0F) +
508  sptr[ w] * (56.0F / 163.0F) +
509  sptr[w2] * (28.0F / 163.0F) +
510  sptr[w3] * ( 8.0F / 163.0F) +
511  sptr[w4] * ( 1.0F / 163.0F);
512  sptr ++;
513  }
514  sptr -= w; // back to top-left
515  for (int i = 0; i < w; i ++)
516  {
517  *dptr++ = (sptr[ 0] + sptr[w2]) * (56.0F / 219.0F) +
518  sptr[ w] * (70.0F / 219.0F) +
519  sptr[w3] * (28.0F / 219.0F) +
520  sptr[w4] * ( 8.0F / 219.0F) +
521  sptr[w5] * ( 1.0F / 219.0F);
522  sptr ++;
523  }
524  sptr -= w; // back to top-left
525  for (int i = 0; i < w; i ++)
526  {
527  *dptr++ = (sptr[ 0] + sptr[w4]) * (28.0F / 247.0F) +
528  (sptr[ w] + sptr[w3]) * (56.0F / 247.0F) +
529  sptr[w2] * (70.0F / 247.0F) +
530  sptr[w5] * ( 8.0F / 247.0F) +
531  sptr[w6] * ( 1.0F / 247.0F);
532  sptr ++;
533  }
534  sptr -= w; // back to top-left
535  for (int i = 0; i < w; i ++)
536  {
537  *dptr++ = (sptr[ 0] + sptr[w6]) * ( 8.0F / 255.0F) +
538  (sptr[ w] + sptr[w5]) * (28.0F / 255.0F) +
539  (sptr[w2] + sptr[w4]) * (56.0F / 255.0F) +
540  sptr[w3] * (70.0F / 255.0F) +
541  sptr[w7] * ( 1.0F / 255.0F);
542  sptr ++;
543  }
544  sptr -= w; // back to top-left
545  for (int j = 0; j < h - 8; j ++)
546  for (int i = 0; i < w; i ++)
547  {
548  *dptr++ = (sptr[ 0] + sptr[w8]) * ( 1.0F / 256.0F) +
549  (sptr[ w] + sptr[w7]) * ( 8.0F / 256.0F) +
550  (sptr[w2] + sptr[w6]) * (28.0F / 256.0F) +
551  (sptr[w3] + sptr[w5]) * (56.0F / 256.0F) +
552  sptr[w4] * (70.0F / 256.0F);
553  sptr ++;
554  }
555  for (int i = 0; i < w; i ++)
556  {
557  *dptr++ = sptr[ 0] * ( 1.0F / 255.0F) +
558  (sptr[ w] + sptr[w7]) * ( 8.0F / 255.0F) +
559  (sptr[w2] + sptr[w6]) * (28.0F / 255.0F) +
560  (sptr[w3] + sptr[w5]) * (56.0F / 255.0F) +
561  sptr[w4] * (70.0F / 255.0F);
562  sptr ++;
563  }
564  for (int i = 0; i < w; i ++)
565  {
566  *dptr++ = sptr[ 0] * ( 1.0F / 247.0F) +
567  sptr[ w] * ( 8.0F / 247.0F) +
568  (sptr[w2] + sptr[w6]) * (28.0F / 247.0F) +
569  (sptr[w3] + sptr[w5]) * (56.0F / 247.0F) +
570  sptr[w4] * (70.0F / 247.0F);
571  sptr ++;
572  }
573  for (int i = 0; i < w; i ++)
574  {
575  *dptr++ = sptr[ 0] * ( 1.0F / 219.0F) +
576  sptr[ w] * ( 8.0F / 219.0F) +
577  sptr[w2] * (28.0F / 219.0F) +
578  (sptr[w3] + sptr[w5]) * (56.0F / 219.0F) +
579  sptr[w4] * (70.0F / 219.0F);
580  sptr ++;
581  }
582  for (int i = 0; i < w; i ++)
583  {
584  *dptr++ = sptr[ 0] * ( 1.0F / 163.0F) +
585  sptr[ w] * ( 8.0F / 163.0F) +
586  sptr[w2] * (28.0F / 163.0F) +
587  sptr[w3] * (56.0F / 163.0F) +
588  sptr[w4] * (70.0F / 163.0F);
589  sptr ++;
590  }
591  return result;
592 }
593 
594 // ######################################################################
595 template <NRT_PROMOTE_PIX_NO_DEFAULT> inline
596 nrt::Image<DestType> nrt::lowPass9(nrt::Image<PixType> const & src, bool const go_x, bool const go_y)
597 {
599  if (go_x) result = nrt::lowPass9x<promo>(result);
600  if (go_y) result = nrt::lowPass9y<promo>(result);
601  return result;
602 }
603 
604 // ######################################################################
605 template <NRT_PROMOTE_PIX_NO_DEFAULT> inline
607 {
608  switch (N)
609  {
610  case 3: return nrt::lowPass3x<promo>(src);
611  case 5: return nrt::lowPass5x<promo>(src);
612  case 9: return nrt::lowPass9x<promo>(src);
613  default: break;
614  }
616  nrt::Image<DestType> empty;
617 
618  assert(int(kern.width()) == N); assert(int(kern.height()) == 1);
619 
620  return nrt::separableFilter<promo>(src, kern, empty, nrt::ConvolutionBoundaryStrategy::CLEAN);
621 }
622 
623 // ######################################################################
624 template <NRT_PROMOTE_PIX_NO_DEFAULT> inline
626 {
627  switch (N)
628  {
629  case 3: return nrt::lowPass3y<promo>(src);
630  case 5: return nrt::lowPass5y<promo>(src);
631  case 9: return nrt::lowPass9y<promo>(src);
632  default: break;
633  }
635  nrt::Image<DestType> empty;
636  kern.reshape(nrt::Dims<nrt::int32>(kern.height(), kern.width()));
637 
638  assert(int(kern.width()) == 1); assert(int(kern.height()) == N);
639 
640  return nrt::separableFilter<promo>(src, empty, kern, nrt::ConvolutionBoundaryStrategy::CLEAN);
641 }
642 
643 // ######################################################################
644 template <NRT_PROMOTE_PIX_NO_DEFAULT> inline
645 nrt::Image<DestType> nrt::lowPass(int const N, nrt::Image<PixType> const & src)
646 {
648  result = nrt::lowPassX<promo>(N, result);
649  result = nrt::lowPassY<promo>(N, result);
650 
651  return result;
652 }
653 
654 #endif // INCLUDE_NRT_IMAGEPROC_FILTERING_DETAILS_LOWPASSIMPL_H
655