contrib/oxl/osl/osl_fit_lines.cxx
Go to the documentation of this file.
00001 // This is oxl/osl/osl_fit_lines.cxx
00002 #include "osl_fit_lines.h"
00003 //:
00004 //  \file
00005 
00006 #include <vcl_cassert.h>
00007 #include <vcl_iostream.h>
00008 #include <vcl_list.h>
00009 
00010 #include <vnl/vnl_math.h>
00011 
00012 #include <osl/osl_canny_port.h>
00013 
00014 #define warn_if_empty(e) \
00015 if (true && e->size()==0) \
00016   vcl_cerr << __FILE__ ": " << __LINE__ << " edge \'" #e "' has zero length\n";
00017 
00018 #define WARN if (false) { } else (vcl_cerr << __FILE__ ": " << __LINE__ << ' ')
00019 
00020 //--------------------------------------------------------------------------------
00021 
00022 osl_fit_lines::osl_fit_lines(osl_fit_lines_params const & params,
00023                              double scale, double x0, double y0)
00024   : osl_fit_lines_params(params)
00025 {
00026   double temp_thresh = this->threshold_;
00027   if (this->use_square_fit_) temp_thresh *= this->threshold_;
00028   this->threshold_ = temp_thresh;
00029   data_ = new osl_OrthogRegress(scale,x0,y0);
00030 }
00031 
00032 osl_fit_lines::~osl_fit_lines()
00033 {
00034   delete data_; data_ = 0;
00035 }
00036 
00037 //-----------------------------------------------------------------------------
00038 
00039 
00040 // This is the simplest option. A non-incremental fitting
00041 // algorithm that attempts to fits straight lines to the line
00042 // segments. For reasonable behaviour the lines should be broken up into
00043 // smaller segments of similar curvature before calling this function.
00044 // When used in conjunction with BreakLines, it mimics Paul Beardsley's
00045 // old straight line fitting code.
00046 //
00047 void osl_fit_lines::simple_fit_to_list(vcl_list<osl_edge *> *myedges,
00048                                        vcl_list<osl_edge *> *outedges)
00049 {
00050   int edge_no = 0;
00051   vcl_list<osl_edgel_chain*> curves;
00052   for (vcl_list<osl_edge*>::iterator i=myedges->begin(); i!=myedges->end(); ++i)
00053   {
00054     osl_edge *edge = *i;
00055 
00056     bool angle_ok = true;
00057     {
00058       osl_edgel_chain *dc = edge;//->GetCurve()->CastToDigitalCurve();
00059       assert(dc!=0);
00060       bool success = false;
00061 
00062       // If the osl_edgel_chain is long enough fit
00063       if ( dc->size() >= min_fit_length_ )
00064       {
00065         double using_degrees = 1.0;
00066         // Since some code sets Theta in degrees, and some radians!
00067         // This is an attempt to guess whether the angles are degrees
00068         // or radians
00069         for (unsigned int ii=0; ii<dc->size(); ii++)
00070         {
00071           if (dc->GetTheta(ii) > 3.2 || dc->GetTheta(ii) < -3.2)
00072           {
00073             using_degrees = vnl_math::pi_over_180;
00074             break;
00075           }
00076         }
00077         data_->Reset();
00078         double angle = vnl_math::pi_over_4;
00079         int  orient0 = int((dc->GetTheta(0) * using_degrees + angle/2.0) / angle);
00080         for (unsigned int ii=0;ii<dc->size();ii++)
00081         {
00082           data_->IncrByXY(dc->GetX(ii), dc->GetY(ii));
00083           int orient = int((dc->GetTheta(ii) * using_degrees + angle/2.0)/angle);
00084           int diff = vnl_math_abs(orient - orient0);
00085           if (diff > 1 && diff < 7)
00086             angle_ok = false;
00087         }
00088         data_->Fit();
00089         float mean_cost = MyGetCost(data_, 0, dc->size(), dc);
00090         float ls_cost = (float)data_->GetCost();
00091         if ((use_square_fit_ && ls_cost < threshold_) ||
00092             (!use_square_fit_ && mean_cost < threshold_ && angle_ok))
00093         {
00094           success = true;
00095           old_finish_ = 0;
00096           if (use_square_fit_)
00097             OutputLine(&curves, 0, dc->size(), dc, ls_cost);
00098           else
00099             OutputLine(&curves, 0, dc->size(), dc, mean_cost);
00100         }
00101       }
00102 
00103       if (success) { // ie. we have fitted a line and have no gunk
00104         osl_edgel_chain *mycurve = fsm_pop(&curves);
00105         // Edge will be an implicit digital line, with vertices on
00106         // the original curve. However, the start and end point of
00107         // the curve will not be the same as the curve data.
00108         //was:edge->SetCurve(mycurve);
00109         osl_edge *newedge = new osl_edge(*mycurve, edge->GetV1(), edge->GetV2());
00110         delete mycurve;
00111         edge->unref();
00112         edge = newedge;
00113       }
00114       edge->SetId(edge_no);
00115       //edge->GetCurve()->SetId(edge_no++);
00116       edge->ref/*Protect*/();
00117       outedges->push_front(edge);
00118       warn_if_empty(edge);
00119     }
00120   }
00121 }
00122 
00123 //-----------------------------------------------------------------------------
00124 //:
00125 // Method that takes a vcl_list<Edge*>* that represents the
00126 // current segmentation and breaks off each Edge for further
00127 // investigation. An incremental fitting process is applied to
00128 // the osl_edgel_chain in each osl_edge. The resultant segmentation
00129 // is then written to output (with edges destroyed).
00130 void osl_fit_lines::incremental_fit_to_list(vcl_list<osl_edge *> *myedges,
00131                                             vcl_list<osl_edge *> *outedges)
00132 {
00133 #ifdef DEBUG
00134   vcl_cout << "Fitting lines to Edge(s), threshold = " << threshold_ << vcl_endl;
00135 #endif
00136   int edge_no = 0;
00137   int vertex_no = 1000;
00138   // Take each Edge in turn and attempt to fit
00139   // a number of straight lines to it
00140 
00141   outedges->clear();
00142   for (vcl_list<osl_edge*>::iterator iter=myedges->begin(); iter!=myedges->end(); ++iter)
00143   {
00144     osl_edge *edge = *iter;
00145     ++edge_no;
00146 
00147     vcl_list<osl_edgel_chain*> curves;
00148     if (use_square_fit_)
00149       SquareIncrementalFit(&curves, edge);
00150     else
00151       MeanIncrementalFit(&curves, edge);
00152 
00153     // Within 'curves' we now have a list of osl_edgel_chains for which the
00154     // fitting succeeded and some for which line fitting failed.
00155     // We want to develop a topology structure from the list that
00156     // will replace the current edge (so that it has consistent endpoints)
00157     // with a number of other edges.
00158 
00159     // If we have failed to find a fit.
00160     if ( curves.empty() )
00161     {
00162       edge->SetId(edge_no);
00163       outedges->push_front(edge);
00164       warn_if_empty(edge);
00165     }
00166 
00167     // If we have fitted a line and have no gunk.
00168     else if ( curves.size() == 1 )
00169     {
00170       osl_edgel_chain *mycurve = fsm_pop(&curves);
00171       outedges->push_front(new osl_edge(*mycurve, edge->GetV1(), edge->GetV2()));
00172       outedges->front()->SetId(edge_no);
00173       warn_if_empty(edge);
00174       edge->unref(); // delete the old one. Can't we reuse it?
00175     }
00176 
00177     // We have multiple lines derived from a single input edge.
00178     else
00179     {
00180       curves.reverse();
00181       osl_vertex *v2 = edge->GetV1();
00182       while (!curves.empty())
00183       {
00184         osl_edgel_chain *next = fsm_pop(&curves);
00185         osl_vertex* v1 = v2;
00186         if (curves.empty()) // last one
00187           v2 = edge->GetV2();
00188         else // somewhere in the middle
00189           v2 = new osl_vertex(next->GetX(next->size()-1), next->GetY(next->size()-1), vertex_no++);
00190         outedges->push_front(new osl_edge(*next, v1, v2));
00191         warn_if_empty(outedges->front());
00192       }
00193     }
00194   }
00195 }
00196 
00197 //-----------------------------------------------------------------------------
00198 //:
00199 // This method is used to fit lines incrementally using the mean
00200 // absolute error instead of the mean square error. The difference
00201 // between this and SquareIncrementalFit is very small.
00202 void osl_fit_lines::MeanIncrementalFit(vcl_list<osl_edgel_chain*> *curves_, osl_edge *edge)
00203 {
00204   //vcl_cerr << "MeanIncrementalFit()\n";
00205   float new_cost, new_est_cost;
00206   // Get the digital curve
00207   osl_edgel_chain *dc = edge;//->GetCurve()->CastToDigitalCurve();
00208   assert(dc!=0);
00209 
00210   // If the EdgelChain is long enough fit
00211   if ( dc->size() < min_fit_length_ )
00212     return;
00213 
00214   bool added = false;
00215   unsigned int i,j,segment_length;
00216 
00217   // Set up the data class for the first set of Edgels in the DigitalCurve
00218   unsigned int start = ignore_end_edgels_;
00219   old_finish_ = 1;  // set old_finish_ so we record from the start
00220   unsigned int finish = start + min_fit_length_;
00221 
00222   data_->Reset();
00223   for (i=start;i<finish;i++)
00224     data_->IncrByXY(dc->GetX(i),dc->GetY(i));
00225 
00226   // Now, until the end of the chain, test whether each
00227   // Edgel belongs to a straight line
00228   while ( finish <= dc->size() )
00229   {
00230     // Define the start and end points of the DigitalCurve segment
00231     segment_length = finish - start;
00232 
00233     // Fit by orthogonal regression
00234     data_->Fit();
00235     new_cost = MyGetCost(data_, start, finish, dc);
00236 
00237     // Now evaluate the fit, and if both good and there are more points, grow
00238     if ( new_cost < threshold_)
00239     {
00240       // If there are no more points, store the current fit,
00241       // and mark finish to ensure the next DigitalCurve is used.
00242       if ( finish == dc->size() )
00243       {
00244         OutputLine(curves_, start,finish-ignore_end_edgels_,dc,new_cost);
00245         finish++;
00246       }
00247       else
00248       {
00249         // Else, if possible, add more points to the current
00250         // interpretation by adapting the data class and moving the
00251         // finish variable. First declare that no points have been
00252         // added, and that the estimated
00253         // fitting cost is equal to the actual cost
00254         added = false;
00255         new_est_cost = new_cost;
00256         while ( (finish<dc->size()) && (new_est_cost<threshold_ ) )
00257         {
00258           // Compute an upper bound estimate on the fitting cost
00259           // by adding the distance of the next point to the
00260           // currently fitted line
00261           double distance =
00262             data_->GetA()*dc->GetX(finish) + data_->GetB()*dc->GetY(finish) + data_->GetC();
00263           new_est_cost = float(segment_length*new_est_cost + vcl_abs(distance)) / (segment_length+1);
00264 
00265           // If this residual is low enough, include the point within
00266           // the orthogonal regression data class
00267           if ( new_est_cost < threshold_ )
00268           {
00269             data_->IncrByXY(dc->GetX(finish),dc->GetY(finish));
00270             finish++;
00271             segment_length++;
00272             added = true;
00273           }
00274         }
00275 
00276         // If no points can be added, output the line, store the fit,
00277         // and reset the data class
00278         if ( !added )
00279         {
00280           // capes Aug 1999 - now backtrack by ignore_end_edgels_ and
00281           // refit to avoid fitting to the garbage end edgels which are curving away
00282           // from the line
00283           for (unsigned int ii=0; ii<ignore_end_edgels_; ++ii)
00284             data_->DecrByXY(dc->GetX(finish-1-ii), dc->GetY(finish-1-ii));
00285           data_->Fit();
00286           new_cost = MyGetCost(data_, start, finish-ignore_end_edgels_, dc);
00287           OutputLine(curves_, start,finish-ignore_end_edgels_,dc,new_cost);
00288           start = finish+ignore_end_edgels_;  finish = start + min_fit_length_;
00289           if ( finish<=dc->size() )
00290           {
00291             data_->Reset();
00292             for (i=start;i<finish;i++)
00293               data_->IncrByXY(dc->GetX(i),dc->GetY(i));
00294           }
00295         }
00296       }
00297     }
00298 
00299     // Else the fit is not good enough. We therefore remove the first
00300     // point and add or delete points from the end of the current line
00301     // segment until the resulting segment length is min_fit_length_
00302     else
00303     {
00304       // Unfortunately, using the mean absolute error sometimes
00305       // means that we have to rewind a bit from the end before
00306       // drawing the next straight line.
00307       if ( added )
00308       {
00309         added = false;
00310         i = finish;
00311         while (new_cost > threshold_ && segment_length > min_fit_length_)
00312         {
00313           data_->DecrByXY(dc->GetX(i), dc->GetY(i));
00314           i --;
00315           segment_length --;
00316           data_->Fit();
00317           new_cost = MyGetCost(data_, start, i, dc);
00318         }
00319         finish = i;
00320         OutputLine(curves_, start,finish,dc,new_cost);
00321         start = finish;  finish = start + min_fit_length_;
00322         if ( finish<=dc->size() )
00323         {
00324           data_->Reset();
00325           for (i=start;i<finish;i++)
00326             data_->IncrByXY(dc->GetX(i),dc->GetY(i));
00327         }
00328       }
00329       else
00330       {
00331         data_->DecrByXY(dc->GetX(start),dc->GetY(start));
00332         start++;
00333 
00334         if ( segment_length > min_fit_length_ )
00335           while ( segment_length > min_fit_length_+1 )
00336           {
00337             data_->DecrByXY(dc->GetX(finish),dc->GetY(finish));
00338             finish--;
00339             segment_length--;
00340           }
00341         else if ( finish<dc->size() )
00342         {
00343           data_->IncrByXY(dc->GetX(finish),dc->GetY(finish));
00344           finish++;
00345         }
00346 
00347         else
00348           finish++;
00349       }
00350     }
00351   }
00352 
00353   // Finally, record any junk edgels at the end of the edgelchain that
00354   // have not been described by a straight line, that is so long as
00355   // a line has been fitted
00356   unsigned int length = dc->size() - old_finish_;
00357   if ( curves_->size() && length )
00358   {
00359     osl_edgel_chain *ndc = new osl_edgel_chain(length);
00360     for (i=0,j=old_finish_;j<dc->size();i++,j++)
00361     {
00362       ndc->SetX(dc->GetX(j),i);
00363       ndc->SetY(dc->GetY(j),i);
00364       ndc->SetGrad(dc->GetGrad(j),i);
00365       ndc->SetTheta(dc->GetTheta(j),i);
00366     }
00367     curves_->push_front(ndc);
00368   }
00369 }
00370 
00371 //--------------------------------------------------------------------------------
00372 //:
00373 // Method that takes the canny edge data stored in an edge
00374 // and its associated DigitalCurve, and fits lines using
00375 // orthogonal regression with mean square error residual and incremental
00376 // fitting.
00377 void osl_fit_lines::SquareIncrementalFit(vcl_list<osl_edgel_chain*> *curves_, osl_edge *edge)
00378 {
00379 #ifdef DEBUG
00380   vcl_cerr << "SquareIncrementalFit()\n";
00381 #endif
00382   // Get the digital curve
00383   osl_edgel_chain *dc = edge;//->GetCurve()->CastToDigitalCurve();
00384   assert(dc!=0);
00385 
00386   // If the EdgelChain is long enough fit
00387   if ( dc->size() < min_fit_length_ )
00388     return;
00389 
00390   bool added = false;
00391   unsigned int i,j,segment_length;
00392 
00393   // Set up the data class for the first set of Edgels in the DigitalCurve
00394   unsigned int start = ignore_end_edgels_;
00395   old_finish_ = 1;  // set old_finish_ so we record from the start
00396   unsigned int finish = start + min_fit_length_;
00397 
00398   data_->Reset();
00399   for (i=start;i<finish;i++)
00400     data_->IncrByXY(dc->GetX(i),dc->GetY(i));
00401 
00402   // Now, until the end of the chain, test whether each
00403   // Edgel belongs to a straight line
00404   while ( finish <= dc->size() )
00405   {
00406     // Define the start and end points of the DigitalCurve segment
00407     segment_length = finish - start;
00408 
00409     // Fit by orthogonal regression
00410     data_->Fit();
00411 
00412 
00413     // Now evaluate the fit, and if both good and there are more points, grow
00414     if ( data_->GetCost() < threshold_ )
00415     {
00416       // If there are no more points, store the current fit,
00417       // and mark finish to ensure the next DigitalCurve is used.
00418       if ( finish == dc->size() )
00419       {
00420         OutputLine(curves_, start,finish-ignore_end_edgels_,dc,float(data_->GetCost()));
00421         finish++;
00422       }
00423 
00424       else
00425       {
00426         // Else, if possible, add more points to the current
00427         // interpretation by adapting the data class and moving the
00428         // finish variable. First declare that no points have been
00429         // added, and that the estimated
00430         // fitting cost is equal to the actual cost
00431         added = false;
00432         data_->SetEstCost(data_->GetCost());
00433         while ( (finish<dc->size()) && (data_->GetEstCost()<threshold_) )
00434         {
00435           // Compute an upper bound estimate on the fitting cost
00436           // by adding the distance of the next point to the
00437           // currently fitted line
00438           double distance =
00439             data_->GetA()*dc->GetX(finish) + data_->GetB()*dc->GetY(finish) + data_->GetC();
00440           data_->SetEstCost( (segment_length*data_->GetEstCost() + distance*distance) / (segment_length+1) );
00441 
00442           // If this residual is low enough, include the point within
00443           // the orthogonal regression data class
00444           if ( data_->GetEstCost()<threshold_ )
00445           {
00446             data_->IncrByXY(dc->GetX(finish),dc->GetY(finish));
00447             finish++;
00448             segment_length++;
00449             added = true;
00450           }
00451         }
00452 
00453         // If no points can be added, output the line, store the fit,
00454         // and reset the data class
00455         if ( !added )
00456         {
00457           // capes Aug 1999 - now backtrack by ignore_end_edgels_ and
00458           // refit to avoid fitting to the garbage end edgels which are curving away
00459           // from the line
00460           for (unsigned int ii=0; ii<ignore_end_edgels_; ++ii)
00461             data_->DecrByXY(dc->GetX(finish-1-ii), dc->GetY(finish-1-ii));
00462           data_->Fit();
00463 
00464           OutputLine(curves_, start,finish-ignore_end_edgels_,dc,float(data_->GetCost()));
00465           start = finish+ignore_end_edgels_;  finish = start + min_fit_length_;
00466           if ( finish<=dc->size() )
00467           {
00468             data_->Reset();
00469             for (i=start;i<finish;i++)
00470               data_->IncrByXY(dc->GetX(i),dc->GetY(i));
00471           }
00472         }
00473       }
00474     }
00475 
00476     // Else the fit is not good enough. We therefore remove the first
00477     // point and add or delete points from the end of the current line
00478     // segment until the resulting segment length is min_fit_length_
00479     else
00480     {
00481       data_->DecrByXY(dc->GetX(start),dc->GetY(start));
00482       start++;
00483 
00484       if ( segment_length > min_fit_length_ )
00485         while ( (segment_length-1) > min_fit_length_ )
00486         {
00487           data_->DecrByXY(dc->GetX(finish),dc->GetY(finish));
00488           finish--;
00489           segment_length--;
00490         }
00491       else if ( finish<dc->size() )
00492       {
00493         data_->IncrByXY(dc->GetX(finish),dc->GetY(finish));
00494         finish++;
00495       }
00496 
00497       else
00498         finish++;
00499     }
00500   }
00501 
00502   // Finally, record any junk edgels at the end of the edgelchain that
00503   // have not been described by a straight line, that is so long as
00504   // a line has been fitted.
00505   unsigned int length = dc->size() - old_finish_;
00506   if ( curves_->size() && length )
00507   {
00508     osl_edgel_chain *ndc = new osl_edgel_chain(length);
00509     for (i=0,j=old_finish_;j<dc->size();i++,j++)
00510     {
00511       ndc->SetX(dc->GetX(j),i);
00512       ndc->SetY(dc->GetY(j),i);
00513       ndc->SetGrad(dc->GetGrad(j),i);
00514       ndc->SetTheta(dc->GetTheta(j),i);
00515     }
00516     curves_->push_front(ndc);
00517   }
00518 }
00519 
00520 //-----------------------------------------------------------------------------
00521 //
00522 //: Output the fitted line.
00523 void osl_fit_lines::OutputLine(vcl_list<osl_edgel_chain*> *curves_,
00524                                int start, int finish,
00525                                osl_edgel_chain *dc,
00526                                float /*cost*/)
00527 {
00528   // First of all, store the edgel data between the current fit
00529   // and the previous fit to a DigitalCurve
00530   int length = start - old_finish_;
00531   if ( length > 0 )
00532   {
00533     osl_edgel_chain *ndc = new osl_edgel_chain(length);
00534     for (int i=0,j=old_finish_;j<start;i++,j++)
00535     {
00536       ndc->SetX(dc->GetX(j),i);
00537       ndc->SetY(dc->GetY(j),i);
00538       ndc->SetGrad(dc->GetGrad(j),i);
00539       ndc->SetTheta(dc->GetTheta(j),i);
00540     }
00541     curves_->push_front(ndc);
00542   }
00543   old_finish_ = finish;
00544 
00545   // Create an osl_edgel_chain from the fit
00546   osl_edgel_chain *line = new osl_edgel_chain(finish-start);
00547   for (int i=0,j=start;j<finish;i++,j++) {  // Copy the edgels into the new chain
00548     line->SetX(dc->GetX(j),i);
00549     line->SetY(dc->GetY(j),i);
00550     line->SetGrad(dc->GetGrad(j),i);
00551     line->SetTheta(dc->GetTheta(j),i);
00552   }
00553   curves_->push_front(line);
00554 
00555   // and check to see if we can merge two lines together
00556   MergeLines(curves_);
00557 }
00558 
00559 //-----------------------------------------------------------------------------
00560 //:
00561 // Takes the top two lines from the vcl_list<ImplicitDigitalLine*>
00562 // and tests whether a single line fit would satisfy them both. The
00563 // process involves fitting a single line to the other two lines by
00564 // minimising the integral of the Euclidean distance between the
00565 // lines. If the lines appear to come from the same line, a refit
00566 // should be done using the underlying edgel data, *** BUT THIS IS
00567 // NOT YET IMPLEMENTED ***
00568 //
00569 void osl_fit_lines::MergeLines(vcl_list<osl_edgel_chain*> *curves_)
00570 {
00571   if ( curves_->size() < 2 )
00572     return;
00573 
00574   // Take the first two lines off the list (must be careful about the
00575   // ordering if we are to produce a correctly formed EdgelChain).
00576   osl_edgel_chain *dc2 = fsm_pop(curves_);
00577   osl_edgel_chain *dc1 = fsm_pop(curves_);
00578 
00579 #ifndef fsm_is_cute
00580   osl_ortho_regress fitter;
00581   fitter.reset();
00582   for (unsigned int i=0; i<dc1->size(); ++i)
00583     fitter.add_point(dc1->GetX(i), dc1->GetY(i));
00584   for (unsigned int i=0; i<dc2->size(); ++i)
00585     fitter.add_point(dc2->GetX(i), dc2->GetY(i));
00586   double a, b, c;
00587   fitter.fit(&a, &b, &c);
00588   if (fitter.cost(a, b, c) >= threshold_)
00589   {
00590     curves_->push_front(dc1);
00591     curves_->push_front(dc2);
00592   }
00593   else
00594   {
00595     // FIXME: what if endpoints are equal?
00596     osl_edgel_chain *dc = new osl_edgel_chain(dc1->size() + dc2->size());
00597     for (unsigned int i=0; i<dc1->size(); ++i)
00598     {
00599       dc->SetX(dc1->GetX(i), i);
00600       dc->SetY(dc1->GetY(i), i);
00601       dc->SetGrad(dc1->GetGrad(i), i);
00602       dc->SetTheta(dc1->GetTheta(i), i);
00603     }
00604     for (unsigned int i=0; i<dc2->size(); ++i)
00605     {
00606       dc->SetX(dc2->GetX(i), i + dc1->size());
00607       dc->SetY(dc2->GetY(i), i + dc1->size());
00608       dc->SetGrad(dc2->GetGrad(i), i + dc1->size());
00609       dc->SetTheta(dc2->GetTheta(i), i + dc1->size());
00610     }
00611     delete dc1;
00612     delete dc2;
00613     curves_->push_front(dc);
00614   }
00615 
00616 #else
00617   ImplicitDigitalLine *l1 = (ImplicitDigitalLine*) dc1;
00618   ImplicitDigitalLine *l2 = (ImplicitDigitalLine*) dc2;
00619 
00620   // Compute the line parameters
00621   double x1[2],y1[2],x2[2],y2[2];
00622   ImplicitDigitalLine *line = l1;
00623   for (int i=0; i<2; i++,line=l2)
00624   {
00625     x1[i] = line->GetStartX();  y1[i] = line->GetStartY();
00626     x2[i] = line->GetEndX();    y2[i] = line->GetEndY();
00627   }
00628 
00629   // We have problems later if either of the lines are vertical. Therefore
00630   // rotate all of the points by phi which is defined in the following way
00631   // (we have to undo this rotation later):
00632   float xstart = vcl_min( vcl_min(x1[0],x1[1]), vcl_min(x2[0],x2[1]) );
00633   float xfinish= vcl_max( vcl_max(x1[0],x1[1]), vcl_max(x2[0],x2[1]) );
00634   float ystart = vcl_min( vcl_min(y1[0],y1[1]), vcl_min(y2[0],y2[1]) );
00635   float yfinish= vcl_max( vcl_max(y1[0],y1[1]), vcl_max(y2[0],y2[1]) );
00636   float phi = vcl_atan2(yfinish-ystart,xfinish-xstart);
00637   double cp = vcl_cos(phi), sp = vcl_sin(phi);
00638 
00639   for (int i=0;i<2;i++)
00640   {
00641     float x = x1[i]*cp + y1[i]*sp;
00642     y1[i]   = y1[i]*cp - x1[i]*sp;
00643     x1[i] = x;
00644 
00645     x     = x2[i]*cp + y2[i]*sp;
00646     y2[i] = y2[i]*cp - x2[i]*sp;
00647     x2[i] = x;
00648   }
00649 
00650   // Determine the rotated line parameters
00651   double a[2],b[2],c[2];
00652   for (int i=0;i<2;i++)
00653   {
00654     // This next bit for the Bhattacharyya comparison
00655     a[i] = y1[i] - y2[i];
00656     b[i] = x2[i] - x1[i];
00657     c[i] = x1[i]*y2[i] - x2[i]*y1[i];
00658 
00659     double m = vcl_sqrt(a[i]*a[i] + b[i]*b[i]);
00660     if ( b[i] > 0 ) m = -m;
00661 
00662     a[i] /= m;  b[i] /= m;  c[i] /= m;
00663   }
00664 
00665   // Check the angle between the lines, if it is too great return
00666   double theta = 180.0*vcl_acos(a[0]*a[1]+b[0]*b[1])/M_PI;
00667   // Best we can do is eliminate cases that don't double
00668   // back on themselves
00669   if ( theta > 90.0 )
00670     theta = 180.0 - theta;
00671   if ( theta > theta_ )
00672   {
00673     curves_->push_front(dc1);  curves_->push_front(dc2);
00674     return;
00675   }
00676 
00677   // Parameters and scatter matrices
00678   double x1_2,x1_3,x2_2,x2_3;
00679   vnl_matrix<double> S(3,3,0.0);
00680   vnl_matrix<double> Si(3,3),S1i(3,3),S2i(3,3),S3i(3,3);
00681   vnl_matrix<double> S11(2,2),S12(2,1),S_lambda,vec3(1,1);
00682   double S22;
00683   double normaliser=0.0; // Effecively the total number of points in the lines
00684 
00685   // Form a scatter matrix for the two lines
00686   for (int i=0;i<2;i++)
00687   {
00688     // Form the scatter matrices for each line
00689     S1i.put(0,0,1.0);  S1i.put(0,1,-a[i]/b[i]);  S1i.put(0,2,0.0);
00690     S1i.put(1,0,-a[i]/b[i]);  S1i.put(1,1,(a[i]*a[i])/(b[i]*b[i]));
00691     S1i.put(1,2,0.0);
00692     S1i.put(2,0,0.0);  S1i.put(2,1,0.0);         S1i.put(2,2,0.0);
00693 
00694     S2i.put(0,0,0.0);  S2i.put(0,1,-c[i]/(2.0*b[i]));
00695     S2i.put(0,2,0.5);
00696     S2i.put(1,0,-c[i]/(2.0*b[i]));  S2i.put(1,1,(a[i]*c[i])/(b[i]*b[i]));
00697     S2i.put(1,2,-a[i]/(2.0*b[i]));
00698     S2i.put(2,0,0.5);  S2i.put(2,1,-a[i]/(2.0*b[i]));
00699     S2i.put(2,2,0.0);
00700 
00701     S3i.put(0,0,0.0);  S3i.put(0,1,0.0);      S3i.put(0,2,0.0);
00702     S3i.put(1,0,0.0);  S3i.put(1,1,(c[i]*c[i])/(b[i]*b[i]));
00703     S3i.put(1,2,-c[i]/b[i]);
00704     S3i.put(2,0,0.0);  S3i.put(2,1,-c[i]/b[i]);
00705     S3i.put(2,2,1.0);
00706 
00707     x1_2 = x1[i]*x1[i];  x1_3 = x1_2*x1[i];
00708     x2_2 = x2[i]*x2[i];  x2_3 = x2_2*x2[i];
00709 
00710     // Compute the total scatter matrix for each line
00711     if ( x2[i] > x1[i] )
00712     {
00713       Si = (x2_3-x1_3)/3.0*S1i;
00714       Si += (x2_2-x1_2)*S2i;
00715       Si += (x2[i]-x1[i])*S3i;
00716       double s = vcl_fabs(b[i]);
00717       Si *= (1.0/s);
00718     }
00719     else
00720     {
00721       Si = ((x1_3-x2_3)*S1i/3.0+(x1_2-x2_2)*S2i+
00722             (x1[i]-x2[i])*S3i);
00723       Si *= 1/vcl_fabs(b[i]);
00724     }
00725 
00726     // and add this to the total scatter matrix
00727     S += Si;
00728 
00729     // Adjust the normaliser (denonimator of the algebraic distance)
00730     normaliser += vcl_fabs((x2[i]-x1[i])/b[i]);
00731   }
00732 
00733   // Form the sub-partitioned scatter matrices
00734   S11.put(0,0,S.get(0,0));  S11.put(0,1,S.get(0,1));
00735   S11.put(1,0,S.get(1,0));  S11.put(1,1,S.get(1,1));
00736   S12.put(0,0,S.get(0,2));  S12.put(1,0,S.get(1,2));
00737   S22 = S.get(2,2);
00738 
00739   // Now compute the matrix whose eigenvalues are required
00740   S_lambda = S11 - S12 * (1.0/S22) * S12.transpose();
00741 
00742   // Assign data for the Eigenvalue/vector computation
00743   vnl_matrix<double> U(2,2),V(2,2),W(2,2);
00744   vnl_matrix<double> eigenvector(2,1);
00745 
00746   // Compute the Eigenvalues and the fitted line parameters
00747   S_lambda.singular_value_decomposition(U,W,V);
00748 
00749   // Check that the Eigenvalues are both semi-positive
00750   if ((W.get(0,0)<0.0) || (W.get(1,1)<0.0))
00751   {
00752     WARN << "numerical ill-conditioning in MergeLines";
00753     curves_->push_front(l1);  curves_->push_front(l2);
00754     return;
00755   }
00756 
00757   double cost;  // Again this is the mean square distance
00758   if ( W.get(0,0) < W.get(1,1) )
00759   {
00760     cost = W.get(0,0)/normaliser;
00761     eigenvector.put(0,0,U.get(0,0));  eigenvector.put(1,0,U.get(1,0));
00762   }
00763   else
00764   {
00765     cost = W.get(1,1)/normaliser;
00766     eigenvector.put(0,0,U.get(0,1));  eigenvector.put(1,0,U.get(1,1));
00767   }
00768 
00769   // If the cost of the fitted line is too high restore the EdgeList and return
00770   if ( cost >= threshold_ )
00771   {
00772     curves_->push_front(l1);  curves_->push_front(l2);
00773     return;
00774   }
00775   else
00776     WARN << "Lines merged with a cost " << vcl_sqrt(cost) << vcl_endl;
00777 
00778   // We now have the parameters for the re-fitted line
00779   vec3 = - (1.0/S22) * S12.transpose() * eigenvector;
00780   double la,lb,lc;
00781   la = eigenvector.get(0,0);  lb = eigenvector.get(1,0);  lc = vec3(0,0);
00782   double m = vcl_sqrt(la*la+lb*lb);
00783   // Make sure that b >= 0 to be consistent with osl_OrthogRegress - though
00784   // we don't actually store {a,b,c}
00785   if ( lb < 0.0 )
00786     m *= -1.0;
00787   la /= m;  lb /= m;  lc /= m;
00788 
00789   // All that is left to do is to project the endpoints of the
00790   // original lines onto the new line segment and to take the
00791   // extremal ones. Thus we can form a new ImplicitDigitalLine
00792   double xa,ya,xb,yb;
00793   xa = lb*lb*x1[0] - la*lb*y1[0] - la*lc;
00794   ya = la*la*y1[0] - la*lb*x1[0] - lb*lc;
00795   xb = lb*lb*x2[1] - la*lb*y2[1] - la*lc;
00796   yb = la*la*y2[1] - la*lb*x2[1] - lb*lc;
00797 
00798   // Set the new edge-line to have these endpoints after putting back the
00799   // rotation
00800   float XA,YA,XB,YB;
00801   XA = xa*cp - ya*sp;
00802   YA = ya*cp + xa*sp;
00803   XB = xb*cp - yb*sp;
00804   YB = yb*cp + xb*sp;
00805 
00806   // Create a new ImplicitLine and EdgelChain with these parameters
00807   line = new ImplicitDigitalLine(l1->size()+l2->size(),
00808                                  new IUPoint(XA,YA,0.0),
00809                                  new IUPoint(XB,YB,0.0));
00810 
00811   int i = 0;
00812   for (int j=0;j<l1->size();++i,++j) {  // Copy the edgels from l1 to new line
00813     line->SetX(l1->GetX(j),i);
00814     line->SetY(l1->GetY(j),i);
00815     line->SetGrad(l1->GetGrad(j),i);
00816     line->SetTheta(l1->GetTheta(j),i);
00817   }
00818   for (int j=0;j<l2->size();++i,++j) {  // Copy the edgels from l2 to new line
00819     line->SetX(l2->GetX(j),i);
00820     line->SetY(l2->GetY(j),i);
00821     line->SetGrad(l2->GetGrad(j),i);
00822     line->SetTheta(l2->GetTheta(j),i);
00823   }
00824 
00825   // Store the new ImplicitDigitalLine
00826   curves_->push_front(line);
00827 
00828   // and remove the old ones
00829 
00830   osl_IUDelete(l1);  osl_IUDelete(l2);
00831 #endif
00832 }
00833 
00834 //-----------------------------------------------------------------------------
00835 //
00836 //: This method calculates the current mean absolute cost of fitting a line.
00837 float osl_fit_lines::MyGetCost(osl_OrthogRegress const *fitter,
00838                                int start, int finish,
00839                                osl_edgel_chain *dc)
00840 {
00841   double A = fitter->GetA();
00842   double B = fitter->GetB();
00843   double C = fitter->GetC();
00844 
00845   float distance = 0;
00846 
00847   for (int i = start; i < finish; i ++)
00848     distance += (float)vcl_fabs(A*dc->GetX(i) + B*dc->GetY(i) + C);
00849 
00850   return distance / (finish - start);
00851 }