contrib/mul/vimt3d/vimt3d_gaussian_pyramid_builder_3d.txx
Go to the documentation of this file.
00001 // This is mul/vimt3d/vimt3d_gaussian_pyramid_builder_3d.txx
00002 #ifndef vimt3d_gaussian_pyramid_builder_3d_txx_
00003 #define vimt3d_gaussian_pyramid_builder_3d_txx_
00004 //:
00005 // \file
00006 // \brief Class to build Gaussian pyramids of vimt3d_image_3d_of<T>
00007 // \author Tim Cootes
00008 
00009 #include "vimt3d_gaussian_pyramid_builder_3d.h"
00010 
00011 #include <vcl_cstdlib.h>
00012 #include <vcl_string.h>
00013 
00014 #include <vgl/vgl_point_3d.h>
00015 #include <vgl/vgl_vector_3d.h>
00016 #include <vimt/vimt_image_pyramid.h>
00017 #include <vil3d/algo/vil3d_gauss_reduce.h>
00018 #include <vcl_cassert.h>
00019 #include <vcl_cmath.h>
00020 
00021 //=======================================================================
00022 
00023 template<class T>
00024 vimt3d_gaussian_pyramid_builder_3d<T>::vimt3d_gaussian_pyramid_builder_3d()
00025 : max_levels_(99),uniform_reduction_(false),filter_width_(5)
00026 {
00027   set_min_size(5, 5, 5);
00028 }
00029 
00030 //=======================================================================
00031 
00032 template<class T>
00033 vimt3d_gaussian_pyramid_builder_3d<T>::~vimt3d_gaussian_pyramid_builder_3d()
00034 {
00035 }
00036 
00037 //=======================================================================
00038 //: Define maximum number of levels to build
00039 //  Limits levels built in subsequent calls to build()
00040 template<class T>
00041 void vimt3d_gaussian_pyramid_builder_3d<T>::set_max_levels(int max_l)
00042 {
00043   if (max_l<1)
00044   {
00045     vcl_cerr<<"vimt3d_gaussian_pyramid_builder_3d<T>::setMaxLevels() param is "
00046             <<max_l<<", must be >=1\n";
00047     vcl_abort();
00048   }
00049   max_levels_ = max_l;
00050 }
00051 
00052 //: Get the current maximum number levels allowed
00053 template<class T>
00054 int vimt3d_gaussian_pyramid_builder_3d<T>::max_levels() const
00055 {
00056   return max_levels_;
00057 }
00058 
00059 //: Select number of levels to use
00060 template<class T>
00061 int vimt3d_gaussian_pyramid_builder_3d<T>::n_levels(const vimt3d_image_3d_of<T>& base_image) const
00062 {
00063   int ni = base_image.image().ni();
00064   int nj = base_image.image().nj();
00065   int nk = base_image.image().nk();
00066   double dx,dy,dz;
00067   get_pixel_size(dx,dy,dz,base_image);
00068   // Compute number of levels to pyramid so that top is no less
00069   // than min_x_size_ x min_y_size_ x min_z_size_
00070   int max_levels = 0;
00071   while ((ni>=int(min_x_size_)) && (nj>=int(min_y_size_)) && (nk>=int(min_z_size_)))
00072   {
00073     if (uniform_reduction_)
00074     {
00075       ni = (ni+1)/2; dx*=2;
00076       nj = (nj+1)/2; dy*=2;
00077       nk = (nk+1)/2; dz*=2;
00078     }
00079     else if (dz*dz/(dx*dx)>2.0)
00080     {
00081       // Pixels large in z, so don't smooth them
00082       ni = (ni+1)/2; dx*=2;
00083       nj = (nj+1)/2; dy*=2;
00084     }
00085     else if (dy*dy/(dx*dx)>2.0)
00086     {
00087       // Pixels large in y, so don't smooth them
00088       ni = (ni+1)/2; dx*=2;
00089       nk = (nk+1)/2; dz*=2;
00090     }
00091     else if (dx*dx/(dy*dy)>2.0)
00092     {
00093       // Pixels large in x, so don't smooth them
00094       nj = (nj+1)/2; dy*=2;
00095       nk = (nk+1)/2; dz*=2;
00096     }
00097     else
00098     {
00099       ni = (ni+1)/2; dx*=2;
00100       nj = (nj+1)/2; dy*=2;
00101       nk = (nk+1)/2; dz*=2;
00102     }
00103     max_levels++;
00104   }
00105   if (max_levels<1) max_levels = 1;
00106   if (max_levels>max_levels_)
00107     max_levels=max_levels_;
00108 
00109   return max_levels;
00110 }
00111 
00112 //=======================================================================
00113 //: Create new (empty) pyramid on heap
00114 //  Caller responsible for its deletion
00115 template<class T>
00116 vimt_image_pyramid* vimt3d_gaussian_pyramid_builder_3d<T>::new_image_pyramid() const
00117 {
00118   return new vimt_image_pyramid;
00119 }
00120 
00121 
00122 //=======================================================================
00123 //: Scale step between levels
00124 template<class T>
00125 double vimt3d_gaussian_pyramid_builder_3d<T>::scale_step() const
00126 {
00127   return 2.0;
00128 }
00129 
00130 //: Set current filter width (must be 3 or 5 at present)
00131 template<class T>
00132 void vimt3d_gaussian_pyramid_builder_3d<T>::set_filter_width(unsigned w)
00133 {
00134   assert(w==5);
00135   filter_width_ = w;
00136 }
00137 
00138 //=======================================================================
00139 //: Smooth and subsample src_im to produce dest_im
00140 //  Applies 1-5-8-5-1 filter in x and y, then samples
00141 //  every other pixel.
00142 template<class T>
00143 void vimt3d_gaussian_pyramid_builder_3d<T>::gauss_reduce(vimt3d_image_3d_of<T>& dest_im,
00144                                                          vimt3d_image_3d_of<T>const& src_im) const
00145 {
00146   // Assume filter width is 5 for the moment.
00147   if (filter_width_!=5)
00148   {
00149     vcl_cerr<<"vimt3d_gaussian_pyramid_builder_3d<T>::gauss_reduce()\n"
00150             <<" Cannot cope with filter width of "<<filter_width_<<'\n';
00151     vcl_abort();
00152   }
00153 
00154   double dx,dy,dz;
00155   get_pixel_size(dx,dy,dz,src_im);
00156 
00157   vimt3d_transform_3d scaling;
00158 
00159   if (uniform_reduction_)
00160   {
00161     vil3d_gauss_reduce(src_im.image(),dest_im.image(),work_im1_,work_im2_);
00162     scaling.set_zoom_only(0.5,0.5,0.5,0,0,0);
00163     dest_im.set_world2im(scaling * src_im.world2im());
00164 
00165     return;
00166   }
00167 
00168   // If dz is much larger than dx, then don't subsample in that direction
00169   if (dz*dz/(dx*dx)>2.0)
00170   {
00171     vil3d_gauss_reduce_ij(src_im.image(),dest_im.image(),work_im1_);
00172     scaling.set_zoom_only(0.5,0.5,1.0,0,0,0);
00173     dest_im.set_world2im(scaling * src_im.world2im());
00174   }
00175   else if (dy*dy/(dx*dx)>2.0)
00176   {
00177     vil3d_gauss_reduce_ik(src_im.image(),dest_im.image(),work_im1_);
00178     scaling.set_zoom_only(0.5,1.0,0.5,0,0,0);
00179     dest_im.set_world2im(scaling * src_im.world2im());
00180   }
00181   else if (dx*dx/(dy*dy)>2.0)
00182   {
00183     vil3d_gauss_reduce_jk(src_im.image(),dest_im.image(),work_im1_);
00184     scaling.set_zoom_only(1.0,0.5,0.5,0,0,0);
00185     dest_im.set_world2im(scaling * src_im.world2im());
00186   }
00187   else
00188   {
00189     vil3d_gauss_reduce(src_im.image(),dest_im.image(),work_im1_,work_im2_);
00190 
00191     scaling.set_zoom_only(0.5,0.5,0.5,0,0,0);
00192     dest_im.set_world2im(scaling * src_im.world2im());
00193   }
00194 }
00195 
00196 //=======================================================================
00197 //: Deletes all data in im_pyr
00198 template<class T>
00199 void vimt3d_gaussian_pyramid_builder_3d<T>::emptyPyr(vimt_image_pyramid& im_pyr) const
00200 {
00201   for (int i=0; i<im_pyr.n_levels();++i)
00202     delete im_pyr.data()[i];
00203 }
00204 
00205 //=======================================================================
00206 //: Checks pyramid has at least n levels
00207 template<class T>
00208 void vimt3d_gaussian_pyramid_builder_3d<T>::checkPyr(vimt_image_pyramid& im_pyr,  int n_levels) const
00209 {
00210   const int got_levels = im_pyr.n_levels();
00211   if (got_levels >= n_levels && im_pyr(0).is_a()==work_im1_.is_a())
00212   {
00213     if (im_pyr.n_levels()==n_levels) return;
00214     else
00215     {
00216       for (int i=n_levels;i<got_levels;++i)
00217         delete im_pyr.data()[i];
00218     }
00219     im_pyr.data().resize(n_levels);
00220     return;
00221   }
00222 
00223   im_pyr.resize(n_levels,vimt3d_image_3d_of<T>());
00224 }
00225 
00226 //: Set the minimum size of the top layer of the pyramid
00227 template<class T>
00228 void vimt3d_gaussian_pyramid_builder_3d<T>::set_min_size(unsigned X, unsigned Y, unsigned Z)
00229 { min_x_size_ = X; min_y_size_ = Y; min_z_size_ = Z; }
00230 
00231 //=======================================================================
00232 //: Build pyramid
00233 template<class T>
00234 void vimt3d_gaussian_pyramid_builder_3d<T>::build(vimt_image_pyramid& image_pyr,
00235                                                   vimt_image const& im) const
00236 {
00237   // Check that the image is a 3d image
00238   assert(im.is_class("vimt3d_image_3d"));
00239 
00240   // Cast to the appropriate class
00241   const vimt3d_image_3d &im3d = static_cast<const vimt3d_image_3d &>(im);
00242 
00243   //  Require image vimt3d_image_3d_of<T>
00244   assert(im3d.image_base().is_a()==work_im1_.is_a());
00245 
00246   const vimt3d_image_3d_of<T>& base_image = static_cast<const vimt3d_image_3d_of<T>&>(im3d);
00247 
00248   int ni = base_image.image().ni();
00249   int nj = base_image.image().nj();
00250   int nk = base_image.image().nk();
00251 
00252   int max_levels=n_levels(base_image);
00253 
00254   // Set up image pyramid
00255   checkPyr(image_pyr,max_levels);
00256 
00257   vimt3d_image_3d_of<T>& im0 = static_cast<vimt3d_image_3d_of<T>&>(image_pyr(0));
00258 
00259   // Shallow copy of part of base_image
00260   im0=base_image;
00261 
00262   int i;
00263   for (i=1;i<max_levels;i++)
00264   {
00265     vimt3d_image_3d_of<T>& im_i0 = static_cast<vimt3d_image_3d_of<T>&>(image_pyr(i));
00266     vimt3d_image_3d_of<T>& im_i1 = static_cast<vimt3d_image_3d_of<T>&>(image_pyr(i-1));
00267 
00268     gauss_reduce(im_i0,im_i1);
00269   }
00270 
00271   // Estimate width of pixels in base image
00272   vgl_point_3d<double>  c0(0.5*(ni-1),0.5*(nj-1),0.5*(nk-1));
00273   vgl_point_3d<double>  c1 = c0 + vgl_vector_3d<double> (1,1,1);
00274   vimt3d_transform_3d im2world = base_image.world2im().inverse();
00275   vgl_vector_3d<double>  dw = im2world(c1) - im2world(c0);
00276 
00277   double base_pixel_width = dw.length()/vcl_sqrt(3.0);
00278   double scale_step = 2.0;
00279 
00280   image_pyr.set_widths(base_pixel_width,scale_step);
00281 }
00282 
00283 //: Compute real world size of pixel
00284 template<class T>
00285 void vimt3d_gaussian_pyramid_builder_3d<T>::get_pixel_size(double &dx, double& dy, double& dz,
00286                                                            const vimt3d_image_3d_of<T>& image) const
00287 {
00288   // Estimate width of pixels in base image
00289   vgl_point_3d<double>  c0(0,0,0);
00290   vgl_point_3d<double>  cx(1,0,0);
00291   vgl_point_3d<double>  cy(0,1,0);
00292   vgl_point_3d<double>  cz(0,0,1);
00293   vimt3d_transform_3d im2world = image.world2im().inverse();
00294   dx = (im2world(cx) - im2world(c0)).length();
00295   dy = (im2world(cy) - im2world(c0)).length();
00296   dz = (im2world(cz) - im2world(c0)).length();
00297 }
00298 
00299 //=======================================================================
00300 //: Extend pyramid
00301 template<class T>
00302 void vimt3d_gaussian_pyramid_builder_3d<T>::extend(vimt_image_pyramid& image_pyr) const
00303 {
00304   //  Require image vimt3d_image_3d_of<T>
00305   assert(image_pyr(0).is_a() == work_im1_.is_a());
00306 
00307   assert(image_pyr.scale_step() == scale_step());
00308 
00309   vimt3d_image_3d_of<T>& im_base = static_cast<vimt3d_image_3d_of<T>&>(image_pyr(0));
00310   int ni = im_base.image().ni();
00311   int nj = im_base.image().nj();
00312   int nk = im_base.image().nk();
00313 
00314   int max_levels=n_levels(static_cast<const vimt3d_image_3d_of<T>&>(image_pyr(0)));
00315 
00316   work_im1_.set_size(ni,nj,nk);
00317   work_im2_.set_size(ni,nj,nk);
00318 
00319   // Set up image pyramid
00320   int oldsize = image_pyr.n_levels();
00321   if (oldsize<max_levels) // only extend, if it isn't already tall enough
00322   {
00323     image_pyr.data().resize(max_levels);
00324 
00325     int i;
00326     for (i=oldsize;i<max_levels;++i)
00327       image_pyr.data()[i] = new vimt3d_image_3d_of<T>;
00328 
00329     for (i=oldsize;i<max_levels;i++)
00330     {
00331       vimt3d_image_3d_of<T>& im_i0 = static_cast<vimt3d_image_3d_of<T>&>(image_pyr(i));
00332       vimt3d_image_3d_of<T>& im_i1 = static_cast<vimt3d_image_3d_of<T>&>(image_pyr(i-1));
00333 
00334       gauss_reduce(im_i0,im_i1);
00335     }
00336   }
00337 }
00338 
00339 //=======================================================================
00340 
00341 template<class T>
00342 bool vimt3d_gaussian_pyramid_builder_3d<T>::is_class(vcl_string const& s) const
00343 {
00344   return s==vimt3d_gaussian_pyramid_builder_3d<T>::is_a() || vimt_image_pyramid_builder::is_class(s);
00345 }
00346 
00347 //=======================================================================
00348 
00349 template<class T>
00350 short vimt3d_gaussian_pyramid_builder_3d<T>::version_no() const
00351 {
00352   return 1;
00353 }
00354 
00355 //=======================================================================
00356 
00357 template<class T>
00358 vimt_image_pyramid_builder* vimt3d_gaussian_pyramid_builder_3d<T>::clone() const
00359 {
00360   return new vimt3d_gaussian_pyramid_builder_3d<T>(*this);
00361 }
00362 
00363 //=======================================================================
00364 
00365 template<class T>
00366 void vimt3d_gaussian_pyramid_builder_3d<T>::print_summary(vcl_ostream&) const
00367 {
00368 }
00369 
00370 //=======================================================================
00371 
00372 template<class T>
00373 void vimt3d_gaussian_pyramid_builder_3d<T>::b_write(vsl_b_ostream& bfs) const
00374 {
00375   vsl_b_write(bfs,version_no());
00376   vsl_b_write(bfs,max_levels_);
00377   vsl_b_write(bfs,uniform_reduction_);
00378   vsl_b_write(bfs,filter_width_);
00379 }
00380 
00381 //=======================================================================
00382 
00383 template<class T>
00384 void vimt3d_gaussian_pyramid_builder_3d<T>::b_read(vsl_b_istream& bfs)
00385 {
00386   if (!bfs) return;
00387 
00388   short version;
00389   vsl_b_read(bfs,version);
00390   switch (version)
00391   {
00392   case (1):
00393     vsl_b_read(bfs,max_levels_);
00394     vsl_b_read(bfs,uniform_reduction_);
00395     vsl_b_read(bfs,filter_width_);
00396     break;
00397   default:
00398     vcl_cerr << "I/O ERROR: vimt3d_gaussian_pyramid_builder_3d<T>::b_read(vsl_b_istream&)\n"
00399              << "           Unknown version number "<< version << '\n';
00400     bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00401     return;
00402   }
00403 }
00404 
00405 #undef VIMT3D_GAUSSIAN_PYRAMID_BUILDER_3D_INSTANTIATE
00406 #define VIMT3D_GAUSSIAN_PYRAMID_BUILDER_3D_INSTANTIATE(T) \
00407 VCL_DEFINE_SPECIALIZATION vcl_string vimt3d_gaussian_pyramid_builder_3d<T >::is_a() const \
00408 { return vcl_string("vimt3d_gaussian_pyramid_builder_3d<" #T ">"); } \
00409 template class vimt3d_gaussian_pyramid_builder_3d<T >
00410 
00411 #endif // vimt3d_gaussian_pyramid_builder_3d_txx_