core/vil/algo/vil_gauss_reduce.txx
Go to the documentation of this file.
00001 // This is core/vil/algo/vil_gauss_reduce.txx
00002 #ifndef vil_gauss_reduce_txx_
00003 #define vil_gauss_reduce_txx_
00004 //:
00005 // \file
00006 // \brief Functions to smooth and sub-sample image in one direction
00007 // \author Tim Cootes
00008 
00009 #include "vil_gauss_reduce.h"
00010 #include <vcl_cassert.h>
00011 #include <vil/vil_bilin_interp.h>
00012 #include <vil/vil_plane.h>
00013 #include <vil/vil_convert.h>
00014 
00015 //: Smooth and subsample src_im to produce dest_im
00016 //  Applies filter in x and y, then samples every other pixel.
00017 //  work_im provides workspace
00018 template<class T>
00019 void vil_gauss_reduce(const vil_image_view<T>& src_im,
00020                       vil_image_view<T>& dest_im,
00021                       vil_image_view<T>& work_im)
00022 {
00023   unsigned ni = src_im.ni();
00024   unsigned nj = src_im.nj();
00025   unsigned n_planes = src_im.nplanes();
00026 
00027   // Output image size
00028   unsigned ni2 = (ni+1)/2;
00029   unsigned nj2 = (nj+1)/2;
00030 
00031   dest_im.set_size(ni2,nj2,n_planes);
00032 
00033   if (work_im.ni()<ni2 || work_im.nj()<nj)
00034     work_im.set_size(ni2,nj);
00035 
00036   // Reduce plane-by-plane
00037   for (unsigned int i=0;i<n_planes;++i)
00038   {
00039     // Smooth and subsample in x, result in work_im
00040     vil_gauss_reduce_1plane(src_im.top_left_ptr()+i*src_im.planestep(),ni,nj,
00041                             src_im.istep(),src_im.jstep(),
00042                             work_im.top_left_ptr(),
00043                             work_im.istep(),work_im.jstep());
00044 
00045     // Smooth and subsample in y (by implicitly transposing work_im)
00046     vil_gauss_reduce_1plane(work_im.top_left_ptr(),nj,ni2,
00047                             work_im.jstep(),work_im.istep(),
00048                             dest_im.top_left_ptr()+i*dest_im.planestep(),
00049                             dest_im.jstep(),dest_im.istep());
00050   }
00051 }
00052 
00053 //: Smooth and subsample src_im to produce dest_im (2/3 size)
00054 //  Applies filter in x and y, then samples every other pixel.
00055 //  work_im provides workspace
00056 template<class T>
00057 void vil_gauss_reduce_2_3(const vil_image_view<T>& src_im,
00058                           vil_image_view<T>& dest_im,
00059                           vil_image_view<T>& work_im)
00060 {
00061   unsigned ni = src_im.ni();
00062   unsigned nj = src_im.nj();
00063   unsigned n_planes = src_im.nplanes();
00064 
00065   // Output image size
00066   unsigned ni2 = (2*ni+1)/3;
00067   unsigned nj2 = (2*nj+1)/3;
00068 
00069   dest_im.set_size(ni2,nj2,n_planes);
00070 
00071   if (work_im.ni()<ni2 || work_im.nj()<nj)
00072     work_im.set_size(ni2,nj);
00073 
00074   // Reduce plane-by-plane
00075   for (unsigned int i=0;i<n_planes;++i)
00076   {
00077     // Smooth and subsample in x, result in work_im
00078     vil_gauss_reduce_2_3_1plane(src_im.top_left_ptr()+i*src_im.planestep(),ni,nj,
00079                                 src_im.istep(),src_im.jstep(),
00080                                 work_im.top_left_ptr(),
00081                                 work_im.istep(),work_im.jstep());
00082 
00083     // Smooth and subsample in y (by implicitly transposing work_im)
00084     vil_gauss_reduce_2_3_1plane(work_im.top_left_ptr(),nj,ni2,
00085                                 work_im.jstep(),work_im.istep(),
00086                                 dest_im.top_left_ptr()+i*dest_im.planestep(),
00087                                 dest_im.jstep(),dest_im.istep());
00088   }
00089 }
00090 
00091 //: Smooth and subsample src_im to produce dest_im
00092 //  Applies filter in x and y, then samples every other pixel.
00093 template<class T>
00094 void vil_gauss_reduce_121(const vil_image_view<T>& src_im,
00095                           vil_image_view<T>& dest_im)
00096 {
00097   unsigned int ni = src_im.ni();
00098   unsigned int nj = src_im.nj();
00099   unsigned int n_planes = src_im.nplanes();
00100 
00101   // Output image size
00102   unsigned int ni2 = (ni+1)/2;
00103   unsigned int nj2 = (nj+1)/2;
00104 
00105   dest_im.set_size(ni2,nj2,n_planes);
00106 
00107   // Reduce plane-by-plane
00108   for (unsigned int i=0;i<n_planes;++i)
00109   {
00110     vil_gauss_reduce_121_1plane(src_im.top_left_ptr() + i*src_im.planestep(),
00111                                 ni, nj,
00112                                 src_im.istep(),src_im.jstep(),
00113                                 dest_im.top_left_ptr() + i*dest_im.planestep(),
00114                                 dest_im.istep(), dest_im.jstep());
00115   }
00116 }
00117 
00118 
00119 //: An optimisable rounding function
00120 inline unsigned char rl_round(double x, unsigned char )
00121 {  return (unsigned char) (x+0.5);}
00122 
00123 inline signed char rl_round(double x, signed char )
00124 {  return (signed char) (x+0.5);}
00125 
00126 inline unsigned short rl_round(double x, unsigned short )
00127 {  return (unsigned short) (x+0.5);}
00128 
00129 inline signed short rl_round(double x, signed short )
00130 {  return (signed short) (x+0.5);}
00131 
00132 inline unsigned int rl_round(double x, unsigned int )
00133 {  return (unsigned int) (x+0.5);}
00134 
00135 inline signed int rl_round(double x, signed int )
00136 {  return (signed int) (x+0.5);}
00137 
00138 inline unsigned long rl_round(double x, unsigned long )
00139 {  return (unsigned long) (x+0.5);}
00140 
00141 inline signed long rl_round(double x, signed long )
00142 {  return (signed long) (x+0.5);}
00143 
00144 inline double rl_round (double x, double )
00145 {  return x; }
00146 
00147 inline float rl_round (double x, float )
00148 {  return (float) x; }
00149 
00150 //=======================================================================
00151 //: Smooth and subsample src_im to produce dest_im
00152 //  Applies 5 pin filter in x and y, then samples
00153 //  every other pixel.
00154 //  Assumes dest_im has sufficient data allocated
00155 
00156 template <class T>
00157 void vil_gauss_reduce_general_plane(const vil_image_view<T>& src,
00158                                     vil_image_view<T>& dest,
00159                                     vil_image_view<T>& worka,
00160                                     vil_image_view<T>& workb,
00161                                     const vil_gauss_reduce_params &params)
00162 {
00163   assert(src.ni() >= 5 && src.nj() >= 5);
00164   // Convolve src with a 5 x 1 gaussian filter,
00165   // placing result in worka
00166 
00167   // First perform horizontal smoothing
00168   for (unsigned y=0;y<src.nj();y++)
00169   {
00170     unsigned ni2 = src.ni()-2;
00171     for (unsigned x=2;x<ni2;++x)
00172       worka(x,y) = rl_round(  params.filt2() * (src(x-2,y) + src(x+2,y))
00173                             + params.filt1() * (src(x-1,y) + src(x+1,y))
00174                             + params.filt0() *  src(x  ,y),
00175                             T(0));
00176 
00177     // Now deal with edge effects :
00178     worka(0,y) = rl_round( params.filt_edge0() * src(0,y)
00179                          + params.filt_edge1() * src(1,y)
00180                          + params.filt_edge2() * src(2,y), (T)0);
00181 
00182     worka(1,y) = rl_round( params.filt_pen_edge_n1() * src(0,y)
00183                          + params.filt_pen_edge0() * src(1,y)
00184                          + params.filt_pen_edge1() * src(2,y)
00185                          + params.filt_pen_edge2() * src(3,y), (T)0);
00186 
00187     worka(src.ni()-2,y) = rl_round( params.filt_pen_edge2() * src(src.ni()-4,y)
00188                                   + params.filt_pen_edge1() * src(src.ni()-3,y)
00189                                   + params.filt_pen_edge0() * src(src.ni()-2,y)
00190                                   + params.filt_pen_edge_n1() * src(src.ni()-1,y), (T)0);
00191 
00192     worka(src.ni()-1,y) = rl_round( params.filt_edge2() * src(src.ni()-3,y)
00193                                   + params.filt_edge1() * src(src.ni()-2,y)
00194                                   + params.filt_edge0() * src(src.ni()-1,y), (T)0);
00195   }
00196 
00197 //  worka_.print_all(vcl_cout);
00198   // Now perform vertical smoothing
00199   for (unsigned int y=2;y+2<src.nj();++y)
00200   {
00201     for (unsigned int x=0; x<src.ni(); x++)
00202       workb(x,y) = rl_round(  params.filt2() *(worka(x,y-2) + worka(x,y+2))
00203                             + params.filt1() *(worka(x,y-1) + worka(x,y+1))
00204                             + params.filt0() * worka(x,  y),
00205                             (T)0);
00206   }
00207 
00208   // Now deal with edge effects :
00209   //
00210   for (unsigned x=0;x<src.ni();x++)
00211   {
00212     workb(x,src.nj()-1) = rl_round( params.filt_edge0() * worka(x,src.nj()-1)
00213                                   + params.filt_edge1() * worka(x,src.nj()-2)
00214                                   + params.filt_edge2() * worka(x,src.nj()-3), (T)0);
00215 
00216     workb(x,src.nj()-2) = rl_round( params.filt_pen_edge2() * worka(x,src.nj()-4)
00217                                   + params.filt_pen_edge1() * worka(x,src.nj()-3)
00218                                   + params.filt_pen_edge0() * worka(x,src.nj()-2)
00219                                   + params.filt_pen_edge_n1() * worka(x,src.nj()-1), (T)0);
00220 
00221     workb(x,1) = rl_round( params.filt_pen_edge2() * worka(x,3)
00222                          + params.filt_pen_edge1() * worka(x,2)
00223                          + params.filt_pen_edge0() * worka(x,1)
00224                          + params.filt_pen_edge_n1() * worka(x,0), (T)0);
00225 
00226     workb(x,0) = rl_round( params.filt_edge2() * worka(x,2)
00227                          + params.filt_edge1() * worka(x,1)
00228                          + params.filt_edge0() * worka(x,0), (T)0);
00229   }
00230 
00231 //  workb_.print_all(vcl_cout);
00232 
00233 
00234 //  assert (dest_ni*scale_step() <= src.ni() && dest_nj*scale_step() <= src.nj());
00235 
00236   const double init_x = 0.5 * (src.ni()-1 - (dest.ni()-1)*params.scale_step());
00237   double yd = 0.5 * (src.nj() -1 - (dest.nj()-1)*params.scale_step());
00238   for (unsigned int yi=0; yi<dest.nj(); yi++)
00239   {
00240     double xd=init_x;
00241     for (unsigned int xi=0; xi<dest.ni(); xi++)
00242     {
00243       dest(xi,yi) = rl_round (vil_bilin_interp_safe_extend(workb, xd, yd), T(0));
00244       xd += params.scale_step();
00245     }
00246     yd+= params.scale_step();
00247   }
00248 }
00249 
00250 
00251 template <class T>
00252 void vil_gauss_reduce_general(const vil_image_view<T>& src,
00253                               vil_image_view<T>& dest,
00254                               vil_image_view<T>& worka,
00255                               vil_image_view<T>& workb,
00256                               const vil_gauss_reduce_params &params)
00257 {
00258   if (worka.ni() < src.ni() || worka.nj() < src.nj())
00259     worka.set_size(src.ni(), src.nj());
00260   if (workb.ni() < src.ni() || workb.nj() < src.nj())
00261     workb.set_size(src.ni(), src.nj());
00262   dest.set_size((unsigned) (src.ni()/params.scale_step()+0.5),
00263     (unsigned) (src.nj()/params.scale_step()+0.5), src.nplanes());
00264 
00265   // Reduce plane-by-plane
00266   for (unsigned p=0;p<src.nplanes();++p) {
00267     vil_image_view<T> src_plane  = vil_plane(src,p);
00268     vil_image_view<T> dest_plane = vil_plane(dest,p);
00269     vil_gauss_reduce_general_plane(src_plane, dest_plane, worka, workb, params);
00270   }
00271 #if 0
00272   vsl_indent_inc(vcl_cout);
00273   vcl_cout << vsl_indent() << "Work image B\n";
00274   workb_.print_all(vcl_cout);
00275   vsl_indent_dec(vcl_cout);
00276 #endif
00277 }
00278 
00279 
00280 template <class T>
00281 void vil_gauss_reduce_1plane(const T* src_im,
00282                              unsigned src_ni, unsigned src_nj,
00283                              vcl_ptrdiff_t s_x_step, vcl_ptrdiff_t s_y_step,
00284                              T* dest_im,
00285                              vcl_ptrdiff_t d_x_step, vcl_ptrdiff_t d_y_step)
00286 {
00287   T* d_row = dest_im;
00288   const T* s_row = src_im;
00289   vcl_ptrdiff_t sxs2 = s_x_step*2;
00290   unsigned ni2 = (src_ni-3)/2;
00291   vil_convert_round_pixel<double,T> rounder;
00292 
00293   for (unsigned y=0;y<src_nj;++y)
00294   {
00295     // Set first element of row
00296     double dsum = 0.071 * static_cast<double>(s_row[sxs2]) +
00297                   0.357 * static_cast<double>(s_row[s_x_step]) +
00298                   0.572 * static_cast<double>(s_row[0]);
00299     rounder(dsum, *d_row);
00300 
00301     T* d = d_row + d_x_step;
00302     const T* s = s_row + sxs2;
00303     for (unsigned x=0;x<ni2;++x)
00304     {
00305       dsum = 0.05*static_cast<double>(s[-sxs2]) + 0.25*static_cast<double>(s[-s_x_step]) +
00306              0.05*static_cast<double>(s[ sxs2]) + 0.25*static_cast<double>(s[ s_x_step]) +
00307              0.4 *static_cast<double>(s[0]);
00308       rounder(dsum, *d);
00309 
00310       d += d_x_step;
00311       s += sxs2;
00312     }
00313     // Set last elements of row
00314     dsum = 0.071 * static_cast<double>(s[-sxs2]) +
00315            0.357 * static_cast<double>(s[-s_x_step]) +
00316            0.572 * static_cast<double>(s[0]);
00317     rounder(dsum, *d);
00318 
00319     d_row += d_y_step;
00320     s_row += s_y_step;
00321   }
00322 }
00323 
00324 
00325 //: Smooth and subsample single plane src_im in x to produce dest_im using 121 filter in x and y
00326 //  Smooths with a 3x3 filter and subsamples
00327 template <class T>
00328 void vil_gauss_reduce_121_1plane(const T* src_im,
00329                                  unsigned src_ni, unsigned src_nj,
00330                                  vcl_ptrdiff_t s_x_step, vcl_ptrdiff_t s_y_step,
00331                                  T* dest_im,
00332                                  vcl_ptrdiff_t d_x_step, vcl_ptrdiff_t d_y_step)
00333 {
00334   vcl_ptrdiff_t sxs2 = s_x_step*2;
00335   vcl_ptrdiff_t sys2 = s_y_step*2;
00336   T* d_row = dest_im+d_y_step;
00337   const T* s_row1 = src_im + s_y_step;
00338   const T* s_row2 = s_row1 + s_y_step;
00339   const T* s_row3 = s_row2 + s_y_step;
00340   unsigned ni2 = (src_ni-2)/2;
00341   unsigned nj2 = (src_nj-2)/2;
00342   vil_convert_round_pixel<double,T> rounder;
00343 
00344   for (unsigned y=0;y<nj2;++y)
00345   {
00346       // Set first element of row
00347       *d_row = *s_row2;
00348       T * d = d_row + d_x_step;
00349       const T* s1 = s_row1 + sxs2;
00350       const T* s2 = s_row2 + sxs2;
00351       const T* s3 = s_row3 + sxs2;
00352       for (unsigned x=0;x<ni2;++x)
00353       {
00354           // The following is a little inefficient - could group terms to reduce arithmetic
00355           double ds1 = 0.0625 * static_cast<double>(s1[-s_x_step])
00356                      + 0.125  * static_cast<double>(s1[0])
00357                      + 0.0625 * static_cast<double>(s1[s_x_step]),
00358                  ds2 = 0.1250 * static_cast<double>(s2[-s_x_step])
00359                      + 0.250  * static_cast<double>(s2[0])
00360                      + 0.1250 * static_cast<double>(s2[s_x_step]),
00361                  ds3 = 0.0625 * static_cast<double>(s3[-s_x_step])
00362                      + 0.125  * static_cast<double>(s3[0])
00363                      + 0.0625 * static_cast<double>(s3[s_x_step]);
00364           rounder(ds1 + ds2 + ds3, *d);
00365 
00366           d += d_x_step;
00367           s1 += sxs2;
00368           s2 += sxs2;
00369           s3 += sxs2;
00370       }
00371       // Set last elements of row
00372       if (src_ni&1)
00373         *d = *s2;
00374 
00375       d_row += d_y_step;
00376       s_row1 += sys2;
00377       s_row2 += sys2;
00378       s_row3 += sys2;
00379   }
00380 
00381   // Need to set first and last rows as well
00382 
00383   // Dest image should be (src_ni+1)/2 x (src_nj+1)/2
00384   const T* s0 = src_im;
00385   unsigned ni=(src_ni+1)/2;
00386   for (unsigned i=0;i<ni;++i)
00387   {
00388     dest_im[i]= *s0;
00389     s0+=sxs2;
00390   }
00391 
00392   if (src_nj&1)
00393   {
00394     unsigned yhi = (src_nj-1)/2;
00395     T* dest_last_row = dest_im + yhi*d_y_step;
00396     const T* s_last = src_im + yhi*sys2;
00397     for (unsigned i=0;i<ni;++i)
00398     {
00399       dest_last_row[i]= *s_last;
00400       s_last+=sxs2;
00401     }
00402   }
00403 }
00404 
00405 
00406 template <class T>
00407 void vil_gauss_reduce_2_3_1plane(const T* src_im,
00408                                  unsigned src_ni, unsigned src_nj,
00409                                  vcl_ptrdiff_t s_x_step, vcl_ptrdiff_t s_y_step,
00410                                  T* dest_im, vcl_ptrdiff_t d_x_step, vcl_ptrdiff_t d_y_step)
00411 {
00412   T* d_row = dest_im;
00413   const T* s_row = src_im;
00414   vcl_ptrdiff_t sxs2 = s_x_step*2,sxs3 = s_x_step*3;
00415   unsigned d_ni = (2*src_ni+1)/3;
00416   unsigned d_ni2 = d_ni/2;
00417   vil_convert_round_pixel<double,T> rounder;
00418 
00419   for (unsigned y=0;y<src_nj;++y)
00420   {
00421     // Set first elements of row
00422     // The 0.5 offset in the following ensures rounding
00423     double drow0=0.75*static_cast<double>(s_row[0]) + 0.25*static_cast<double>(s_row[s_x_step]);
00424     double drow_xs=0.5*static_cast<double>(s_row[s_x_step]) + 0.5*static_cast<double>(s_row[sxs2]);
00425     rounder(drow0,d_row[0]);
00426     rounder(drow_xs,d_row[d_x_step]);
00427 
00428     T* d = d_row + 2*d_x_step;
00429     const T* s = s_row + sxs3;
00430     for (unsigned x=1;x<d_ni2;++x)
00431     {
00432       double df= 0.2 * ( static_cast<double>(s[-s_x_step]) + static_cast<double>(s[s_x_step]) )
00433                + 0.6 * static_cast<double>(s[0]) ;
00434       rounder(df,*d);
00435 
00436       d += d_x_step;
00437 
00438       df = 0.5*(static_cast<double>(s[s_x_step])  + static_cast<double>(s[sxs2]) );
00439       rounder(df,*d);
00440 
00441       d += d_x_step;
00442       s += sxs3;
00443     }
00444     // Set last elements of row
00445     if (src_ni%3==1)
00446     {
00447       double df=0.75*static_cast<double>(s[-s_x_step]) + 0.25*static_cast<double>(s[0]);
00448       rounder(df,*d);
00449     }
00450     else if (src_ni%3==2)
00451     {
00452       double df = 0.2 * (static_cast<double>(s[-s_x_step]) + static_cast<double>(s[s_x_step]) )
00453                 + 0.6 * static_cast<double>(s[0]);
00454       rounder(df,*d);
00455     }
00456     d_row += d_y_step;
00457     s_row += s_y_step;
00458   }
00459 }
00460 
00461 
00462 #undef VIL_GAUSS_REDUCE_INSTANTIATE
00463 #define VIL_GAUSS_REDUCE_INSTANTIATE(T) \
00464 template void vil_gauss_reduce(const vil_image_view<T >& src, \
00465                                vil_image_view<T >& dest, \
00466                                vil_image_view<T >& work_im); \
00467 template void vil_gauss_reduce_2_3(const vil_image_view<T >& src, \
00468                                    vil_image_view<T >& dest, \
00469                                    vil_image_view<T >& work_im); \
00470 template void vil_gauss_reduce_121(const vil_image_view<T >& src, \
00471                                    vil_image_view<T >& dest); \
00472 template void vil_gauss_reduce_general(const vil_image_view<T >& src_im, \
00473                                        vil_image_view<T >& dest_im, \
00474                                        vil_image_view<T >& worka, \
00475                                        vil_image_view<T >& workb, \
00476                                        const vil_gauss_reduce_params& params)
00477 
00478 #endif // vil_gauss_reduce_txx_