contrib/mul/vimt/vimt_scale_pyramid_builder_2d.txx
Go to the documentation of this file.
00001 // This is mul/vimt/vimt_scale_pyramid_builder_2d.txx
00002 #ifndef vimt_scale_pyramid_builder_2d_txx_
00003 #define vimt_scale_pyramid_builder_2d_txx_
00004 //:
00005 // \file
00006 // \brief Build Gaussian image pyramids at any scale separation
00007 // \author Ian Scott
00008 
00009 #include "vimt_scale_pyramid_builder_2d.h"
00010 #include <vcl_cstdlib.h>
00011 #include <vcl_cassert.h>
00012 #include <vcl_string.h>
00013 #include <vcl_iostream.h>
00014 #include <vil/vil_bilin_interp.h>
00015 #include <vgl/vgl_point_2d.h>
00016 #include <vgl/vgl_vector_2d.h>
00017 #include <vimt/vimt_image_2d_of.h>
00018 #include <vimt/vimt_image_pyramid.h>
00019 #include <vsl/vsl_indent.h>
00020 #include <vcl_cmath.h>
00021 #include <vimt/vimt_crop.h>
00022 
00023 //=======================================================================
00024 
00025 template <class T>
00026 vimt_scale_pyramid_builder_2d<T>::vimt_scale_pyramid_builder_2d()
00027 : max_levels_(99)
00028 {
00029   set_scale_step(2.0);
00030   set_min_size(5, 5);
00031 }
00032 
00033 //: Construct with given scale_step
00034 template <class T>
00035 vimt_scale_pyramid_builder_2d<T>::vimt_scale_pyramid_builder_2d(double scale_step)
00036 {
00037   set_scale_step(scale_step);
00038 }
00039 
00040 //=======================================================================
00041 
00042 template <class T>
00043 vimt_scale_pyramid_builder_2d<T>::~vimt_scale_pyramid_builder_2d()
00044 {
00045 }
00046 
00047 template <class T>
00048 void vimt_scale_pyramid_builder_2d<T>::scale_reduce(
00049            vimt_image_2d_of<T>& dest_im,
00050            const vimt_image_2d_of<T>& src_im) const
00051 {
00052   int src_ni = src_im.image().ni();
00053   int src_nj = src_im.image().nj();
00054   int dest_ni = dest_im.image().ni();
00055   int dest_nj = dest_im.image().nj();
00056   vcl_ptrdiff_t istep = src_im.image().istep();
00057   vcl_ptrdiff_t jstep = src_im.image().jstep();
00058   int n_planes = src_im.image().nplanes();
00059 
00060   // Reduce plane-by-plane
00061 
00062   // use n-1 because we are trying to align inter-pixel spaces, so that the
00063   // centre pixel is most accurately registered despite buildup of rounding errors.
00064   double init_x = 0.5 * (src_ni - 1 - (dest_ni-1)*scale_step());
00065   double init_y = 0.5 * (src_nj - 1 - (dest_nj-1)*scale_step());
00066 
00067 
00068   for (int i=0;i<n_planes;++i)
00069     scale_reduce(&dest_im.image()(0,0,i),dest_im.image().jstep(),
00070                  &src_im.image()(0,0,i),src_ni,src_nj,dest_ni,dest_nj,istep,jstep);
00071 #if 0
00072   vsl_indent_inc(vcl_cout);
00073   vcl_cout << vsl_indent() << "Work image B\n";
00074   workb_.print_all(vcl_cout);
00075   vsl_indent_dec(vcl_cout);
00076 #endif
00077 
00078     // Sort out world to image transformation for destination image
00079   vimt_transform_2d scaling;
00080   scaling.set_zoom_only(1/scale_step(),-init_x,-init_y);
00081   dest_im.set_world2im(scaling * src_im.world2im());
00082 }
00083 
00084 
00085 //: An optimisable rounding function
00086 inline unsigned char l_round(double x, unsigned char )
00087 {  return (unsigned char) (x+0.5);}
00088 
00089 inline signed char l_round(double x, signed char )
00090 {  return (signed char) (x+0.5);}
00091 
00092 inline unsigned short l_round(double x, unsigned short )
00093 {  return (unsigned short) (x+0.5);}
00094 
00095 inline signed short l_round(double x, signed short )
00096 {  return (signed short) (x+0.5);}
00097 
00098 inline unsigned int l_round(double x, unsigned int )
00099 {  return (unsigned int) (x+0.5);}
00100 
00101 inline signed int l_round(double x, signed int )
00102 {  return (signed int) (x+0.5);}
00103 
00104 inline unsigned long l_round(double x, unsigned long )
00105 {  return (unsigned long) (x+0.5);}
00106 
00107 inline signed long l_round(double x, signed long )
00108 {  return (signed long) (x+0.5);}
00109 
00110 inline double l_round (double x, double )
00111 {  return x; }
00112 
00113 inline float l_round (double x, float )
00114 {  return (float) x; }
00115 
00116 //=======================================================================
00117 //: Smooth and subsample src_im to produce dest_im
00118 //  Applies 5 pin filter in x and y, then samples
00119 //  every other pixel.
00120 //  Assumes dest_im has sufficient data allocated
00121 
00122 template <class T>
00123 void vimt_scale_pyramid_builder_2d<T>::scale_reduce(
00124            T* dest_im, vcl_ptrdiff_t dest_jstep,
00125            const T* src_im,
00126            int src_ni, int src_nj, int dest_ni, int dest_nj,
00127            vcl_ptrdiff_t src_istep, vcl_ptrdiff_t src_jstep) const
00128 {
00129   T* dest_row = dest_im;
00130 
00131   const double init_x = 0.5 * (src_ni-1 - (dest_ni-1)*scale_step());
00132   double y = 0.5 * (src_nj -1 - (dest_nj-1)*scale_step());
00133   for (int yi=0; yi<dest_nj; yi++)
00134   {
00135     double x=init_x;
00136     for (int xi=0; xi<dest_ni; xi++)
00137     {
00138       dest_row[xi] = l_round (vil_bilin_interp_safe_extend(x, y,
00139                               src_im, src_ni, src_nj, src_istep, src_jstep), (T)0);
00140       x += scale_step_;
00141     }
00142     y+= scale_step_;
00143     dest_row += dest_jstep;
00144   }
00145 }
00146 
00147 
00148 //: Set the Scale step
00149 template <class T>
00150 void vimt_scale_pyramid_builder_2d<T>::set_scale_step(double scaleStep)
00151 {
00152   assert(scaleStep> 1.0  && scaleStep<=2.0);
00153   scale_step_ = scaleStep;
00154 }
00155 
00156 
00157 //=======================================================================
00158 //: Deletes all data in im_pyr
00159 template<class T>
00160 void vimt_scale_pyramid_builder_2d<T>::emptyPyr(vimt_image_pyramid& im_pyr) const
00161 {
00162   for (int i=0; i<im_pyr.n_levels();++i)
00163     delete im_pyr.data()[i];
00164 }
00165 
00166 //=======================================================================
00167 //: Checks pyramid has at least n levels
00168 template<class T>
00169 void vimt_scale_pyramid_builder_2d<T>::checkPyr(vimt_image_pyramid& im_pyr,  int n_levels) const
00170 {
00171   const int got_levels = im_pyr.n_levels();
00172   if (got_levels >= n_levels) //&& im_pyr(0).is_a()==work_im_.is_a())
00173   {
00174     if (im_pyr.n_levels()==n_levels) return;
00175     else
00176     {
00177       for (int i=n_levels;i<got_levels;++i)
00178         delete im_pyr.data()[i];
00179     }
00180     im_pyr.data().resize(n_levels);
00181     return;
00182   }
00183 
00184   im_pyr.data().resize(n_levels);
00185   emptyPyr(im_pyr);
00186 
00187   for (int i=0;i<n_levels;++i)
00188     im_pyr.data()[i] = new vimt_image_2d_of<T>;
00189 }
00190 
00191 //=======================================================================
00192 
00193 template <class T>
00194 void vimt_scale_pyramid_builder_2d<T>::build(
00195                   vimt_image_pyramid& im_pyr,
00196                   const vimt_image& im) const
00197 {
00198   const vimt_image_2d_of<T>& base_image = static_cast<const vimt_image_2d_of<T>&>( im);
00199 
00200   int ni = base_image.image().ni();
00201   int nj = base_image.image().nj();
00202 
00203   // Compute number of levels to pyramid so that top is no less
00204   // than miniSize_ x minjSize_
00205   double s = scale_step();
00206   int max_lev = 1;
00207   while ( ((unsigned int)(ni/s+0.5)>=min_x_size_)
00208           &&
00209           ((unsigned int)(nj/s+0.5)>=min_y_size_)
00210         )
00211   {
00212     max_lev++;
00213     s *= scale_step();
00214   }
00215 
00216   if (max_lev>max_levels())
00217     max_lev=max_levels();
00218 
00219 
00220   // Set up image pyramid
00221   checkPyr(im_pyr,max_lev);
00222 
00223   vimt_image_2d_of<T>& im0 = static_cast<vimt_image_2d_of<T>&>(im_pyr(0));
00224 
00225   // Shallow copy of part of base_image
00226   im0 = vimt_crop(base_image,0,ni,0,nj);
00227 
00228   s = scale_step();
00229   for (int i=1;i<max_lev;i++)
00230   {
00231     vimt_image_2d_of<T>& im_i0 = static_cast<vimt_image_2d_of<T>&>(im_pyr(i));
00232     vimt_image_2d_of<T>& im_i1 = static_cast<vimt_image_2d_of<T>&>(im_pyr(i-1));
00233     im_i0.image().set_size((int) (ni/s+0.5), (int) (nj/s+0.5),im_i1.image().nplanes());
00234 
00235     s*=scale_step();
00236     scale_reduce(im_i0,im_i1);
00237   }
00238 
00239   // Estimate width of pixels in base image
00240   vgl_point_2d<double>  c0(0.5*(ni-1),0.5*(nj-1));
00241   vgl_point_2d<double>  c1 = c0 + vgl_vector_2d<double> (1,1);
00242   vimt_transform_2d im2world = base_image.world2im().inverse();
00243   vgl_vector_2d<double>  dw = im2world(c1) - im2world(c0);
00244 
00245   double base_pixel_width = vcl_sqrt(0.5*(dw.x()*dw.x() + dw.y()*dw.y()));
00246 
00247   im_pyr.set_widths(base_pixel_width,scale_step());
00248 }
00249 
00250 
00251 //=======================================================================
00252 //: Extend pyramid
00253 // The first layer of the pyramid must already be set.
00254 template<class T>
00255 void vimt_scale_pyramid_builder_2d<T>::extend(vimt_image_pyramid& image_pyr) const
00256 {
00257   assert(image_pyr.scale_step() == scale_step());
00258 
00259   const int ni = static_cast<const vimt_image_2d&>(image_pyr(0)).image_base().ni();
00260   const int nj = static_cast<const vimt_image_2d&>(image_pyr(0)).image_base().nj();
00261 
00262   // Compute number of levels to pyramid so that top is no less
00263   // than 5 x 5
00264   double s = scale_step();
00265   int max_lev = 1;
00266   while (((unsigned int)(ni/s+0.5)>=min_x_size_) &&
00267          ((unsigned int)(nj/s+0.5)>=min_y_size_))
00268   {
00269      max_lev++;
00270      s*=scale_step();
00271   }
00272 
00273   if (max_lev>max_levels())
00274       max_lev=max_levels();
00275 
00276   // Set up image pyramid
00277   int oldsize = image_pyr.n_levels();
00278   if (oldsize<max_lev) // only extend, if it isn't already tall enough
00279   {
00280     image_pyr.data().resize(max_lev);
00281 
00282     s = vcl_pow(scale_step(), oldsize);
00283     for (int i=oldsize;i<max_lev;i++)
00284     {
00285       image_pyr.data()[i] = new vimt_image_2d_of<T>;
00286       vimt_image_2d_of<T>& im_i0 = static_cast<vimt_image_2d_of<T>&>(image_pyr(i));
00287       vimt_image_2d_of<T>& im_i1 = static_cast<vimt_image_2d_of<T>&>(image_pyr(i-1));
00288       im_i0.image().set_size((int)(ni/s+0.5),(int)(nj/s+0.5),im_i1.image().nplanes());
00289 
00290       s*=scale_step();
00291       scale_reduce(im_i0,im_i1);
00292     }
00293   }
00294 }
00295 
00296 //=======================================================================
00297 //: Define maximum number of levels to build
00298 //  Limits levels built in subsequent calls to build()
00299 template<class T>
00300 void vimt_scale_pyramid_builder_2d<T>::set_max_levels(int max_l)
00301 {
00302   if (max_l<1)
00303   {
00304     vcl_cerr << "vimt_gaussian_pyramid_builder_2d<T>::set_max_levels() "
00305              << "Must be >=1, not " << max_l << '\n';
00306     vcl_abort();
00307   }
00308   max_levels_ = max_l;
00309 }
00310 
00311 //: Get the current maximum number levels allowed
00312 template<class T>
00313 int vimt_scale_pyramid_builder_2d<T>::max_levels() const
00314 {
00315   return max_levels_;
00316 }
00317 
00318 
00319 //=======================================================================
00320 //: Create new (empty) pyramid on heap
00321 //  Caller responsible for its deletion
00322 template<class T>
00323 vimt_image_pyramid* vimt_scale_pyramid_builder_2d<T>::new_image_pyramid() const
00324 {
00325   return new vimt_image_pyramid;
00326 }
00327 
00328 
00329 //=======================================================================
00330 #if 0
00331 template <class T>
00332 vcl_string vimt_scale_pyramid_builder_2d<T>::is_a() const
00333 {
00334   return vcl_string("vimt_scale_pyramid_builder_2d<T>");
00335 }
00336 #endif
00337 
00338 //=======================================================================
00339 
00340 template <class T>
00341 bool vimt_scale_pyramid_builder_2d<T>::is_class(vcl_string const& s) const
00342 {
00343   return s==vimt_scale_pyramid_builder_2d<T>::is_a() || vimt_scale_pyramid_builder_2d<T>::is_class(s);
00344 }
00345 
00346 //=======================================================================
00347 
00348 template <class T>
00349 short vimt_scale_pyramid_builder_2d<T>::version_no() const
00350 {
00351   return 1;
00352 }
00353 
00354 //=======================================================================
00355 
00356 template <class T>
00357 vimt_image_pyramid_builder* vimt_scale_pyramid_builder_2d<T>::clone() const
00358 {
00359   return new vimt_scale_pyramid_builder_2d(*this);
00360 }
00361 
00362 //=======================================================================
00363 
00364 template <class T>
00365 void vimt_scale_pyramid_builder_2d<T>::print_summary(vcl_ostream& /*os*/) const
00366 {
00367   vcl_cerr << "vimt_scale_pyramid_builder_2d<T>::print_summary() NYI\n";
00368 }
00369 
00370 //=======================================================================
00371 
00372 template <class T>
00373 void vimt_scale_pyramid_builder_2d<T>::b_write(vsl_b_ostream& bfs) const
00374 {
00375   vsl_b_write(bfs,version_no());
00376   vsl_b_write(bfs,scale_step());
00377 }
00378 
00379 //=======================================================================
00380 
00381 template <class T>
00382 void vimt_scale_pyramid_builder_2d<T>::b_read(vsl_b_istream& bfs)
00383 {
00384   if (!bfs) return;
00385 
00386   short version;
00387   vsl_b_read(bfs,version);
00388   double scale;
00389 
00390   switch (version)
00391   {
00392   case (1):
00393     vsl_b_read(bfs,scale);
00394     set_scale_step(scale);
00395     break;
00396   default:
00397     vcl_cerr << "I/O ERROR: vimt_scale_pyramid_builder_2d<T>::b_read(vsl_b_istream&)\n"
00398              << "           Unknown version number "<< version << '\n';
00399     bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00400     return;
00401   }
00402 }
00403 
00404 #define VIMT_SCALE_PYRAMID_BUILDER_2D_INSTANTIATE(T) \
00405 VCL_DEFINE_SPECIALIZATION vcl_string vimt_scale_pyramid_builder_2d<T >::is_a() const \
00406 {  return vcl_string("vimt_scale_pyramid_builder_2d<" #T ">"); }\
00407 template class vimt_scale_pyramid_builder_2d<T >
00408 
00409 #endif // vimt_scale_pyramid_builder_2d_txx_