core/vil/algo/vil_gauss_filter.cxx
Go to the documentation of this file.
00001 // This is core/vil/algo/vil_gauss_filter.cxx
00002 #include "vil_gauss_filter.h"
00003 //:
00004 // \file
00005 // \brief Functions to smooth an image
00006 // \author Ian Scott
00007 
00008 #include <vcl_cmath.h>
00009 #include <vcl_cassert.h>
00010 #include <vcl_algorithm.h>
00011 #include <vcl_functional.h>
00012 #include <vnl/vnl_erf.h>
00013 #include <vnl/vnl_double_2.h>
00014 #include <vnl/vnl_real_polynomial.h>
00015 
00016 vil_gauss_filter_5tap_params::vil_gauss_filter_5tap_params(double val_sigma)
00017 {
00018   sigma_ = val_sigma;
00019   const double z = 1/(vcl_sqrt(2.0)*val_sigma);
00020   filt0_ = vnl_erf(0.5 * z) - vnl_erf(-0.5 * z);
00021   filt1_ = vnl_erf(1.5 * z) - vnl_erf(0.5 * z);
00022   filt2_ = vnl_erf(2.5 * z) - vnl_erf(1.5 * z);
00023 
00024   double five_tap_total = 2*(filt2_ + filt1_) + filt0_;
00025 //  double four_tap_total = filt2_ + 2*(filt1_) + filt0_;
00026 //  double three_tap_total = filt2_ + filt1_ + filt0_;
00027 
00028 //  Calculate 3 tap half Gaussian filter assuming constant edge extension
00029   filt_edge0_ = (filt0_ + filt1_ + filt2_) / five_tap_total;
00030   filt_edge1_ = filt1_ / five_tap_total;
00031   filt_edge2_ = filt2_ / five_tap_total;
00032 #if 0
00033   filt_edge0_ = 1.0;
00034   filt_edge1_ = 0.0;
00035   filt_edge2_ = 0.0;
00036 #endif
00037 //  Calculate 4 tap skewed Gaussian filter assuming constant edge extension
00038   filt_pen_edge_n1_ = (filt1_+filt2_) / five_tap_total;
00039   filt_pen_edge0_ = filt0_ / five_tap_total;
00040   filt_pen_edge1_ = filt1_ / five_tap_total;
00041   filt_pen_edge2_ = filt2_ / five_tap_total;
00042 
00043 //  Calculate 5 tap Gaussian filter
00044   filt0_ = filt0_ / five_tap_total;
00045   filt1_ = filt1_ / five_tap_total;
00046   filt2_ = filt2_ / five_tap_total;
00047 
00048   assert(filt_edge0_ >= filt_edge1_);
00049   assert(filt_edge1_ >= filt_edge2_);
00050 }
00051 
00052 
00053 //: Generate an n-tap FIR filter from a Gaussian function.
00054 // The filter uses the equation $k D^d \exp -\frac{x^2}{2\sigma^2} $,
00055 // where D is the differential operator, and k is a normalising constant.
00056 // \param diff The number of differential operators to apply to the filter.
00057 // If you want just a normal gaussian, set diff to 0.
00058 // \param sd The width of the gaussian.
00059 //
00060 // The taps will be calculated using the integral of the above equation over
00061 // the pixel width. However, aliasing will reduce the meaningfulness of
00062 // your filter when sd << (diff+1). In most applications you will
00063 // want filter.size() ~= sd*7, which will avoid significant truncation,
00064 // without wasting the outer taps on near-zero values.
00065 void vil_gauss_filter_gen_ntap(double sd, unsigned diff,
00066                                vcl_vector<double> &filter)
00067 {
00068   vcl_size_t centre = filter.size()/2; // or just past centre if even length
00069   double sum=0.0; // area under sampled curve.
00070   double tap; // workspace
00071 
00072   if (diff==0)
00073   {
00074     const double z = 1/(vcl_sqrt(2.0)*sd);
00075     if (filter.size() % 2 == 0)  // even length filter - off-centre
00076     {
00077       for (unsigned i=0 ; i<centre; ++i)
00078       {
00079         tap = vnl_erf((i+1.0) * z) - vnl_erf(i * z);
00080         sum += tap;
00081         filter[centre+i] = filter[centre-i-1] = tap;
00082       }
00083       sum *= 2.0;
00084     }
00085     else // odd length filter - centre on zero
00086     {
00087       for (unsigned i=1 ; i<=centre; ++i)
00088       {
00089         tap = vnl_erf((i+0.5) * z) - vnl_erf((i-0.5) * z);
00090         sum += tap;
00091         filter[centre+i] = filter[centre-i] = tap;
00092       }
00093       sum *= 2.0;
00094       tap = vnl_erf(0.5 * z) - vnl_erf(-0.5 * z);
00095       sum += tap;
00096       filter[centre] = tap;
00097     }
00098   }
00099   else
00100   {
00101     const double offset = filter.size() % 2 == 0 ? 0.0 : -0.5;
00102     vnl_real_polynomial poly(1.0);
00103     const double eta = -0.5/(sd*sd);
00104     const vnl_real_polynomial d_gauss(vnl_double_2(eta, 0.0).as_ref());
00105     for (unsigned i=1; i<diff; ++i)
00106     {
00107       // Evaluate d/dx (poly * gauss) where gauss = exp(-0.5*x^2/sd^2)
00108       // n.b. d/dx gauss = d_gauss * gauss
00109       poly = poly * d_gauss + poly.derivative();
00110     }
00111 
00112     for (int i=-(int)centre ; i+centre<filter.size(); ++i)
00113     {
00114       tap = poly.evaluate(i+1.0+offset)*vcl_exp(eta*(i+1.0+offset)*(i+1.0+offset))
00115           - poly.evaluate(i+    offset)*vcl_exp(eta*(i+    offset)*(i+    offset));
00116       sum += vcl_abs(tap);
00117       filter[centre+i] = tap;
00118     }
00119   }
00120 
00121   // normalise the result
00122   assert(sum >= 0.0);
00123   double norm = 1.0 / sum;
00124   vcl_transform(filter.begin(), filter.end(), filter.begin(),
00125                 vcl_bind2nd(vcl_multiplies<double>(), norm));
00126 }