[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

nonlineardiffusion.hxx VIGRA

1 /************************************************************************/
2 /* */
3 /* Copyright 1998-2002 by Ullrich Koethe */
4 /* */
5 /* This file is part of the VIGRA computer vision library. */
6 /* The VIGRA Website is */
7 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
8 /* Please direct questions, bug reports, and contributions to */
9 /* ullrich.koethe@iwr.uni-heidelberg.de or */
10 /* vigra@informatik.uni-hamburg.de */
11 /* */
12 /* Permission is hereby granted, free of charge, to any person */
13 /* obtaining a copy of this software and associated documentation */
14 /* files (the "Software"), to deal in the Software without */
15 /* restriction, including without limitation the rights to use, */
16 /* copy, modify, merge, publish, distribute, sublicense, and/or */
17 /* sell copies of the Software, and to permit persons to whom the */
18 /* Software is furnished to do so, subject to the following */
19 /* conditions: */
20 /* */
21 /* The above copyright notice and this permission notice shall be */
22 /* included in all copies or substantial portions of the */
23 /* Software. */
24 /* */
25 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
26 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
27 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
28 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
29 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
30 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
31 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
32 /* OTHER DEALINGS IN THE SOFTWARE. */
33 /* */
34 /************************************************************************/
35 
36 #ifndef VIGRA_NONLINEARDIFFUSION_HXX
37 #define VIGRA_NONLINEARDIFFUSION_HXX
38 
39 #include <vector>
40 #include "stdimage.hxx"
41 #include "stdimagefunctions.hxx"
42 #include "imageiteratoradapter.hxx"
43 #include "functortraits.hxx"
44 #include "multi_shape.hxx"
45 
46 namespace vigra {
47 
48 template <class SrcIterator, class SrcAccessor,
49  class CoeffIterator, class DestIterator>
50 void internalNonlinearDiffusionDiagonalSolver(
51  SrcIterator sbegin, SrcIterator send, SrcAccessor sa,
52  CoeffIterator diag, CoeffIterator upper, CoeffIterator lower,
53  DestIterator dbegin)
54 {
55  int w = send - sbegin - 1;
56 
57  int i;
58 
59  for(i=0; i<w; ++i)
60  {
61  lower[i] = lower[i] / diag[i];
62 
63  diag[i+1] = diag[i+1] - lower[i] * upper[i];
64  }
65 
66  dbegin[0] = sa(sbegin);
67 
68  for(i=1; i<=w; ++i)
69  {
70  dbegin[i] = sa(sbegin, i) - lower[i-1] * dbegin[i-1];
71  }
72 
73  dbegin[w] = dbegin[w] / diag[w];
74 
75  for(i=w-1; i>=0; --i)
76  {
77  dbegin[i] = (dbegin[i] - upper[i] * dbegin[i+1]) / diag[i];
78  }
79 }
80 
81 
82 template <class SrcIterator, class SrcAccessor,
83  class WeightIterator, class WeightAccessor,
84  class DestIterator, class DestAccessor>
85 void internalNonlinearDiffusionAOSStep(
86  SrcIterator sul, SrcIterator slr, SrcAccessor as,
87  WeightIterator wul, WeightAccessor aw,
88  DestIterator dul, DestAccessor ad, double timestep)
89 {
90  // use traits to determine SumType as to prevent possible overflow
91  typedef typename
92  NumericTraits<typename WeightAccessor::value_type>::RealPromote
93  WeightType;
94 
95  // calculate width and height of the image
96  int w = slr.x - sul.x;
97  int h = slr.y - sul.y;
98  int d = (w < h) ? h : w;
99 
100  std::vector<WeightType> lower(d),
101  diag(d),
102  upper(d),
103  res(d);
104 
105  int x,y;
106 
107  WeightType one = NumericTraits<WeightType>::one();
108 
109  // create y iterators
110  SrcIterator ys = sul;
111  WeightIterator yw = wul;
112  DestIterator yd = dul;
113 
114  // x-direction
115  for(y=0; y<h; ++y, ++ys.y, ++yd.y, ++yw.y)
116  {
117  typename SrcIterator::row_iterator xs = ys.rowIterator();
118  typename WeightIterator::row_iterator xw = yw.rowIterator();
119  typename DestIterator::row_iterator xd = yd.rowIterator();
120 
121  // fill 3-diag matrix
122 
123  diag[0] = one + timestep * (aw(xw) + aw(xw, 1));
124  for(x=1; x<w-1; ++x)
125  {
126  diag[x] = one + timestep * (2.0 * aw(xw, x) + aw(xw, x+1) + aw(xw, x-1));
127  }
128  diag[w-1] = one + timestep * (aw(xw, w-1) + aw(xw, w-2));
129 
130  for(x=0; x<w-1; ++x)
131  {
132  lower[x] = -timestep * (aw(xw, x) + aw(xw, x+1));
133  upper[x] = lower[x];
134  }
135 
136  internalNonlinearDiffusionDiagonalSolver(xs, xs+w, as,
137  diag.begin(), upper.begin(), lower.begin(), res.begin());
138 
139  for(x=0; x<w; ++x, ++xd)
140  {
141  ad.set(res[x], xd);
142  }
143  }
144 
145  // y-direction
146  ys = sul;
147  yw = wul;
148  yd = dul;
149 
150  for(x=0; x<w; ++x, ++ys.x, ++yd.x, ++yw.x)
151  {
152  typename SrcIterator::column_iterator xs = ys.columnIterator();
153  typename WeightIterator::column_iterator xw = yw.columnIterator();
154  typename DestIterator::column_iterator xd = yd.columnIterator();
155 
156  // fill 3-diag matrix
157 
158  diag[0] = one + timestep * (aw(xw) + aw(xw, 1));
159  for(y=1; y<h-1; ++y)
160  {
161  diag[y] = one + timestep * (2.0 * aw(xw, y) + aw(xw, y+1) + aw(xw, y-1));
162  }
163  diag[h-1] = one + timestep * (aw(xw, h-1) + aw(xw, h-2));
164 
165  for(y=0; y<h-1; ++y)
166  {
167  lower[y] = -timestep * (aw(xw, y) + aw(xw, y+1));
168  upper[y] = lower[y];
169  }
170 
171  internalNonlinearDiffusionDiagonalSolver(xs, xs+h, as,
172  diag.begin(), upper.begin(), lower.begin(), res.begin());
173 
174  for(y=0; y<h; ++y, ++xd)
175  {
176  ad.set(0.5 * (ad(xd) + res[y]), xd);
177  }
178  }
179 }
180 
181 /** \addtogroup NonLinearDiffusion Non-linear Diffusion and Total Variation
182 
183  Perform edge-preserving smoothing.
184 */
185 //@{
186 
187 /********************************************************/
188 /* */
189 /* nonlinearDiffusion */
190 /* */
191 /********************************************************/
192 
193 /** \brief Perform edge-preserving smoothing at the given scale.
194 
195  The algorithm solves the non-linear diffusion equation
196 
197  \f[
198  \frac{\partial}{\partial t} u =
199  \frac{\partial}{\partial x}
200  \left( g(|\nabla u|)
201  \frac{\partial}{\partial x} u
202  \right)
203  \f]
204 
205  where <em> t</em> is the time, <b> x</b> is the location vector,
206  <em> u(</em><b> x</b><em> , t)</em> is the smoothed image at time <em> t</em>, and
207  <em> g(.)</em> is the location dependent diffusivity. At time zero, the image
208  <em> u(</em><b> x</b><em> , 0)</em> is simply the original image. The time is
209  proportional to the square of the scale parameter: \f$t = s^2\f$.
210  The diffusion equation is solved iteratively according
211  to the Additive Operator Splitting Scheme (AOS) from
212 
213  J. Weickert: <em>"Recursive Separable Schemes for Nonlinear Diffusion
214  Filters"</em>,
215  in: B. ter Haar Romeny, L. Florack, J. Koenderingk, M. Viergever (eds.):
216  1st Intl. Conf. on Scale-Space Theory in Computer Vision 1997,
217  Springer LNCS 1252
218 
219  <TT>DiffusivityFunctor</TT> implements the gradient-dependent local diffusivity.
220  It is passed
221  as an argument to \ref gradientBasedTransform(). The return value must be
222  between 0 and 1 and determines the weight a pixel gets when
223  its neighbors are smoothed. Weickert recommends the use of the diffusivity
224  implemented by class \ref DiffusivityFunctor. It's also possible to use
225  other functors, for example one that always returns 1, in which case
226  we obtain the solution to the linear diffusion equation, i.e.
227  Gaussian convolution.
228 
229  The source value type must be a
230  linear space with internal addition, scalar multiplication, and
231  NumericTraits defined. The value_type of the DiffusivityFunctor must be the
232  scalar field over wich the source value type's linear space is defined.
233 
234  In addition to <TT>nonlinearDiffusion()</TT>, there is an algorithm
235  <TT>nonlinearDiffusionExplicit()</TT> which implements the Explicit Scheme
236  described in the above article. Both algorithms have the same interface,
237  but the explicit scheme gives slightly more accurate approximations of
238  the diffusion process at the cost of much slower processing.
239 
240  <b> Declarations:</b>
241 
242  pass 2D array views:
243  \code
244  namespace vigra {
245  template <class T1, class S1,
246  class T2, class S2,
247  class DiffusivityFunc>
248  void
249  nonlinearDiffusion(MultiArrayView<2, T1, S1> const & src,
250  MultiArrayView<2, T2, S2> dest,
251  DiffusivityFunc const & weight, double scale);
252 
253  template <class T1, class S1,
254  class T2, class S2,
255  class DiffusivityFunc>
256  void
257  nonlinearDiffusionExplicit(MultiArrayView<2, T1, S1> const & src,
258  MultiArrayView<2, T2, S2> dest,
259  DiffusivityFunc const & weight, double scale);
260  }
261  \endcode
262 
263  \deprecatedAPI{nonlinearDiffusion}
264  pass \ref ImageIterators and \ref DataAccessors :
265  \code
266  namespace vigra {
267  template <class SrcIterator, class SrcAccessor,
268  class DestIterator, class DestAccessor,
269  class DiffusivityFunctor>
270  void nonlinearDiffusion(SrcIterator sul, SrcIterator slr, SrcAccessor as,
271  DestIterator dul, DestAccessor ad,
272  DiffusivityFunctor const & weight, double scale);
273  }
274  \endcode
275  use argument objects in conjunction with \ref ArgumentObjectFactories :
276  \code
277  namespace vigra {
278  template <class SrcIterator, class SrcAccessor,
279  class DestIterator, class DestAccessor,
280  class DiffusivityFunctor>
281  void nonlinearDiffusion(
282  triple<SrcIterator, SrcIterator, SrcAccessor> src,
283  pair<DestIterator, DestAccessor> dest,
284  DiffusivityFunctor const & weight, double scale);
285  }
286  \endcode
287  \deprecatedEnd
288 
289  <b> Usage:</b>
290 
291  <b>\#include</b> <vigra/nonlineardiffusion.hxx><br/>
292  Namespace: vigra
293 
294  \code
295  MultiArray<2, float> src(w,h), dest(w,h);
296  float edge_threshold, scale;
297  ...
298 
299  nonlinearDiffusion(src, dest,
300  DiffusivityFunctor<float>(edge_threshold), scale);
301  \endcode
302 
303  \deprecatedUsage{nonlinearDiffusion}
304  \code
305  FImage src(w,h), dest(w,h);
306  float edge_threshold, scale;
307  ...
308 
309  nonlinearDiffusion(srcImageRange(src), destImage(dest),
310  DiffusivityFunctor<float>(edge_threshold), scale);
311  \endcode
312  <b> Required Interface:</b>
313  <ul>
314  <li> <TT>SrcIterator</TT> and <TT>DestIterator</TT> are models of ImageIterator
315  <li> <TT>SrcAccessor</TT> and <TT>DestAccessor</TT> are models of StandardAccessor
316  <li> <TT>SrcAccessor::value_type</TT> is a linear space
317  <li> <TT>DiffusivityFunctor</TT> conforms to the requirements of
318  \ref gradientBasedTransform(). Its range is between 0 and 1.
319  <li> <TT>DiffusivityFunctor::value_type</TT> is an algebraic field
320  </ul>
321  \deprecatedEnd
322 
323  <b> Precondition:</b>
324 
325  <TT>scale > 0</TT>
326 
327  \see vigra::DiffusivityFunctor
328 */
330 
331 template <class SrcIterator, class SrcAccessor,
332  class DestIterator, class DestAccessor,
333  class DiffusivityFunc>
334 void nonlinearDiffusion(SrcIterator sul, SrcIterator slr, SrcAccessor as,
335  DestIterator dul, DestAccessor ad,
336  DiffusivityFunc const & weight, double scale)
337 {
338  vigra_precondition(scale > 0.0, "nonlinearDiffusion(): scale must be > 0");
339 
340  double total_time = scale*scale/2.0;
341  const double time_step = 5.0;
342  int number_of_steps = (int)(total_time / time_step);
343  double rest_time = total_time - time_step * number_of_steps;
344 
345  Size2D size(slr.x - sul.x, slr.y - sul.y);
346 
347  typedef typename
348  NumericTraits<typename SrcAccessor::value_type>::RealPromote
349  TmpType;
350  typedef typename DiffusivityFunc::value_type WeightType;
351 
352  BasicImage<TmpType> smooth1(size);
353  BasicImage<TmpType> smooth2(size);
354 
355  BasicImage<WeightType> weights(size);
356 
357  typename BasicImage<TmpType>::Iterator s1 = smooth1.upperLeft(),
358  s2 = smooth2.upperLeft();
359  typename BasicImage<TmpType>::Accessor a = smooth1.accessor();
360 
361  typename BasicImage<WeightType>::Iterator wi = weights.upperLeft();
362  typename BasicImage<WeightType>::Accessor wa = weights.accessor();
363 
364  gradientBasedTransform(sul, slr, as, wi, wa, weight);
365 
366  internalNonlinearDiffusionAOSStep(sul, slr, as, wi, wa, s1, a, rest_time);
367 
368  for(int i = 0; i < number_of_steps; ++i)
369  {
370  gradientBasedTransform(s1, s1+size, a, wi, wa, weight);
371 
372  internalNonlinearDiffusionAOSStep(s1, s1+size, a, wi, wa, s2, a, time_step);
373 
374  std::swap(s1, s2);
375  }
376 
377  copyImage(s1, s1+size, a, dul, ad);
378 }
379 
380 template <class SrcIterator, class SrcAccessor,
381  class DestIterator, class DestAccessor,
382  class DiffusivityFunc>
383 inline void
384 nonlinearDiffusion(triple<SrcIterator, SrcIterator, SrcAccessor> src,
385  pair<DestIterator, DestAccessor> dest,
386  DiffusivityFunc const & weight, double scale)
387 {
388  nonlinearDiffusion(src.first, src.second, src.third,
389  dest.first, dest.second,
390  weight, scale);
391 }
392 
393 template <class T1, class S1,
394  class T2, class S2,
395  class DiffusivityFunc>
396 inline void
397 nonlinearDiffusion(MultiArrayView<2, T1, S1> const & src,
398  MultiArrayView<2, T2, S2> dest,
399  DiffusivityFunc const & weight, double scale)
400 {
401  vigra_precondition(src.shape() == dest.shape(),
402  "nonlinearDiffusion(): shape mismatch between input and output.");
403  nonlinearDiffusion(srcImageRange(src),
404  destImage(dest),
405  weight, scale);
406 }
407 
408 /********************************************************/
409 
410 template <class SrcIterator, class SrcAccessor,
411  class WeightIterator, class WeightAccessor,
412  class DestIterator, class DestAccessor>
413 void internalNonlinearDiffusionExplicitStep(
414  SrcIterator sul, SrcIterator slr, SrcAccessor as,
415  WeightIterator wul, WeightAccessor aw,
416  DestIterator dul, DestAccessor ad,
417  double time_step)
418 {
419  // use traits to determine SumType as to prevent possible overflow
420  typedef typename
421  NumericTraits<typename SrcAccessor::value_type>::RealPromote
422  SumType;
423 
424  typedef typename
425  NumericTraits<typename WeightAccessor::value_type>::RealPromote
426  WeightType;
427 
428  // calculate width and height of the image
429  int w = slr.x - sul.x;
430  int h = slr.y - sul.y;
431 
432  int x,y;
433 
434  const Diff2D left(-1, 0);
435  const Diff2D right(1, 0);
436  const Diff2D top(0, -1);
437  const Diff2D bottom(0, 1);
438 
439  WeightType gt, gb, gl, gr, g0;
440  WeightType one = NumericTraits<WeightType>::one();
441  SumType sum;
442 
443  time_step /= 2.0;
444 
445  // create y iterators
446  SrcIterator ys = sul;
447  WeightIterator yw = wul;
448  DestIterator yd = dul;
449 
450  SrcIterator xs = ys;
451  WeightIterator xw = yw;
452  DestIterator xd = yd;
453 
454  gt = (aw(xw) + aw(xw, bottom)) * time_step;
455  gb = (aw(xw) + aw(xw, bottom)) * time_step;
456  gl = (aw(xw) + aw(xw, right)) * time_step;
457  gr = (aw(xw) + aw(xw, right)) * time_step;
458  g0 = one - gt - gb - gr - gl;
459 
460  sum = g0 * as(xs);
461  sum += gt * as(xs, bottom);
462  sum += gb * as(xs, bottom);
463  sum += gl * as(xs, right);
464  sum += gr * as(xs, right);
465 
466  ad.set(sum, xd);
467 
468  for(x=2, ++xs.x, ++xd.x, ++xw.x; x<w; ++x, ++xs.x, ++xd.x, ++xw.x)
469  {
470  gt = (aw(xw) + aw(xw, bottom)) * time_step;
471  gb = (aw(xw) + aw(xw, bottom)) * time_step;
472  gl = (aw(xw) + aw(xw, left)) * time_step;
473  gr = (aw(xw) + aw(xw, right)) * time_step;
474  g0 = one - gt - gb - gr - gl;
475 
476  sum = g0 * as(xs);
477  sum += gt * as(xs, bottom);
478  sum += gb * as(xs, bottom);
479  sum += gl * as(xs, left);
480  sum += gr * as(xs, right);
481 
482  ad.set(sum, xd);
483  }
484 
485  gt = (aw(xw) + aw(xw, bottom)) * time_step;
486  gb = (aw(xw) + aw(xw, bottom)) * time_step;
487  gl = (aw(xw) + aw(xw, left)) * time_step;
488  gr = (aw(xw) + aw(xw, left)) * time_step;
489  g0 = one - gt - gb - gr - gl;
490 
491  sum = g0 * as(xs);
492  sum += gt * as(xs, bottom);
493  sum += gb * as(xs, bottom);
494  sum += gl * as(xs, left);
495  sum += gr * as(xs, left);
496 
497  ad.set(sum, xd);
498 
499  for(y=2, ++ys.y, ++yd.y, ++yw.y; y<h; ++y, ++ys.y, ++yd.y, ++yw.y)
500  {
501  xs = ys;
502  xd = yd;
503  xw = yw;
504 
505  gt = (aw(xw) + aw(xw, top)) * time_step;
506  gb = (aw(xw) + aw(xw, bottom)) * time_step;
507  gl = (aw(xw) + aw(xw, right)) * time_step;
508  gr = (aw(xw) + aw(xw, right)) * time_step;
509  g0 = one - gt - gb - gr - gl;
510 
511  sum = g0 * as(xs);
512  sum += gt * as(xs, top);
513  sum += gb * as(xs, bottom);
514  sum += gl * as(xs, right);
515  sum += gr * as(xs, right);
516 
517  ad.set(sum, xd);
518 
519  for(x=2, ++xs.x, ++xd.x, ++xw.x; x<w; ++x, ++xs.x, ++xd.x, ++xw.x)
520  {
521  gt = (aw(xw) + aw(xw, top)) * time_step;
522  gb = (aw(xw) + aw(xw, bottom)) * time_step;
523  gl = (aw(xw) + aw(xw, left)) * time_step;
524  gr = (aw(xw) + aw(xw, right)) * time_step;
525  g0 = one - gt - gb - gr - gl;
526 
527  sum = g0 * as(xs);
528  sum += gt * as(xs, top);
529  sum += gb * as(xs, bottom);
530  sum += gl * as(xs, left);
531  sum += gr * as(xs, right);
532 
533  ad.set(sum, xd);
534  }
535 
536  gt = (aw(xw) + aw(xw, top)) * time_step;
537  gb = (aw(xw) + aw(xw, bottom)) * time_step;
538  gl = (aw(xw) + aw(xw, left)) * time_step;
539  gr = (aw(xw) + aw(xw, left)) * time_step;
540  g0 = one - gt - gb - gr - gl;
541 
542  sum = g0 * as(xs);
543  sum += gt * as(xs, top);
544  sum += gb * as(xs, bottom);
545  sum += gl * as(xs, left);
546  sum += gr * as(xs, left);
547 
548  ad.set(sum, xd);
549  }
550 
551  xs = ys;
552  xd = yd;
553  xw = yw;
554 
555  gt = (aw(xw) + aw(xw, top)) * time_step;
556  gb = (aw(xw) + aw(xw, top)) * time_step;
557  gl = (aw(xw) + aw(xw, right)) * time_step;
558  gr = (aw(xw) + aw(xw, right)) * time_step;
559  g0 = one - gt - gb - gr - gl;
560 
561  sum = g0 * as(xs);
562  sum += gt * as(xs, top);
563  sum += gb * as(xs, top);
564  sum += gl * as(xs, right);
565  sum += gr * as(xs, right);
566 
567  ad.set(sum, xd);
568 
569  for(x=2, ++xs.x, ++xd.x, ++xw.x; x<w; ++x, ++xs.x, ++xd.x, ++xw.x)
570  {
571  gt = (aw(xw) + aw(xw, top)) * time_step;
572  gb = (aw(xw) + aw(xw, top)) * time_step;
573  gl = (aw(xw) + aw(xw, left)) * time_step;
574  gr = (aw(xw) + aw(xw, right)) * time_step;
575  g0 = one - gt - gb - gr - gl;
576 
577  sum = g0 * as(xs);
578  sum += gt * as(xs, top);
579  sum += gb * as(xs, top);
580  sum += gl * as(xs, left);
581  sum += gr * as(xs, right);
582 
583  ad.set(sum, xd);
584  }
585 
586  gt = (aw(xw) + aw(xw, top)) * time_step;
587  gb = (aw(xw) + aw(xw, top)) * time_step;
588  gl = (aw(xw) + aw(xw, left)) * time_step;
589  gr = (aw(xw) + aw(xw, left)) * time_step;
590  g0 = one - gt - gb - gr - gl;
591 
592  sum = g0 * as(xs);
593  sum += gt * as(xs, top);
594  sum += gb * as(xs, top);
595  sum += gl * as(xs, left);
596  sum += gr * as(xs, left);
597 
598  ad.set(sum, xd);
599 }
600 
601 /** \brief Perform edge-preserving smoothing at the given scale using an explicit scheme.
602 
603  See \ref nonlinearDiffusion().
604 */
606 
607 template <class SrcIterator, class SrcAccessor,
608  class DestIterator, class DestAccessor,
609  class DiffusivityFunc>
610 void nonlinearDiffusionExplicit(SrcIterator sul, SrcIterator slr, SrcAccessor as,
611  DestIterator dul, DestAccessor ad,
612  DiffusivityFunc const & weight, double scale)
613 {
614  vigra_precondition(scale > 0.0, "nonlinearDiffusionExplicit(): scale must be > 0");
615 
616  double total_time = scale*scale/2.0;
617  const double time_step = 0.25;
618  int number_of_steps = total_time / time_step;
619  double rest_time = total_time - time_step * number_of_steps;
620 
621  Size2D size(slr.x - sul.x, slr.y - sul.y);
622 
623  typedef typename
624  NumericTraits<typename SrcAccessor::value_type>::RealPromote
625  TmpType;
626  typedef typename DiffusivityFunc::value_type WeightType;
627 
628  BasicImage<TmpType> smooth1(size);
629  BasicImage<TmpType> smooth2(size);
630 
631  BasicImage<WeightType> weights(size);
632 
633  typename BasicImage<TmpType>::Iterator s1 = smooth1.upperLeft(),
634  s2 = smooth2.upperLeft();
635  typename BasicImage<TmpType>::Accessor a = smooth1.accessor();
636 
637  typename BasicImage<WeightType>::Iterator wi = weights.upperLeft();
638  typename BasicImage<WeightType>::Accessor wa = weights.accessor();
639 
640  gradientBasedTransform(sul, slr, as, wi, wa, weight);
641 
642  internalNonlinearDiffusionExplicitStep(sul, slr, as, wi, wa, s1, a, rest_time);
643 
644  for(int i = 0; i < number_of_steps; ++i)
645  {
646  gradientBasedTransform(s1, s1+size, a, wi, wa, weight);
647 
648  internalNonlinearDiffusionExplicitStep(s1, s1+size, a, wi, wa, s2, a, time_step);
649 
650  swap(s1, s2);
651  }
652 
653  copyImage(s1, s1+size, a, dul, ad);
654 }
655 
656 template <class SrcIterator, class SrcAccessor,
657  class DestIterator, class DestAccessor,
658  class DiffusivityFunc>
659 inline void
660 nonlinearDiffusionExplicit(triple<SrcIterator, SrcIterator, SrcAccessor> src,
661  pair<DestIterator, DestAccessor> dest,
662  DiffusivityFunc const & weight, double scale)
663 {
664  nonlinearDiffusionExplicit(src.first, src.second, src.third,
665  dest.first, dest.second,
666  weight, scale);
667 }
668 
669 template <class T1, class S1,
670  class T2, class S2,
671  class DiffusivityFunc>
672 inline void
673 nonlinearDiffusionExplicit(MultiArrayView<2, T1, S1> const & src,
674  MultiArrayView<2, T2, S2> dest,
675  DiffusivityFunc const & weight, double scale)
676 {
677  vigra_precondition(src.shape() == dest.shape(),
678  "nonlinearDiffusionExplicit(): shape mismatch between input and output.");
679  nonlinearDiffusionExplicit(srcImageRange(src),
680  destImage(dest),
681  weight, scale);
682 }
683 
684 /********************************************************/
685 /* */
686 /* DiffusivityFunctor */
687 /* */
688 /********************************************************/
689 
690 /** \brief Diffusivity functor for non-linear diffusion.
691 
692  This functor implements the diffusivity recommended by Weickert:
693 
694  \f[
695  g(|\nabla u|) = 1 -
696  \exp{\left(\frac{-3.315}{(|\nabla u| / thresh)^4}\right)}
697  \f]
698 
699 
700  where <TT>thresh</TT> is a threshold that determines whether a specific gradient
701  magnitude is interpreted as a significant edge (above the threshold)
702  or as noise. It is meant to be used with \ref nonlinearDiffusion().
703 */
704 template <class Value>
706 {
707  public:
708  /** the functors first argument type (must be an algebraic field with <TT>exp()</TT> defined).
709  However, the functor also works with RGBValue<first_argument_type> (this hack is
710  necessary since Microsoft C++ doesn't support partial specialization).
711  */
712  typedef Value first_argument_type;
713 
714  /** the functors second argument type (same as first).
715  However, the functor also works with RGBValue<second_argument_type> (this hack is
716  necessary since Microsoft C++ doesn't support partial specialization).
717  */
718  typedef Value second_argument_type;
719 
720  /** the functors result type
721  */
722  typedef typename NumericTraits<Value>::RealPromote result_type;
723 
724  /** \deprecated use first_argument_type, second_argument_type, result_type
725  */
726  typedef Value value_type;
727 
728  /** init functor with given edge threshold
729  */
730  DiffusivityFunctor(Value const & thresh)
731  : weight_(thresh*thresh),
732  one_(NumericTraits<result_type>::one()),
733  zero_(NumericTraits<result_type>::zero())
734  {}
735 
736  /** calculate diffusivity from scalar arguments
737  */
739  {
740  Value mag = (gx*gx + gy*gy) / weight_;
741 
742  return (mag == zero_) ? one_ : one_ - VIGRA_CSTD::exp(-3.315 / mag / mag);
743  }
744 
745  /** calculate diffusivity from RGB arguments
746  */
748  {
749  result_type mag = (gx.red()*gx.red() +
750  gx.green()*gx.green() +
751  gx.blue()*gx.blue() +
752  gy.red()*gy.red() +
753  gy.green()*gy.green() +
754  gy.blue()*gy.blue()) / weight_;
755 
756  return (mag == zero_) ? one_ : one_ - VIGRA_CSTD::exp(-3.315 / mag / mag);
757  }
758 
759  result_type weight_;
760  result_type one_;
761  result_type zero_;
762 };
763 
764 template <class ValueType>
765 class FunctorTraits<DiffusivityFunctor<ValueType> >
766 : public FunctorTraitsBase<DiffusivityFunctor<ValueType> >
767 {
768  public:
769  typedef VigraTrueType isBinaryFunctor;
770 };
771 
772 
773 //@}
774 
775 } // namespace vigra
776 
777 #endif /* VIGRA_NONLINEARDIFFUSION_HXX */
Value first_argument_type
Definition: nonlineardiffusion.hxx:712
Diffusivity functor for non-linear diffusion.
Definition: nonlineardiffusion.hxx:705
linalg::TemporaryMatrix< T > exp(MultiArrayView< 2, T, C > const &v)
value_type & red()
Definition: rgbvalue.hxx:278
Value value_type
Definition: nonlineardiffusion.hxx:726
Value second_argument_type
Definition: nonlineardiffusion.hxx:718
value_type & blue()
Definition: rgbvalue.hxx:286
DiffusivityFunctor(Value const &thresh)
Definition: nonlineardiffusion.hxx:730
value_type & green()
Definition: rgbvalue.hxx:282
NumericTraits< V >::Promote sum(TinyVectorBase< V, SIZE, D1, D2 > const &l)
sum of the vector's elements
Definition: tinyvector.hxx:2073
doxygen_overloaded_function(template<...> void separableConvolveBlockwise) template< unsigned int N
Separated convolution on ChunkedArrays.
void copyImage(...)
Copy source image into destination image.
result_type operator()(RGBValue< Value > const &gx, RGBValue< Value > const &gy) const
Definition: nonlineardiffusion.hxx:747
void gradientBasedTransform(...)
Calculate a function of the image gradient.
void nonlinearDiffusionExplicit(...)
Perform edge-preserving smoothing at the given scale using an explicit scheme.
void nonlinearDiffusion(...)
Perform edge-preserving smoothing at the given scale.
result_type operator()(first_argument_type const &gx, second_argument_type const &gy) const
Definition: nonlineardiffusion.hxx:738
Class for a single RGB value.
Definition: accessor.hxx:938
NumericTraits< Value >::RealPromote result_type
Definition: nonlineardiffusion.hxx:722

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de)
Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany

html generated using doxygen and Python
vigra 1.11.1 (Fri May 19 2017)