contrib/mul/mfpf/mfpf_dp_snake.cxx
Go to the documentation of this file.
00001 //:
00002 // \file
00003 // \author Tim Cootes
00004 // \brief Basic snake, using dynamic programming to update.
00005 
00006 #include "mfpf_dp_snake.h"
00007 
00008 #include <vcl_cmath.h>
00009 
00010 #include <vsl/vsl_indent.h>
00011 #include <vsl/vsl_binary_loader.h>
00012 #include <vsl/vsl_vector_io.h>
00013 
00014 #include <vgl/vgl_point_2d.h>
00015 #include <vgl/vgl_vector_2d.h>
00016 
00017 #include <mbl/mbl_dyn_prog.h>
00018 #include <mbl/mbl_stats_1d.h>
00019 
00020 //=======================================================================
00021 // Dflt ctor
00022 //=======================================================================
00023 
00024 mfpf_dp_snake::mfpf_dp_snake()
00025   : max_its_(10)
00026 {
00027 }
00028 
00029 
00030 //=======================================================================
00031 // Destructor
00032 //=======================================================================
00033 
00034 mfpf_dp_snake::~mfpf_dp_snake()
00035 {
00036 }
00037 
00038 //: Finder used to search for good points along profiles
00039 mfpf_point_finder& mfpf_dp_snake::finder()
00040 {
00041   return finder_;
00042 }
00043 
00044 //: Initialise as a circle of given radius about the given centre
00045 //  Clone taken of finder object
00046 void mfpf_dp_snake::set_to_circle(const mfpf_point_finder& finder,
00047                                   unsigned n_points,
00048                                   const vgl_point_2d<double>& c,
00049                                   double r)
00050 {
00051   finder_=finder;
00052 
00053   pts_.resize(n_points);
00054   for (unsigned iA=0;iA<n_points;++iA)
00055   {
00056     double A = iA*6.283/n_points;
00057     double dx=vcl_cos(A),dy=vcl_sin(A);
00058     pts_[iA] = vgl_point_2d<double>(c.x()+r*dx,c.y()+r*dy);
00059   }
00060 }
00061 
00062 //: Replace each point with the average of it and its neighbours
00063 void mfpf_dp_snake::smooth_curve(
00064                     vcl_vector<vgl_point_2d<double> >& src_pts,
00065                     vcl_vector<vgl_point_2d<double> >& dest_pts)
00066 {
00067   unsigned n = src_pts.size();
00068   dest_pts.resize(n);
00069   for (unsigned i=0;i<n;++i)
00070     dest_pts[i] = centre(src_pts[(i+n-1)%n],src_pts[i],src_pts[(i+1)%n]);
00071 }
00072 
00073 
00074 //: Perform one iteration of snake search algorithm
00075 //  Return the mean movement of each point
00076 double mfpf_dp_snake::update_step(const vimt_image_2d_of<float>& image)
00077 {
00078   unsigned n = pts_.size();
00079 
00080   unsigned s_ni = finder().search_ni();
00081   unsigned nr = 1+2*s_ni;
00082 
00083   vcl_vector<vgl_vector_2d<double> > u(n); // Space for normals
00084 
00085   // Create reference shape,
00086   // which will be slightly smoothed version of pts_
00087   vcl_vector<vgl_point_2d<double> > ref_pts(n);
00088 
00089   // Space to hold response data
00090   vnl_matrix<double> D(n,nr);
00091 
00092   int dt = 3;  // Displacement index used to compute normals
00093 
00094   vimt_image_2d_of<double> response;
00095 
00096   for (unsigned i=0;i<n;++i)
00097   {
00098     // Tangent vector
00099     vgl_vector_2d<double> t = pts_[(i+n-dt)%n]-pts_[(i+dt)%n];
00100     t/=(t.length()+1e-6);
00101     double dx=-t.y(),dy=t.x();  // Unit normal
00102     u[i]=vgl_vector_2d<double>(dx,dy);
00103 
00104     // Search around the average of the neighbours of pt i
00105     ref_pts[i] = centre(pts_[(i+n-1)%n],pts_[(i+1)%n]);
00106 
00107     finder().evaluate_region(image,ref_pts[i],u[i],response);
00108 
00109     for (unsigned j=0;j<nr;++j)
00110     {
00111       double d= response.image()(j,0);
00112       D(i,j)=d;
00113     }
00114   }
00115 
00116   mbl_dyn_prog dp;
00117   vnl_vector<double> move_cost(nr);
00118   double shape_k=1.0;
00119   for (unsigned int i=0;i<nr;++i)
00120   {
00121     move_cost[i]=shape_k*i;
00122   }
00123 
00124   vcl_vector<int> x;
00125   dp.solve_loop(x,D,move_cost);
00126 
00127   // Compute new image positions
00128   double step_size = finder().step_size();
00129   double move_sum=0.0;
00130 
00131   for (unsigned i=0;i<n;++i)
00132   {
00133     // If x[i]==s_ni, then no movement (central position)
00134     vgl_point_2d<double> new_pt = ref_pts[i] + (x[i]-int(s_ni))*step_size*u[i];
00135 
00136     move_sum += (new_pt-pts_[i]).length();
00137     pts_[i] = new_pt;
00138   }
00139 
00140   return move_sum/n;
00141 }
00142 
00143 //: Search image (running iterations until convergence)
00144 void mfpf_dp_snake::search(const vimt_image_2d_of<float>& image)
00145 {
00146   // Just run fixed number of iterations
00147   for (unsigned i=0;i<max_its_;++i)
00148     update_step(image);
00149 }
00150 
00151 
00152 //: Replace each point with the average of it and its neighbours
00153 void mfpf_dp_snake::smooth_curve()
00154 {
00155   vcl_vector<vgl_point_2d<double> > old_pts = pts_;
00156   smooth_curve(old_pts,pts_);
00157 }
00158 
00159 //: Centre of gravity of points
00160 vgl_point_2d<double> mfpf_dp_snake::cog() const
00161 {
00162   unsigned n = pts_.size();
00163   if (n==0) return vgl_point_2d<double>(0,0);
00164 
00165   double xsum=0,ysum=0;
00166   for (unsigned i=0;i<n;++i)
00167   {
00168     xsum+=pts_[i].x();
00169     ysum+=pts_[i].y();
00170   }
00171   return vgl_point_2d<double>(xsum/n,ysum/n);
00172 }
00173 
00174 //: Mean distance of points to cog()
00175 double mfpf_dp_snake::mean_radius() const
00176 {
00177   unsigned n = pts_.size();
00178   if (n==0) return 0.0;
00179 
00180   vgl_point_2d<double> c = cog();
00181   double r_sum=0.0;
00182   for (unsigned i=0;i<n;++i)
00183     r_sum += (pts_[i]-c).length();
00184   return r_sum/n;
00185 }
00186 
00187 //: Compute mean and sd of distance to cog()
00188 void mfpf_dp_snake::radius_stats(double& mean, double& sd) const
00189 {
00190   mbl_stats_1d stats;
00191   unsigned n = pts_.size();
00192 
00193   vgl_point_2d<double> c = cog();
00194   for (unsigned i=0;i<n;++i)
00195     stats.obs((pts_[i]-c).length());
00196 
00197   mean=stats.mean();
00198   sd=stats.sd();
00199 }
00200 
00201 
00202 //=======================================================================
00203 // Method: version_no
00204 //=======================================================================
00205 
00206 short mfpf_dp_snake::version_no() const
00207 {
00208   return 1;
00209 }
00210 
00211 
00212 //=======================================================================
00213 // Method: is_a
00214 //=======================================================================
00215 
00216 vcl_string mfpf_dp_snake::is_a() const
00217 {
00218   return vcl_string("mfpf_dp_snake");
00219 }
00220 
00221 //: Print class to os
00222 void mfpf_dp_snake::print_summary(vcl_ostream& os) const
00223 {
00224   vsl_indent_inc(os);
00225 
00226   os<<vsl_indent()<<"n_points: "<<pts_.size()
00227     <<vsl_indent()<<"finder: "<<finder_<<'\n';
00228   vgl_point_2d<double> c = cog();
00229   os<<vsl_indent()<<" CoG: ("<<c.x()<<','<<c.y();
00230 
00231   double r_mean,r_sd;
00232   radius_stats(r_mean,r_sd);
00233   os<<") mean radius: "<<r_mean<<" SD: "<<r_sd<<'\n';
00234   vsl_indent_dec(os);
00235 }
00236 
00237 //=======================================================================
00238 // Method: save
00239 //=======================================================================
00240 
00241 void mfpf_dp_snake::b_write(vsl_b_ostream& bfs) const
00242 {
00243   vsl_b_write(bfs,version_no());
00244   vsl_b_write(bfs,max_its_);
00245   vsl_b_write(bfs,finder_);
00246   vsl_b_write(bfs,pts_);
00247 }
00248 
00249 //=======================================================================
00250 // Method: load
00251 //=======================================================================
00252 
00253 void mfpf_dp_snake::b_read(vsl_b_istream& bfs)
00254 {
00255   if (!bfs) return;
00256   short version;
00257   vsl_b_read(bfs,version);
00258   switch (version)
00259   {
00260     case (1):
00261       vsl_b_read(bfs,max_its_);
00262       vsl_b_read(bfs,finder_);
00263       vsl_b_read(bfs,pts_);
00264       break;
00265     default:
00266       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&)\n"
00267                << "           Unknown version number "<< version << vcl_endl;
00268       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00269       return;
00270   }
00271 }
00272 
00273 //=======================================================================
00274 // Associated function: operator<<
00275 //=======================================================================
00276 
00277 vcl_ostream& operator<<(vcl_ostream& os,const mfpf_dp_snake& b)
00278 {
00279   os << b.is_a() << ": ";
00280   vsl_indent_inc(os);
00281   b.print_summary(os);
00282   vsl_indent_dec(os);
00283   return os;
00284 }
00285 
00286 //: Binary file stream output operator for class reference
00287 void vsl_b_write(vsl_b_ostream& bfs, const mfpf_dp_snake& b)
00288 {
00289   b.b_write(bfs);
00290 }
00291 
00292 //: Binary file stream input operator for class reference
00293 void vsl_b_read(vsl_b_istream& bfs, mfpf_dp_snake& b)
00294 {
00295   b.b_read(bfs);
00296 }
00297