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