$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
gaussian.hh
1 // Copyright (C) 2001, 2002, 2003, 2004, 2007, 2008, 2009, 2010, 2011,
2 // 2012, 2013 EPITA Research and Development Laboratory (LRDE)
3 //
4 // This file is part of Olena.
5 //
6 // Olena is free software: you can redistribute it and/or modify it under
7 // the terms of the GNU General Public License as published by the Free
8 // Software Foundation, version 2 of the License.
9 //
10 // Olena is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Olena. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // As a special exception, you may use this file as part of a free
19 // software project without restriction. Specifically, if other files
20 // instantiate templates or use macros or inline functions from this
21 // file, or you compile this file and link it with other files to produce
22 // an executable, this file does not by itself cause the resulting
23 // executable to be covered by the GNU General Public License. This
24 // exception does not however invalidate any other reasons why the
25 // executable file might be covered by the GNU General Public License.
26 
27 #ifndef MLN_LINEAR_GAUSSIAN_HH
28 # define MLN_LINEAR_GAUSSIAN_HH
29 
36 
37 # include <vector>
38 # include <cmath>
39 
40 # include <mln/core/concept/image.hh>
41 # include <mln/core/alias/point2d.hh>
42 # include <mln/core/alias/dpoint1d.hh>
43 # include <mln/core/alias/dpoint3d.hh>
44 # include <mln/extension/adjust_fill.hh>
45 # include <mln/geom/ncols.hh>
46 # include <mln/geom/nrows.hh>
47 # include <mln/geom/min_col.hh>
48 # include <mln/geom/max_col.hh>
49 # include <mln/geom/min_row.hh>
50 # include <mln/geom/max_row.hh>
51 # include <mln/geom/min_sli.hh>
52 # include <mln/geom/max_sli.hh>
53 # include <mln/geom/ninds.hh>
54 # include <mln/geom/nslis.hh>
55 # include <mln/data/paste.hh>
56 # include <mln/data/stretch.hh>
57 # include <mln/algebra/vec.hh>
58 
59 
60 namespace mln
61 {
62 
63  namespace linear
64  {
65 
72  template <typename I>
73  mln_concrete(I)
74  gaussian(const Image<I>& input, float sigma);
75 
76 
80  template <typename I>
81  mln_concrete(I)
82  gaussian(const Image<I>& input, float sigma, int dir);
83 
84 
85 # ifndef MLN_INCLUDE_ONLY
86 
87  namespace impl
88  {
89 
90  typedef float norm_fun(float, float,
91  float, float,
92  float, float,
93  float, float,
94  float, float,
95  int&);
96 
97  struct recursivefilter_coef_
98  {
99 
103  recursivefilter_coef_(float a0, float a1,
104  float b0, float b1,
105  float c0, float c1,
106  float w0, float w1,
107  float s, norm_fun norm);
108  std::vector<float> n, d, nm, dm;
109  };
110 
111  inline
112  recursivefilter_coef_::recursivefilter_coef_(float a0, float a1,
113  float b0, float b1,
114  float c0, float c1,
115  float w0, float w1,
116  float s, norm_fun norm)
117  {
118  n.reserve(5);
119  d.reserve(5);
120  nm.reserve(5);
121  dm.reserve(5);
122 
123  b0 /= s;
124  b1 /= s;
125  w0 /= s;
126  w1 /= s;
127 
128  float sin0 = std::sin(w0);
129  float sin1 = std::sin(w1);
130  float cos0 = std::cos(w0);
131  float cos1 = std::cos(w1);
132 
133  int sign = 1;
134  float n_ = norm(a0, a1, b0, b1, c0, c1, cos0, sin0, cos1, sin1, sign);
135 
136  a0 /= n_;
137  a1 /= n_;
138  c0 /= n_;
139  c1 /= n_;
140 
141  n[3] =
142  std::exp(-b1 - 2*b0) * (c1 * sin1 - cos1 * c0) +
143  std::exp(-b0 - 2*b1) * (a1 * sin0 - cos0 * a0);
144  n[2] =
145  2 * std::exp(-b0 - b1) * ((a0 + c0) * cos1 * cos0 -
146  cos1 * a1 * sin0 -
147  cos0 * c1 * sin1) +
148  c0 * std::exp(-2*b0) + a0 * std::exp(-2*b1);
149  n[1] =
150  std::exp(-b1) * (c1 * sin1 - (c0 + 2 * a0) * cos1) +
151  std::exp(-b0) * (a1 * sin0 - (2 * c0 + a0) * cos0);
152  n[0] =
153  a0 + c0;
154 
155  d[4] =
156  std::exp(-2 * b0 - 2 * b1);
157  d[3] =
158  -2 * cos0 * std::exp(-b0 - 2*b1) -
159  2 * cos1 * std::exp(-b1 - 2*b0);
160  d[2] =
161  4 * cos1 * cos0 * std::exp(-b0 - b1) +
162  std::exp(-2*b1) + std::exp(-2*b0);
163  d[1] =
164  -2 * std::exp(-b1) * cos1 - 2 * std::exp(-b0) * cos0;
165 
166  for (unsigned i = 1; i <= 3; ++i)
167  {
168  dm[i] = d[i];
169  nm[i] = float(sign) * (n[i] - d[i] * n[0]);
170  }
171  dm[4] = d[4];
172  nm[4] = float(sign) * (-d[4] * n[0]);
173  }
174 
175 
176 
177  template <class WorkType, class I>
178  inline
179  void
180  recursivefilter_(I& ima,
181  const recursivefilter_coef_& c,
182  const mln_psite(I)& start,
183  const mln_psite(I)& finish,
184  int len,
185  const mln_deduce(I, psite, delta)& d)
186  {
187  std::vector<WorkType> tmp1(len);
188  std::vector<WorkType> tmp2(len);
189 
190  // The fourth degree approximation implies to have a special
191  // look on the four first points we consider that there is
192  // no signal before 0 (to be discussed)
193 
194  // --
195  // Causal part
196 
197  tmp1[0] =
198  c.n[0] * ima(start);
199 
200  tmp1[1] =
201  c.n[0] * ima(start + d)
202  + c.n[1] * ima(start)
203  - c.d[1] * tmp1[0];
204 
205  tmp1[2] =
206  c.n[0] * ima(start + d + d)
207  + c.n[1] * ima(start + d)
208  + c.n[2] * ima(start)
209  - c.d[1] * tmp1[1]
210  - c.d[2] * tmp1[0];
211 
212  tmp1[3] =
213  c.n[0] * ima(start + d + d + d)
214  + c.n[1] * ima(start + d + d)
215  + c.n[2] * ima(start + d)
216  + c.n[3] * ima(start)
217  - c.d[1] * tmp1[2] - c.d[2] * tmp1[1]
218  - c.d[3] * tmp1[0];
219 
220  mln_psite(I) current(start + d + d + d + d);
221  for (mln_deduce(I, site, coord) i = 4; i < len; ++i)
222  {
223  tmp1[i] =
224  c.n[0] * ima(current)
225  + c.n[1] * ima(current - d)
226  + c.n[2] * ima(current - d - d)
227  + c.n[3] * ima(current - d - d - d)
228  - c.d[1] * tmp1[i - 1] - c.d[2] * tmp1[i - 2]
229  - c.d[3] * tmp1[i - 3] - c.d[4] * tmp1[i - 4];
230  current = current + d;
231  }
232 
233  // Non causal part
234 
235  tmp2[len - 1] = WorkType(); // FIXME : = 0, literal::zero ...?
236 
237  tmp2[len - 2] =
238  c.nm[1] * ima(finish);
239 
240  tmp2[len - 3] =
241  c.nm[1] * ima(finish - d)
242  + c.nm[2] * ima(finish)
243  - c.dm[1] * tmp2[len - 2];
244 
245  tmp2[len - 4] =
246  c.nm[1] * ima(finish - d - d)
247  + c.nm[2] * ima(finish - d)
248  + c.nm[3] * ima(finish)
249  - c.dm[1] * tmp2[len - 3]
250  - c.dm[2] * tmp2[len - 2];
251 
252  current = finish - d - d - d ;
253 
254  for (int i = len - 5; i >= 0; --i)
255  {
256  tmp2[i] =
257  c.nm[1] * ima(current)
258  + c.nm[2] * ima(current + d)
259  + c.nm[3] * ima(current + d + d)
260  + c.nm[4] * ima(current + d + d + d)
261  - c.dm[1] * tmp2[i + 1] - c.dm[2] * tmp2[i + 2]
262  - c.dm[3] * tmp2[i + 3] - c.dm[4] * tmp2[i + 4];
263  current = current - d;
264  }
265 
266  // Combine results from causal and non-causal parts.
267  current = start;
268  for (int i = 0; i < len; ++i)
269  {
270  ima(current) = tmp1[i] + tmp2[i];
271  current = current + d;
272  }
273  }
274 
275 
276  inline
277  float gaussian_norm_coef_(float a0, float a1,
278  float b0, float b1,
279  float c0, float c1,
280  float cos0, float sin0,
281  float cos1, float sin1,
282  int& sign)
283  {
284  float expb0 = std::exp(b0);
285  float exp2b0 = std::exp(2.f * b0);
286 
287  float scale0 = 1 + exp2b0 - 2 * cos0 * expb0;
288  float scaleA = 2 * a1 * sin0 * expb0 - a0 * (1 - exp2b0);
289 
290  float expb1 = std::exp(b1);
291  float exp2b1 = std::exp(2.f * b1);
292 
293  float scale1 = 1 + exp2b1 - 2 * cos1 * expb1;
294  float scaleC = 2 * c1 * sin1 * expb1 - c0 * (1 - exp2b1);
295 
296  float sumA = scaleA / scale0;
297  float sumC = scaleC / scale1;
298 
299  sign = 1;
300 
301  return (sumA + sumC);
302  }
303 
304  inline
305  float gaussian_1st_deriv_coef_norm_(float a0, float a1,
306  float b0, float b1,
307  float c0, float c1,
308  float cos0, float sin0,
309  float cos1, float sin1,
310  int& sign)
311  {
312  float expb0 = std::exp(b0);
313  float exp2b0 = std::exp(2.f * b0);
314 
315  float scale0 = 1 + exp2b0 - 2 * cos0 * expb0;
316  scale0 *= scale0;
317  float scaleA = - 2 * a1 * sin0 * expb0 * (1 - exp2b0) +
318  2 * a0 * expb0 * (2 * expb0 - cos0 * (1 + exp2b0));
319 
320  float expb1 = std::exp(b1);
321  float exp2b1 = std::exp(2.f * b1);
322 
323  float scale1 = 1 + exp2b1 - 2 * cos1 * expb1;
324  scale1 *= scale1;
325  float scaleC = - 2 * c1 * sin1 * expb1 * (1 - exp2b1) +
326  2 * c0 * expb1 * (2 * expb1 - cos1 * (1 + exp2b1));
327 
328  float sumA = scaleA / scale0;
329  float sumC = scaleC / scale1;
330 
331  sign = -1;
332 
333  return (sumA + sumC);
334  }
335 
336  inline
337  float gaussian_2nd_deriv_coef_norm_(float a0, float a1,
338  float b0, float b1,
339  float c0, float c1,
340  float cos0, float sin0,
341  float cos1, float sin1,
342  int& sign)
343  {
344  float expb0 = std::exp(b0);
345  float exp2b0 = std::exp(2.f * b0);
346 
347  float scale0 = 1 + exp2b0 - 2 * cos0 * expb0;
348  scale0 *= scale0 * scale0;
349 
350  float scaleA = a1 * sin0 * expb0 *
351  (1 + expb0 * (2 * cos0 * (1 + exp2b0) + exp2b0 - 6)) +
352  a0 * expb0 * (2 * expb0 * (2 - cos0 * cos0) *
353  (1 - exp2b0) - cos0 * (1 - exp2b0 * exp2b0));
354 
355  float expb1 = std::exp(b1);
356  float exp2b1 = std::exp(2.f * b1);
357 
358  float scale1 = 1 + exp2b1 - 2 * cos1 * expb1;
359  scale1 *= scale1 * scale1;
360 
361  float scaleC = c1 * sin1 * expb1 *
362  (1 + expb1 * (2 * cos1 * (1 + exp2b1) + exp2b1 - 6)) +
363  c0 * expb1 * (2 * expb1 * (2 - cos1 * cos1) *
364  (1 - exp2b1) - cos1 * (1 - exp2b1 * exp2b1));
365 
366  float sumA = scaleA / scale0;
367  float sumC = scaleC / scale1;
368  sign = 1;
369 
370  return (sumA + sumC);
371  }
372 
373 
374  template <class I, class F>
375  inline
376  void
377  generic_filter_(trait::image::dimension::one_d,
378  Image<I>& img_, const F& coef, int dir)
379  {
380  I& img = exact(img_);
381  (void) dir;
382  mln_precondition(dir < I::site::dim);
383 
384 
385  recursivefilter_<mln_value(I)>(img, coef,
386  point1d(static_cast<def::coord>(-img.border())),
387  point1d(static_cast<def::coord>(geom::ninds(img) - 1 +
388  img.border())),
389  geom::ninds(img) + 2 * img.border(),
390  dpoint1d(1));
391  }
392 
393  template <class I, class F>
394  inline
395  void
396  generic_filter_(trait::image::dimension::two_d,
397  Image<I>& img_, const F& coef, int dir)
398  {
399  I& img = exact(img_);
400 
401  mln_precondition(dir < I::site::dim);
402 
403 
404  if (dir == 0)
405  {
406  // Apply on rows.
407  for (def::coord j = geom::min_col(img); j <= geom::max_col(img); ++j)
408  recursivefilter_<mln_value(I)>(img, coef,
409  point2d(static_cast<def::coord>(geom::min_row(img) - img.border()),
410  static_cast<def::coord>(j)),
411  point2d(static_cast<def::coord>(geom::max_row(img) +
412  img.border()),
413  static_cast<def::coord>(j)),
414  geom::nrows(img) + 2 * img.border(),
415  dpoint2d(1, 0));
416  }
417 
418  if (dir == 1)
419  {
420  // Apply on columns.
421  for (def::coord i = geom::min_row(img); i <= geom::max_row(img); ++i)
422  recursivefilter_<mln_value(I)>(img, coef,
423  point2d(static_cast<def::coord>(i),
424  static_cast<def::coord>(geom::min_col(img) - img.border())),
425  point2d(static_cast<def::coord>(i),
426  static_cast<def::coord>(geom::max_col(img) +
427  img.border())),
428  geom::ncols(img) + 2 * img.border(),
429  dpoint2d(0, 1));
430  }
431  }
432 
433  template <class I, class F>
434  inline
435  void
436  generic_filter_(trait::image::dimension::three_d,
437  Image<I>& img_, const F& coef, int dir)
438  {
439  I& img = exact(img_);
440  mln_precondition(dir < I::site::dim);
441 
442  if (dir == 0)
443  {
444  // Apply on slices.
445  for (def::coord j = geom::min_row(img); j <= geom::max_row(img); ++j)
446  for (def::coord k = geom::min_col(img); k <= geom::max_col(img); ++k)
447  recursivefilter_<mln_value(I)>(img, coef,
448  point3d(static_cast<def::coord>(-img.border()),
449  static_cast<def::coord>(j),
450  static_cast<def::coord>(k)),
451  point3d(static_cast<def::coord>(geom::nslis(img) - 1 +
452  img.border()),
453  static_cast<def::coord>(j),
454  static_cast<def::coord>(k)),
455  geom::nslis(img) + 2 *
456  img.border(),
457  dpoint3d(1, 0, 0));
458  }
459 
460 
461  if (dir == 1)
462  {
463  // Apply on rows.
464  for (def::coord i = geom::min_sli(img); i <= geom::max_sli(img); ++i)
465  for (def::coord k = geom::min_col(img); k <= geom::max_col(img); ++k)
466  recursivefilter_<mln_value(I)>(img, coef,
467  point3d(static_cast<def::coord>(i),
468  static_cast<def::coord>(-img.border()),
469  static_cast<def::coord>(k)),
470  point3d(static_cast<def::coord>(i),
471  static_cast<def::coord>(geom::nrows(img) - 1 +
472  img.border()),
473  static_cast<def::coord>(k)),
474  geom::nrows(img) + 2 *
475  img.border(),
476  dpoint3d(0, 1, 0));
477  }
478 
479  if (dir == 2)
480  {
481  // Apply on columns.
482  for (def::coord i = geom::min_sli(img); i <= geom::max_sli(img); ++i)
483  for (def::coord j = geom::min_row(img); j <= geom::max_row(img); ++j)
484  recursivefilter_<mln_value(I)>(img, coef,
485  point3d(static_cast<def::coord>(i),
486  static_cast<def::coord>(j),
487  static_cast<def::coord>(-img.border())),
488  point3d(static_cast<def::coord>(i),
489  static_cast<def::coord>(j),
490  static_cast<def::coord>(geom::ncols(img) -
491  1 + img.border())),
492  geom::ncols(img) + 2 *
493  img.border(),
494  dpoint3d(0, 0, 1));
495  }
496  }
497 
498 
499 
500  template <class I, class F, class O>
501  inline
502  void
503  generic_filter_common_(trait::value::nature::floating,
504  const Image<I>& in,
505  const F& coef,
506  float sigma,
507  Image<O>& out)
508  {
509  mln_ch_value(O, float) work_img(exact(in).domain());
510  data::paste(in, work_img);
511  extension::adjust_fill(work_img, 4, 0);
512 
513  // On tiny sigma, Derich algorithm doesn't work.
514  // It is the same thing that to convolve with a Dirac.
515  if (sigma > 0.006)
516  for (int i = 0; i < I::site::dim; ++i)
517  generic_filter_(mln_trait_image_dimension(I)(),
518  work_img, coef, i);
519 
520  // We don't need to convert work_img
521  data::paste(work_img, out);
522  }
523 
524  template <class I, class F, class O>
525  inline
526  void
527  generic_filter_common_(trait::value::nature::floating,
528  const Image<I>& in,
529  const F& coef,
530  float sigma,
531  Image<O>& out,
532  int dir)
533  {
534  mln_ch_value(O, float) work_img(exact(in).domain());
535  data::paste(in, work_img);
536  extension::adjust_fill(work_img, 4, 0);
537 
538  // On tiny sigma, Derich algorithm doesn't work.
539  // It is the same thing that to convolve with a Dirac.
540  if (sigma > 0.006)
541  generic_filter_(mln_trait_image_dimension(I)(),
542  work_img, coef, dir);
543 
544  // We don't need to convert work_img
545  data::paste(work_img, out);
546  }
547 
548 
549  template <class I, class F, class O>
550  inline
551  void
552  generic_filter_common_(trait::value::nature::scalar,
553  const Image<I>& in,
554  const F& coef,
555  float sigma,
556  Image<O>& out)
557  {
558  mln_ch_value(O, float) work_img(exact(in).domain());
559  data::paste(in, work_img);
560  extension::adjust_fill(work_img, 4, 0);
561 
562  // On tiny sigma, Derich algorithm doesn't work.
563  // It is the same thing that to convolve with a Dirac.
564  if (sigma > 0.006)
565  for (int i = 0; i < I::site::dim; ++i)
566  generic_filter_(mln_trait_image_dimension(I)(),
567  work_img, coef, i);
568 
569  // Convert work_img into result type
570  data::paste(data::stretch(mln_value(I)(), work_img), out);
571  }
572 
573  template <class I, class F, class O>
574  inline
575  void
576  generic_filter_common_(trait::value::nature::scalar,
577  const Image<I>& in,
578  const F& coef,
579  float sigma,
580  Image<O>& out,
581  int dir)
582  {
583  mln_ch_value(O, float) work_img(exact(in).domain());
584  data::paste(in, work_img);
585  extension::adjust_fill(work_img, 4, 0);
586 
587  // On tiny sigma, Derich algorithm doesn't work.
588  // It is the same thing that to convolve with a Dirac.
589  if (sigma > 0.006)
590  generic_filter_(mln_trait_image_dimension(I)(),
591  work_img, coef, dir);
592 
593  // Convert work_img into result type
594  data::paste(data::stretch(mln_value(I)(), work_img), out);
595  }
596 
597 
598 
599  template <class I, class F, class O>
600  inline
601  void
602  generic_filter_common_(trait::value::nature::vectorial,
603  const Image<I>& in,
604  const F& coef,
605  float sigma,
606  Image<O>& out)
607  {
608  // typedef algebra::vec<3, float> vec3f;
609  // mln_ch_value(O, vec3f) work_img(exact(in).domain());
610  // FIXME : paste does not work (rgb8 -> vec3f).
611  data::paste(in, out);
612 
613  // On tiny sigma, Derich algorithm doesn't work.
614  // It is the same thing that to convolve with a Dirac.
615  if (sigma > 0.006)
616  for (int i = 0; i < I::site::dim; ++i)
617  generic_filter_(mln_trait_image_dimension(I)(),
618  out, coef, i);
619  }
620 
621  template <class I, class F, class O>
622  inline
623  void
624  generic_filter_common_(trait::value::nature::vectorial,
625  const Image<I>& in,
626  const F& coef,
627  float sigma,
628  Image<O>& out,
629  int dir)
630  {
631  // typedef algebra::vec<3, float> vec3f;
632  // mln_ch_value(O, vec3f) work_img(exact(in).domain());
633  // FIXME : paste does not work (rgb8 -> vec3f).
634  data::paste(in, out);
635 
636  // On tiny sigma, Derich algorithm doesn't work.
637  // It is the same thing that to convolve with a Dirac.
638  if (sigma > 0.006)
639  generic_filter_(mln_trait_image_dimension(I)(),
640  out, coef, dir);
641  }
642 
643  } // end of namespace mln::linear::impl
644 
645  // Facade.
646 
656  template <typename I>
657  inline
658  mln_concrete(I)
659  gaussian(const Image<I>& input, float sigma, int dir)
660  {
661  mln_precondition(exact(input).is_valid());
662  mln_precondition(dir < I::site::dim);
663 
664  mln_concrete(I) output;
665  initialize(output, input);
666  impl::recursivefilter_coef_ coef(1.68f, 3.735f,
667  1.783f, 1.723f,
668  -0.6803f, -0.2598f,
669  0.6318f, 1.997f,
670  sigma, impl::gaussian_norm_coef_);
671 
672  impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
673  input, coef, sigma, output, dir);
674  return output;
675  }
676 
677 
688  template <typename I>
689  inline
690  mln_concrete(I)
691  gaussian_1st_derivative(const Image<I>& input, float sigma, int dir)
692  {
693  mln_precondition(exact(input).is_valid());
694  mln_precondition(dir < I::site::dim);
695 
696  mln_concrete(I) output;
697  initialize(output, input);
698 
699  impl::recursivefilter_coef_
700  coef(-0.6472f, -4.531f,
701  1.527f, 1.516f,
702  0.6494f, 0.9557f,
703  0.6719f, 2.072f,
704  sigma, impl::gaussian_1st_deriv_coef_norm_);
705 
706  impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
707  input, coef, sigma, output, dir);
708  return output;
709  }
710 
721  template <typename I>
722  inline
723  mln_concrete(I)
724  gaussian_2nd_derivative(const Image<I>& input, float sigma, int dir)
725  {
726  mln_precondition(exact(input).is_valid());
727  mln_precondition(dir < I::site::dim);
728 
729  mln_concrete(I) output;
730  initialize(output, input);
731 
732  impl::recursivefilter_coef_
733  coef(-1.331f, 3.661f,
734  1.24f, 1.314f,
735  0.3225f, -1.738f,
736  0.748f, 2.166f,
737  sigma, impl::gaussian_2nd_deriv_coef_norm_);
738 
739  impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
740  input, coef, sigma, output, dir);
741  return output;
742  }
743 
744 
745 
746 
747 
753  template <typename I>
754  inline
755  mln_concrete(I)
756  gaussian(const Image<I>& input, float sigma)
757  {
758  mln_precondition(exact(input).is_valid());
759 
760  mln_concrete(I) output;
761  initialize(output, input);
762 
763  impl::recursivefilter_coef_
764  coef(1.68f, 3.735f,
765  1.783f, 1.723f,
766  -0.6803f, -0.2598f,
767  0.6318f, 1.997f,
768  sigma, impl::gaussian_norm_coef_);
769  impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
770  input, coef, sigma, output);
771 
772  return output;
773  }
774 
775 
782  template <typename I>
783  inline
784  mln_concrete(I)
785  gaussian_1st_derivative(const Image<I>& input, float sigma)
786  {
787  mln_precondition(exact(input).is_valid());
788 
789  mln_concrete(I) output;
790  initialize(output, input);
791 
792  impl::recursivefilter_coef_
793  coef(-0.6472f, -4.531f,
794  1.527f, 1.516f,
795  0.6494f, 0.9557f,
796  0.6719f, 2.072f,
797  sigma, impl::gaussian_1st_deriv_coef_norm_);
798  impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
799  input, coef, sigma, output);
800  return output;
801  }
802 
803 
810  template <typename I>
811  inline
812  mln_concrete(I)
813  gaussian_2nd_derivative(const Image<I>& input, float sigma)
814  {
815  mln_precondition(exact(input).is_valid());
816 
817  mln_concrete(I) output;
818  initialize(output, input);
819 
820  impl::recursivefilter_coef_
821  coef(-1.331f, 3.661f,
822  1.24f, 1.314f,
823  0.3225f, -1.738f,
824  0.748f, 2.166f,
825  sigma, impl::gaussian_2nd_deriv_coef_norm_);
826  impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
827  input, coef, sigma, output);
828  return output;
829  }
830 
831 # endif // ! MLN_INCLUDE_ONLY
832 
833  } // end of namespace mln::linear
834 
835 } // end of namespace mln
836 
837 
838 #endif // ! MLN_LINEAR_GAUSSIAN_HH