Milena (Olena)  User documentation 2.0a Id
 All Classes Namespaces Functions Variables Typedefs Enumerator Groups Pages
gaussian.hh
1 // Copyright (C) 2001, 2002, 2003, 2004, 2007, 2008, 2009, 2010, 2011
2 // 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 
70  template <typename I>
71  mln_concrete(I)
72  gaussian(const Image<I>& input, float sigma);
73 
74 
75  template <typename I>
76  mln_concrete(I)
77  gaussian(const Image<I>& input, float sigma, int dir);
78 
79 
80 # ifndef MLN_INCLUDE_ONLY
81 
82  namespace impl
83  {
84 
85  typedef float norm_fun(float, float,
86  float, float,
87  float, float,
88  float, float,
89  float, float,
90  int&);
91 
92  struct recursivefilter_coef_
93  {
94 
98  recursivefilter_coef_(float a0, float a1,
99  float b0, float b1,
100  float c0, float c1,
101  float w0, float w1,
102  float s, norm_fun norm);
103  std::vector<float> n, d, nm, dm;
104  };
105 
106  inline
107  recursivefilter_coef_::recursivefilter_coef_(float a0, float a1,
108  float b0, float b1,
109  float c0, float c1,
110  float w0, float w1,
111  float s, norm_fun norm)
112  {
113  n.reserve(5);
114  d.reserve(5);
115  nm.reserve(5);
116  dm.reserve(5);
117 
118  b0 /= s;
119  b1 /= s;
120  w0 /= s;
121  w1 /= s;
122 
123  float sin0 = std::sin(w0);
124  float sin1 = std::sin(w1);
125  float cos0 = std::cos(w0);
126  float cos1 = std::cos(w1);
127 
128  int sign = 1;
129  float n_ = norm(a0, a1, b0, b1, c0, c1, cos0, sin0, cos1, sin1, sign);
130 
131  a0 /= n_;
132  a1 /= n_;
133  c0 /= n_;
134  c1 /= n_;
135 
136  n[3] =
137  std::exp(-b1 - 2*b0) * (c1 * sin1 - cos1 * c0) +
138  std::exp(-b0 - 2*b1) * (a1 * sin0 - cos0 * a0);
139  n[2] =
140  2 * std::exp(-b0 - b1) * ((a0 + c0) * cos1 * cos0 -
141  cos1 * a1 * sin0 -
142  cos0 * c1 * sin1) +
143  c0 * std::exp(-2*b0) + a0 * std::exp(-2*b1);
144  n[1] =
145  std::exp(-b1) * (c1 * sin1 - (c0 + 2 * a0) * cos1) +
146  std::exp(-b0) * (a1 * sin0 - (2 * c0 + a0) * cos0);
147  n[0] =
148  a0 + c0;
149 
150  d[4] =
151  std::exp(-2 * b0 - 2 * b1);
152  d[3] =
153  -2 * cos0 * std::exp(-b0 - 2*b1) -
154  2 * cos1 * std::exp(-b1 - 2*b0);
155  d[2] =
156  4 * cos1 * cos0 * std::exp(-b0 - b1) +
157  std::exp(-2*b1) + std::exp(-2*b0);
158  d[1] =
159  -2 * std::exp(-b1) * cos1 - 2 * std::exp(-b0) * cos0;
160 
161  for (unsigned i = 1; i <= 3; ++i)
162  {
163  dm[i] = d[i];
164  nm[i] = float(sign) * (n[i] - d[i] * n[0]);
165  }
166  dm[4] = d[4];
167  nm[4] = float(sign) * (-d[4] * n[0]);
168  }
169 
170 
171 
172  template <class WorkType, class I>
173  inline
174  void
175  recursivefilter_(I& ima,
176  const recursivefilter_coef_& c,
177  const mln_psite(I)& start,
178  const mln_psite(I)& finish,
179  int len,
180  const mln_deduce(I, psite, delta)& d)
181  {
182  std::vector<WorkType> tmp1(len);
183  std::vector<WorkType> tmp2(len);
184 
185  // The fourth degree approximation implies to have a special
186  // look on the four first points we consider that there is
187  // no signal before 0 (to be discussed)
188 
189  // --
190  // Causal part
191 
192  tmp1[0] =
193  c.n[0] * ima(start);
194 
195  tmp1[1] =
196  c.n[0] * ima(start + d)
197  + c.n[1] * ima(start)
198  - c.d[1] * tmp1[0];
199 
200  tmp1[2] =
201  c.n[0] * ima(start + d + d)
202  + c.n[1] * ima(start + d)
203  + c.n[2] * ima(start)
204  - c.d[1] * tmp1[1]
205  - c.d[2] * tmp1[0];
206 
207  tmp1[3] =
208  c.n[0] * ima(start + d + d + d)
209  + c.n[1] * ima(start + d + d)
210  + c.n[2] * ima(start + d)
211  + c.n[3] * ima(start)
212  - c.d[1] * tmp1[2] - c.d[2] * tmp1[1]
213  - c.d[3] * tmp1[0];
214 
215  mln_psite(I) current(start + d + d + d + d);
216  for (mln_deduce(I, site, coord) i = 4; i < len; ++i)
217  {
218  tmp1[i] =
219  c.n[0] * ima(current)
220  + c.n[1] * ima(current - d)
221  + c.n[2] * ima(current - d - d)
222  + c.n[3] * ima(current - d - d - d)
223  - c.d[1] * tmp1[i - 1] - c.d[2] * tmp1[i - 2]
224  - c.d[3] * tmp1[i - 3] - c.d[4] * tmp1[i - 4];
225  current = current + d;
226  }
227 
228  // Non causal part
229 
230  tmp2[len - 1] = WorkType(); // FIXME : = 0, literal::zero ...?
231 
232  tmp2[len - 2] =
233  c.nm[1] * ima(finish);
234 
235  tmp2[len - 3] =
236  c.nm[1] * ima(finish - d)
237  + c.nm[2] * ima(finish)
238  - c.dm[1] * tmp2[len - 2];
239 
240  tmp2[len - 4] =
241  c.nm[1] * ima(finish - d - d)
242  + c.nm[2] * ima(finish - d)
243  + c.nm[3] * ima(finish)
244  - c.dm[1] * tmp2[len - 3]
245  - c.dm[2] * tmp2[len - 2];
246 
247  current = finish - d - d - d ;
248 
249  for (int i = len - 5; i >= 0; --i)
250  {
251  tmp2[i] =
252  c.nm[1] * ima(current)
253  + c.nm[2] * ima(current + d)
254  + c.nm[3] * ima(current + d + d)
255  + c.nm[4] * ima(current + d + d + d)
256  - c.dm[1] * tmp2[i + 1] - c.dm[2] * tmp2[i + 2]
257  - c.dm[3] * tmp2[i + 3] - c.dm[4] * tmp2[i + 4];
258  current = current - d;
259  }
260 
261  // Combine results from causal and non-causal parts.
262  current = start;
263  for (int i = 0; i < len; ++i)
264  {
265  ima(current) = tmp1[i] + tmp2[i];
266  current = current + d;
267  }
268  }
269 
270 
271  inline
272  float gaussian_norm_coef_(float a0, float a1,
273  float b0, float b1,
274  float c0, float c1,
275  float cos0, float sin0,
276  float cos1, float sin1,
277  int& sign)
278  {
279  float expb0 = std::exp(b0);
280  float exp2b0 = std::exp(2.f * b0);
281 
282  float scale0 = 1 + exp2b0 - 2 * cos0 * expb0;
283  float scaleA = 2 * a1 * sin0 * expb0 - a0 * (1 - exp2b0);
284 
285  float expb1 = std::exp(b1);
286  float exp2b1 = std::exp(2.f * b1);
287 
288  float scale1 = 1 + exp2b1 - 2 * cos1 * expb1;
289  float scaleC = 2 * c1 * sin1 * expb1 - c0 * (1 - exp2b1);
290 
291  float sumA = scaleA / scale0;
292  float sumC = scaleC / scale1;
293 
294  sign = 1;
295 
296  return (sumA + sumC);
297  }
298 
299  inline
300  float gaussian_1st_deriv_coef_norm_(float a0, float a1,
301  float b0, float b1,
302  float c0, float c1,
303  float cos0, float sin0,
304  float cos1, float sin1,
305  int& sign)
306  {
307  float expb0 = std::exp(b0);
308  float exp2b0 = std::exp(2.f * b0);
309 
310  float scale0 = 1 + exp2b0 - 2 * cos0 * expb0;
311  scale0 *= scale0;
312  float scaleA = - 2 * a1 * sin0 * expb0 * (1 - exp2b0) +
313  2 * a0 * expb0 * (2 * expb0 - cos0 * (1 + exp2b0));
314 
315  float expb1 = std::exp(b1);
316  float exp2b1 = std::exp(2.f * b1);
317 
318  float scale1 = 1 + exp2b1 - 2 * cos1 * expb1;
319  scale1 *= scale1;
320  float scaleC = - 2 * c1 * sin1 * expb1 * (1 - exp2b1) +
321  2 * c0 * expb1 * (2 * expb1 - cos1 * (1 + exp2b1));
322 
323  float sumA = scaleA / scale0;
324  float sumC = scaleC / scale1;
325 
326  sign = -1;
327 
328  return (sumA + sumC);
329  }
330 
331  inline
332  float gaussian_2nd_deriv_coef_norm_(float a0, float a1,
333  float b0, float b1,
334  float c0, float c1,
335  float cos0, float sin0,
336  float cos1, float sin1,
337  int& sign)
338  {
339  float expb0 = std::exp(b0);
340  float exp2b0 = std::exp(2.f * b0);
341 
342  float scale0 = 1 + exp2b0 - 2 * cos0 * expb0;
343  scale0 *= scale0 * scale0;
344 
345  float scaleA = a1 * sin0 * expb0 *
346  (1 + expb0 * (2 * cos0 * (1 + exp2b0) + exp2b0 - 6)) +
347  a0 * expb0 * (2 * expb0 * (2 - cos0 * cos0) *
348  (1 - exp2b0) - cos0 * (1 - exp2b0 * exp2b0));
349 
350  float expb1 = std::exp(b1);
351  float exp2b1 = std::exp(2.f * b1);
352 
353  float scale1 = 1 + exp2b1 - 2 * cos1 * expb1;
354  scale1 *= scale1 * scale1;
355 
356  float scaleC = c1 * sin1 * expb1 *
357  (1 + expb1 * (2 * cos1 * (1 + exp2b1) + exp2b1 - 6)) +
358  c0 * expb1 * (2 * expb1 * (2 - cos1 * cos1) *
359  (1 - exp2b1) - cos1 * (1 - exp2b1 * exp2b1));
360 
361  float sumA = scaleA / scale0;
362  float sumC = scaleC / scale1;
363  sign = 1;
364 
365  return (sumA + sumC);
366  }
367 
368 
369  template <class I, class F>
370  inline
371  void
372  generic_filter_(trait::image::dimension::one_d,
373  Image<I>& img_, const F& coef, int dir)
374  {
375  I& img = exact(img_);
376  mln_precondition(dir < I::site::dim);
377 
378 
379  recursivefilter_<mln_value(I)>(img, coef,
380  point1d(static_cast<def::coord>(-img.border())),
381  point1d(static_cast<def::coord>(geom::ninds(img) - 1 +
382  img.border())),
383  geom::ninds(img) + 2 * img.border(),
384  dpoint1d(1));
385  }
386 
387  template <class I, class F>
388  inline
389  void
390  generic_filter_(trait::image::dimension::two_d,
391  Image<I>& img_, const F& coef, int dir)
392  {
393  I& img = exact(img_);
394 
395  mln_precondition(dir < I::site::dim);
396 
397 
398  if (dir == 0)
399  {
400  // Apply on rows.
401  for (def::coord j = geom::min_col(img); j <= geom::max_col(img); ++j)
402  recursivefilter_<mln_value(I)>(img, coef,
403  point2d(static_cast<def::coord>(geom::min_row(img) - img.border()),
404  static_cast<def::coord>(j)),
405  point2d(static_cast<def::coord>(geom::max_row(img) +
406  img.border()),
407  static_cast<def::coord>(j)),
408  geom::nrows(img) + 2 * img.border(),
409  dpoint2d(1, 0));
410  }
411 
412  if (dir == 1)
413  {
414  // Apply on columns.
415  for (def::coord i = geom::min_row(img); i <= geom::max_row(img); ++i)
416  recursivefilter_<mln_value(I)>(img, coef,
417  point2d(static_cast<def::coord>(i),
418  static_cast<def::coord>(geom::min_col(img) - img.border())),
419  point2d(static_cast<def::coord>(i),
420  static_cast<def::coord>(geom::max_col(img) +
421  img.border())),
422  geom::ncols(img) + 2 * img.border(),
423  dpoint2d(0, 1));
424  }
425  }
426 
427  template <class I, class F>
428  inline
429  void
430  generic_filter_(trait::image::dimension::three_d,
431  Image<I>& img_, const F& coef, int dir)
432  {
433  I& img = exact(img_);
434  mln_precondition(dir < I::site::dim);
435 
436  if (dir == 0)
437  {
438  // Apply on slices.
439  for (def::coord j = geom::min_row(img); j <= geom::max_row(img); ++j)
440  for (def::coord k = geom::min_col(img); k <= geom::max_col(img); ++k)
441  recursivefilter_<mln_value(I)>(img, coef,
442  point3d(static_cast<def::coord>(-img.border()),
443  static_cast<def::coord>(j),
444  static_cast<def::coord>(k)),
445  point3d(static_cast<def::coord>(geom::nslis(img) - 1 +
446  img.border()),
447  static_cast<def::coord>(j),
448  static_cast<def::coord>(k)),
449  geom::nslis(img) + 2 *
450  img.border(),
451  dpoint3d(1, 0, 0));
452  }
453 
454 
455  if (dir == 1)
456  {
457  // Apply on rows.
458  for (def::coord i = geom::min_sli(img); i <= geom::max_sli(img); ++i)
459  for (def::coord k = geom::min_col(img); k <= geom::max_col(img); ++k)
460  recursivefilter_<mln_value(I)>(img, coef,
461  point3d(static_cast<def::coord>(i),
462  static_cast<def::coord>(-img.border()),
463  static_cast<def::coord>(k)),
464  point3d(static_cast<def::coord>(i),
465  static_cast<def::coord>(geom::nrows(img) - 1 +
466  img.border()),
467  static_cast<def::coord>(k)),
468  geom::nrows(img) + 2 *
469  img.border(),
470  dpoint3d(0, 1, 0));
471  }
472 
473  if (dir == 2)
474  {
475  // Apply on columns.
476  for (def::coord i = geom::min_sli(img); i <= geom::max_sli(img); ++i)
477  for (def::coord j = geom::min_row(img); j <= geom::max_row(img); ++j)
478  recursivefilter_<mln_value(I)>(img, coef,
479  point3d(static_cast<def::coord>(i),
480  static_cast<def::coord>(j),
481  static_cast<def::coord>(-img.border())),
482  point3d(static_cast<def::coord>(i),
483  static_cast<def::coord>(j),
484  static_cast<def::coord>(geom::ncols(img) -
485  1 + img.border())),
486  geom::ncols(img) + 2 *
487  img.border(),
488  dpoint3d(0, 0, 1));
489  }
490  }
491 
492 
493 
494  template <class I, class F, class O>
495  inline
496  void
497  generic_filter_common_(trait::value::nature::floating,
498  const Image<I>& in,
499  const F& coef,
500  float sigma,
501  Image<O>& out)
502  {
503  mln_ch_value(O, float) work_img(exact(in).domain());
504  data::paste(in, work_img);
505  extension::adjust_fill(work_img, 4, 0);
506 
507  // On tiny sigma, Derich algorithm doesn't work.
508  // It is the same thing that to convolve with a Dirac.
509  if (sigma > 0.006)
510  for (int i = 0; i < I::site::dim; ++i)
511  generic_filter_(mln_trait_image_dimension(I)(),
512  work_img, coef, i);
513 
514  // We don't need to convert work_img
515  data::paste(work_img, out);
516  }
517 
518  template <class I, class F, class O>
519  inline
520  void
521  generic_filter_common_(trait::value::nature::floating,
522  const Image<I>& in,
523  const F& coef,
524  float sigma,
525  Image<O>& out,
526  int dir)
527  {
528  mln_ch_value(O, float) work_img(exact(in).domain());
529  data::paste(in, work_img);
530  extension::adjust_fill(work_img, 4, 0);
531 
532  // On tiny sigma, Derich algorithm doesn't work.
533  // It is the same thing that to convolve with a Dirac.
534  if (sigma > 0.006)
535  generic_filter_(mln_trait_image_dimension(I)(),
536  work_img, coef, dir);
537 
538  // We don't need to convert work_img
539  data::paste(work_img, out);
540  }
541 
542 
543  template <class I, class F, class O>
544  inline
545  void
546  generic_filter_common_(trait::value::nature::scalar,
547  const Image<I>& in,
548  const F& coef,
549  float sigma,
550  Image<O>& out)
551  {
552  mln_ch_value(O, float) work_img(exact(in).domain());
553  data::paste(in, work_img);
554  extension::adjust_fill(work_img, 4, 0);
555 
556  // On tiny sigma, Derich algorithm doesn't work.
557  // It is the same thing that to convolve with a Dirac.
558  if (sigma > 0.006)
559  for (int i = 0; i < I::site::dim; ++i)
560  generic_filter_(mln_trait_image_dimension(I)(),
561  work_img, coef, i);
562 
563  // Convert work_img into result type
564  data::paste(data::stretch(mln_value(I)(), work_img), out);
565  }
566 
567  template <class I, class F, class O>
568  inline
569  void
570  generic_filter_common_(trait::value::nature::scalar,
571  const Image<I>& in,
572  const F& coef,
573  float sigma,
574  Image<O>& out,
575  int dir)
576  {
577  mln_ch_value(O, float) work_img(exact(in).domain());
578  data::paste(in, work_img);
579  extension::adjust_fill(work_img, 4, 0);
580 
581  // On tiny sigma, Derich algorithm doesn't work.
582  // It is the same thing that to convolve with a Dirac.
583  if (sigma > 0.006)
584  generic_filter_(mln_trait_image_dimension(I)(),
585  work_img, coef, dir);
586 
587  // Convert work_img into result type
588  data::paste(data::stretch(mln_value(I)(), work_img), out);
589  }
590 
591 
592 
593  template <class I, class F, class O>
594  inline
595  void
596  generic_filter_common_(trait::value::nature::vectorial,
597  const Image<I>& in,
598  const F& coef,
599  float sigma,
600  Image<O>& out)
601  {
602  // typedef algebra::vec<3, float> vec3f;
603  // mln_ch_value(O, vec3f) work_img(exact(in).domain());
604  // FIXME : paste does not work (rgb8 -> vec3f).
605  data::paste(in, out);
606 
607  // On tiny sigma, Derich algorithm doesn't work.
608  // It is the same thing that to convolve with a Dirac.
609  if (sigma > 0.006)
610  for (int i = 0; i < I::site::dim; ++i)
611  generic_filter_(mln_trait_image_dimension(I)(),
612  out, coef, i);
613  }
614 
615  template <class I, class F, class O>
616  inline
617  void
618  generic_filter_common_(trait::value::nature::vectorial,
619  const Image<I>& in,
620  const F& coef,
621  float sigma,
622  Image<O>& out,
623  int dir)
624  {
625  // typedef algebra::vec<3, float> vec3f;
626  // mln_ch_value(O, vec3f) work_img(exact(in).domain());
627  // FIXME : paste does not work (rgb8 -> vec3f).
628  data::paste(in, out);
629 
630  // On tiny sigma, Derich algorithm doesn't work.
631  // It is the same thing that to convolve with a Dirac.
632  if (sigma > 0.006)
633  generic_filter_(mln_trait_image_dimension(I)(),
634  out, coef, dir);
635  }
636 
637  } // end of namespace mln::linear::impl
638 
639  // Facade.
640 
650  template <typename I>
651  inline
652  mln_concrete(I)
653  gaussian(const Image<I>& input, float sigma, int dir)
654  {
655  mln_precondition(exact(input).is_valid());
656  mln_precondition(dir < I::site::dim);
657 
658  mln_concrete(I) output;
659  initialize(output, input);
660  impl::recursivefilter_coef_ coef(1.68f, 3.735f,
661  1.783f, 1.723f,
662  -0.6803f, -0.2598f,
663  0.6318f, 1.997f,
664  sigma, impl::gaussian_norm_coef_);
665 
666  impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
667  input, coef, sigma, output, dir);
668  return output;
669  }
670 
671 
682  template <typename I>
683  inline
684  mln_concrete(I)
685  gaussian_1st_derivative(const Image<I>& input, float sigma, int dir)
686  {
687  mln_precondition(exact(input).is_valid());
688  mln_precondition(dir < I::site::dim);
689 
690  mln_concrete(I) output;
691  initialize(output, input);
692 
693  impl::recursivefilter_coef_
694  coef(-0.6472f, -4.531f,
695  1.527f, 1.516f,
696  0.6494f, 0.9557f,
697  0.6719f, 2.072f,
698  sigma, impl::gaussian_1st_deriv_coef_norm_);
699 
700  impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
701  input, coef, sigma, output, dir);
702  return output;
703  }
704 
715  template <typename I>
716  inline
717  mln_concrete(I)
718  gaussian_2nd_derivative(const Image<I>& input, float sigma, int dir)
719  {
720  mln_precondition(exact(input).is_valid());
721  mln_precondition(dir < I::site::dim);
722 
723  mln_concrete(I) output;
724  initialize(output, input);
725 
726  impl::recursivefilter_coef_
727  coef(-1.331f, 3.661f,
728  1.24f, 1.314f,
729  0.3225f, -1.738f,
730  0.748f, 2.166f,
731  sigma, impl::gaussian_2nd_deriv_coef_norm_);
732 
733  impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
734  input, coef, sigma, output, dir);
735  return output;
736  }
737 
738 
739 
740 
741 
747  template <typename I>
748  inline
749  mln_concrete(I)
750  gaussian(const Image<I>& input, float sigma)
751  {
752  mln_precondition(exact(input).is_valid());
753 
754  mln_concrete(I) output;
755  initialize(output, input);
756 
757  impl::recursivefilter_coef_
758  coef(1.68f, 3.735f,
759  1.783f, 1.723f,
760  -0.6803f, -0.2598f,
761  0.6318f, 1.997f,
762  sigma, impl::gaussian_norm_coef_);
763  impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
764  input, coef, sigma, output);
765 
766  return output;
767  }
768 
769 
776  template <typename I>
777  inline
778  mln_concrete(I)
779  gaussian_1st_derivative(const Image<I>& input, float sigma)
780  {
781  mln_precondition(exact(input).is_valid());
782 
783  mln_concrete(I) output;
784  initialize(output, input);
785 
786  impl::recursivefilter_coef_
787  coef(-0.6472f, -4.531f,
788  1.527f, 1.516f,
789  0.6494f, 0.9557f,
790  0.6719f, 2.072f,
791  sigma, impl::gaussian_1st_deriv_coef_norm_);
792  impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
793  input, coef, sigma, output);
794  return output;
795  }
796 
797 
804  template <typename I>
805  inline
806  mln_concrete(I)
807  gaussian_2nd_derivative(const Image<I>& input, float sigma)
808  {
809  mln_precondition(exact(input).is_valid());
810 
811  mln_concrete(I) output;
812  initialize(output, input);
813 
814  impl::recursivefilter_coef_
815  coef(-1.331f, 3.661f,
816  1.24f, 1.314f,
817  0.3225f, -1.738f,
818  0.748f, 2.166f,
819  sigma, impl::gaussian_2nd_deriv_coef_norm_);
820  impl::generic_filter_common_(mln_trait_value_nature(mln_value(I))(),
821  input, coef, sigma, output);
822  return output;
823  }
824 
825 # endif // ! MLN_INCLUDE_ONLY
826 
827  } // end of namespace mln::linear
828 
829 } // end of namespace mln
830 
831 
832 #endif // ! MLN_LINEAR_GAUSSIAN_HH