contrib/brl/bseg/sdet/sdet_grid_finder.cxx
Go to the documentation of this file.
00001 // This is brl/bseg/sdet/sdet_grid_finder.cxx
00002 #include "sdet_grid_finder.h"
00003 //:
00004 // \file
00005 //
00006 // \verbatim
00007 //  Modifications
00008 //  Bing Yu 1/18/2008
00009 //    - removed unnecessary check on line length in compute_affine_homography()
00010 //    - bug fixed: n_lines_x_/n_lines_y were switched when computing
00011 //      grid offset in compute_homography_linear_chamfer(),
00012 //      writing grid points in init_output_file(), and write_image_points()
00013 //    - bug fixed in iterator over "squares" in a few places in
00014 //      get_square_pixel_stats()
00015 // \endverbatim
00016 
00017 #include <vcl_fstream.h>
00018 #include <vcl_algorithm.h> // for vcl_sort() and vcl_find()
00019 #include <vnl/vnl_matrix_fixed.h>
00020 #include <vnl/vnl_numeric_traits.h>
00021 #include <vnl/vnl_math.h>
00022 #include <vnl/vnl_det.h>
00023 #include <vbl/vbl_bounding_box.h>
00024 #include <vgl/vgl_homg_line_2d.h>
00025 #include <vgl/vgl_homg_point_2d.h>
00026 #include <vgl/vgl_point_2d.h>
00027 #include <vgl/vgl_vector_2d.h>
00028 #include <vgl/algo/vgl_homg_operators_2d.h>
00029 #include <vgl/algo/vgl_h_matrix_2d_compute_4point.h>
00030 #include <vgl/algo/vgl_h_matrix_2d_compute_linear.h>
00031 #include <vsol/vsol_point_2d.h>
00032 #include <vsol/vsol_line_2d.h>
00033 #include <bsol/bsol_hough_line_index.h>
00034 #include <bsol/bsol_algs.h>
00035 #include <bsol/bsol_distance_histogram.h>
00036 #include <vnl/algo/vnl_convolve.h>
00037 #include <gevd/gevd_bufferxy.h>
00038 static const double collection_grid_radius = 15;
00039 static const double grid_radius = 15;
00040 
00041 static void print_lines(vcl_vector<vsol_line_2d_sptr>& lines)
00042 {
00043   for (vcl_vector<vsol_line_2d_sptr>::iterator lit = lines.begin(); lit != lines.end();
00044        lit++)
00045   {
00046     vgl_homg_line_2d<double> l = (*lit)->vgl_hline_2d();
00047     l.normalize();
00048     vcl_cout << l << '\n';
00049   }
00050 }
00051 
00052 
00053 // Gives a sort on signed distance of the
00054 // line from the origin along the line normal.
00055 // note that for line, hl, distance = -hl.c()
00056 static bool line_distance(const vsol_line_2d_sptr & l1,
00057                           const vsol_line_2d_sptr & l2)
00058 {
00059   vgl_homg_line_2d<double> hl1 = l1->vgl_hline_2d();
00060   vgl_homg_line_2d<double> hl2 = l2->vgl_hline_2d();
00061   hl1.normalize(); hl2.normalize();
00062   return hl1.c() > hl2.c();
00063 }
00064 
00065 #if 0
00066 line_chamfer_1d::line_chamfer_1d()
00067 {
00068   size_=0;
00069   dmin_=0;
00070   dmax_=0;
00071 }
00072 
00073 line_chamfer_1d::~line_chamfer_1d()
00074 {
00075   for (int i = 0; i<size_; i++)
00076     delete line_index_[i];
00077 }
00078 
00079 bool line_chamfer_1d::insert_lines(vcl_vector<vsol_line_2d_sptr> const& lines,
00080                                    bool horizontal_lines)
00081 {
00082   if (!lines.size())
00083     return false;
00084   dmin_ = vnl_numeric_traits<double>::maxval;
00085   dmax_ = -dmin_;
00086   distances_.clear();
00087 
00088   for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = lines.begin();
00089        lit != lines.end(); lit++)
00090   {
00091     vgl_homg_line_2d<double> l = (*lit)->vgl_hline_2d();
00092     l.normalize();
00093     double d = -l.c(); // perpendicular distance
00094     distances_.push_back(d);
00095     dmin_ = vnl_math_min(d,dmin_);
00096     dmax_ = vnl_math_max(d,dmax_);
00097   }
00098   if (!(dmax_>=dmin_))
00099     return false;
00100   size_=(int)(dmax_-dmin_ +1);
00101   index_.resize(size_, vnl_numeric_traits<int>::maxval);
00102 
00103   line_index_.resize(size_);
00104   for (int i = 0; i<size_; i++)
00105     line_index_[i]= new vcl_vector<vsol_line_2d_sptr>;
00106 
00107   int line = 0;
00108   for (vcl_vector<double>::iterator dit = distances_.begin();
00109        dit != distances_.end(); dit++, line++)
00110   {
00111     int id = (int)((*dit)-dmin_);
00112     index_[id]=0;
00113     line_index_[id]->push_back(lines[line]);
00114   }
00115   this->forward_champher();
00116   this->backward_champher();
00117 #ifdef DEBUG
00118    for (int i = 0; i<size_; i++)
00119      vcl_cout << "line_index_["<<i+(int)dmin_<<"]="<<line_index_[i]->size() << '\n';
00120 #endif
00121   return true;
00122 }
00123 
00124 void line_chamfer_1d::forward_champher()
00125 {
00126   for (int i = 1; i<size_; i++)
00127     if (index_[i-1]+1<=index_[i])
00128       index_[i]=index_[i-1]+1;
00129 }
00130 
00131 void line_chamfer_1d::backward_champher()
00132 {
00133   for (int i = size_-2; i>=0; i--)
00134     if (index_[i]>index_[i+1]+1)
00135       index_[i]=index_[i+1]+1;
00136 }
00137 
00138 double line_chamfer_1d::distance(double x) const
00139 {
00140   if (x<dmin_)
00141     return vnl_numeric_traits<double>::maxval;
00142   if (x>dmax_)
00143     return vnl_numeric_traits<double>::maxval;
00144   int i = (int)(x-dmin_);
00145   return index_[i];
00146 }
00147 
00148 bool
00149 line_chamfer_1d::get_lines_in_interval(const double x,
00150                                        const double radius,
00151                                        vcl_vector<vsol_line_2d_sptr>& lines) const
00152 {
00153   lines.clear();
00154   if (x<dmin_)
00155     return false;
00156   if (x>dmax_)
00157     return false;
00158   double x0 = x - dmin_;
00159   int lo = (int)(x0 - radius);
00160   int hi = (int)(x0 + radius);
00161   if (lo<0)
00162     lo=0;
00163   if (hi>(size_-1))
00164     hi = size_ - 1;
00165   for (int d = lo; d<=hi; d++)
00166     if (line_index_[d])
00167       for (vcl_vector<vsol_line_2d_sptr>::iterator lit=line_index_[d]->begin();
00168            lit != line_index_[d]->end(); lit++)
00169         lines.push_back(*lit);
00170   return true;
00171 }
00172 #endif
00173 
00174 // DEC - use total length of lines within radius instead of distance to nearest
00175 grid_profile_matcher::grid_profile_matcher()
00176 {
00177   size_=0;
00178   dmin_=0;
00179   dmax_=0;
00180 }
00181 
00182 grid_profile_matcher::~grid_profile_matcher()
00183 {
00184   for (int i = 0; i<size_; i++)
00185     delete line_index_[i];
00186 }
00187 
00188 bool grid_profile_matcher::insert_lines(vcl_vector<vsol_line_2d_sptr> const& lines,
00189                                         bool horizontal_lines)
00190 {
00191   if (!lines.size())
00192     return false;
00193   dmin_ = vnl_numeric_traits<double>::maxval;
00194   dmax_ = -dmin_;
00195   distances_.clear();
00196   for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = lines.begin();
00197        lit != lines.end(); lit++)
00198   {
00199     double d;
00200 #ifdef USE_C_FOR_DISTANCE
00201     vgl_homg_line_2d<double> l = (*lit)->vgl_hline_2d();
00202     l.normalize();
00203     d = -l.c(); // perpendicular distance
00204 #else
00205     vsol_point_2d_sptr mid = (*lit)->middle();
00206     if (horizontal_lines)
00207       d = mid->y();
00208     else
00209       d = mid->x();
00210 #endif
00211     distances_.push_back(d);
00212     dmin_ = vnl_math_min(d,dmin_);
00213     dmax_ = vnl_math_max(d,dmax_);
00214   }
00215   if (!(dmax_>=dmin_))
00216     return false;
00217   size_=(int)(dmax_-dmin_ +1);
00218   image_profile_.set_size(size_);
00219   image_profile_.fill(0.0);
00220 
00221   line_index_.resize(size_);
00222   for (int i = 0; i<size_; i++)
00223     line_index_[i]= new vcl_vector<vsol_line_2d_sptr>;
00224 
00225   int line = 0;
00226   for (vcl_vector<double>::iterator dit = distances_.begin();
00227        dit != distances_.end(); dit++, line++)
00228   {
00229     int id = (int)((*dit)-dmin_);
00230     image_profile_[id] += lines[line]->length();
00231     line_index_[id]->push_back(lines[line]);
00232   }
00233 
00234   if (false)
00235   {
00236     if (horizontal_lines)
00237       vcl_cout << "------horizontal profile: ---------\n";
00238     else
00239       vcl_cout << "------vertical profile: -----------\n";
00240     for (int i = 0; i < size_; i++)
00241     {
00242       vcl_cout <<"profile["<<int(i+dmin_)<<"] =  "<<image_profile_[i]<<'\n';
00243     }
00244   }
00245   return true;
00246 }
00247 
00248 bool
00249 grid_profile_matcher::get_lines_in_interval(const double x,
00250                                             const double radius,
00251                                             vcl_vector<vsol_line_2d_sptr>& lines) const
00252 {
00253   lines.clear();
00254   if (x<dmin_)
00255     return false;
00256   if (x>dmax_)
00257     return false;
00258   double x0 = x - dmin_;
00259   int lo = (int)(x0 - radius);
00260   int hi = (int)(x0 + radius);
00261   if (lo<0)
00262     lo=0;
00263   if (hi>(size_-1))
00264     hi = size_ - 1;
00265   for (int d = lo; d<=hi; d++)
00266     if (line_index_[d])
00267       for (vcl_vector<vsol_line_2d_sptr>::iterator lit=line_index_[d]->begin();
00268            lit != line_index_[d]->end(); lit++)
00269         lines.push_back(*lit);
00270   return true;
00271 }
00272 
00273 double grid_profile_matcher::calculate_grid_offset(int n_grid_lines, double spacing)
00274 {
00275   // construct grid profile vector
00276   int grid_profile_len = int((n_grid_lines+1) * spacing) + 1;
00277   vnl_vector<double> grid_profile(grid_profile_len,0.0);
00278   int max_offset = int(grid_radius);
00279 #ifdef DEBUG
00280   vcl_cout <<"max_offset = "<<max_offset<<'\n';
00281 #endif
00282   for (int i = 1; i <= n_grid_lines; i++)
00283   {
00284     for (int offset = -max_offset; offset < max_offset; offset++)
00285     {
00286       grid_profile[int(i*spacing)+offset] = ((double)(max_offset -
00287                                                       vcl_abs(double(offset))))/(max_offset);
00288     }
00289   }
00290 #ifdef DEBUG
00291     vcl_cout << "___GRID PROFILE__\n";
00292     for (int i = 0; i < grid_profile_len; i++)
00293       vcl_cout << "grid_profile["<<i<<"] = "<<grid_profile[i]<<'\n';
00294 #endif
00295   // now convolve with image profile
00296   vnl_vector<double> convolution = vnl_convolve(image_profile_,grid_profile,0);
00297   // max value in convolution should correspond to best grid offset
00298   double max_value = -vnl_numeric_traits<double>::maxval;
00299   int max_index = -1;
00300 #ifdef DEBUG
00301   vcl_cout << "__CONVOLUTION__\n";
00302 #endif
00303   for (unsigned int i = 0; i < convolution.size(); i++)
00304   {
00305 #ifdef DEBUG
00306     vcl_cout << "convolution["<<i-grid_profile_len+spacing+dmin_<<"] = "<<convolution[i]<<'\n';
00307 #endif
00308     if (convolution[i] > max_value)
00309     {
00310       max_index = i;
00311       max_value = convolution[i];
00312     }
00313   }
00314   return max_index - grid_profile_len + spacing + dmin_;
00315 }
00316 
00317 //---------------------------------------------------------------
00318 // Constructors
00319 //
00320 //----------------------------------------------------------------
00321 
00322 //: constructor from a parameter block (the only way)
00323 //
00324 sdet_grid_finder::sdet_grid_finder(sdet_grid_finder_params& gfp)
00325   : sdet_grid_finder_params(gfp),
00326     length_threshold_(15.0)
00327 {
00328   groups_valid_ = false;
00329   vanishing_points_valid_=false;
00330   projective_homography_valid_ = false;
00331   affine_homography_valid_ = false;
00332   homography_valid_ = false;
00333   index_ = 0;
00334 }
00335 
00336 //:Default Destructor
00337 sdet_grid_finder::~sdet_grid_finder()
00338 {
00339 }
00340 
00341 static void group_angle_stats(vcl_vector<vsol_line_2d_sptr> const & group,
00342                               const double angle_tol,
00343                               double & avg_angle, double& min_angle,
00344                               double& max_angle)
00345 {
00346   min_angle = 360;
00347   max_angle = -360;
00348   int n_lines = 0;
00349   avg_angle=0;
00350 
00351   for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = group.begin();
00352        lit != group.end(); lit++)
00353   {
00354     double ang = (*lit)->tangent_angle();
00355     if (ang>180)
00356       ang -= 180.0;
00357     if (ang<min_angle)
00358       min_angle = ang;
00359     if (ang>max_angle)
00360       max_angle = ang;
00361   }
00362   // See if we are on the 180-0 cut
00363   double cut_thresh = 3.0*angle_tol + 15.0;
00364   bool on_cut = (max_angle-min_angle)>cut_thresh;
00365   if (on_cut)
00366     vcl_cout << "On cut [" << min_angle << ' ' << max_angle << "] >"
00367              << cut_thresh << '\n';
00368   for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = group.begin();
00369        lit != group.end(); lit++, n_lines++)
00370   {
00371     double ang = (*lit)->tangent_angle();
00372     if (ang>180.0)
00373       ang-=180.0;
00374     if (on_cut&&ang>90)
00375       ang -=180;
00376     avg_angle += ang;
00377   }
00378   if (n_lines)
00379   {
00380     avg_angle /= n_lines;
00381   }
00382   else
00383     avg_angle=0;
00384 }
00385 
00386 //-------------------------------------------------------------------------
00387 //: Set the edges to be processed
00388 //
00389 bool sdet_grid_finder::set_lines(const float xsize, const float ysize,
00390                                  vcl_vector<vsol_line_2d_sptr> const& lines)
00391 {
00392   debug_lines_.clear();
00393   debug_grid_lines_.clear();
00394   display_lines_.clear();
00395   matched_lines_.clear();
00396   groups_valid_ = false;
00397   vanishing_points_valid_=false;
00398   projective_homography_valid_=false;
00399   affine_homography_valid_ = false;
00400   homography_valid_=false;
00401   lines_=lines;
00402   xmax_ = xsize;
00403   ymax_ = ysize;
00404   index_ = new bsol_hough_line_index(0,0,xsize, ysize);
00405   for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = lines.begin();
00406        lit != lines.end(); lit++)
00407     index_->index(*lit);
00408   vcl_vector<vcl_vector<vsol_line_2d_sptr> > groups;
00409   if (index_->dominant_line_groups(thresh_, angle_tol_, groups)<2)
00410     return false;
00411   group0_ = groups[0];
00412   group1_ = groups[1];
00413   groups_valid_ = true;
00414   if (debug_state_==VANISHING_POINT)
00415   {
00416     for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = groups[0].begin();
00417          lit != groups[0].end(); lit++)
00418       debug_lines_.push_back(*lit);
00419     for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = groups[1].begin();
00420          lit != groups[1].end(); lit++)
00421       debug_lines_.push_back(*lit);
00422   }
00423   return true;
00424 }
00425 
00426 bool sdet_grid_finder::
00427 get_vanishing_point(vcl_vector<vsol_line_2d_sptr> const & para_lines,
00428                     vgl_homg_point_2d<double>& vp)
00429 {
00430   vcl_vector<vgl_homg_line_2d<double> > vlines, tvlines, stvlines;
00431 
00432   int nlines =0;
00433   double tx = 0, ty =0;
00434   //
00435   // convert the input lines to vgl_homg_line(s)
00436   // and get the average distance of the intersection of the perpendicular
00437   // line from the origin with each line.  This point set defines the
00438   // translation offset for the lines.
00439   //
00440   for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = para_lines.begin();
00441        lit != para_lines.end(); lit++)
00442   {
00443     if ((*lit)->length() < length_threshold_)
00444     {
00445       vcl_cout << "discarding line with length < "<<length_threshold_<<'\n';
00446       continue;
00447     }
00448     vgl_homg_line_2d<double> l= (*lit)->vgl_hline_2d();
00449     l.normalize();
00450     tx -= l.a()*l.c();
00451     ty -= l.b()*l.c();
00452     vlines.push_back(l);
00453     nlines++;
00454   }
00455   if (nlines<2)
00456     return false;
00457   tx /=nlines;
00458   ty /=nlines;
00459   // Offset the lines with the translation
00460   for (vcl_vector<vgl_homg_line_2d<double> >::iterator lit = vlines.begin();
00461        lit != vlines.end(); lit++)
00462   {
00463     vgl_homg_line_2d<double>& l = (*lit);
00464     double c = l.c();
00465     c -= l.a()*tx + l.b()*ty;
00466     l.set(l.a(), l.b(), c);
00467     tvlines.push_back(l);
00468   }
00469   // Scale the lines so that the mean squared distance from the origin is one.
00470   double c_sq = 0;
00471   for (vcl_vector<vgl_homg_line_2d<double> >::iterator lit = tvlines.begin();
00472        lit != tvlines.end(); lit++)
00473   {
00474     vgl_homg_line_2d<double>& l = (*lit);
00475     c_sq += l.c()*l.c();
00476   }
00477   c_sq /=nlines;
00478   double sigma_c = vcl_sqrt(c_sq);
00479   for (vcl_vector<vgl_homg_line_2d<double> >::iterator lit = tvlines.begin();
00480        lit != tvlines.end(); lit++)
00481   {
00482     vgl_homg_line_2d<double>& l = (*lit);
00483     double c = l.c();
00484     if (sigma_c>1e-8)
00485       c /= sigma_c;
00486     l.set(l.a(), l.b(), c);
00487     stvlines.push_back(l);
00488   }
00489   // Compute the intersection of the normalized lines to define the vanishing point
00490   vp = vgl_homg_operators_2d<double>::lines_to_point(stvlines);
00491 
00492   // restore normalizing transform
00493   // scale factor;
00494   double lambda = vp.w()/sigma_c;
00495   vp.set((vp.x()+lambda*tx), (vp.y()+lambda*ty), lambda);
00496 
00497   vcl_cout << "returning from get_vanishing_point()\n";
00498 
00499   return true;
00500 }
00501 
00502 bool sdet_grid_finder::compute_vanishing_points()
00503 {
00504   if (!groups_valid_)
00505     return false;
00506   vgl_homg_point_2d<double> vp0, vp1;
00507   if (!get_vanishing_point(group0_, vp0))
00508     return false;
00509   if (!get_vanishing_point(group1_, vp1))
00510     return false;
00511   // if the x component of the first vanishing point is more horizontal then
00512   // make it the horizontal vanishing point
00513   if (vcl_fabs(vp0.x())>vcl_fabs(vp0.y()))
00514   {
00515     vp0_= vp0;
00516     vp90_= vp1;
00517   }
00518   else
00519   {
00520     vp0_ = vp1;
00521     vp90_=vp0;
00522   }
00523   vanishing_points_valid_ = true;
00524   vcl_cout << "returning from compute_vanishing_points()\n";
00525   return true;
00526 }
00527 
00528 //--------------------------------------------------------------------------
00529 //: find the grid in the set of line segments
00530 bool sdet_grid_finder::match_grid()
00531 {
00532   return true;
00533 }
00534 
00535 //-------------------------------------------------------------------------
00536 //: Compute a projective homography that sends the two major group vanishing points to the x and y directions
00537 bool sdet_grid_finder::compute_projective_homography()
00538 {
00539   if (projective_homography_valid_)
00540     return true;
00541 
00542   if (!this->compute_vanishing_points())
00543   {
00544     vcl_cout<< "In sdet_grid_finder::compute_projective_homography() -"
00545             << " vanishing point computation failed\n";
00546     return false;
00547   }
00548   affine_homography_valid_ = false;
00549   homography_valid_ = false;
00550   if (verbose_)
00551   {
00552     vcl_cout << "VP0 " << vp0_ << '\n'
00553              << "VP90 " << vp90_ << '\n';
00554   }
00555 
00556   // Keep the sense of the axes pointing to infinity
00557   vgl_homg_point_2d<double> x_inf(1,0,0), x_minus_inf(-1,0,0);
00558   vgl_homg_point_2d<double> y_inf(0,1,0), y_minus_inf(0,-1,0);
00559   vgl_homg_point_2d<double> origin(0,0,1);
00560   vgl_homg_point_2d<double> x_finite(xmax_,0,1), y_finite(0,ymax_,1);
00561   vgl_homg_point_2d<double> max_corner(xmax_,ymax_,1);
00562 
00563   // compute a point homography that maps the
00564   // vanishing points to infinity and leaves the corners of the image invariant
00565   vcl_vector<vgl_homg_point_2d<double> > image;
00566   vcl_vector<vgl_homg_point_2d<double> > grid;
00567   // First, if the vanishing points are already quite close to infinity
00568   // then just form an identity transform, otherwise map to the vanishing pts
00569   double at_infinity = 1.0e-8;
00570   if (vcl_fabs(vp0_.w())<at_infinity)
00571   {
00572     image.push_back(x_finite);
00573     grid.push_back(x_finite);
00574   }
00575   else
00576   {
00577     image.push_back(vp0_);
00578     if (vp0_.x()/vp0_.w()>0)
00579       grid.push_back(x_inf);
00580     else
00581       grid.push_back(x_minus_inf);
00582   }
00583 
00584   if (vcl_fabs(vp90_.w())<at_infinity)
00585   {
00586     image.push_back(y_finite);
00587     grid.push_back(y_finite);
00588   }
00589   else
00590   {
00591     image.push_back(vp90_);
00592     if (vp90_.y()/vp90_.w()>0)
00593       grid.push_back(y_inf);
00594     else
00595       grid.push_back(y_minus_inf);
00596   }
00597 
00598   image.push_back(origin); image.push_back(max_corner);
00599   grid.push_back(origin); grid.push_back(max_corner);
00600   vgl_h_matrix_2d_compute_4point comp_4pt;
00601   if (!comp_4pt.compute(image, grid, projective_homography_))
00602     return false;
00603   if (verbose_)
00604     vcl_cout << "The projective homography\n" << projective_homography_ << '\n';
00605   projective_homography_valid_ = true;
00606   return true;
00607 }
00608 
00609 //---------------------------------------------------------------
00610 //:
00611 //  Remove any remaining skew by finding an affine transformation
00612 //  the maps horizontally inclined lines (angle ah) to zero degrees
00613 //  and vertically inclined lines (angle av) to 90 degrees.
00614 //  That is, find a transformation of the line normals
00615 // \verbatim
00616 //  |q00  q01 ||-sin(av) -sin(ah)|    |-1 0|
00617 //  |q10  q11 || cos(av)  cos(ah)|  = | 0 1|
00618 // \endverbatim
00619 //  The point transformation,Q, is then the inverse transpose of q
00620 // \verbatim
00621 //      | sin(av) -cos(av)|
00622 //  Q = |-sin(ah)  cos(ah)|  = [q]^-t
00623 // \endverbatim
00624 static vnl_matrix_fixed<double, 3,3> skew_transform(const double ah,
00625                                                     const double av)
00626 {
00627   vnl_matrix_fixed<double, 3, 3> Q;
00628   Q.put(0, 0, vcl_sin(av)); Q.put(0,1, -vcl_cos(av)); Q.put(0,2,0);
00629   Q.put(1, 0, -vcl_sin(ah)); Q.put(1,1, vcl_cos(ah)); Q.put(1,2,0);
00630   Q.put(2, 0, 0); Q.put(2,1, 0); Q.put(2,2,1);
00631   return Q;
00632 }
00633 
00634 //------------------------------------------------------------------------
00635 //:Form a histogram of all pairwise distances of lines from the origin.
00636 // h_i is the count, d_i is the average distance in a bin
00637 // The first peak is the distance between grid lines in the horizontal (gh)
00638 // and vertical (gv) directions. The transformation makes each of these
00639 // distances equal to spacing. The result is returned in S.
00640 
00641 bool sdet_grid_finder::scale_transform(const double /* max_distance */,
00642                                        vcl_vector<vsol_line_2d_sptr> const& gh,
00643                                        vcl_vector<vsol_line_2d_sptr> const& gv,
00644                                        vnl_matrix_fixed<double, 3, 3>& S)
00645 {
00646   int nbins = 100;
00647   bsol_distance_histogram Hh(nbins, gh), Hv(nbins, gv);
00648   double hp1=0, hp2=0, vp1=0, vp2=0;
00649 
00650 #undef DEC_DEBUG
00651 #ifdef DEC_DEBUG
00652   vcl_cout << "\nHorizontal Distances\n"
00653            << "*********************************\n";
00654   for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = gh.begin();
00655        lit != gh.end(); lit++)
00656   {
00657     vgl_homg_line_2d<double> hline = (*lit)->vgl_hline_2d();
00658     hline.normalize();
00659     vcl_cout << hline.c() << '\n';
00660   }
00661   vcl_cout << "********************************\n\n"
00662            << "Vertical Distances\n"
00663            << "*********************************\n";
00664   for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = gv.begin();
00665        lit != gv.end(); lit++)
00666   {
00667     vgl_homg_line_2d<double> hline = (*lit)->vgl_hline_2d();
00668     hline.normalize();
00669     vcl_cout << hline.c() << '\n';
00670   }
00671   vcl_cout << "********************************\n\n";
00672 #endif
00673   if (verbose_)
00674   {
00675     vcl_cout << "Horizontal Histogram\n" << Hh << "\n\n"
00676              << "Vertical Histogram\n" << Hv << "\n\n";
00677   }
00678   if (!Hh.distance_peaks(hp1, hp2))
00679   {
00680     vcl_cout << "In sdet_grid_finder::scale_transform(.) - failed"
00681              << " to find horizontal distance peaks\n";
00682     return false;
00683   }
00684   if (!Hv.distance_peaks(vp1, vp2))
00685   {
00686     vcl_cout << "In sdet_grid_finder::scale_transform(.) - failed"
00687              << " to find vertical distance peaks\n";
00688     return false;
00689   }
00690   double ph = (hp2-hp1), pv = (vp2-vp1);
00691   if (verbose_)
00692   {
00693     vcl_cout << "Horizontal Peaks " << hp1 << ' ' << hp2 << '\n'
00694              << "Vertical Peaks " << vp1 << ' ' << vp2 << '\n'
00695              << "Horizontal Dist " << ph << '\n'
00696              << "Vertical Dist " << pv << '\n';
00697   }
00698   // failed to find peaks
00699   if (ph<0||pv<0)
00700     return false;
00701 
00702   // adjust the spacing to be equal.
00703   S.put(0, 0, spacing_/pv); S.put(0,1, 0); S.put(0,2,0);
00704   S.put(1, 0, 0); S.put(1,1, spacing_/ph); S.put(1,2,0);
00705   S.put(2, 0, 0); S.put(2,1, 0); S.put(2,2,1);
00706   return true;
00707 }
00708 
00709 bool sdet_grid_finder::compute_affine_homography()
00710 {
00711   if (!projective_homography_valid_)
00712     return false;
00713   if (affine_homography_valid_)
00714     return true;
00715   float affine_angle_factor = 3.0; // more tolerance for line groups for affine processing
00716 
00717   // transform all the lines using the projective homography defined
00718   // by vanishing points
00719   vcl_vector<vsol_line_2d_sptr> affine_lines;
00720   for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = lines_.begin();
00721        lit != lines_.end(); lit++)
00722   {
00723     vsol_line_2d_sptr pline = this->transform_line(projective_homography_,*lit);
00724     if (verbose_)
00725     {
00726       vcl_cout << "*lit = ["<<(*lit)->p0()->x()<<','<<(*lit)->p0()->y()<<"] ["
00727                << (*lit)->p1()->x()<<','<<(*lit)->p1()->y()<<"]\n"
00728                << "pline = ["<<pline->p0()->x()<<','<<pline->p0()->y()<<"] ["
00729                << pline->p1()->x()<<','<<pline->p1()->y()<<"]\n";
00730     }
00731     // weed out lines with _huge_ coordinates
00732     if ( (vcl_abs(pline->p0()->x()) > 10000) ||
00733          (vcl_abs(pline->p0()->y()) > 10000) ||
00734          (vcl_abs(pline->p1()->x()) > 10000) ||
00735          (vcl_abs(pline->p1()->y()) > 10000) )
00736       continue;
00737     affine_lines.push_back(pline);
00738   }
00739 
00740   // Get the bounds of the affine lines (lines with vpoints at infinity)
00741   vbl_bounding_box<double, 2> b = bsol_algs::bounding_box(affine_lines);
00742   index_ = new bsol_hough_line_index(b);
00743 
00744   for (vcl_vector<vsol_line_2d_sptr>::iterator lit = affine_lines.begin();
00745        lit != affine_lines.end(); lit++)
00746     index_->index(*lit);
00747 
00748   vcl_vector<vcl_vector<vsol_line_2d_sptr> > groups;
00749   if (index_->dominant_line_groups(thresh_,
00750                                    affine_angle_factor*angle_tol_,
00751                                    groups)<2)
00752     return false; // failed to find enough groups.
00753 
00754   afgroup0_ = groups[0];
00755   afgroup1_ = groups[1];
00756   if (debug_state_==AFFINE_GROUP_BEFORE_SKEW_SCALE)
00757   {
00758     for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = afgroup0_.begin();
00759          lit != afgroup0_.end(); lit++)
00760       debug_lines_.push_back(*lit);
00761     for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = afgroup1_.begin();
00762          lit != afgroup1_.end(); lit++)
00763       debug_lines_.push_back(*lit);
00764   }
00765 
00766   double avg_angle0=0, avg_angle1=0, min_angle0=0, max_angle0=0,
00767     min_angle1=0, max_angle1=0;
00768 
00769   group_angle_stats(afgroup0_, angle_tol_,avg_angle0, min_angle0, max_angle0);
00770   group_angle_stats(afgroup1_, angle_tol_,avg_angle1, min_angle1, max_angle1);
00771 
00772   if (verbose_)
00773   {
00774     vcl_cout << "Affine angles\n"
00775              << "G[" << afgroup0_.size() << "] avg_angle = " << avg_angle0
00776              << " min_angle = " << min_angle0 << " max_angle = "
00777              << max_angle0 << '\n'
00778              << "G[" << afgroup1_.size() << "] avg_angle = " << avg_angle1
00779              << " min_angle = " << min_angle1 << " max_angle = "
00780              << max_angle1 << '\n';
00781   }
00782   // Get the orientation of roughly vertical and horizontal lines
00783   // ang0 is the horizontal direction and ang1 is the vertical direction
00784   double deg_to_rad = vnl_math::pi_over_180;
00785   double ang0 =0, ang90=0;
00786   bool zero_is_zero=true;;
00787   if (vcl_fabs(vcl_fabs(avg_angle0)-90)>vcl_fabs(vcl_fabs(avg_angle1)-90))
00788   {
00789     ang0 = avg_angle0*deg_to_rad;
00790     ang90= avg_angle1*deg_to_rad;
00791     zero_is_zero = true;
00792   }
00793   else
00794   {
00795     ang0 = avg_angle1*deg_to_rad;
00796     ang90 = avg_angle0*deg_to_rad;
00797     zero_is_zero = false;
00798   }
00799   // lines should be along the positive x axis.
00800   if (ang0>vnl_math::pi_over_2)
00801     ang0-=vnl_math::pi;
00802   // lines should be along the positive y axis.
00803   if (ang90<0)
00804     ang90+=vnl_math::pi;
00805   // Need to have skew angle at least 60 deg.
00806   if ((vcl_fabs(ang90)-vcl_fabs(ang0))< vnl_math::pi/3.0)
00807   {
00808     vcl_cout << "In sdet_grid_finder::compute_affine_homography() -"
00809              << " failed to find dominant groups with at least 60 deg"
00810              << " orientation\n";
00811     return false;
00812   }
00813   vnl_matrix_fixed<double, 3,3> Q = skew_transform(ang0, ang90);
00814 
00815   if (verbose_)
00816     vcl_cout << "The skew transform\n" << Q << '\n';
00817 
00818   // max distance for distance histogram
00819   // the distance could be larger but unlikely
00820   double dx = vcl_fabs(b.xmax()-b.xmin()), dy = vcl_fabs(b.ymax()-b.ymin());
00821   double max_distance = dx;
00822   if (dx<dy)
00823     max_distance = dy;
00824   vnl_matrix_fixed<double, 3, 3> S;
00825   if (zero_is_zero)
00826   {
00827     if (!scale_transform(max_distance, afgroup0_, afgroup1_, S))
00828       return false; // failed to find a first distance peak
00829   }
00830   else
00831     if (!scale_transform(max_distance, afgroup1_, afgroup0_, S))
00832       return false; // failed to find a first distance peak
00833   if (verbose_)
00834     vcl_cout << "The scale transform\n" << S << '\n';
00835 
00836   affine_homography_ = vgl_h_matrix_2d<double>(S*Q);
00837 
00838   // Finally we translate until the first row and column of lines are at (0,0)
00839 #if 0 // Hack! needs to be removed JLM
00840   double length_threshold = 7.0;
00841 #endif
00842   vcl_vector<vsol_line_2d_sptr> grid_lines0, grid_lines90;
00843   for (vcl_vector<vsol_line_2d_sptr>::iterator lit = afgroup0_.begin();
00844        lit != afgroup0_.end(); lit++)
00845   {
00846 #if 0 // Hack! needs to be removed JLM
00847     if ((*lit)->length()<length_threshold)
00848       continue;
00849 #endif
00850     if (zero_is_zero)
00851       grid_lines0.push_back(this->transform_line(affine_homography_,*lit));
00852     else
00853       grid_lines90.push_back(this->transform_line(affine_homography_,*lit));
00854   }
00855   for (vcl_vector<vsol_line_2d_sptr>::iterator lit = afgroup1_.begin();
00856        lit != afgroup1_.end(); lit++)
00857   {
00858 #if 0 // Hack! needs to be removed JLM
00859     if ((*lit)->length()<length_threshold)
00860       continue;
00861 #endif
00862     if (zero_is_zero)
00863       grid_lines90.push_back(this->transform_line(affine_homography_,*lit));
00864     else
00865       grid_lines0.push_back(this->transform_line(affine_homography_,*lit));
00866   }
00867   if ( (debug_state_==AFFINE_GROUP_AFTER_SKEW_SCALE) ||
00868        (debug_state_==TRANS_PERIM_LINES) )
00869   {
00870     for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit =
00871          grid_lines0.begin(); lit != grid_lines0.end(); lit++)
00872       debug_lines_.push_back(*lit);
00873     for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit =
00874          grid_lines90.begin(); lit != grid_lines90.end(); lit++)
00875       debug_lines_.push_back(*lit);
00876   }
00877 
00878   vcl_sort(grid_lines0.begin(), grid_lines0.end(), line_distance);
00879   vcl_sort(grid_lines90.begin(), grid_lines90.end(), line_distance);
00880 
00881 #ifdef DEBUG
00882     vcl_cout <<  "Grid Lines 0\n"
00883 #endif
00884   if (!chamf0_.insert_lines(grid_lines0,true))
00885     return false;
00886 
00887 #ifdef DEBUG
00888     vcl_cout <<  "Grid Lines 90\n"
00889 #endif
00890   if (!chamf90_.insert_lines(grid_lines90,false))
00891     return false;
00892 
00893   if (false)
00894   {
00895     vcl_cout << "Grid Lines 0\n";
00896     print_lines(grid_lines0);
00897     vcl_cout << "\n\nGrid Lines 90\n";
00898     print_lines(grid_lines90);
00899   }
00900   for (vcl_vector<vsol_line_2d_sptr>::iterator lit = grid_lines0.begin();
00901        lit != grid_lines0.end(); lit++)
00902     display_lines_.push_back(*lit);
00903 
00904   for (vcl_vector<vsol_line_2d_sptr>::iterator lit = grid_lines90.begin();
00905        lit != grid_lines90.end(); lit++)
00906     display_lines_.push_back(*lit);
00907 
00908   vgl_h_matrix_2d<double> T;
00909   this->compute_homography_linear_chamfer(T);
00910   affine_homography_ = T*affine_homography_;
00911   if (verbose_)
00912     vcl_cout << "The affine homography\n" << affine_homography_ << '\n';
00913   affine_homography_valid_ = true;
00914   return true;
00915 }
00916 
00917 
00918 //------------------------------------------------------
00919 //: Assumes that a set of lines have been binned in the 1-d chamfer index
00920 //  Assumes that dindex0_ are vertical lines and dindex90_ are horizontal
00921 bool sdet_grid_finder::
00922 compute_homography_linear_chamfer(vgl_h_matrix_2d<double> & H)
00923 {
00924   double transx=0, transy=0;
00925 
00926   transx = chamf90_.calculate_grid_offset(n_lines_y_,spacing_);
00927   transy = chamf0_.calculate_grid_offset(n_lines_x_,spacing_);
00928 
00929   if (verbose_)
00930     vcl_cout << " Translation (" << transx << ' ' << transy << ")\n";
00931 
00932   if (debug_state_ == TRANS_PERIM_LINES)
00933   {
00934     float xmin = 0, ymin = 0;
00935     float xmax = 2000, ymax = 2000;
00936     // vertical lines
00937     for (int i = 0; i < n_lines_y_; i++)
00938     {
00939       vsol_point_2d_sptr p0 = new vsol_point_2d(i*spacing_+transx,ymin);
00940       vsol_point_2d_sptr p1 = new vsol_point_2d(i*spacing_+transx,ymax);
00941       vsol_line_2d_sptr line = new vsol_line_2d(p0,p1);
00942       debug_grid_lines_.push_back(line);
00943     }
00944     // horizontal lines
00945     for (int i = 0; i < n_lines_x_; i++)
00946     {
00947       vsol_point_2d_sptr p0 = new vsol_point_2d(xmin,i*spacing_+transy);
00948       vsol_point_2d_sptr p1 = new vsol_point_2d(xmax,i*spacing_+transy);
00949       vsol_line_2d_sptr line = new vsol_line_2d(p0,p1);
00950       debug_grid_lines_.push_back(line);
00951     }
00952   }
00953 
00954   // compute new homography so we can double check matched lines
00955   vnl_matrix_fixed<double, 3, 3> T;
00956   T.put(0, 0, 1); T.put(0,1, 0); T.put(0,2,-transx);
00957   T.put(1, 0, 0); T.put(1,1, 1); T.put(1,2,-transy);
00958   T.put(2, 0, 0); T.put(2,1, 0); T.put(2,2,1);
00959   vgl_h_matrix_2d<double> Htrans(T);
00960   double min_x_offset = -10.0, max_x_offset =(n_lines_y_-1)*spacing_ + 10;
00961   double min_y_offset = -10.0, max_y_offset =(n_lines_x_-1)*spacing_ + 10;
00962   vcl_vector<vgl_homg_line_2d<double> > lines_grid, lines_image;
00963   vcl_vector<double> weights;
00964   // insert horizontal line correspondences
00965   double length_sum = 0;
00966   for (int i0 = 0; i0< n_lines_x_; i0++)
00967   {
00968     double dy = i0*spacing_;
00969     vgl_homg_line_2d<double> lh(0.0, 1.0, -dy);
00970     dy += transy;
00971     vcl_vector<vsol_line_2d_sptr> h_lines;
00972     chamf0_.get_lines_in_interval(dy, collection_grid_radius, h_lines);
00973 
00974     if (!h_lines.size())
00975       continue;
00976     for (unsigned int j0 = 0; j0<h_lines.size(); j0++)
00977     {
00978       vsol_line_2d_sptr l0 = h_lines[j0];
00979 
00980       // check x offset
00981       vsol_line_2d_sptr l0_xformed = this->transform_line(Htrans,l0);
00982       vsol_point_2d_sptr mid = l0_xformed->middle();
00983 #ifdef DEBUG
00984       vcl_cout << "horizontal, p=("<<mid->x()<<','<<mid->y()<<")\n";
00985 #endif
00986       if ( (mid->x() < min_x_offset) || (mid->x() > max_x_offset) )
00987       {
00988 #ifdef DEBUG
00989         vcl_cout << "discarding line with offset "<<mid->x()<<'\n';
00990 #endif
00991         continue;
00992       }
00993       // line passes: add to set of correspondences
00994       lines_grid.push_back(lh);
00995       matched_lines_.push_back(l0);
00996       if (debug_state_==AFFINE_GROUP_AFTER_TRANS)
00997         debug_lines_.push_back(l0);
00998       double length = l0->length();
00999       length_sum += length;
01000       weights.push_back(length);
01001       vgl_homg_line_2d<double> homgl = l0->vgl_hline_2d();
01002       homgl.normalize();
01003 #ifdef DEBUG
01004       vcl_cout << homgl << '\n';
01005 #endif
01006       lines_image.push_back(homgl);
01007     }
01008   }
01009 
01010   for (int i90 = 0; i90< n_lines_y_; i90++)
01011   {
01012     double dx = i90*spacing_;
01013     vgl_homg_line_2d<double> lv(1.0, 0.0, -dx);
01014     dx += transx;
01015     vcl_vector<vsol_line_2d_sptr> v_lines;
01016     chamf90_.get_lines_in_interval(dx, collection_grid_radius, v_lines);
01017     if (!v_lines.size())
01018       continue;
01019     for (unsigned int j90 = 0; j90<v_lines.size(); j90++)
01020     {
01021       vsol_line_2d_sptr l90 = v_lines[j90];
01022       // check y offset
01023       vsol_line_2d_sptr l90_xformed = this->transform_line(Htrans,l90);
01024       vsol_point_2d_sptr mid = l90_xformed->middle();
01025 #ifdef DEBUG
01026       vcl_cout << "vertical, p=("<<mid->x()<<','<<mid->y()<<")\n";
01027 #endif
01028       if ( (mid->y() < min_y_offset) || (mid->y() > max_y_offset) )
01029       {
01030 #ifdef DEBUG
01031         vcl_cout << "discarding line with offset "<<mid->y()<<'\n';
01032 #endif
01033         continue;
01034       }
01035       // line passes: add to set of correspondences
01036       lines_grid.push_back(lv);
01037 
01038       matched_lines_.push_back(l90);
01039       if (debug_state_==AFFINE_GROUP_AFTER_TRANS)
01040         debug_lines_.push_back(l90);
01041       double length = l90->length();
01042       length_sum += length;
01043       weights.push_back(length);
01044       vgl_homg_line_2d<double>  homgl = l90->vgl_hline_2d();
01045       homgl.normalize();
01046 #ifdef DEBUG
01047        vcl_cout << homgl << '\n';
01048 #endif
01049       lines_image.push_back(homgl);
01050     }
01051   }
01052   if (debug_state_==AFFINE_GROUP_AFTER_TRANS)
01053   {
01054     vnl_matrix_fixed<double, 3, 3> T;
01055     T.put(0, 0, 1); T.put(0,1, 0); T.put(0,2,-transx);
01056     T.put(1, 0, 0); T.put(1,1, 1); T.put(1,2,-transy);
01057     T.put(2, 0, 0); T.put(2,1, 0); T.put(2,2,1);
01058     vgl_h_matrix_2d<double> h(T);
01059     vcl_vector<vsol_line_2d_sptr> temp;
01060     for (vcl_vector<vsol_line_2d_sptr>::iterator lit = debug_lines_.begin();
01061          lit != debug_lines_.end(); lit++)
01062     {
01063       vsol_line_2d_sptr l = this->transform_line(h,*lit);
01064       temp.push_back(l);
01065     }
01066     debug_lines_.clear();
01067     debug_lines_ = temp;
01068   }
01069 
01070   if (length_sum)
01071     for (vcl_vector<double>::iterator wit = weights.begin();
01072          wit != weights.end(); wit++)
01073       (*wit)/=length_sum;
01074 
01075   vgl_h_matrix_2d_compute_linear hcl;
01076 #if 0
01077   double error_term = -1;
01078 #endif
01079   H = hcl.compute(lines_image, lines_grid, weights /* , error_term */);
01080   vcl_cout << "Translation\n" << H << '\n';
01081 #if 0
01082   vcl_cout << "Error Term = " << error_term << "\n\n";
01083 #endif
01084 
01085   return true;
01086 }
01087 
01088 //: The user will select the four corners of the grid in order to provide
01089 //  a rough estimate of the homography, then grid lines will be used to
01090 //  calculate a fine-tuned homography
01091 bool sdet_grid_finder::compute_manual_homography(vsol_point_2d_sptr ul,
01092                                                  vsol_point_2d_sptr ur,
01093                                                  vsol_point_2d_sptr lr,
01094                                                  vsol_point_2d_sptr ll)
01095 {
01096   // compute initial homography estimate based on manually picked points
01097   vcl_vector<vgl_homg_point_2d<double> > image_pts, grid_pts;
01098   // manually selected image points
01099   vgl_homg_point_2d<double> im_ul(ul->x(), ul->y());
01100   vgl_homg_point_2d<double> im_ur(ur->x(), ur->y());
01101   vgl_homg_point_2d<double> im_lr(lr->x(), lr->y());
01102   vgl_homg_point_2d<double> im_ll(ll->x(), ll->y());
01103   image_pts.push_back(im_ul);
01104   image_pts.push_back(im_ur);
01105   image_pts.push_back(im_lr);
01106   image_pts.push_back(im_ll);
01107   // grid corners
01108   double min_x = 0, min_y = 0;
01109   double max_x = (n_lines_x_ - 1) * spacing_;
01110   double max_y = (n_lines_y_ - 1) * spacing_;
01111   vgl_homg_point_2d<double> gr_ul(min_x, min_y);
01112   vgl_homg_point_2d<double> gr_ur(max_x, min_y);
01113   vgl_homg_point_2d<double> gr_lr(max_x, max_y);
01114   vgl_homg_point_2d<double> gr_ll(min_x, max_y);
01115   grid_pts.push_back(gr_ul);
01116   grid_pts.push_back(gr_ur);
01117   grid_pts.push_back(gr_lr);
01118   grid_pts.push_back(gr_ll);
01119   vgl_h_matrix_2d_compute_linear hcl;
01120   vgl_h_matrix_2d<double> H = hcl.compute(image_pts, grid_pts);
01121   vcl_cout << "initial homography estimate\n" << H << '\n';
01122   vgl_h_matrix_2d<double> Hinv = H.get_inverse();
01123   vcl_cout << "initial homography estimate inverse\n" << Hinv << '\n';
01124 
01125   // associate image lines with grid lines and compute fine-tuned homography
01126   double bound_x_min = min_x - 5;
01127   double bound_x_max = max_x + 5;
01128   double bound_y_min = min_y - 5;
01129   double bound_y_max = max_y + 5;
01130   vcl_vector<vgl_homg_line_2d<double> > grid_lines, image_lines;
01131   vcl_vector<double> weights;
01132   double length_sum = 0;
01133 
01134   for (vcl_vector<vsol_line_2d_sptr>::const_iterator lit = lines_.begin();
01135        lit != lines_.end(); lit++)
01136   {
01137     // make sure line is correctly defined
01138     if ( *((*lit)->p0()) == *((*lit)->p1()) )
01139       continue;
01140 
01141     vgl_homg_line_2d<double> homgl = (*lit)->vgl_hline_2d();
01142     homgl.normalize();
01143     // use homography estimate to transform line
01144     vsol_line_2d_sptr tline = this->transform_line(H,*lit);
01145 
01146 #ifdef USE_HTLINE // currently not active
01147     vgl_homg_line_2d<double> htline = tline->vgl_hline_2d();
01148     htline.normalize();
01149 #endif
01150     // weed out lines not in bounding box of grid
01151     if ( (tline->p0()->x() > bound_x_max) || (tline->p0()->x() < bound_x_min) ||
01152          (tline->p0()->y() > bound_y_max) || (tline->p0()->y() < bound_y_min) ||
01153          (tline->p1()->y() > bound_x_max) || (tline->p1()->x() < bound_x_min) ||
01154          (tline->p1()->y() > bound_y_max) || (tline->p1()->y() < bound_y_min) )
01155       continue;
01156     // weed out lines not close to 0 or 90 degrees
01157     vgl_vector_2d<double> direction = tline->direction();
01158     // normalize direction vector
01159     double direction_len = direction.length();
01160     direction.set(direction.x()/direction_len,
01161                   direction.y()/direction_len);
01162     double max_orthog_component = 0.1;
01163 
01164     if (vcl_fabs(direction.y()) < max_orthog_component)
01165     {
01166       // horizontal line
01167       // find closest grid line
01168       double length = (*lit)->length();
01169 #ifdef USE_HTLINE // currently not active
01170       int closest_line = vnl_math_rnd(-htline.c() / spacing_);
01171 #else
01172       int closest_line = vnl_math_rnd(tline->middle()->y() / spacing_);
01173 #endif
01174       // TODO: weed out lines not close enough to closest grid line
01175       vgl_homg_line_2d<double> gl(0.0, 1.0, -spacing_*closest_line);
01176       grid_lines.push_back(gl);
01177       length_sum += length;
01178       weights.push_back(length);
01179       image_lines.push_back(homgl);
01180       if (debug_state_==TRANS_PERIM_LINES)
01181         debug_lines_.push_back(*lit);
01182       if (debug_state_==AFFINE_GROUP_AFTER_TRANS)
01183         debug_lines_.push_back(tline);
01184     }
01185     else if (vcl_fabs(direction.x()) < max_orthog_component)
01186     {
01187       // vertical line
01188       // find closest grid line
01189       double length = (*lit)->length();
01190       vgl_homg_line_2d<double> homgl = (*lit)->vgl_hline_2d();
01191       homgl.normalize();
01192 #ifdef USE_HTLINE // currently not active
01193       int closest_line = vnl_math_rnd(-htline.c() / spacing_);
01194 #else
01195       int closest_line = vnl_math_rnd(tline->middle()->x() / spacing_);
01196 #endif
01197       // TODO: weed out lines not close enough to closest grid line
01198       vgl_homg_line_2d<double> gl(1.0, 0.0, -spacing_*closest_line);
01199       grid_lines.push_back(gl);
01200       length_sum += length;
01201       weights.push_back(length);
01202       image_lines.push_back(homgl);
01203       if (debug_state_==TRANS_PERIM_LINES)
01204         debug_lines_.push_back(*lit);
01205       if (debug_state_==AFFINE_GROUP_AFTER_TRANS)
01206         debug_lines_.push_back(tline);
01207     }
01208   }
01209   if (length_sum)
01210     for (vcl_vector<double>::iterator wit = weights.begin();
01211          wit != weights.end(); wit++)
01212       (*wit)/=length_sum;
01213 
01214 #if 0
01215   double error_term = -1;
01216 #endif
01217   H = hcl.compute(image_lines, grid_lines, weights /*, error_term */);
01218   vcl_cout << "fine tuned homography\n" << H << '\n';
01219   Hinv = H.get_inverse();
01220   vcl_cout << "inverse H =\n" << Hinv << '\n';
01221 #if 0
01222   vcl_cout << "Error Term = " << error_term << "\n\n";
01223 #endif
01224 
01225   homography_ = H;
01226 
01227   homography_valid_ = true;
01228 
01229   return true;
01230 }
01231 
01232 bool sdet_grid_finder::compute_homography()
01233 {
01234   static int framenum = 0;
01235   vcl_cout << "framenum = "<<framenum++<<'\n';
01236 
01237   if (!this->compute_projective_homography())
01238   {
01239     vcl_cout << "In sdet_grid_finder::compute_homography() -"
01240              << " projective homography failed\n";
01241     return false;
01242   }
01243 
01244   if (!this->compute_affine_homography())
01245   {
01246     vcl_cout << "In sdet_grid_finder::compute_homography() -"
01247              << " affine homography failed\n";
01248     return false;
01249   }
01250 
01251   homography_ = affine_homography_*projective_homography_;
01252 
01253   if (verbose_)
01254   {
01255     vcl_cout << "The composite homography\n" << homography_ << '\n';
01256   }
01257 
01258   double det = vnl_det(homography_.get_matrix());
01259   if (det != 0.0)
01260     homography_valid_ = true;
01261 
01262   return true;
01263 }
01264 
01265 //------------------------------------------------------------------------
01266 //: Transform a vsol line using the point transform
01267 //
01268 vsol_line_2d_sptr
01269 sdet_grid_finder::transform_line(vgl_h_matrix_2d<double> const& h,
01270                                  vsol_line_2d_sptr const & l)
01271 {
01272   vsol_point_2d_sptr p0 = l->p0();
01273   vsol_point_2d_sptr p1 = l->p1();
01274   vgl_homg_point_2d<double> vp0(p0->x(), p0->y());
01275   vgl_homg_point_2d<double> vp1(p1->x(), p1->y());
01276   vgl_point_2d<double> tvp0 = h(vp0);
01277   vgl_point_2d<double> tvp1 = h(vp1);
01278   vsol_point_2d_sptr tp0 = new vsol_point_2d(tvp0.x(), tvp0.y());
01279   vsol_point_2d_sptr tp1 = new vsol_point_2d(tvp1.x(), tvp1.y());
01280   return new vsol_line_2d(tp0, tp1);
01281 }
01282 
01283 bool
01284 sdet_grid_finder::get_affine_lines(vcl_vector<vsol_line_2d_sptr> & lines)
01285 {
01286   lines = display_lines_;
01287   return true;
01288 }
01289 
01290 bool
01291 sdet_grid_finder::get_grid_points(vcl_vector<double> &image_x, vcl_vector<double> &image_y)
01292 {
01293   if (!homography_valid_)
01294     return false;
01295 
01296   // grid points are stored in image_x_ and image_y_ here, in
01297   // anticipation of being used elsewhere by this class
01298 
01299   image_x.clear();
01300   image_y.clear();
01301   image_x_.clear();
01302   image_y_.clear();
01303   vgl_h_matrix_2d<double> grid_to_image = homography_.get_inverse();
01304 
01305   // x/y mismatch ok - see comment in sdet_grid_finder_params.h
01306   for (int x = 0; x < n_lines_y_; x++)
01307   {
01308     for (int y = 0; y < n_lines_x_; y++)
01309     {
01310       vgl_homg_point_2d<double> hp(x*spacing_, y*spacing_);
01311       vgl_homg_point_2d<double> thp = grid_to_image(hp);
01312       double imgx = thp.x() / thp.w();
01313       double imgy = thp.y() / thp.w();
01314       image_x_.push_back(imgx);
01315       image_y_.push_back(imgy);
01316     }
01317   }
01318   image_x = image_x_;
01319   image_y = image_y_;
01320   return true;
01321 }
01322 
01323 bool
01324 sdet_grid_finder::get_debug_lines(vcl_vector<vsol_line_2d_sptr> & lines)
01325 {
01326   lines.clear();
01327   lines = debug_lines_;
01328   return true;
01329 }
01330 
01331 bool
01332 sdet_grid_finder::get_debug_grid_lines(vcl_vector<vsol_line_2d_sptr> & lines)
01333 {
01334   lines.clear();
01335   lines = debug_grid_lines_;
01336   return true;
01337 }
01338 
01339 bool
01340 sdet_grid_finder::get_matched_lines(vcl_vector<vsol_line_2d_sptr> & lines)
01341 {
01342   lines = matched_lines_;
01343   return true;
01344 }
01345 
01346 //-------------------------------------------------------------------------
01347 //: Get the original set of lines mapped by the line homography
01348 //
01349 bool
01350 sdet_grid_finder::get_mapped_lines(vcl_vector<vsol_line_2d_sptr> & lines)
01351 {
01352   if (!homography_valid_)
01353     return false;
01354   lines.clear();
01355   for (vcl_vector<vsol_line_2d_sptr>::iterator lit = lines_.begin();
01356        lit != lines_.end(); lit++)
01357   {
01358     vsol_line_2d_sptr l = this->transform_line(homography_,*lit);
01359     lines.push_back(l);
01360   }
01361   return true;
01362 }
01363 
01364 
01365 //-------------------------------------------------------------------------
01366 //: Get the grid lines mapped back onto the image
01367 //
01368 bool
01369 sdet_grid_finder::get_backprojected_grid(vcl_vector<vsol_line_2d_sptr> & lines)
01370 {
01371   if (!homography_valid_)
01372     return false;
01373 
01374   lines.clear();
01375   vgl_h_matrix_2d<double> grid_to_image = homography_.get_inverse();
01376 
01377   // transform the vertical grid lines back to the image
01378   for (int y = 0; y<n_lines_y_; y++)
01379   {
01380     double xv = y*spacing_, maxy = (n_lines_x_-1)*spacing_;
01381     vgl_homg_point_2d<double> p0(xv,0), p1(xv, maxy);
01382     vgl_homg_point_2d<double> tp0, tp1;
01383     tp0 = grid_to_image(p0);  tp1 = grid_to_image(p1);
01384     vsol_point_2d_sptr sp0 = new vsol_point_2d(tp0);
01385     vsol_point_2d_sptr sp1 = new vsol_point_2d(tp1);
01386     vsol_line_2d_sptr lv= new vsol_line_2d(sp0, sp1);
01387     lines.push_back(lv);
01388   }
01389   // transform the horizontal grid lines back to the image
01390   for (int x = 0; x<n_lines_x_; x++)
01391   {
01392     double yv = x*spacing_, maxx = (n_lines_y_-1)*spacing_;
01393     vgl_homg_point_2d<double> p0(0,yv), p1(maxx, yv);
01394     vgl_homg_point_2d<double> tp0, tp1;
01395     tp0 = grid_to_image(p0);  tp1 = grid_to_image(p1);
01396     vsol_point_2d_sptr sp0 = new vsol_point_2d(tp0);
01397     vsol_point_2d_sptr sp1 = new vsol_point_2d(tp1);
01398     vsol_line_2d_sptr lv = new vsol_line_2d(sp0, sp1);
01399     lines.push_back(lv);
01400   }
01401 #ifdef DEBUG
01402   // temp for kongbin -DEC
01403    write_image_points(grid_to_image);
01404 #endif
01405 
01406   return true;
01407 }
01408 
01409 //: initialize and output file. The file will be in the format taken by
01410 //  the zhang calibration routine located in bmvl/bcal
01411 bool sdet_grid_finder::init_output_file(vcl_ofstream & outstream)
01412 {
01413   outstream << (n_lines_x_*n_lines_y_) << '\n';
01414   // x/y mismatch ok - see comment in sdet_grid_finder_params.h
01415   for (int x = 0; x < n_lines_y_; x++)
01416   {
01417     for (int y = 0; y < n_lines_x_; y++)
01418     {
01419       outstream << x*spacing_<<' '<<y*spacing_<<'\n';
01420     }
01421   }
01422   outstream << "NUM_VIEWS_PLACEHOLDER\n";
01423   return true;
01424 }
01425 
01426 //: append the output file with points calculated using homography_
01427 bool sdet_grid_finder::write_image_points(vcl_ofstream & outstream)
01428 {
01429   vgl_h_matrix_2d<double> grid_to_image = homography_.get_inverse();
01430 
01431   // x/y mismatch ok - see comment in sdet_grid_finder_params.h
01432   for (int x=0; x<n_lines_y_; x++)
01433   {
01434     for (int y=0; y<n_lines_x_; y++)
01435     {
01436       vgl_homg_point_2d<double> hp(x*spacing_, y*spacing_);
01437       vgl_homg_point_2d<double> thp = grid_to_image(hp);
01438       double image_x = thp.x() / thp.w();
01439       double image_y = thp.y() / thp.w();
01440       outstream << image_x <<' '<<image_y<<'\n';
01441     }
01442   }
01443   outstream << '\n';
01444 
01445   return true;
01446 }
01447 
01448 
01449 bool sdet_grid_finder::transform_grid_points(vnl_matrix_fixed<double,3,3> & K,
01450                                              vnl_matrix_fixed<double,3,4> & M,
01451                                              vcl_vector<vsol_point_2d_sptr> & points)
01452 {
01453   vnl_matrix_fixed<double,3,4> grid_to_image_debug = K*M;
01454   // transform the vertical grid lines back to the image
01455   for (int x = 0; x<n_lines_x_; x++)
01456   {
01457     for (int y = 0; y<n_lines_y_; y++)
01458     {
01459       vnl_matrix_fixed<double,4,1> p;
01460       p.put(0,0,x*spacing_); p.put(1,0,y*spacing_); p.put(2,0,0); p.put(3,0,1);
01461 
01462       vnl_matrix_fixed<double,3,1> tp = grid_to_image_debug * p;
01463 
01464       vsol_point_2d_sptr sp = new vsol_point_2d(tp.get(0,0)/tp.get(2,0),
01465                                                 tp.get(1,0)/tp.get(2,0));
01466       points.push_back(sp);
01467     }
01468   }
01469   return true;
01470 }
01471 
01472 //----------------------------------------------------------
01473 //: Clear internal storage
01474 //
01475 void sdet_grid_finder::clear()
01476 {
01477   lines_.clear();
01478   vanishing_points_valid_=false;
01479   projective_homography_valid_ = false;
01480   homography_valid_ = false;
01481 }
01482 
01483 
01484 //: Check grid match by verifying image intensity values within grid squares
01485 bool sdet_grid_finder::check_grid_match(vil1_image img)
01486 {
01487   if (!homography_valid_)
01488     return false;
01489 
01490   double mean_intensity, intensity_sigma;
01491   // we want to make sure all of the squares in one category (the white squares)
01492   // have intensity greater than all the squares in the other category (black squares)
01493   double square_max_mins[2][2];
01494   // category 1 min
01495   square_max_mins[0][0] = vnl_numeric_traits<double>::maxval;
01496   // category 1 max
01497   square_max_mins[0][1] = 0;
01498   // category 2 min
01499   square_max_mins[1][0] = vnl_numeric_traits<double>::maxval;
01500   // category 2 max
01501   square_max_mins[1][1] = 0;
01502 
01503   int category;
01504   // check left row of grid squares
01505   for (int i = 0; i < n_lines_x_-1; i++)
01506   {
01507     if (!get_square_pixel_stats(img,0,i,mean_intensity,intensity_sigma))
01508       return false;
01509     category = i%2;
01510     if (mean_intensity < square_max_mins[category][0])
01511       square_max_mins[category][0] = mean_intensity;
01512     if (mean_intensity > square_max_mins[category][1])
01513       square_max_mins[category][1] = mean_intensity;
01514   }
01515   // check right row of grid squares
01516   for (int i = 0; i < n_lines_x_-1; i++)
01517   {
01518     if (!get_square_pixel_stats(img,n_lines_y_-2,i,mean_intensity,intensity_sigma))
01519       return false;
01520     category = int(vcl_abs(double((n_lines_y_%2) - (i%2))));
01521     if (mean_intensity < square_max_mins[category][0])
01522       square_max_mins[category][0] = mean_intensity;
01523     if (mean_intensity > square_max_mins[category][1])
01524       square_max_mins[category][1] = mean_intensity;
01525   }
01526   // check upper row of grid squares
01527   for (int i = 0; i < n_lines_y_-1; i++)
01528   {
01529     if (!get_square_pixel_stats(img,i,0,mean_intensity,intensity_sigma))
01530       return false;
01531     category = (i%2);
01532     if (mean_intensity < square_max_mins[category][0])
01533       square_max_mins[category][0] = mean_intensity;
01534     if (mean_intensity > square_max_mins[category][1])
01535       square_max_mins[category][1] = mean_intensity;
01536   }
01537   // check lower row of grid squares
01538   for (int i = 0; i < n_lines_y_-1; i++)
01539   {
01540     if (!get_square_pixel_stats(img,i,n_lines_x_-2,mean_intensity,intensity_sigma))
01541       return false;
01542     category = int(vcl_abs(double((n_lines_x_%2) - (i%2))));
01543     if (mean_intensity < square_max_mins[category][0])
01544       square_max_mins[category][0] = mean_intensity;
01545     if (mean_intensity > square_max_mins[category][1])
01546       square_max_mins[category][1] = mean_intensity;
01547   }
01548   vcl_cout << "cat 1: min = "<<square_max_mins[0][0]<<" max = "<<square_max_mins[0][1]<<'\n'
01549            << "cat 2: min = "<<square_max_mins[1][0]<<" max = "<<square_max_mins[1][1]<<'\n';
01550 
01551   if ( (square_max_mins[0][0] > square_max_mins[1][1]) ||
01552        (square_max_mins[1][0] > square_max_mins[0][1]) )
01553   {
01554     return true;
01555   }
01556   else
01557   {
01558     vcl_cout << "INVALID GRID MATCH\n";
01559     return false;
01560   }
01561 }
01562 
01563 //: gets pixels stats from img within grid square specified by x,y
01564 //  Not returning a valid sigma value for now -DEC
01565 bool sdet_grid_finder::get_square_pixel_stats(vil1_image img,
01566                                               int x,int y,
01567                                               double & mean_intensity,
01568                                               double & /* intensity_sigma */)
01569 {
01570   gevd_bufferxy img_buff(img);
01571 
01572   vgl_homg_point_2d<double> gul(x*spacing_,y*spacing_);
01573   vgl_homg_point_2d<double> gur((x+1)*spacing_,y*spacing_);
01574   vgl_homg_point_2d<double> glr((x+1)*spacing_,(y+1)*spacing_);
01575   vgl_homg_point_2d<double> gll(x*spacing_,(y+1)*spacing_);
01576   vgl_homg_point_2d<double> ul,ur,lr,ll;
01577 
01578   vgl_h_matrix_2d<double> grid_to_image = homography_.get_inverse();
01579 
01580   ul = grid_to_image(gul); ul.set(ul.x()/ul.w(), ul.y()/ul.w(),1.0);
01581   ur = grid_to_image(gur); ur.set(ur.x()/ur.w(), ur.y()/ur.w(),1.0);
01582   lr = grid_to_image(glr); lr.set(lr.x()/lr.w(), lr.y()/lr.w(),1.0);
01583   ll = grid_to_image(gll); ll.set(ll.x()/ll.w(), ll.y()/ll.w(),1.0);
01584 
01585 #ifdef DEBUG
01586   vcl_cout << "ul "<<ul<<" ur "<<ur<<" lr "<<lr<<" ll "<<ll<<'\n';
01587 #endif
01588   // add/subtract 1 from min_y and max_y to account for possible rounding error
01589   int min_y =
01590     vnl_math_rnd(vnl_math_min(vnl_math_min(vnl_math_min(ul.y(),ur.y()),ll.y()),lr.y())) -1;
01591   int max_y =
01592     vnl_math_rnd(vnl_math_max(vnl_math_max(vnl_math_max(ul.y(),ur.y()),ll.y()),lr.y())) +1;
01593 
01594   int n_scan_rows = max_y - min_y + 1;
01595 
01596   if (n_scan_rows < 2*n_lines_x_)
01597   {
01598     // less than 2 pixels per square - something 's wrong
01599     return false;
01600   }
01601 
01602   vnl_matrix<int> scan_rows(n_scan_rows,2);
01603 
01604   for (int i = 0; i < n_scan_rows; i++)
01605   {
01606     scan_rows[i][0] = vnl_numeric_traits<int>::maxval;
01607     scan_rows[i][1] = -vnl_numeric_traits<int>::maxval;
01608   }
01609 
01610   // start_t and end_t are set so we stay within the square region,
01611   // avoiding the boundary pixels
01612 
01613   // upper line
01614   int start_t = vnl_math_rnd(ul.x()+0.5);
01615   int end_t = vnl_math_rnd(ur.x()-0.5);
01616   double slope = (ur.y() - ul.y())/(ur.x() - ul.x());
01617   double real_pix = ul.y();
01618   int rounded_pix;
01619   int row_idx;
01620 #ifdef DEBUG
01621   vcl_cout << "start_t = "<<start_t<<" end_t= "<<end_t<<" slope = "<<slope<<'\n';
01622 #endif
01623   for (int t = start_t; t <= end_t; t++)
01624   {
01625     rounded_pix = vnl_math_rnd(real_pix);
01626     row_idx = rounded_pix - min_y;
01627     scan_rows[row_idx][0] = vnl_math_min(scan_rows[row_idx][0],t);
01628     scan_rows[row_idx][1] = vnl_math_max(scan_rows[row_idx][1],t);
01629     real_pix += slope;
01630   }
01631 
01632   // lower line
01633   start_t = vnl_math_rnd(ll.x()+0.5);
01634   end_t = vnl_math_rnd(lr.x()-0.5);
01635   slope = (lr.y() - ll.y())/(lr.x() - ll.x());
01636   real_pix = ll.y();
01637 #ifdef DEBUG
01638   vcl_cout << "start_t = "<<start_t<<" end_t= "<<end_t<<" slope = "<<slope<<'\n';
01639 #endif
01640   for (int t = start_t; t <= end_t; t++)
01641   {
01642     rounded_pix = vnl_math_rnd(real_pix);
01643     row_idx = rounded_pix - min_y;
01644     scan_rows[row_idx][0] = vnl_math_min(scan_rows[row_idx][0],t);
01645     scan_rows[row_idx][1] = vnl_math_max(scan_rows[row_idx][1],t);
01646     real_pix += slope;
01647   }
01648 
01649   // left line
01650   start_t = vnl_math_rnd(ul.y()+0.5);
01651   end_t = vnl_math_rnd(ll.y()-0.5);
01652   slope = (ll.x() - ul.x())/(ll.y() - ul.y());
01653   real_pix = ul.x();
01654 #ifdef DEBUG
01655   vcl_cout << "start_t = "<<start_t<<" end_t= "<<end_t<<" slope = "<<slope<<'\n';
01656 #endif
01657   for (int t = start_t; t <= end_t; t++)
01658   {
01659     rounded_pix = vnl_math_rnd(real_pix);
01660     row_idx = t - min_y;
01661     scan_rows[row_idx][0] = vnl_math_min(scan_rows[row_idx][0],rounded_pix);
01662     scan_rows[row_idx][1] = vnl_math_max(scan_rows[row_idx][1],rounded_pix);
01663     real_pix += slope;
01664   }
01665 
01666   // right line
01667   start_t = vnl_math_rnd(ur.y()+0.5);
01668   end_t = vnl_math_rnd(lr.y()-0.5);
01669   slope = (lr.x() - ur.x())/(lr.y() - ur.y());
01670   real_pix = ur.x();
01671 #ifdef DEBUG
01672   vcl_cout << "start_t = "<<start_t<<" end_t= "<<end_t<<" slope = "<<slope<<'\n';
01673 #endif
01674   for (int t = start_t; t <= end_t; t++)
01675   {
01676     rounded_pix = vnl_math_rnd(real_pix);
01677     row_idx = t - min_y;
01678     scan_rows[row_idx][0] = vnl_math_min(scan_rows[row_idx][0],rounded_pix);
01679     scan_rows[row_idx][1] = vnl_math_max(scan_rows[row_idx][1],rounded_pix);
01680     real_pix += slope;
01681   }
01682   // now we can scan the pixels and grab their intensities
01683   double intensity_total = 0.0;
01684   int n_pix = 0;
01685   for (int i = 0; i < n_scan_rows; i++)
01686   {
01687     for (int j = scan_rows[i][0]; j <= scan_rows[i][1]; ++j)
01688     {
01689       int xx = j, yy = i + min_y;
01690       if (xx < 0 || xx >= img_buff.GetSizeX())
01691         continue;
01692       if (yy < 0 || yy >= img_buff.GetSizeY())
01693         continue;
01694       intensity_total+= *((unsigned char*)(img_buff.GetElementAddr(xx,yy)));
01695       ++n_pix;
01696     }
01697   }
01698   if (!n_pix)
01699   {
01700     vcl_cout << "grid square contains 0 pixels\n";
01701     return false;
01702   }
01703   mean_intensity =  intensity_total / n_pix;
01704   if (verbose_)
01705     vcl_cout << '('<<x<<','<<y<<") mean_intensity = "<<mean_intensity<<'\n';
01706   return true;
01707 }
01708