Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

vil_convolve_1d.h

Go to the documentation of this file.
00001 // This is core/vil/algo/vil_convolve_1d.h
00002 #ifndef vil_convolve_1d_h_
00003 #define vil_convolve_1d_h_
00004 //:
00005 // \file
00006 // \brief 1D Convolution with cunning boundary options
00007 // \author Tim Cootes, Ian Scott (based on work by fsm)
00008 //
00009 // Note. The convolution operation is defined by
00010 //    $(f*g)(x) = \int f(x-y) g(y) dy$
00011 // i.e. the kernel g is reflected before the integration is performed.
00012 // If you don't want this to happen, the behaviour you want is not
00013 // called "convolution". So don't break the convolution routines in
00014 // that particular way.
00015 
00016 #include <vcl_compiler.h>
00017 #include <vcl_algorithm.h>
00018 #include <vcl_cstdlib.h> // for vcl_abort()
00019 #include <vcl_cstring.h>
00020 #include <vcl_cassert.h>
00021 #include <vcl_iostream.h>
00022 #include <vil/vil_image_view.h>
00023 #include <vil/vil_image_resource.h>
00024 #include <vil/vil_property.h>
00025 
00026 
00027 //: Available options for boundary behavior
00028 // When convolving a finite signal the boundaries may be
00029 // treated in various ways which can often be expressed in terms
00030 // of ways to extend the signal outside its original range.
00031 enum vil_convolve_boundary_option
00032 {
00033   //: Do not fill destination edges at all.
00034   // i.e. leave them unchanged.
00035   vil_convolve_ignore_edge,
00036 
00037   //: Do not to extend the signal, but pad with zeros.
00038   // \verbatim
00039   //     |                               |
00040   // K                       ----*-------
00041   // in   ... ---------------------------
00042   // out  ... --------------------0000000
00043   // \endverbatim
00044   vil_convolve_no_extend,
00045 
00046   //: Zero-extend the input signal beyond the boundary.
00047   // \verbatim
00048   //     |                               |
00049   // K                              ----*--------
00050   // in   ... ---------------------------000000000000...
00051   // out  ... ---------------------------
00052   // \endverbatim
00053   vil_convolve_zero_extend,
00054 
00055   //: Extend the signal to be constant beyond the boundary.
00056   // \verbatim
00057   //     |                               |
00058   // K                              ----*--------
00059   // in   ... --------------------------aaaaaaaaaaaaa...
00060   // out  ... ---------------------------
00061   // \endverbatim
00062   vil_convolve_constant_extend,
00063 
00064   //: Extend the signal periodically beyond the boundary.
00065   // \verbatim
00066   //     |                               |
00067   // K                              ----*--------
00068   // in   abc...-------------------------abc...------..
00069   // out  ... ---------------------------
00070   // \endverbatim
00071   vil_convolve_periodic_extend,
00072 
00073   //: Extend the signal by reflection about the boundary.
00074   // \verbatim
00075   //     |                               |
00076   // K                              ----*--------
00077   // in   ... -------------------...edcbabcde...
00078   // out  ... ---------------------------
00079   // \endverbatim
00080   vil_convolve_reflect_extend,
00081 
00082   //: Kernel is trimmed and reweighed, to allow convolution up to boundary.
00083   // This one is slightly different. The input signal is not
00084   // extended in any way, but the kernel is trimmed to allow
00085   // convolution to proceed up to the boundary and reweighed
00086   // to keep the total area the same.
00087   // \note may not work with kernels which take negative values.
00088   vil_convolve_trim
00089 };
00090 
00091 //: Convolve edge with kernel[x*kstep] x in [k_lo,k_hi] (k_hi>=0)
00092 //  Fills only edge: dest[i], i=0..(k_hi-1)
00093 template <class srcT, class destT, class kernelT, class accumT>
00094 inline void vil_convolve_edge_1d(const srcT* src, unsigned n, vcl_ptrdiff_t s_step,
00095                                  destT* dest, vcl_ptrdiff_t d_step,
00096                                  const kernelT* kernel,
00097                                  vcl_ptrdiff_t k_lo, vcl_ptrdiff_t k_hi,
00098                                  vcl_ptrdiff_t kstep, accumT,
00099                                  vil_convolve_boundary_option option)
00100 {
00101   switch (option)
00102   {
00103    case vil_convolve_ignore_edge:
00104     return;
00105    case vil_convolve_no_extend:
00106     // Initialise first elements of row to zero
00107     for (vcl_ptrdiff_t i=-k_hi;i<0;++i,dest+=d_step)
00108       *dest = 0;
00109     return;
00110    case vil_convolve_zero_extend:
00111     // Assume src[i]==0 for i<0
00112 //    for (vcl_ptrdiff_t i=-k_hi+1;i<=0;++i,dest+=d_step,src+=s_step)
00113     for (vcl_ptrdiff_t i=0;i<k_hi;++i,dest+=d_step)
00114     {
00115       accumT sum = 0;
00116       const srcT* s = src;
00117       const kernelT* k = kernel+i*kstep;
00118       for (vcl_ptrdiff_t j=i;j>=k_lo;--j,s+=s_step,k-=kstep)
00119         sum+= (accumT)((*s)*(*k));
00120       *dest=(destT)sum;
00121     }
00122     return;
00123    case vil_convolve_constant_extend:
00124    {
00125     // Assume src[i]=src[0] for i<0
00126     vcl_ptrdiff_t i_max = k_hi-1;
00127     for (vcl_ptrdiff_t i=0;i<=i_max;++i)
00128     {
00129       accumT sum=0;
00130       for (vcl_ptrdiff_t j=-k_hi;j<=-k_lo;++j)
00131       {
00132         if ((i+j)<0) sum+=(accumT)(src[0]*kernel[j*(-kstep)]);
00133         else         sum+=(accumT)(src[(i+j)*s_step]*kernel[j*(-kstep)]);
00134       }
00135       dest[i*d_step]=(destT)sum;
00136     }
00137     return;
00138    }
00139    case vil_convolve_reflect_extend:
00140    {
00141     // Assume src[i]=src[0] for i<0
00142     vcl_ptrdiff_t i_max = k_hi-1;
00143     for (vcl_ptrdiff_t i=0;i<=i_max;++i)
00144     {
00145       accumT sum=0;
00146       for (vcl_ptrdiff_t j=-k_hi;j<=-k_lo;++j)
00147       {
00148         if ((i+j)<0) sum+=(accumT)(src[-(i+j)*s_step]*kernel[j*(-kstep)]);
00149         else         sum+=(accumT)(src[(i+j)*s_step]*kernel[j*(-kstep)]);
00150       }
00151       dest[i*d_step]=(destT)sum;
00152     }
00153     return;
00154    }
00155    case vil_convolve_periodic_extend:
00156    {
00157     // Assume src[i]=src[n+i] for i<0
00158     vcl_ptrdiff_t i_max = k_hi-1;
00159     for (int i=0;i<=i_max;++i)
00160     {
00161       accumT sum=0;
00162       for (vcl_ptrdiff_t j=k_hi;j>=k_lo;--j)
00163         sum+=(accumT)(src[((i-j+n)%n)*s_step]*kernel[j*kstep]);
00164       dest[i*d_step]=(destT)sum;
00165     }
00166     return;
00167    }
00168    case vil_convolve_trim:
00169    {
00170     // Truncate and reweight kernel
00171     accumT k_sum_all=0;
00172     for (vcl_ptrdiff_t j=-k_hi;j<=-k_lo;++j) k_sum_all+=(accumT)(kernel[j*(-kstep)]);
00173 
00174     vcl_ptrdiff_t i_max = k_hi-1;
00175     for (vcl_ptrdiff_t i=0;i<=i_max;++i)
00176     {
00177       accumT sum=0;
00178       accumT k_sum=0;
00179       // Sum elements which overlap src
00180       // ie i+j>=0  (so j starts at -i)
00181       for (vcl_ptrdiff_t j=-i;j<=-k_lo;++j)
00182       {
00183         sum+=(accumT)(src[(i+j)*s_step]*kernel[j*(-kstep)]);
00184         k_sum += (accumT)(kernel[j*(-kstep)]);
00185       }
00186       dest[i*d_step]=(destT)(sum*k_sum_all/k_sum);
00187     }
00188     return;
00189    }
00190    default:
00191     vcl_cout<<"ERROR: vil_convolve_edge_1d: "
00192             <<"Sorry, can't deal with supplied edge option.\n";
00193     vcl_abort();
00194   }
00195 }
00196 
00197 //: Convolve kernel[x] (x in [k_lo,k_hi]) with srcT
00198 // Assumes dest and src same size (nx)
00199 // Kernel must not be larger than nx;
00200 template <class srcT, class destT, class kernelT, class accumT>
00201 inline void vil_convolve_1d(const srcT* src0, unsigned nx, vcl_ptrdiff_t s_step,
00202                             destT* dest0, vcl_ptrdiff_t d_step,
00203                             const kernelT* kernel,
00204                             vcl_ptrdiff_t k_lo, vcl_ptrdiff_t k_hi,
00205                             accumT ac,
00206                             vil_convolve_boundary_option start_option,
00207                             vil_convolve_boundary_option end_option)
00208 {
00209   assert(k_hi - k_lo < int(nx));
00210 
00211   // Deal with start (fill elements 0..1+k_hi of dest)
00212   vil_convolve_edge_1d(src0,nx,s_step,dest0,d_step,kernel,k_lo,k_hi,1,ac,start_option);
00213 
00214   const kernelT* k_rbegin = kernel+k_hi;
00215   const kernelT* k_rend   = kernel+k_lo-1;
00216   assert(k_rbegin >= k_rend);
00217   const srcT* src = src0;
00218 
00219   
00220   for (destT       * dest = dest0 + d_step*k_hi,
00221          * const end_dest = dest0 + d_step*(int(nx)+k_lo);
00222        dest!=end_dest;
00223        dest+=d_step,src+=s_step)
00224   {
00225     accumT sum = 0;
00226     const srcT* s= src;
00227     for (const kernelT *k = k_rbegin;k!=k_rend;--k,s+=s_step)
00228       sum+= (accumT)((*k)*(*s));
00229     *dest = destT(sum);
00230   }
00231 
00232   // Deal with end  (reflect data and kernel!)
00233   vil_convolve_edge_1d(src0+(nx-1)*s_step,nx,-s_step,
00234                        dest0+(nx-1)*d_step,-d_step,
00235                        kernel,-k_hi,-k_lo,-1,ac,end_option);
00236 }
00237 
00238 //: Convolve kernel[i] (i in [k_lo,k_hi]) with srcT in i-direction
00239 // On exit dest_im(i,j) = sum src(i-x,j)*kernel(x)  (x=k_lo..k_hi)
00240 // \note  This function reverses the kernel. If you don't want the
00241 // kernel reversed, use vil_correlate_1d instead. The kernel must
00242 // not be larger than src_im.ni()
00243 // \param kernel should point to tap 0.
00244 // \param dest_im will be resized to size of src_im.
00245 // \relates vil_image_view
00246 template <class srcT, class destT, class kernelT, class accumT>
00247 inline void vil_convolve_1d(const vil_image_view<srcT>& src_im,
00248                             vil_image_view<destT>& dest_im,
00249                             const kernelT* kernel,
00250                             vcl_ptrdiff_t k_lo, vcl_ptrdiff_t k_hi,
00251                             accumT ac,
00252                             vil_convolve_boundary_option start_option,
00253                             vil_convolve_boundary_option end_option)
00254 {
00255   unsigned n_i = src_im.ni();
00256   unsigned n_j = src_im.nj();
00257   assert(k_hi - k_lo +1 <= (int) n_i);
00258   vcl_ptrdiff_t s_istep = src_im.istep(), s_jstep = src_im.jstep();
00259 
00260   dest_im.set_size(n_i,n_j,src_im.nplanes());
00261   vcl_ptrdiff_t d_istep = dest_im.istep(),d_jstep = dest_im.jstep();
00262 
00263   for (unsigned int p=0;p<src_im.nplanes();++p)
00264   {
00265     // Select first row of p-th plane
00266     const srcT*  src_row  = src_im.top_left_ptr()+p*src_im.planestep();
00267     destT* dest_row = dest_im.top_left_ptr()+p*dest_im.planestep();
00268 
00269     // Apply convolution to each row in turn
00270     // First check if either istep is 1 for speed optimisation.
00271 
00272     if (s_istep == 1)
00273     {
00274       if (d_istep == 1)
00275         for (unsigned int j=0;j<n_j;++j,src_row+=s_jstep,dest_row+=d_jstep)
00276           vil_convolve_1d(src_row,n_i,1,  dest_row,1,
00277                           kernel,k_lo,k_hi,ac,start_option,end_option);
00278       else
00279         for (unsigned int j=0;j<n_j;++j,src_row+=s_jstep,dest_row+=d_jstep)
00280           vil_convolve_1d(src_row,n_i,1,  dest_row,d_istep,
00281                           kernel,k_lo,k_hi,ac,start_option,end_option);
00282     }
00283     else
00284     {
00285       if (d_istep == 1)
00286         for (unsigned int j=0;j<n_j;++j,src_row+=s_jstep,dest_row+=d_jstep)
00287           vil_convolve_1d(src_row,n_i,s_istep,  dest_row,1,
00288                           kernel,k_lo,k_hi,ac,start_option,end_option);
00289       else
00290         for (unsigned int j=0;j<n_j;++j,src_row+=s_jstep,dest_row+=d_jstep)
00291           vil_convolve_1d(src_row,n_i,s_istep,  dest_row,d_istep,
00292                           kernel,k_lo,k_hi,ac,start_option,end_option);
00293     }
00294   }
00295 }
00296 
00297 template <class destT, class kernelT, class accumT>
00298 inline vil_image_resource_sptr vil_convolve_1d(
00299                const vil_image_resource_sptr& src_im,
00300                const destT dt,
00301                const kernelT* kernel, int k_lo, int k_hi,
00302                const accumT ac,
00303                vil_convolve_boundary_option start_option,
00304                vil_convolve_boundary_option end_option);
00305 
00306 //: A resource adaptor that behaves like a convolved version of its input
00307 template <class kernelT, class accumT, class destT>
00308 class vil_convolve_1d_resource : public vil_image_resource
00309 {
00310   //: Construct a convolve filter.
00311   // You can't create one of these directly, use vil_convolve_1d instead
00312   vil_convolve_1d_resource(const vil_image_resource_sptr& src,
00313                            const kernelT* kernel, int k_lo, int k_hi,
00314                            vil_convolve_boundary_option start_option,
00315                            vil_convolve_boundary_option end_option)  :
00316       src_(src), kernel_(kernel), klo_(k_lo), khi_(k_hi),
00317       start_option_(start_option), end_option_(end_option)
00318     {
00319       // Can't do period extension yet.
00320       assert (start_option != vil_convolve_periodic_extend ||
00321               end_option != vil_convolve_periodic_extend);
00322     }
00323 
00324   friend vil_image_resource_sptr vil_convolve_1d VCL_NULL_TMPL_ARGS (
00325     const vil_image_resource_sptr& src_im, const destT dt, const kernelT* kernel,
00326     int k_lo, int k_hi, const accumT ac,
00327     vil_convolve_boundary_option start_option,
00328     vil_convolve_boundary_option end_option);
00329 
00330  public:
00331   virtual vil_image_view_base_sptr get_copy_view(unsigned i0, unsigned n_i,
00332                                                  unsigned j0, unsigned n_j) const
00333   {
00334     if (i0 + n_i > src_->ni() || j0 + n_j > src_->nj())  return 0;
00335     const unsigned lsrc = (unsigned) vcl_max(0,(int)i0 + klo_); // lhs of input window
00336     const unsigned hsrc = vcl_min(src_->ni(),i0 + n_i - klo_ + khi_); // 1+rhs of input window.
00337     const unsigned lboundary = vcl_min((unsigned) -klo_, i0); // width of lhs boundary area.
00338     assert (hsrc > lsrc);
00339     vil_image_view_base_sptr vs = src_->get_view(lsrc, hsrc-lsrc, j0, n_j);
00340     vil_image_view<destT> dest(vs->ni(), vs->nj(), vs->nplanes());
00341     switch (vs->pixel_format())
00342     {
00343 #define macro( F , T ) \
00344      case F : \
00345       vil_convolve_1d(static_cast<vil_image_view<T >&>(*vs),dest, \
00346                       kernel_, klo_, khi_, accumT(), start_option_, end_option_); \
00347       return new vil_image_view<destT>(vil_crop(dest, lboundary, n_i, 0, n_j));
00348 
00349       macro(VIL_PIXEL_FORMAT_BYTE , vxl_byte )
00350       macro(VIL_PIXEL_FORMAT_SBYTE , vxl_sbyte )
00351       macro(VIL_PIXEL_FORMAT_UINT_32 , vxl_uint_32 )
00352       macro(VIL_PIXEL_FORMAT_UINT_16 , vxl_uint_16 )
00353       macro(VIL_PIXEL_FORMAT_INT_32 , vxl_int_32 )
00354       macro(VIL_PIXEL_FORMAT_INT_16 , vxl_int_16 )
00355       macro(VIL_PIXEL_FORMAT_BOOL , bool )
00356       macro(VIL_PIXEL_FORMAT_FLOAT , float )
00357       macro(VIL_PIXEL_FORMAT_DOUBLE , double )
00358 // complex<float> should work - but causes all manner of compiler template errors.
00359 // maybe need a better compiler, maybe there is a code fix - IMS
00360 #undef macro
00361      default:
00362       return 0;
00363     }
00364   }
00365 
00366   virtual unsigned nplanes() const { return src_->nplanes(); }
00367   virtual unsigned ni() const { return src_->ni(); }
00368   virtual unsigned nj() const { return src_->nj(); }
00369 
00370   virtual enum vil_pixel_format pixel_format() const
00371   { return vil_pixel_format_of(accumT()); }
00372 
00373 
00374   //: Put the data in this view back into the image source.
00375   virtual bool put_view(const vil_image_view_base&  /*im*/, unsigned  /*i0*/, unsigned  /*j0*/)
00376   {
00377     vcl_cerr << "WARNING: vil_convolve_1d_resource::put_back\n"
00378              << "\tYou can't push data back into a convolve filter.\n";
00379     return false;
00380   }
00381 
00382   //: Extra property information
00383   virtual bool get_property(char const* tag, void* property_value = 0) const
00384   {
00385     if (0==vcl_strcmp(tag, vil_property_read_only))
00386       return property_value ? (*static_cast<bool*>(property_value)) = true : true;
00387 
00388     return src_->get_property(tag, property_value);
00389   }
00390 
00391  protected:
00392   vil_image_resource_sptr src_;
00393   const kernelT* kernel_;
00394   int klo_, khi_;
00395   vil_convolve_boundary_option start_option_, end_option_;
00396 };
00397 
00398 //: Create an image_resource object which convolve kernel[x] x in [k_lo,k_hi] with srcT
00399 // \note  This function reverses the kernel. If you don't want the
00400 // kernel reversed, use vil_correlate_1d instead.
00401 // \param kernel should point to tap 0.
00402 // \relates vil_image_resource
00403 template <class destT, class kernelT, class accumT>
00404 inline vil_image_resource_sptr vil_convolve_1d(
00405                          const vil_image_resource_sptr& src_im,
00406                          const destT  /*dt*/,
00407                          const kernelT* kernel, int k_lo, int k_hi,
00408                          const accumT,
00409                          vil_convolve_boundary_option start_option,
00410                          vil_convolve_boundary_option end_option)
00411 {
00412   return new vil_convolve_1d_resource<kernelT, accumT, destT>(src_im, kernel, k_lo, k_hi, start_option, end_option);
00413 }
00414 
00415 #endif // vil_convolve_1d_h_
00416 

Generated on Thu Jan 10 14:39:59 2008 for core/vil by  doxygen 1.4.4