vil_gauss_filter.cxx
Go to the documentation of this file.
1 // This is core/vil/algo/vil_gauss_filter.cxx
2 #include <cmath>
3 #include <algorithm>
4 #include <functional>
5 #include "vil_gauss_filter.h"
6 //:
7 // \file
8 // \brief Functions to smooth an image
9 // \author Ian Scott
10 
11 #ifdef _MSC_VER
12 # include <vcl_msvc_warnings.h>
13 #endif
14 #include <cassert>
15 #include <vnl/vnl_erf.h>
16 #include <vnl/vnl_double_2.h>
18 
20 {
21  sigma_ = val_sigma;
22  const double z = 1/(std::sqrt(2.0)*val_sigma);
23  filt0_ = vnl_erf(0.5 * z) - vnl_erf(-0.5 * z);
24  filt1_ = vnl_erf(1.5 * z) - vnl_erf(0.5 * z);
25  filt2_ = vnl_erf(2.5 * z) - vnl_erf(1.5 * z);
26 
27  double five_tap_total = 2*(filt2_ + filt1_) + filt0_;
28 // double four_tap_total = filt2_ + 2*(filt1_) + filt0_;
29 // double three_tap_total = filt2_ + filt1_ + filt0_;
30 
31 // Calculate 3 tap half Gaussian filter assuming constant edge extension
32  filt_edge0_ = (filt0_ + filt1_ + filt2_) / five_tap_total;
33  filt_edge1_ = filt1_ / five_tap_total;
34  filt_edge2_ = filt2_ / five_tap_total;
35 #if 0
36  filt_edge0_ = 1.0;
37  filt_edge1_ = 0.0;
38  filt_edge2_ = 0.0;
39 #endif
40 // Calculate 4 tap skewed Gaussian filter assuming constant edge extension
41  filt_pen_edge_n1_ = (filt1_+filt2_) / five_tap_total;
42  filt_pen_edge0_ = filt0_ / five_tap_total;
43  filt_pen_edge1_ = filt1_ / five_tap_total;
44  filt_pen_edge2_ = filt2_ / five_tap_total;
45 
46 // Calculate 5 tap Gaussian filter
47  filt0_ = filt0_ / five_tap_total;
48  filt1_ = filt1_ / five_tap_total;
49  filt2_ = filt2_ / five_tap_total;
50 
51  assert(filt_edge0_ >= filt_edge1_);
52  assert(filt_edge1_ >= filt_edge2_);
53 }
54 
55 
56 //: Generate an n-tap FIR filter from a Gaussian function.
57 // The filter uses the equation $k D^d \exp -\frac{x^2}{2\sigma^2} $,
58 // where D is the differential operator, and k is a normalising constant.
59 // \param diff The number of differential operators to apply to the filter.
60 // If you want just a normal gaussian, set diff to 0.
61 // \param sd The width of the gaussian.
62 //
63 // The taps will be calculated using the integral of the above equation over
64 // the pixel width. However, aliasing will reduce the meaningfulness of
65 // your filter when sd << (diff+1). In most applications you will
66 // want filter.size() ~= sd*7, which will avoid significant truncation,
67 // without wasting the outer taps on near-zero values.
68 void vil_gauss_filter_gen_ntap(double sd, unsigned diff,
69  std::vector<double> &filter)
70 {
71  std::size_t centre = filter.size()/2; // or just past centre if even length
72  double sum=0.0; // area under sampled curve.
73  double tap; // workspace
74 
75  if (diff==0)
76  {
77  const double z = 1/(std::sqrt(2.0)*sd);
78  if (filter.size() % 2 == 0) // even length filter - off-centre
79  {
80  for (unsigned i=0 ; i<centre; ++i)
81  {
82  tap = vnl_erf((i+1.0) * z) - vnl_erf(i * z);
83  sum += tap;
84  filter[centre+i] = filter[centre-i-1] = tap;
85  }
86  sum *= 2.0;
87  }
88  else // odd length filter - centre on zero
89  {
90  for (unsigned i=1 ; i<=centre; ++i)
91  {
92  tap = vnl_erf((i+0.5) * z) - vnl_erf((i-0.5) * z);
93  sum += tap;
94  filter[centre+i] = filter[centre-i] = tap;
95  }
96  sum *= 2.0;
97  tap = vnl_erf(0.5 * z) - vnl_erf(-0.5 * z);
98  sum += tap;
99  filter[centre] = tap;
100  }
101  }
102  else
103  {
104  const double offset = filter.size() % 2 == 0 ? 0.0 : -0.5;
105  vnl_real_polynomial poly(1.0);
106  const double eta = -0.5/(sd*sd);
107  const vnl_real_polynomial d_gauss(vnl_double_2(eta, 0.0).as_ref());
108  for (unsigned i=1; i<diff; ++i)
109  {
110  // Evaluate d/dx (poly * gauss) where gauss = exp(-0.5*x^2/sd^2)
111  // n.b. d/dx gauss = d_gauss * gauss
112  poly = poly * d_gauss + poly.derivative();
113  }
114 
115  for (int i=-(int)centre ; i+centre<filter.size(); ++i)
116  {
117  tap = poly.evaluate(i+1.0+offset)*std::exp(eta*(i+1.0+offset)*(i+1.0+offset))
118  - poly.evaluate(i+ offset)*std::exp(eta*(i+ offset)*(i+ offset));
119  sum += std::abs(tap);
120  filter[centre+i] = tap;
121  }
122  }
123 
124  // normalise the result
125  assert(sum >= 0.0);
126  double norm = 1.0 / sum;
127  std::transform(filter.begin(), filter.end(), filter.begin(),
128  std::bind2nd(std::multiplies<double>(), norm));
129 }
void vil_gauss_filter_gen_ntap(double sd, unsigned diff, std::vector< double > &filter)
Generate an n-tap FIR filter from a Gaussian function.
Smooths images.
vil_gauss_filter_5tap_params(double sigma_)
Set the.
double evaluate(double x) const
double vnl_erf(double x)
vnl_real_polynomial derivative() const