Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

LineData.cc

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #include <iostream>
00003 #include <math.h>
00004 #include <vector>
00005 #include <list>
00006 
00007 #include "SketchSpace.h"
00008 #include "Sketch.h"
00009 #include "Region.h"
00010 #include "visops.h"
00011 
00012 #include "ShapeSpace.h"
00013 #include "ShapeRoot.h"
00014 
00015 #include "PointData.h"
00016 #include "LineData.h"
00017 #include "ShapeLine.h"
00018 
00019 #ifdef PLATFORM_APERIOS
00020 //! this is normally defined in <math.h>, but the OPEN-R cross-compiler isn't configured right
00021 template<typename T> static inline int signbit(T x) { return x<0; }
00022 #endif
00023 
00024 using namespace std;
00025 
00026 namespace DualCoding {
00027 
00028 DATASTUFF_CC(LineData);
00029 
00030 const float LineData::DEFAULT_MIN_LENGTH = 0.025f;
00031 //const float LineData::DEFAULT_MIN_LENGTH = 8.0f;
00032 
00033 const Point LineData::origin_pt = Point(0,0);
00034 
00035 LineData::LineData(ShapeSpace& _space, const Point &p1, orientation_t orient)
00036   : BaseData(_space,getStaticType()), end1_pt(p1), end2_pt(), 
00037     rho_norm(0), theta_norm(0), orientation(0), length(0) {
00038   int const width = space->getDualSpace().getWidth();
00039   int const height = space->getDualSpace().getHeight();
00040   // Use a large offset from p1 to p2 because SketchGUI must calculate
00041   // the line slope from p1/p2 coords transmitted as strings; we don't
00042   // transmit the orientation.  But don't use an offset so large that the line
00043   // goes off-screen.
00044   float p2x=0, p2y=0;
00045   if ( fabs(orient-M_PI/2) < 0.001 ) {
00046     p2x = p1.coordX();
00047     p2y = p1.coordY() > height/2 ? 0 : height-1;
00048   } else {
00049     float slope = tan(orient);
00050     float intcpt = p1.coordY() - p1.coordX()*slope;
00051     p2x = p1.coordX() >= width/2 ? 0 : width-1;
00052     p2y = p2x * slope + intcpt;
00053     if ( p2y > height-1 ) {
00054       p2y = height-1;
00055       p2x = (p2y-intcpt) / slope;
00056     } else if ( p2y < 0 ) {
00057       p2y = 0;
00058       p2x = -intcpt/slope;
00059     }
00060   }
00061   end2_pt = Point(p2x,p2y);
00062   end1_pt.setValid(false);
00063   end1_pt.setActive(false);
00064   end2_pt.setValid(false);
00065   end2_pt.setActive(false);
00066   update_derived_properties();
00067 }
00068 
00069 LineData::LineData(const LineData& other)
00070   : BaseData(other),
00071     end1_pt(other.end1_pt), end2_pt(other.end2_pt),
00072     rho_norm(other.rho_norm), theta_norm(other.theta_norm),
00073     orientation(other.orientation), length(other.length) {}
00074 
00075 Point LineData::getCentroid() const { return (end1Pt()+end2Pt())*0.5f; }
00076 
00077 void LineData::setInfinite(bool value) {
00078   end1_pt.setActive(!value);
00079   end2_pt.setActive(!value);
00080 }
00081 
00082 #define NORMPOINT_MATCH_DISTSQ 400
00083 #define LINE_MATCH_OVERLAP -20
00084 #define LINE_ORIENTATION_MATCH M_PI/12
00085 #define LINE_PERPDIST 30
00086 //#define LINE_PERPDIST 20
00087 
00088 bool LineData::isMatchFor(const ShapeRoot& other) const {
00089   if (!(isSameTypeAs(other) && isSameColorAs(other)))
00090     return false;
00091   else {
00092     const Shape<LineData>& other_ln = ShapeRootTypeConst(other,LineData);
00093     return isMatchFor(other_ln.getData());
00094   }
00095 }
00096 
00097 bool LineData::isMatchFor(const LineData& other_line) const {
00098   //float const px = rho_norm*cos(theta_norm), 
00099   //  py = rho_norm*sin(theta_norm);
00100   //float const qx = other_line.rho_norm*cos(other_line.theta_norm),
00101   //  qy = other_line.rho_norm*sin(other_line.theta_norm);
00102   AngPi theta_diff = float(theta_norm) - float(other_line.theta_norm);
00103   if ( (orientation_t)theta_diff > (orientation_t)M_PI/2 )
00104     theta_diff = (orientation_t)M_PI - theta_diff;
00105   //float normpointdistsq = (px-qx)*(px-qx) + (py-qy)*(py-qy);
00106    //cout << "id=" << getId() << " id2=" << other_line.getId() << " px=" << px << "  py=" << py << "  qx=" << qx << "  qy=" << qy << "  normpointdistsq=" << normpointdistsq 
00107    //  << " th=" << theta_norm << " th2=" << other_line.theta_norm << " theta_diff=" << theta_diff
00108    //  << "  perpdist=" << perpendicularDistanceFrom(other_line.getCentroid())
00109    //  << "  isoverlapped=" << isOverlappedWith(other_line,LINE_MATCH_OVERLAP) << endl;
00110   float const linePerpDist =
00111     space->getRefFrameType() == camcentric ? 20 : 100;
00112   AngPi const lineOrientationMatch =
00113     space->getRefFrameType() == camcentric ? M_PI/6 : M_PI/4;
00114   //std::cout << "theta_diff: " << theta_diff 
00115   //  << ", perpendicularDistance1: " << perpendicularDistanceFrom(other_line.getCentroid()) 
00116   //  << ", perpendicularDistance2: " << other_line.perpendicularDistanceFrom(getCentroid()) 
00117   //  << ", isOverlappedWith: " << isOverlappedWith(other_line,LINE_MATCH_OVERLAP) << std::endl;
00118   return //normpointdistsq < NORMPOINT_MATCH_DISTSQ  // *** DST hack
00119     theta_diff < lineOrientationMatch
00120     && (perpendicularDistanceFrom(other_line.getCentroid()) < linePerpDist || other_line.perpendicularDistanceFrom(getCentroid()) < linePerpDist)  // was 100
00121     && isOverlappedWith(other_line,LINE_MATCH_OVERLAP);
00122 }
00123 
00124 bool LineData::isOverlappedWith(const LineData& otherline, int amount) const {
00125   fmat::Column<3> thisVec = end2_pt.coords - end1_pt.coords;
00126   fmat::Column<2> v = fmat::SubMatrix<2,1>(thisVec);
00127   float theta = atan2(v[1],v[0]);
00128   fmat::Matrix<2,2> rot = fmat::rotation2D(-theta);
00129   v = rot * v;
00130   fmat::Column<3> otherPt1 = otherline.end1_pt.coords - end1_pt.coords;
00131   fmat::Column<3> otherPt2 = otherline.end2_pt.coords - end1_pt.coords;
00132   fmat::Column<2> op1 = rot * fmat::SubMatrix<2,1>(otherPt1);
00133   fmat::Column<2> op2 = rot * fmat::SubMatrix<2,1>(otherPt2);
00134   float thisMin = min(0.f, v[0]);
00135   float thisMax = max(0.f, v[0]);
00136   float otherMin = min(op1[0], op2[0]);
00137   float otherMax = max(op1[0], op2[0]);
00138   return (thisMin < otherMin) ? (otherMin+amount <= thisMax) : (thisMin+amount <= otherMax);
00139 }
00140 
00141 void LineData::mergeWith(const ShapeRoot& other) {
00142   const Shape<LineData>& other_line = ShapeRootTypeConst(other,LineData);
00143   if (other_line->confidence <= 0)
00144     return;
00145   //const int other_conf = other_line->confidence;
00146   //confidence += other_conf;
00147   // Favor longer lines because they're likely to be more reliable
00148   float const totalLengthSq = length * length + other_line->length * other_line->length;
00149   EndPoint new_end1_pt = (end1_pt*length*length/totalLengthSq + other_line->end1Pt()*other_line->length*other_line->length/totalLengthSq);
00150   EndPoint new_end2_pt = (end2_pt*length*length/totalLengthSq + other_line->end2Pt()*other_line->length*other_line->length/totalLengthSq);
00151   new_end1_pt.valid = end1_pt.isValid() && other_line->end1Pt().isValid();
00152   new_end2_pt.valid = end2_pt.isValid() && other_line->end2Pt().isValid();
00153   end1_pt = new_end1_pt;
00154   end2_pt = new_end2_pt;
00155   update_derived_properties();
00156 }
00157 
00158 bool LineData::isValidUpdate(coordinate_t c1_cur, coordinate_t c2_cur, coordinate_t c1_new, coordinate_t c2_new) {
00159   const float c1_noise = 10 + std::abs(c1_cur+c1_new) / 20.f; // allow larger error for shapes far from the robot
00160   const float c2_noise = 10 + std::abs(c2_cur+c2_new) / 20.f;
00161   return (c1_new-c1_noise < c1_cur && c2_cur < c2_new+c2_noise);
00162 }
00163 
00164 
00165 //! Update a line in the local map with info from a matching line in the ground space.
00166 bool LineData::updateParams(const ShapeRoot& ground_root, bool force) {
00167   const Shape<LineData>& ground_line = ShapeRootTypeConst(ground_root,LineData);
00168   //  cout << "updating local Line " << getId() << " with data from ground line " << ground_line->getId() << ":" << endl;
00169   //  ground_line->printEnds();
00170   //  cout << "Update from " << endl;
00171   //  printEnds();
00172   
00173   const coordinate_t c1_cur = firstPtCoord();
00174   const coordinate_t c2_cur = secondPtCoord();
00175 
00176   Point _end1_pt = firstPt();
00177   Point _end2_pt = secondPt();
00178   
00179   updateLinePt(firstPt(), firstPtCoord(), firstPt(ground_line), firstPtCoord(ground_line), -1);
00180   updateLinePt(secondPt(), secondPtCoord(), secondPt(ground_line), secondPtCoord(ground_line), +1);
00181   //  cout << "to" << endl;
00182   //  printEnds();
00183   
00184   const coordinate_t c1_new = firstPtCoord();
00185   const coordinate_t c2_new = secondPtCoord();
00186   
00187   if (isValidUpdate(c1_cur, c2_cur, c1_new, c2_new) || force){
00188     //    cout << "was accepted, line updated" << endl;
00189     //    ++nsamples;
00190     update_derived_properties();
00191     return true;
00192   }
00193   
00194   //  cout << "was denied, line not updated" << endl;
00195   setEndPts(_end1_pt, _end2_pt);
00196   return false;
00197 }
00198 
00199 void LineData::updateLinePt(EndPoint& localPt, coordinate_t local_coord,
00200           const EndPoint& groundPt, coordinate_t ground_coord,
00201           int sign) {
00202   if ( groundPt.isValid() ) {
00203     if ( localPt.isValid() )
00204       localPt.updateParams(groundPt);
00205     else
00206       localPt = groundPt;
00207   }
00208   else if ( (ground_coord - local_coord)*sign > 0 )
00209     localPt = groundPt;
00210 }
00211 
00212 bool LineData::isAdmissible() const {
00213   if (end1Pt().isValid() && end2Pt().isValid())
00214     return length >= 70.0;  // *** DST hack: lines should be at least 70 mm long
00215   else
00216     return length >= 40.0;
00217 }
00218 
00219 //! Print information about this shape. (Virtual in BaseData.)
00220 void LineData::printParams() const {
00221   cout << "Type = " << getTypeName() << "  ID=" << getId() << "  ParentID=" << getParentId() << endl;
00222   cout << "  end1{" << end1Pt().coordX() << ", " << end1Pt().coordY()  << "}"
00223        << " active=" << end1Pt().isActive()
00224        << " valid=" << end1Pt().isValid() << endl;
00225   
00226   cout << "  end2{" << end2Pt().coordX() << ", " << end2Pt().coordY() <<  "}"
00227        << " active=" << end2Pt().isActive()
00228        << " valid=" << end2Pt().isValid() << std::endl;
00229   
00230   cout << "  rho_norm=" << rho_norm
00231        << ", theta_norm=" << theta_norm
00232        << ", orientation=" << getOrientation() 
00233        << ", length=" << getLength() << endl;
00234   
00235   printf("  color = %d %d %d\n",getColor().red,getColor().green,getColor().blue);
00236   
00237   cout << "  mobile=" << getMobile()
00238        << ", viewable=" << isViewable() << endl;
00239   
00240   vector<float> abc = lineEquation_abc();
00241   printf("  equ = %f %f %f\n",abc[0],abc[1],abc[2]);
00242 }
00243 
00244 void LineData::printEnds() const {
00245   cout << "  end1{" << end1Pt().coordX() << ", " << end1Pt().coordY()  << "}";
00246   cout << "  active=" << end1Pt().isActive() << ", valid=" << end1Pt().isValid() << endl;
00247   cout << "  end2{" << end2Pt().coordX() << ", " << end2Pt().coordY() <<  "}";
00248   cout << "  active=" << end2Pt().isActive() << ", valid=" << end2Pt().isValid() << endl;
00249   // cout << endl;
00250 }
00251 
00252 
00253 
00254 // *** Transformations. *** //
00255 
00256 //! Apply a transformation to this shape.
00257 void LineData::applyTransform(const fmat::Transform& Tmat, const ReferenceFrameType_t newref) {
00258   end1Pt().applyTransform(Tmat,newref);
00259   end2Pt().applyTransform(Tmat,newref);
00260   update_derived_properties();
00261 }
00262 
00263 void LineData::projectToGround(const fmat::Transform& camToBase, const PlaneEquation& groundplane) {
00264   end1Pt().projectToGround(camToBase,groundplane);
00265   end2Pt().projectToGround(camToBase,groundplane);
00266   update_derived_properties();
00267 }
00268 
00269 
00270 // *** Logical endpoints *** //
00271 
00272 EndPoint& LineData::leftPt() { return end1Pt().isLeftOf(end2Pt()) ? end1_pt : end2_pt; }
00273 const EndPoint& LineData::leftPt() const { return end1Pt().isLeftOf(end2Pt()) ? end1_pt : end2_pt; }
00274 EndPoint& LineData::rightPt() { return end1Pt().isLeftOf(end2Pt()) ? end2_pt : end1_pt; }
00275 const EndPoint& LineData::rightPt() const { return end1Pt().isLeftOf(end2Pt()) ? end2_pt : end1_pt; }
00276 EndPoint& LineData::topPt() { return end1Pt().isAbove(end2Pt()) ? end1_pt : end2_pt; }
00277 const EndPoint& LineData::topPt() const { return end1Pt().isAbove(end2Pt()) ? end1_pt : end2_pt; }
00278 EndPoint& LineData::bottomPt() { return end1Pt().isAbove(end2Pt()) ? end2_pt : end1_pt; }
00279 const EndPoint& LineData::bottomPt() const { return end1Pt().isAbove(end2Pt()) ? end2_pt : end1_pt; }
00280 
00281 Shape<PointData> LineData::leftPtShape() {
00282   Shape<PointData> result(new PointData(*space, leftPt()));
00283   result->setName("leftPt");
00284   result->inheritFrom(*this);
00285   result->setViewable(false);
00286   return result;
00287 }
00288 
00289 Shape<PointData> LineData::rightPtShape() {
00290   Shape<PointData> result(new PointData(*space, rightPt()));
00291   result->setName("rightPt");
00292   result->inheritFrom(*this);
00293   result->setViewable(false);
00294   return result;
00295 }
00296 
00297 Shape<PointData> LineData::topPtShape() {
00298   Shape<PointData> result(new PointData(*space, topPt()));
00299   result->setName("topPt");
00300   result->inheritFrom(*this);
00301   result->setViewable(false);
00302   return result;
00303 }
00304 
00305 Shape<PointData> LineData::bottomPtShape() {
00306   Shape<PointData> result(new PointData(*space, bottomPt()));
00307   result->setName("bottomPt");
00308   result->inheritFrom(*this);
00309   result->setViewable(false);
00310   return result;
00311 }
00312 
00313 EndPoint& LineData::firstPt() {
00314   if ( isNotVertical() )
00315     if ( end1Pt().coordX() < end2Pt().coordX() )
00316       return end1Pt();
00317     else return end2Pt();
00318   else
00319     if ( end1Pt().coordY() < end2Pt().coordY() )
00320       return end1Pt();
00321     else return end2Pt();
00322 }
00323     
00324 const EndPoint& LineData::firstPt() const {
00325   if ( isNotVertical() )
00326     if ( end1Pt().coordX() < end2Pt().coordX() )
00327       return end1Pt();
00328     else return end2Pt();
00329   else
00330     if ( end1Pt().coordY() < end2Pt().coordY() )
00331       return end1Pt();
00332     else return end2Pt();
00333 }
00334     
00335 const EndPoint& LineData::firstPt(const Shape<LineData> &otherline) const {
00336   if ( isNotVertical() )
00337     if ( otherline->end1Pt().coordX() < otherline->end2Pt().coordX() )
00338       return otherline->end1Pt();
00339     else return otherline->end2Pt();
00340   else
00341     if ( otherline->end1Pt().coordY() < otherline->end2Pt().coordY() )
00342       return otherline->end1Pt();
00343     else return otherline->end2Pt();
00344 }
00345     
00346 EndPoint& LineData::secondPt() {
00347   if ( isNotVertical() )
00348     if ( end1Pt().coordX() > end2Pt().coordX() )
00349       return end1Pt();
00350     else return end2Pt();
00351   else
00352     if ( end1Pt().coordY() > end2Pt().coordY() )
00353       return end1Pt();
00354     else return end2Pt();
00355 }
00356     
00357 const EndPoint& LineData::secondPt() const {
00358   if ( isNotVertical() )
00359     if ( end1Pt().coordX() > end2Pt().coordX() )
00360       return end1Pt();
00361     else return end2Pt();
00362   else
00363     if ( end1Pt().coordY() > end2Pt().coordY() )
00364       return end1Pt();
00365     else return end2Pt();
00366 }
00367     
00368 const EndPoint& LineData::secondPt(const Shape<LineData> &otherline) const {
00369   if ( isNotVertical() )
00370     if ( otherline->end1Pt().coordX() > otherline->end2Pt().coordX() )
00371       return otherline->end1Pt();
00372     else return otherline->end2Pt();
00373   else
00374     if ( otherline->end1Pt().coordY() > otherline->end2Pt().coordY() )
00375       return otherline->end1Pt();
00376     else return otherline->end2Pt();
00377 }
00378     
00379 Shape<PointData> LineData::firstPtShape() {
00380   Shape<PointData> result(new PointData(*space, firstPt()));
00381   result->setName("firstPt");
00382   result->inheritFrom(*this);
00383   result->setViewable(false);
00384   return result;
00385 }
00386 
00387 Shape<PointData> LineData::secondPtShape() {
00388   Shape<PointData> result(new PointData(*space, secondPt()));
00389   result->setName("secondPt");
00390   result->inheritFrom(*this);
00391   result->setViewable(false);
00392   return result;
00393 }
00394 
00395 coordinate_t LineData::firstPtCoord() const {
00396   return  isNotVertical() ? firstPt().coordX() : firstPt().coordY();
00397 }
00398 
00399 coordinate_t LineData::firstPtCoord(const Shape<LineData> &otherline) const {
00400   return  isNotVertical() ?
00401     firstPt(otherline).coordX() :
00402     firstPt(otherline).coordY();
00403 }
00404 
00405 coordinate_t LineData::secondPtCoord() const {
00406   return  isNotVertical() ? secondPt().coordX() : secondPt().coordY();
00407 }
00408 
00409 coordinate_t LineData::secondPtCoord(const Shape<LineData> &otherline) const {
00410   return  isNotVertical() ?
00411     secondPt(otherline).coordX() :
00412     secondPt(otherline).coordY();
00413 }
00414 
00415 
00416 // *** Functions to set endpoints. *** //
00417 
00418 void LineData::setEndPts(const EndPoint& _end1_pt, const EndPoint& _end2_pt) {
00419   end1_pt.setCoords(_end1_pt);
00420   end1_pt.setActive(_end1_pt.isActive());
00421   end1_pt.setValid(_end1_pt.isValid());
00422   end1_pt.setNumUpdates(_end1_pt.numUpdates());
00423 
00424   end2_pt.setCoords(_end2_pt);
00425   end2_pt.setActive(_end2_pt.isActive());
00426   end2_pt.setValid(_end2_pt.isValid());
00427   end2_pt.setNumUpdates(_end2_pt.numUpdates());
00428 
00429   update_derived_properties();
00430 }
00431 
00432 
00433 // *** Properties functions. *** //
00434 
00435 std::pair<float,float> LineData::lineEquation_mb() const {
00436   float m;
00437   if ((fabs(end2Pt().coordX() - end1Pt().coordX()) * BIG_SLOPE)
00438       <= fabs(end2Pt().coordY() - end2Pt().coordY()))
00439     m = BIG_SLOPE;
00440   else 
00441     m = (end2Pt().coordY() - end1Pt().coordY())/(end2Pt().coordX() - end1Pt().coordX());
00442   float b = end1Pt().coordY() - m * end1Pt().coordX();
00443   return pair<float,float>(m,b);
00444 }
00445 
00446 
00447 //! Determine parameters a, b, c, d satisfying the equation ax + bz = c.
00448 std::vector<float> LineData::lineEquation_abc_xz() const {
00449   float dx = end2Pt().coordX() - end1Pt().coordX();
00450   float dz = end2Pt().coordZ() - end1Pt().coordZ();
00451 
00452   std::vector<float> abc(3,1);
00453   float& a = abc[0];
00454   float& b = abc[1];
00455   float& c = abc[2];
00456 
00457   // If vertical...b = 0
00458   if((dx == 0)
00459      || (dz/dx > BIG_SLOPE)) {
00460     a = 1;
00461     b = 0;
00462     c = end1Pt().coordX();
00463   }
00464   
00465   // If horizontal...a = 0
00466   else if((dz == 0)
00467     || (dx/dz > BIG_SLOPE)) {
00468     a = 0;
00469     b = 1;
00470     c = end1Pt().coordZ();
00471   }
00472   
00473   // If not horizontal or vertical...a = 1.0
00474   else {
00475     a = 1;
00476     b = (end1Pt().coordX() - end2Pt().coordX()) 
00477       / (end2Pt().coordZ() - end1Pt().coordZ());
00478     c = end1Pt().coordX() + b*end1Pt().coordZ();
00479   }
00480   
00481   return(abc);
00482 
00483 }
00484 
00485 //! Determine parameters a, b, c satisfying the equation ax + by = c.
00486 std::vector<float> LineData::lineEquation_abc() const {
00487   float dx = end2Pt().coordX() - end1Pt().coordX();
00488   float dy = end2Pt().coordY() - end1Pt().coordY();
00489   
00490   std::vector<float> abc(3,1);
00491   float& a = abc[0];
00492   float& b = abc[1];
00493   float& c = abc[2];
00494 
00495   // If vertical...b = 0
00496   if( std::abs(dx) < 1.0e-6f || Slope(dy/dx) > BIG_SLOPE) {
00497     a = 1;
00498     b = 0;
00499     c = end1Pt().coordX();
00500   }
00501   
00502   // If horizontal...a = 0
00503  else if ( std::abs(dy) < 1.0e-6f || Slope(dx/dy) > BIG_SLOPE ) {
00504     a = 0;
00505     b = 1;
00506     c = end1Pt().coordY();
00507   }
00508   
00509   // If not horizontal or vertical...a = 1.0
00510   else {
00511     a = 1;
00512     //    b = (end1Pt().coordX() - end2Pt().coordX()) 
00513     // / (end2Pt().coordY() - end1Pt().coordY());
00514     b = -dx / dy;
00515     c = end1Pt().coordX() + b*end1Pt().coordY();
00516   }
00517   
00518   return(abc);
00519 }
00520 
00521 
00522 // *** Functions to set values dealing with orientation. *** //
00523 
00524 void LineData::update_derived_properties() {
00525   rho_norm = perpendicularDistanceFrom(origin_pt);
00526   const vector<float> abc = lineEquation_abc();
00527   const float& a1 = abc[0];
00528   const float& b1 = abc[1];
00529   const float& c1 = abc[2];
00530   const float c1sign = (c1 >= 0) ? 1 : -1;
00531   theta_norm = atan2(b1*c1sign, a1*c1sign);
00532   orientation = theta_norm + AngPi((orientation_t)M_PI/2);
00533   length = end1Pt().distanceFrom(end2Pt());
00534   const ReferenceFrameType_t ref = getRefFrameType();
00535   end1_pt.setRefFrameType(ref);
00536   end2_pt.setRefFrameType(ref);
00537   deleteRendering();
00538 }
00539 
00540 bool LineData::isNotVertical() const {
00541   const AngPi threshold = (orientation_t)M_PI / 3;
00542   const AngPi orient = getOrientation();
00543   return (orient <= threshold) || (orient >= (orientation_t)M_PI - threshold);
00544 }
00545 
00546 /*
00547 bool LineData::isRoughlyPerpendicularTo(Shape<LineData>& other) {
00548   AngPi threshold = M_PI_4;
00549   AngPi orientation_diff = getOrientation() - other->getOrientation();
00550   if((orientation_diff >= threshold) && (orientation_diff < (M_PI-threshold)))
00551     return true;
00552   else
00553     return false;
00554 }
00555 
00556 bool LineData::isExactlyPerpendicularTo(Shape<LineData>& other) {
00557   AngPi orientation_diff = getOrientation() - other->getOrientation();
00558   return (orientation_diff == M_PI_2);
00559 }
00560 
00561 */
00562 
00563 //================================================================
00564 
00565 /* There is a standard formula to determine whether a point is to the
00566 left of a ray.  However, we're using lines, not rays (so there is no
00567 direction), and we mean "left of" from the viewer's point of view, not
00568 the ray's.  So this code is a little more complex.  We must decide on
00569 canonical start and end points for the line (ray) based on the
00570 comparison we're trying to make.  (To test this, draw two lines
00571 forming an "X" and make sure the code works correctly for both lines.)
00572 Also, we want this to work in both camcentric and ego/allocentric
00573 reference frames, which adds further complexity. -- DST */
00574 
00575 bool LineData::pointIsLeftOf(const Point& pt) const {
00576   const EndPoint &p1 = 
00577     ( getRefFrameType() == camcentric )
00578     ? ((end1_pt.coordY() < end2_pt.coordY()) ? end1_pt : end2_pt)
00579     : ((end1_pt.coordX() < end2_pt.coordX()) ? end1_pt : end2_pt);
00580   const EndPoint &p2 = (&p1 == &end1_pt) ? end2_pt : end1_pt;
00581   if ( (getRefFrameType()==camcentric) ? (p1.coordY() == p2.coordY()) : (p1.coordX() == p2.coordX()) )
00582     return false;
00583   else
00584     return ( (p2.coordX() - p1.coordX()) * (pt.coordY() - p1.coordY()) -
00585        (p2.coordY() - p1.coordY()) * (pt.coordX() - p1.coordX()) ) > 0;
00586 }
00587 
00588 bool LineData::pointIsRightOf(const Point& pt) const {
00589   const EndPoint &p1 = 
00590     ( getRefFrameType() == camcentric )
00591     ? ((end1_pt.coordY() < end2_pt.coordY()) ? end1_pt : end2_pt)
00592     : ((end1_pt.coordX() < end2_pt.coordX()) ? end1_pt : end2_pt);
00593   const EndPoint &p2 = (&p1 == &end1_pt) ? end2_pt : end1_pt;
00594   if ( (getRefFrameType()==camcentric) ? (p1.coordY() == p2.coordY()) : (p1.coordX() == p2.coordX()) )
00595     return false;
00596   else
00597     return ( (p2.coordX() - p1.coordX()) * (pt.coordY() - p1.coordY()) -
00598        (p2.coordY() - p1.coordY()) * (pt.coordX() - p1.coordX()) ) < 0;
00599 }
00600 
00601 bool LineData::pointIsAbove(const Point& pt) const {
00602   const EndPoint &p1 = 
00603     ( getRefFrameType() == camcentric )
00604     ? ((end1_pt.coordX() < end2_pt.coordX()) ? end1_pt : end2_pt)
00605     : ((end1_pt.coordY() < end2_pt.coordY()) ? end1_pt : end2_pt);
00606   const EndPoint &p2 = (&p1 == &end1_pt) ? end2_pt : end1_pt;
00607   if ( (getRefFrameType()==camcentric) ? (p1.coordX() == p2.coordX()) : (p1.coordY() == p2.coordY()) )
00608     return false;
00609   else
00610     return ( (p2.coordX() - p1.coordX()) * (pt.coordY() - p1.coordY()) -
00611        (p2.coordY() - p1.coordY()) * (pt.coordX() - p1.coordX()) ) > 0;
00612 }
00613 
00614 bool LineData::pointIsBelow(const Point& pt) const {
00615   const EndPoint &p1 = 
00616     ( getRefFrameType() == camcentric )
00617     ? ((end1_pt.coordX() < end2_pt.coordX()) ? end1_pt : end2_pt)
00618     : ((end1_pt.coordY() < end2_pt.coordY()) ? end1_pt : end2_pt);
00619   const EndPoint &p2 = (&p1 == &end1_pt) ? end2_pt : end1_pt;
00620   if ( (getRefFrameType()==camcentric) ? (p1.coordX() == p2.coordX()) : (p1.coordY() == p2.coordY()) )
00621     return false;
00622   else
00623     return ( (p2.coordX() - p1.coordX()) * (pt.coordY() - p1.coordY()) -
00624        (p2.coordY() - p1.coordY()) * (pt.coordX() - p1.coordX()) ) < 0;
00625 }
00626 
00627 // *** These functions are true based on line length. *** //
00628 
00629 bool LineData::isLongerThan(const Shape<LineData>& other) const {
00630   return length > other->length; }
00631 
00632 bool LineData::isLongerThan(float ref_length) const {
00633   return length > ref_length; }
00634 
00635 bool LineData::isShorterThan(const Shape<LineData>& other) const {
00636   return length < other->length; }
00637 
00638 bool LineData::isShorterThan(float ref_length) const {
00639   return length < ref_length; }
00640 
00641 bool LineData::isBetween(const Point &p, const LineData &other) const {
00642   if (getOrientation() == other.getOrientation()) { // parallel lines
00643     float dl = perpendicularDistanceFrom(other.end1Pt()); // distance between the lines
00644     return (perpendicularDistanceFrom(p) <= dl && other.perpendicularDistanceFrom(p) <= dl);
00645   }
00646   else {
00647     bool b;
00648     const LineData p_line (*space,  p,  // line from intersection of this and other to p
00649          intersectionWithLine(other, b, b));
00650     const AngPi theta_pline = p_line.getOrientation();
00651     const AngPi theta_greater = 
00652       (getOrientation() > other.getOrientation()) ? getOrientation() : other.getOrientation();
00653     const AngPi theta_smaller = 
00654       (getOrientation() < other.getOrientation()) ? getOrientation() : other.getOrientation();
00655     if (theta_greater - theta_smaller > M_PI/2)
00656       return (theta_pline >= theta_greater || theta_pline <= theta_smaller);
00657     else
00658       return (theta_pline <= theta_greater && theta_pline >= theta_smaller);
00659   }
00660 }
00661 
00662   
00663 // ***  Check intersection. *** //
00664 
00665 bool
00666 LineData::intersectsLine(const Shape<LineData>& other) const {
00667   return intersectsLine(other.getData());
00668 }
00669 
00670 bool
00671 LineData::intersectsLine(const LineData& other) const {
00672   // Calculate F(x,y) = 0 for this line (x1,y1) to (x2,y2).
00673   pair<float,float> F = lineEquation_mb();
00674   
00675   // Calculate G(x,y) = 0 for L (x3,y3) to (x4,y4).
00676   pair<float,float> G = other.lineEquation_mb();
00677   
00678   // NOTE: These tests are assumed to be taking place in the space of
00679   // "this" line.  Therefore, the limits of line extent (for lines
00680   // with inactive endpoints) are calculated in the space of "this"
00681   // line.
00682   
00683   // JJW *** YOU NEED TO TAKE ACCOUNT OF END POINTS BEING TURNED OFF.
00684   
00685   //  float end1x, end1y, end2x, end2y, other_end1x, other_end1y, other_end2x, other_end2y;
00686   //  if(end1Pt().isActive()) {
00687   //    end1x = end1Pt().coordX();
00688   //    end1y = end1Pt().coordY();
00689   //  } else {
00690   //    end1x = 
00691   
00692   
00693   // TEST 1
00694   
00695   // Calculate r3 = F(x3,y3)
00696   float r3 = F.first * other.end1Pt().coordX() + F.second - other.end1Pt().coordY();
00697   
00698   // Calculate r4 = F(x4,y4)
00699   float r4 = F.first * other.end2Pt().coordX() + F.second - other.end2Pt().coordY();
00700   
00701   // If r3 != 0...
00702   // ...AND r4 != 0...
00703   // ...AND signbit(r3) == signbit(r4)...
00704   // ...THEN the lines do not intersect.
00705   
00706   if((r3 != 0)
00707      && (r4 != 0)
00708      && (signbit(r3) == signbit(r4))
00709      )
00710     return false;
00711   
00712   
00713   // TEST 2
00714   
00715   // Calculate r1 = G(x1,y1)
00716   float r1 = G.first * end1Pt().coordX() + G.second - end1Pt().coordY();
00717   
00718   // Calculate r2 = G(x2,y2)
00719   float r2 = G.first * end2Pt().coordX() + G.second - end2Pt().coordY(); 
00720   
00721   // If r1 != 0...
00722   // ...AND r2 != 0...
00723   // ...AND signbit(r1) == signbit(r2)...
00724   // ...THEN the lines do not intersect.
00725   
00726   if((r1 != 0)
00727      && (r2 != 0)
00728      && (signbit(r1) == signbit(r2))
00729      )
00730     return false;
00731   
00732   // Otherwise, the lines DO intersect.
00733   return true;
00734 }
00735 
00736 
00737 Point LineData::intersectionWithLine(const Shape<LineData>& other,
00738              bool& intersection_on_this,
00739              bool& intersection_on_other) const { 
00740   return intersectionWithLine(other.getData(), intersection_on_this,intersection_on_other); 
00741 }
00742 
00743 Point
00744 LineData::intersectionWithLine(const LineData& other, 
00745              bool& intersection_on_this, bool& intersection_on_other) const {
00746 
00747   // Based upon algorithm written by Paul Bourke, April 1989.
00748   // http://astronomy.swin.edu/~pbourke/geometry/lineline2d/  
00749   // Accessed July 20th 2004
00750   
00751   // Points 1 and 2 are on "this" line. Points 3 and 4 define the "other" line.
00752   float x1, x2, x3, x4, y1, y2, y3, y4;
00753   x1 = end1Pt().coordX();
00754   x2 = end2Pt().coordX();
00755   x3 = other.end1Pt().coordX();
00756   x4 = other.end2Pt().coordX();
00757   y1 = end1Pt().coordY();
00758   y2 = end2Pt().coordY();
00759   y3 = other.end1Pt().coordY();
00760   y4 = other.end2Pt().coordY();
00761   
00762   // x1 + u_a(x2-x1) = x3 + u_b(x4-x3)
00763   // y1 + u_a(y2-y1) = y3 + u_b(y4-y3)
00764   
00765   // u_a = (x4-x3)(y1-y3) - (y4-y3)(x1-x3)
00766   //       -------------------------------
00767   //       (y4-y3)(x2-x1) - (x4-x3)(y2-y1)
00768   
00769   // u_b = (x2-x1)(y1-y3) - (y2-y1)(x1-x3)
00770   //       -------------------------------
00771   //       (y4-y3)(x2-x1) - (x4-x3)(y2-y1)
00772   
00773   float denom = ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
00774   float u_a_numerator = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3));
00775   float u_b_numerator = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3));
00776   
00777   // If denominators of u_a and u_b are zero, then lines are parallel.
00778   if(denom == 0.0) {
00779     if (u_a_numerator == 0.0 && u_b_numerator == 0.0) {
00780       // cerr << "intersectionWithLine: lines are coincident!\n";
00781       // printParams();
00782       // other.printParams();
00783       return(end1Pt());
00784     }
00785     else {
00786        cout << x1 << " " << x2 << " " << x3 << " " << x4 << " "
00787       << y1 << " " << y2 << " " << y3 << " " << y4 << endl;
00788        cout << "this theta; " << getOrientation() << ", other theta: " << other.getOrientation() << endl;
00789        cerr << "ERROR in intersectionWithLine: lines are parallel!\n";
00790       return(Point(-9999.0f,-99999.0f));
00791     }
00792   }
00793   
00794   else {
00795     float u_a = u_a_numerator / denom;
00796     float u_b = u_b_numerator / denom;
00797     
00798     // If 0 <= u_a <=1  then intersection point is on segment a.
00799     if(0<=u_a && u_a<=1)
00800       intersection_on_this = true;
00801     else
00802       intersection_on_this = false;
00803     
00804     // If 0 <= u_b <=1  then intersection point is on segment b.
00805     if(0<=u_b && u_b<=1)
00806       intersection_on_other = true;
00807     else
00808       intersection_on_other = false;
00809     
00810     return(Point((x1+u_a*(x2-x1)),
00811      (y1+u_a*(y2-y1))));
00812   }
00813 }
00814 
00815 
00816 // *** Distance. *** //
00817 
00818 float LineData::perpendicularDistanceFrom(const Point& otherPt) const {
00819   // NOTE that this formula is rather slow, as it involves the
00820   // calculation of the line equation in addition to a square root here.
00821   
00822   // Using the formula from http://www.tpub.com/math2/8.htm
00823   vector<float> abc = lineEquation_abc();
00824   float& a = abc[0];
00825   float& b = abc[1];
00826   float& c = abc[2];
00827   
00828   // Distance...
00829   //    (x*A + y*B - C  )
00830   // abs(-------------  )
00831   //    (sqrt(a^2 + b^2)) 
00832   float d = fabs((a * otherPt.coordX() + b * otherPt.coordY() - c)/sqrt(a*a + b*b));
00833   //  cout << "abcd: " << a << ", " << b << ", " << c << ", " << d << endl;
00834   return(d);
00835 }
00836 
00837 
00838 
00839 // ==================================================
00840 // BEGIN LINE EXTRACTION CODE
00841 // ==================================================
00842 
00843 //#define EXTRACT_LINE_MIN_AREA 20
00844 #define EXTRACT_LINE_MIN_AREA 60
00845 
00846 #define BEG_DIST_THRESH 2
00847 #define END_DIST_THRESH 2
00848 
00849 #define EXTRACT_LINE_MIN_SCORE 60
00850 #define BIG_SLOPE_CS 5000.0
00851 //#define BEG_DIST_THRESH 2
00852 
00853 bool LineData::pointsOnPerimeterOfWindow(Sketch<bool> const& sketch, double t, double r, Point& pt1, Point& pt2) {
00854   const int width = sketch->getWidth(), height = sketch->getHeight();
00855   r -= RSIZE / 2;
00856   t *= M_PI/180;
00857   
00858   pt1.setCoords(cos(t), sin(t));
00859   pt1 *= r;
00860   t += M_PI/2.0;
00861   pt2.setCoords(cos(t), sin(t));
00862   pt2 *= 10;
00863   pt2 += pt1;
00864   
00865   //check if line is vertical, then we don't want to calculate slope
00866   if (fabs(pt2.coordX() - pt1.coordX()) <= 0.001) {
00867     pt2.setCoords(pt1.coordX(), height);
00868     return true;
00869   }
00870     
00871   double m = (pt2.coordY() - pt1.coordY()) / (pt2.coordX() - pt1.coordX());
00872   double b = pt1.coordY() - m*pt1.coordX();
00873   
00874   static vector<Point> accOfPoints;
00875   accOfPoints.clear();
00876   
00877   //If line intercepts y axis in the image frame
00878   if (b >= 0 && b <= height)
00879     accOfPoints.push_back(Point(0, b));
00880   
00881   //If the line intercepts the right of the image frame
00882   if ((m*width + b) >= 0 && (m*width + b) <= height)
00883     accOfPoints.push_back(Point( width, m*width + b));
00884     
00885   //if the line intercepts the bottom of the image frame
00886   if ((height-b)/m >= 0 && (height-b)/m <= width)
00887     accOfPoints.push_back(Point((height-b)/m, height));
00888     
00889   //if the line intercepts the top of the image frame
00890   if (-b/m >= 0 && -b/m <= width)
00891     accOfPoints.push_back(Point( -b/m, 0));
00892   
00893   //See if we have a valid intersection
00894   if (accOfPoints.size() != 2)
00895     return false;
00896     
00897   pt1 = accOfPoints[0];
00898   pt2 = accOfPoints[1];
00899   return true;
00900 }
00901 
00902 double LineData::getScore(int t, int r, int height, short hough[TSIZE][RSIZE], float ptsArray[TSIZE][RSIZE]) {
00903   int tally = hough[t][r];
00904   
00905   //scale by how long this line could be in the picture frame. Bias against horizontal lines.
00906   //Point pt1, pt2;
00907   //pointsOnPerimeterOfWindow(t, r, pt1, pt2);
00908   //double scale = min((height / (pt1-pt2).xyzNorm()), 1.0f);
00909   if(ptsArray[t][r] == 0)
00910     return 0;
00911   double scale = min((height / ptsArray[t][r]), 1.0f);
00912   if(tally * scale > 15)
00913     return tally * scale;
00914   return 0;
00915 }
00916 
00917 vector<Shape<LineData> > LineData::houghExtractLines(Sketch<bool> const& sketch, Sketch<bool> const& occluders, const int num_lines) {
00918   const int width = sketch->getWidth(), height = sketch->getHeight();
00919   SketchSpace &SkS = sketch->getSpace();
00920   ShapeSpace &ShS = SkS.getDualSpace();
00921   //NEW_SKETCH_N(fatmask,bool,visops::fillin(sketch,1,2,8));
00922   //NEW_SKETCH_N(skel,bool,visops::skel(fatmask));
00923   //NEW_SKETCH_N(skelDist,uint,visops::mdist(skel));
00924   NEW_SKETCH_N(skelDist,uint,visops::mdist(sketch));
00925   
00926   short hough[TSIZE][RSIZE];
00927   float ptsArray[TSIZE][RSIZE];
00928   
00929   // Initialize matrices
00930   memset(hough, 0, TSIZE*RSIZE*sizeof(short));
00931   memset(ptsArray, 0, TSIZE*RSIZE*sizeof(float));
00932   Point pt1, pt2;
00933   for (int t = 0; t < TSIZE; t++)
00934     for (int r = 0; r < RSIZE; r++)
00935       if (pointsOnPerimeterOfWindow(sketch, t, r, pt1, pt2))
00936         ptsArray[t][r] = (pt1-pt2).xyNorm();
00937   
00938   //clean out pixels on image edges
00939   NEW_SKETCH_N(edges, bool, visops::non_bounds(sketch, 2));
00940   
00941   //Execute Hough transformation
00942   for(int x = 0; x < width; x++) {
00943     for(int y = 0; y < height; y++) {
00944       if(edges(x,y)) {
00945         for(int t = 0; t < TSIZE; t++) {
00946           double theta = M_PI/180.0 * t;
00947           int r = int(x*cos(theta) + y*sin(theta) + RSIZE/2);
00948           hough[t][r]++;
00949         }
00950       }
00951     }
00952   }
00953   
00954   //Bias the scores for their chance of actually being a line
00955   for(int t = 0; t < TSIZE; t++) {
00956     for(int r = 0; r < RSIZE; r++) {
00957       hough[t][r] = (short)getScore(t, r, height, hough, ptsArray);
00958     }
00959   }
00960   
00961   const int tRange = 10;
00962   const int rRange = 20;
00963   vector<Shape<LineData> > lines_vec;
00964   for (int k = 0; k < num_lines; k++) {
00965     int maxScore = 0;
00966     int maxT = 0;
00967     int maxR = 0;
00968     for (int i = 0; i < TSIZE; i++)
00969       for(int j = 0; j < RSIZE; j++)
00970         if (hough[i][j] > maxScore) {
00971           maxScore = hough[i][j];
00972           maxT = i;
00973           maxR = j;
00974         }
00975     if (maxScore < EXTRACT_LINE_MIN_SCORE) break;
00976     //std::cout << k << ". Score: " << maxScore << ", T: " << maxT << ", R: " << maxR << std::endl;
00977     if(!pointsOnPerimeterOfWindow(sketch, maxT, maxR, pt1, pt2)) {
00978       //std::cout << "Bad line " << endl;
00979       continue;
00980     }
00981     
00982     //make line
00983     float x_normpoint = (maxR -RSIZE/2)*cos(maxT * M_PI/180.0);
00984     float y_normpoint = (maxR -RSIZE/2)*sin(maxT * M_PI/180.0);
00985     // m is slope of this line (not the normal line)
00986     float m = std::max(std::min(std::tan((maxT * M_PI/180.0)+AngPi((orientation_t)M_PI/2)), BIG_SLOPE_CS), -BIG_SLOPE_CS);
00987     float b = y_normpoint - m*x_normpoint;
00988     Point pt(0, b);
00989     AngPi orientation = atan(m);
00990     Shape<LineData> extracted_line(ShS, pt, orientation);
00991     if ( std::abs(m) <= 1 )  // when |slope| <= 1, scan along x, else scan along y
00992       extracted_line->scanHorizForEndPts(skelDist,occluders,m,b);
00993     else
00994       extracted_line->scanVertForEndPts(skelDist,occluders,m,b);
00995     //extracted_line->setInfinite(true);
00996     extracted_line->setColor(sketch->getColor());
00997     extracted_line->setParentId(sketch->getViewableId());
00998     extracted_line->firstPt().checkValidity(width,height,BEG_DIST_THRESH);
00999     extracted_line->secondPt().checkValidity(width,height,BEG_DIST_THRESH);
01000     // Transform from SketchSpace coordinates to ShapeSpace coordinates
01001     SkS.applyTmatinv(extracted_line->firstPt().getCoords());
01002     SkS.applyTmatinv(extracted_line->secondPt().getCoords());
01003     extracted_line->update_derived_properties();
01004     bool matched = false;
01005     for ( vector<Shape<LineData> >::iterator ln_it = lines_vec.begin(); ln_it != lines_vec.end(); ln_it++ ) {
01006       Shape<LineData> &ln = *ln_it;
01007       if ( extracted_line->isMatchFor(ln) ) {
01008         matched = true;
01009         extracted_line.deleteShape();
01010         k--;
01011         break;
01012       }
01013     }
01014     if (matched == false)
01015       lines_vec.push_back(extracted_line);
01016     
01017     //clear nearby area
01018     for (int i = maxT-tRange; i <= maxT+tRange; i++ )
01019       for (int j = maxR-rRange; j <= maxR+rRange; j++ )
01020         if (i >=0 && i < TSIZE && j >=0 && j < RSIZE) {
01021           //std::cout << "clear: " << i << ", " << j << std::endl;
01022           hough[i][j] = 0;
01023         }
01024   }
01025   return lines_vec;
01026 }
01027 
01028 Shape<LineData> LineData::extractLine(Sketch<bool>& sketch) {
01029   //copy sketch as occluders
01030   NEW_SKETCH_N(occluders, bool, visops::copy(sketch));
01031   return houghExtractLines(sketch, occluders, 1)[0];
01032   
01033   //return houghExtractLines(sketch, sketch, 1)[0];
01034 }
01035 
01036 Shape<LineData> LineData::extractLine(Sketch<bool>& sketch, const Sketch<bool>& occlusions) {
01037   //copy sketch as occluders
01038   //NEW_SKETCH_N(occluders, bool, visops::copy(sketch));
01039   //return houghExtractLines(sketch, occluders, 1)[0];
01040   
01041   return houghExtractLines(sketch, occlusions, 1)[0];
01042 }
01043 
01044 vector<Shape<LineData> > LineData::extractLines(Sketch<bool> const& sketch, const int num_lines) {
01045   //copy sketch as occluders
01046   NEW_SKETCH_N(occluders, bool, visops::copy(sketch));
01047   return houghExtractLines(sketch, occluders, num_lines);
01048   
01049   //return houghExtractLines(sketch, sketch, num_lines);
01050 }
01051 
01052 vector<Shape<LineData> > LineData::extractLines(Sketch<bool> const& sketch, Sketch<bool> const& occluders, const int num_lines) {
01053   //copy sketch as occluders
01054   //NEW_SKETCH_N(occluders, bool, visops::copy(sketch));
01055   //return houghExtractLines(sketch, occluders, num_lines);
01056   
01057   return houghExtractLines(sketch, occluders, num_lines);
01058 }
01059 
01060 /*
01061 Shape<LineData> LineData::extractLine(Sketch<bool>& sketch) {
01062   NEW_SKETCH(fatmask,bool,visops::fillin(sketch,1,2,8));
01063   NEW_SKETCH(skel,bool,visops::skel(fatmask));
01064   return extractLine(skel, sketch);
01065 }
01066 
01067 
01068 Shape<LineData> LineData::oldExtractLine(Sketch<bool>& sketch) {
01069   NEW_SKETCH_N(fatmask,bool,visops::fillin(sketch,1,2,8));
01070   NEW_SKETCH_N(skel,bool,visops::skel(fatmask));
01071   return oldExtractLine(skel, sketch);
01072 }
01073 
01074 
01075 Shape<LineData> LineData::extractLine(Sketch<bool>& skeleton, 
01076               const Sketch<bool>& occlusions) {
01077   const int width = skeleton->getWidth(), height = skeleton->getHeight();
01078   SketchSpace &SkS = skeleton->getSpace();
01079   ShapeSpace &ShS = SkS.getDualSpace();
01080 
01081   // approximate largest skel region with line
01082   NEW_SKETCH_N(fatskel, bool, visops::fillin(skeleton,2,2,8));
01083   //NEW_SKETCH(fatskel, bool, visops::fillin(skeleton,1,2,8));
01084   NEW_SKETCH_N(labels,uint,visops::oldlabelcc(fatskel,visops::EightWayConnect));
01085   //NEW_SKETCH(labels,uint,visops::labelcc(skeleton, 10));
01086   list<Region> regions = Region::extractRegions(labels,EXTRACT_LINE_MIN_AREA);
01087   if ( regions.empty() ) {
01088     ShapeRoot invalid; // need to define a named variable to avoid warning on next line
01089     return ShapeRootType(invalid,LineData);
01090   }
01091   Region skelchunk = regions.front();  // regions not empty, so use the largest....
01092   AngPi orientation(skelchunk.findPrincipalAxisOrientation());
01093   Point centroid(skelchunk.findCentroid());
01094   if (! skelchunk.isContained(centroid, 3) ) {  //  region does not look like a straight line
01095     Shape<LineData> result(splitLine(ShS, skelchunk, skeleton, occlusions));
01096     if ( result.isValid() )
01097       return result;
01098     else
01099       cout << "*** LineData::splitLine returned an invalid shape!" << endl;
01100   }
01101 
01102   AngPi d_orientation = 0.005;
01103   int twists = 5;
01104   vector<LineData> lv_temp;
01105   NEW_SKETCH_N(skelDist,uint,visops::mdist(skeleton));
01106   for (int j = -twists; j <  twists; j++) {
01107     // Create a symbolic line object.
01108     LineData extracted_line(ShS, centroid, orientation + AngPi(j*d_orientation));
01109     // rho and theta describe the normal line.
01110     // normpoint is where the normal line intersects with this line.
01111     // normpoint may actually be off-image.
01112     float x_normpoint = extracted_line.getRhoNorm()*cos(extracted_line.getThetaNorm());
01113     float y_normpoint = extracted_line.getRhoNorm()*sin(extracted_line.getThetaNorm());
01114 
01115     // m is slope of this line (not the normal line)
01116     float m = std::max(std::min(std::tan(extracted_line.getThetaNorm()+AngPi((orientation_t)M_PI/2)), BIG_SLOPE), -BIG_SLOPE);
01117     float b = y_normpoint - m*x_normpoint;
01118     if ( std::abs(m) <= 1 )  // when |slope| <= 1, scan along x, else scan along y
01119       extracted_line.scanHorizForEndPts(skelDist,occlusions,m,b);
01120     else
01121       extracted_line.scanVertForEndPts(skelDist,occlusions,m,b);
01122     lv_temp.push_back(extracted_line);
01123     extracted_line.update_derived_properties();
01124   }
01125   stable_sort(lv_temp.begin(),lv_temp.end(),not2(LineData::LineDataLengthLessThan()));
01126   NEW_SHAPE(adjusted_line, LineData, new LineData(lv_temp[0]));
01127   adjusted_line->setName("LineData");
01128   adjusted_line->setColor(skeleton->getColor());
01129   adjusted_line->setParentId(skeleton->getViewableId());
01130   // Check to see if any endpoints are near any edge of the screen.
01131   // If they are, invalidate them, assuming that line continues beyond screen.
01132   adjusted_line->firstPt().checkValidity(width,height,BEG_DIST_THRESH);
01133   adjusted_line->secondPt().checkValidity(width,height,BEG_DIST_THRESH);
01134   // Transform from SketchSpace coordinates to ShapeSpace coordinates
01135   SkS.applyTmatinv(adjusted_line->firstPt().getCoords());
01136   SkS.applyTmatinv(adjusted_line->secondPt().getCoords());
01137   adjusted_line->update_derived_properties();
01138   return adjusted_line;
01139 }
01140 
01141 Shape<LineData> LineData::oldExtractLine(Sketch<bool>& skeleton, 
01142               const Sketch<bool>& occlusions) {
01143   const int width = skeleton->getWidth(), height = skeleton->getHeight();
01144   SketchSpace &SkS = skeleton->getSpace();
01145   ShapeSpace &ShS = SkS.getDualSpace();
01146 
01147   // approximate largest skel region with line
01148   NEW_SKETCH_N(labels,uint,visops::oldlabelcc(skeleton,visops::EightWayConnect));
01149   list<Region> regions = Region::extractRegions(labels,EXTRACT_LINE_MIN_AREA);
01150   if ( regions.empty() ) {
01151     ShapeRoot invalid; // need to define a named variable to avoid warning on next line
01152     return ShapeRootType(invalid,LineData);
01153   }
01154   Region skelchunk = regions.front();  // regions not empty, so use the largest....
01155   AngPi orientation(skelchunk.findPrincipalAxisOrientation());
01156   Point centroid(skelchunk.findCentroid());
01157   // cout << " orientation=" << orientation << "  centroid=" << centroid.getCoords() << endl;
01158   if (! skelchunk.isContained(centroid, 3) ) {   //  region does not look like a straight line
01159     Shape<LineData> result(oldSplitLine(ShS, skelchunk, skeleton, occlusions));
01160     if ( result.isValid() )
01161       return result;
01162   }
01163   
01164   // Create a symbolic line object.
01165   Shape<LineData> extracted_line(ShS, centroid, orientation);
01166   extracted_line->setColor(skeleton->getColor());
01167   extracted_line->setParentId(skeleton->getViewableId());
01168 
01169   // rho and theta describe the normal line.
01170   // normpoint is where the normal line intersects with this line.
01171   // normpoint may actually be off-image.
01172   float x_normpoint = extracted_line->rho_norm*cos(extracted_line->theta_norm);
01173   float y_normpoint = extracted_line->rho_norm*sin(extracted_line->theta_norm);
01174   
01175   // m is slope of this line (not the normal line)
01176   // float m = (y_normpoint != 0) ? -(x_normpoint/y_normpoint) : BIG_SLOPE;
01177   float m = std::max(std::min(std::tan(extracted_line->theta_norm+AngPi((orientation_t)M_PI/2)), BIG_SLOPE), -BIG_SLOPE);
01178   float b = y_normpoint - m*x_normpoint;
01179   // cout << "  x_normpoint=" << x_normpoint << "  y_normpoint=" << y_normpoint
01180   // << "  m=" << m << "  b=" << b <<std::endl;
01181   
01182   NEW_SKETCH_N(skelDist,uint,visops::mdist(skeleton));
01183   if ( std::abs(m) <= 1 )  // when |slope| <= 1, scan along x, else scan along y
01184     extracted_line->scanHorizForEndPts(skelDist,occlusions,m,b);
01185   else
01186     extracted_line->scanVertForEndPts(skelDist,occlusions,m,b);
01187 
01188   // Check to see if any endpoints are near any edge of the screen.
01189   // If they are, invalidate them, assuming that line continues beyond screen.
01190   extracted_line->end1_pt.checkValidity(width,height,BEG_DIST_THRESH);
01191   extracted_line->end2_pt.checkValidity(width,height,BEG_DIST_THRESH);
01192 
01193   // Transform from SketchSpace coordinates to ShapeSpace coordinates
01194   SkS.applyTmatinv(extracted_line->end1_pt.getCoords());
01195   SkS.applyTmatinv(extracted_line->end2_pt.getCoords());
01196   extracted_line->update_derived_properties();
01197   return extracted_line;
01198 }
01199 */
01200 
01201   // hack to split a non-straight region by kei
01202 Shape<LineData> LineData::splitLine(ShapeSpace &ShS, Region &skelchunk, 
01203         Sketch<bool> &skeleton, const Sketch<bool> &occlusions) {
01204   //cout << "this region is not straight, needs be split into two regions" << endl;
01205   Point bounds[4] = {skelchunk.findTopBoundPoint(), skelchunk.findLeftBoundPoint(),
01206          skelchunk.findRightBoundPoint(), skelchunk.findBotBoundPoint()};
01207   //  cout << "top(0): " << bounds[0] << endl;
01208   //  cout << "left(1): " << bounds[1] << endl;
01209   //  cout << "right(2): " << bounds[2] << endl;
01210   //  cout << "bottom(3): " << bounds[3] << endl;
01211   for (int i = 0; i < 4; i++) {
01212     for (int j = i+1; j < 4; j++) {
01213       if (bounds[i].distanceFrom(bounds[j]) > 20 && ! skelchunk.isContained((bounds[i]+bounds[j])/2.f, 3)) {
01214         //  cout << "[" << i << "," << j << "] form a line from which most distant point is where the region should split " << endl;
01215         LineData ln(ShS,bounds[i],bounds[j]);
01216         Point most_distant(skelchunk.mostDistantPtFrom(ln));
01217         ShS.getDualSpace().applyTmat(most_distant.getCoords());
01218         int const clear_dist = 15;  // was 10
01219         int x1 = max(0, int(most_distant.coordX() - clear_dist));
01220         int x2 = min(skeleton.width-1, int(most_distant.coordX() + clear_dist));
01221         int y1 = max(0, int(most_distant.coordY() - clear_dist));
01222         int y2 = min(skeleton.height-1, int(most_distant.coordY() + clear_dist));
01223         for (int x = x1; x <= x2; x++)
01224           for (int y = y1; y <= y2; y++)
01225             skeleton(x,y) = false;
01226         return extractLine(skeleton, occlusions);
01227       }
01228     }
01229   }
01230   ShapeRoot invalid; // need to define a named variable to avoid warning on next line
01231   return ShapeRootType(invalid,LineData);
01232 }
01233 
01234 /*
01235   // hack to split a non-straight region by kei
01236 Shape<LineData> LineData::oldSplitLine(ShapeSpace &ShS, Region &skelchunk, 
01237         Sketch<bool> &skeleton, const Sketch<bool> &occlusions) {
01238   //cout << "this region is not straight, needs be split into two regions" << endl;
01239   Point bounds[4] = {skelchunk.findTopBoundPoint(), skelchunk.findLeftBoundPoint(),
01240          skelchunk.findRightBoundPoint(), skelchunk.findBotBoundPoint()};
01241   //  cout << "top(0): " << bounds[0] << endl;
01242   //  cout << "left(1): " << bounds[1] << endl;
01243   //  cout << "right(2): " << bounds[2] << endl;
01244   //  cout << "bottom(3): " << bounds[3] << endl;
01245   for (int i = 0; i < 4; i++) {
01246     for (int j = i+1; j < 4; j++) {
01247       if (bounds[i].distanceFrom(bounds[j]) > 20 && ! skelchunk.isContained((bounds[i]+bounds[j])/2.f, 3)) {
01248   //  cout << "[" << i << "," << j << "] form a line from which most distant point is where the region should split " << endl;
01249   LineData ln(ShS,bounds[i],bounds[j]);
01250   PointData most_distant_pt(ShS, skelchunk.mostDistantPtFrom(ln));
01251   //  cout << "Point of split: " << most_distant_pt.getCentroid() << endl;
01252   const Sketch<bool>& point_rendering = most_distant_pt.getRendering();
01253   uint const clear_dist = 10;
01254   NEW_SKETCH_N(not_too_close, bool, visops::mdist(point_rendering) >= clear_dist);
01255   skeleton &= not_too_close;
01256   return oldExtractLine(skeleton, occlusions);
01257       }
01258     }
01259   }
01260   ShapeRoot invalid; // need to define a named variable to avoid warning on next line
01261   return ShapeRootType(invalid,LineData);
01262 }
01263 */
01264 void LineData::scanHorizForEndPts(const Sketch<uint>& skelDist, 
01265           const Sketch<bool>& occlusions, 
01266           float m, float b) {
01267   // Scan along the infinite line, looking for segments in the image.
01268   bool on_line = false;        // true if tracing a line skeleton
01269   int beg_dist_thresh = BEG_DIST_THRESH;   // skel has to be <= this close to start segment
01270   int end_dist_thresh = END_DIST_THRESH;   // how far line should extend from skel
01271   int curxstart = -1;    // start of current segment we're examining
01272   int possxstop = -1;    // end of current segment unless we bridge a gap
01273   int bestxstart = -1;   // start of best (longest) segment seen so far
01274   int bestxstop = -1;    // end of best segment seen so far
01275   int bestlength = -1;   // length of best segment seen so far
01276   for (int x = 0, y, dist=0; x < skelDist.width; x++) {
01277     y = int(m*x+b);
01278     
01279     // If y is on-screen...
01280     if (y >= 0 && y < skelDist.height) {
01281       dist = skelDist(x,y);
01282 
01283       if (on_line == false) {   // not currently on a line segment
01284   if (dist <= beg_dist_thresh) {
01285     // start a new segment
01286     on_line = true;
01287     curxstart = x - dist;
01288     // if new segment begins at an occluder, back up over the occluder
01289     int curystart;
01290     bool backupflag = false;
01291     while ( curxstart >= 0 && (curystart=int(m*curxstart+b)) >= 0 && curystart < skelDist.height )
01292       if ( occlusions(curxstart,curystart) || skelDist(curxstart,curystart) == 0 ) {
01293         --curxstart;
01294         backupflag = true;
01295       } else { // if we backed up past the occluder, go one step forward
01296         curxstart += backupflag;
01297         break;
01298       }
01299     if ( curxstart < 0) // occluder extended to left edge of image
01300       curxstart = 0;
01301   }
01302       }
01303       else {   // on_line == true:  currently on a line segment
01304   const bool occ = occlusions(x,y);
01305   // cout << "x=" << x << "  dist=" << dist <<"  occ=" << occ;
01306   if ( dist <= end_dist_thresh || occ ) {
01307     if ( occ )
01308       possxstop = x;
01309     else
01310       possxstop = x - dist;
01311     // cout << "  possxstop=" << possxstop;
01312     if ( possxstop-curxstart > bestlength ) {
01313       bestxstart = curxstart;
01314       bestxstop = possxstop;
01315       bestlength = bestxstop-bestxstart;
01316       // cout << "  bestxstop=" << bestxstop << "  bestlength=" << bestlength;
01317     }
01318   }
01319   else if ( dist > extractorGapTolerance ) {
01320     // we're traversing a gap, and it just got too long
01321     on_line = false;
01322     // cout << "  on_line=" << on_line << "  dist=" << dist;
01323   };  
01324   // cout << endl;
01325       }
01326     }
01327   }
01328 
01329   // cout << "xstart:" << bestxstart << "xstop:" << bestxstop << endl;
01330   float y1 = m*bestxstart + b;
01331   float y2 = m*bestxstop + b;
01332   setEndPts(Point(bestxstart,y1), Point(bestxstop,y2));
01333   // cout << "m = " << m << "   b = " << b << endl;
01334   balanceEndPointHoriz(end1_pt,occlusions,m,b);
01335   balanceEndPointHoriz(end2_pt,occlusions,m,b);
01336 }
01337 
01338 void LineData::scanVertForEndPts(const Sketch<uint>& skelDist, 
01339           const Sketch<bool>& occlusions, 
01340           float m, float b) {
01341   // Scan along the infinite line, looking for segments in the image.
01342   bool on_line = false;        // true if tracing a line skeleton
01343   int beg_dist_thresh = BEG_DIST_THRESH;   // skel has to be <= this close to start segment
01344   int end_dist_thresh = END_DIST_THRESH;   // how far line should extend from skel
01345   int curystart = -1;    // start of current segment we're examining
01346   int possystop = -1;    // end of current segment unless we bridge a gap
01347   int bestystart = -1;   // start of best (longest) segment seen so far
01348   int bestystop = -1;    // end of best segment seen so far
01349   int bestlength = -1;   // length of best segment seen so far
01350   for (int x, y = 0, dist=0; y < skelDist.height; y++) {
01351     x = int((y-b)/m);
01352     
01353     // If x is on-screen...
01354     if (x >= 0 && x < skelDist.width) {
01355       dist = int(skelDist(x,y));
01356 
01357       if (on_line == false) {   // not currently on a line segment
01358   if (dist <= beg_dist_thresh) {
01359     // start a new segment
01360     on_line = true;
01361     curystart = y - dist;
01362     // if new segment begins at an occluder, back up over the occluder
01363     int curxstart;
01364     bool backupflag = false;
01365     while ( curystart >= 0 && (curxstart=int((curystart-b)/m)) >= 0 && curxstart < skelDist.width )
01366       if ( occlusions(curxstart,curystart) || skelDist(curxstart,curystart) == 0 ){
01367         --curystart;
01368         backupflag = true;
01369       } else { // if we backed up past the occluder, go one step forward
01370         curystart += backupflag;
01371         break;
01372       }
01373     if ( curystart < 0) // occluder extended to top edge of image
01374       curystart = 0;
01375   }
01376       }
01377       else {   // on_line == true:  currently on a line segment
01378   const bool occ = occlusions(x,y);
01379   // cout << "y=" << y << "  dist=" << dist <<"  occ=" << occ;
01380   if ( dist <= end_dist_thresh || occ ) {
01381     if ( occ )
01382       possystop = y;
01383     else
01384       possystop = y - dist;
01385     // cout << "  possystop=" << possystop;
01386     if ( possystop-curystart > bestlength ) {
01387       bestystart = curystart;
01388       bestystop = possystop;
01389       bestlength = bestystop-bestystart;
01390       // cout << "  bestystop=" << bestystop << "  bestlength=" << bestlength;
01391     }
01392   }
01393   else if ( dist > extractorGapTolerance ) {
01394     // we're traversing a gap, and it just got too long
01395     on_line = false;
01396     // cout << "  on_line=" << on_line;
01397   };  
01398   // cout << endl;
01399       }
01400     }
01401   }
01402 
01403   float x1 = (bestystart-b)/m;
01404   float x2 = (bestystop-b)/m;
01405   // cout << "x1=" << x1 << ", ystart=" << bestystart << "   x2=" << x2 << ", ystop=" << bestystop << endl;
01406   setEndPts(Point(x1,bestystart), Point(x2,bestystop));
01407   // cout << "m = " << m << "   b = " << b << endl;
01408   balanceEndPointVert(end1_pt,occlusions,m,b);
01409   balanceEndPointVert(end2_pt,occlusions,m,b);
01410 }
01411 
01412 void LineData::balanceEndPointHoriz(EndPoint &, Sketch<bool> const &, float, float) {
01413   /*
01414   int xstep = ( pt.coordX() < max(end1_pt.coordX(),end2_pt.coordX()) ? +1 : -1 );
01415   int toppix = 0, bottompix = 0;
01416   int const balance_rows = 5;    // check this many rows from the line endpoint inward
01417   int const row_samples = 10; // for each row, check this many pixels each side of the line
01418   */
01419   return;
01420 }
01421 
01422 void LineData::balanceEndPointVert(EndPoint &pt, Sketch<bool> const &occluders, float m, float b) {
01423   int ystep = ( pt.coordY() < max(end1_pt.coordY(),end2_pt.coordY()) ? +1 : -1 );
01424   int leftpix = 0, rightpix = 0;
01425   int const balance_rows = 8;    // check this many rows from the line endpoint inward
01426   int const row_samples = 10; // for each row, check this many pixels each side of the line
01427   // cout << endl << " ystep=" << ystep << " coords= ( " << pt.coordX() << " , " << pt.coordY() << ")"  << endl;
01428   for ( int y = (int)pt.coordY(), ycnt=balance_rows ;
01429   y>= 0 && y<occluders.height && ycnt-- > 0 ; y+=ystep ) {
01430     int const xstart = (int) ((y-b)/m);
01431     for ( int x = xstart-row_samples; x < xstart; x++ )
01432       if ( x >= 0 )
01433   leftpix += occluders(x,y);
01434     for ( int x = xstart+row_samples; x > xstart; x-- )
01435       if ( x < occluders.width )
01436   rightpix += occluders(x,y);
01437     // cout << "   " << xstart << "/" << y << "  =  " << leftpix << " " << rightpix << endl;;
01438   }
01439    float const new_x = pt.coordX() + (rightpix-leftpix)/(2*balance_rows);
01440    // cout << "    leftpix=" << leftpix <<"  rightpix=" << rightpix << endl;
01441    pt.setCoords(new_x, pt.coordY());
01442 }
01443 
01444 /*
01445 vector<Shape<LineData> > LineData::oldExtractLines(Sketch<bool> const& sketch,
01446             const int num_lines) {
01447   NEW_SKETCH_N(fatmask,bool,visops::fillin(sketch,1,2,8));
01448   NEW_SKETCH_N(skel,bool,visops::skel(fatmask));
01449   return oldExtractLines(skel, sketch, num_lines);
01450 }
01451 
01452 
01453 vector<Shape<LineData> > LineData::extractLines(Sketch<bool> const& sketch,
01454             const int num_lines) {
01455   NEW_SKETCH_N(fatmask,bool,visops::fillin(sketch,1,2,8));
01456   NEW_SKETCH_N(skel,bool,visops::skel(fatmask));
01457   return extractLines(skel, sketch, num_lines);
01458 }
01459 
01460 
01461 vector<Shape<LineData> > LineData::oldExtractLines(Sketch<bool> const& skel,
01462             Sketch<bool> const& occluders,
01463             const int num_lines) {
01464   vector<Shape<LineData> > lines_vec;
01465   NEW_SKETCH_N(temp,bool,visops::copy(skel))
01466   for (int gloop = 0; gloop<num_lines; gloop++) {  // <---- HACK TO LIMIT NUMBER OF LINES RETURNED
01467     Shape<LineData> newline(oldExtractLine(temp,occluders));
01468     if ( !newline.isValid() ) break;
01469     newline->clearLine(temp);
01470     if (newline->isLongerThan(skel.width*DEFAULT_MIN_LENGTH)) {
01471       lines_vec.push_back(newline);
01472     }
01473   }
01474   // std::cout << "extractLines returning " << lines_vec.size() << " lines." << std::endl;
01475   
01476   return lines_vec;
01477 }
01478 
01479 
01480 vector<Shape<LineData> > LineData::extractLines(Sketch<bool> const& skel, Sketch<bool> const& occluders, const int num_lines) {
01481   vector<Shape<LineData> > lines_vec;
01482   NEW_SKETCH_N(temp,bool,visops::copy(skel));
01483   Sketch<bool> tempArg;
01484   for (int gloop = 0; gloop<num_lines; gloop++) {
01485     tempArg = temp;
01486     Shape<LineData> newline(extractLine(tempArg,occluders));
01487     if ( !newline.isValid() ) break;
01488     newline->clearLine(temp);
01489     if ( newline->isLongerThan(skel.width*DEFAULT_MIN_LENGTH) ) {
01490       bool changed = true;
01491       while ( changed ) {
01492         changed = false;
01493         for ( vector<Shape<LineData> >::iterator ln_it = lines_vec.begin(); ln_it != lines_vec.end(); ln_it++ ) {
01494           Shape<LineData> &ln = *ln_it;
01495           if ( newline->isMatchFor(ln) ) {
01496             // cout << "line " << newline << " matches line " << ln << endl;
01497             newline->mergeWith(ln);
01498             ln_it->deleteShape();
01499             lines_vec.erase(ln_it);
01500             changed = true;
01501             break;
01502           }
01503         }
01504       }
01505       lines_vec.push_back(newline);
01506     }
01507   }
01508   return lines_vec;
01509 }
01510 */
01511 
01512 //! Clear out pixels that are on or close to this line.
01513 void LineData::clearLine(Sketch<bool>& sketch) {
01514   const Sketch<bool>& line_rendering = getRendering();
01515   uint const clear_dist = 5;
01516   Sketch<bool> not_too_close = (visops::mdist(line_rendering) >= clear_dist);
01517   sketch &= not_too_close;
01518 } 
01519 
01520   
01521 
01522 // ==================================================
01523 // BEGIN LINE RENDERING CODE
01524 // ==================================================
01525 
01526 Sketch<bool>& LineData::getRendering() {
01527   if ( ! end1Pt().rendering_valid || ! end2Pt().rendering_valid )
01528     deleteRendering();
01529   if ( rendering_sketch == NULL )
01530     rendering_sketch = render();
01531   return *rendering_sketch;
01532 }
01533 
01534 //! Render line to SketchSpace and return a pointer to the sketch.
01535 //! This function does not link the Sketch<bool>* in the shape to the sketch returned.
01536 Sketch<bool>* LineData::render() const {
01537   SketchSpace &renderspace = space->getDualSpace();
01538   int const width = renderspace.getWidth();
01539   int const height = renderspace.getHeight();
01540   float x1,y1,x2,y2;
01541   setDrawCoords(x1, y1, x2, y2, width, height);
01542   Sketch<bool>* draw_result = 
01543     new Sketch<bool>(renderspace, "render("+getName()+")");
01544   //  (*draw_result)->setParentId(getViewableId());
01545   //  (*draw_result)->setColor(getColor());
01546   (*draw_result)->inheritFrom(*this);
01547   *draw_result = 0;
01548   drawline2d(*draw_result, (int)x1, (int)y1, (int)x2, (int)y2);
01549   end1Pt().rendering_valid = true;
01550   end2Pt().rendering_valid = true;
01551   return draw_result;
01552 }  
01553  
01554   
01555 // This function will be called by both LineData and PolygonData renderers
01556 void LineData::setDrawCoords(float& x1,float& y1, float& x2, float& y2,
01557            const int width, const int height) const {
01558   EndPoint e1(end1Pt());
01559   EndPoint e2(end2Pt());
01560   //std::cout << "setDrawCoords: e1=" << e1 << " e2=" << e2 << std::endl;
01561   space->getDualSpace().applyTmat(e1.getCoords());
01562   space->getDualSpace().applyTmat(e2.getCoords());
01563   const EndPoint &left_point = e1.coordX() <= e2.coordX() ? e1 : e2;
01564   const EndPoint &right_point = e1.coordX() <= e2.coordX() ? e2 : e1;
01565   //std::cout << "setDrawCoords: left=" << left_point << " right=" << right_point << std::endl;
01566 
01567   // Check if horizontal
01568   if( left_point.coordY() == right_point.coordY()) { 
01569     if (!left_point.isActive())
01570       x1 = 0;
01571     else x1 = max(0.0f,min(width-1.0f,left_point.coordX()));
01572     
01573     if (!right_point.isActive())
01574       x2 = width-1;
01575     else x2 = max(0.0f,min(width-1.0f,right_point.coordX()));
01576     
01577     y1 = left_point.coordY();
01578     y2 = y1;
01579   } 
01580   else if ( left_point.coordX() == right_point.coordX()) {  // Check if vertical...
01581 
01582     const EndPoint &top_point =    e1.coordY() <= e2.coordY() ? e1 : e2;
01583     const EndPoint &bottom_point = e1.coordY() <= e2.coordY() ? e2 : e1;
01584 
01585   //std::cout << "top_point=" << top_point << " bottom_point=" << bottom_point << std::endl;
01586     if(!top_point.isActive())
01587       y1 = 0;
01588     else y1 = max(0.0f,min(height-1.0f,top_point.coordY()));
01589     
01590     if (!bottom_point.isActive())
01591       y2 = height-1;
01592     else y2 = max(0.f,min(height-1.0f,bottom_point.coordY()));
01593 
01594     x1 = left_point.coordX();
01595     x2 = x1;
01596   } 
01597 
01598   else {   // Neither horizontal nor vertical...
01599     float const m = (right_point.coordY()-left_point.coordY())/(right_point.coordX()-left_point.coordX());
01600     float const b = left_point.coordY() - m*left_point.coordX();
01601     
01602     // find image edge intersections
01603     int const i0x = (int)((0-b)/m);
01604     int const ihx = (int)(((height-1)-b)/m);
01605     int const i0y = (int)(m*0+b);
01606     int const iwy = (int)(m*(width-1)+b);
01607     
01608     // If left point is active, set starting x and y accordingly.
01609     if ( left_point.isActive() ) {
01610       x1 = left_point.coordX();
01611       y1 = left_point.coordY();
01612     } 
01613     
01614     // If endpoint 1 extends past image edge...
01615     else { 
01616       
01617       // intersects left edge
01618       if ( i0y >= 0 && i0y < height ) { 
01619   x1 = 0;
01620   y1 = i0y;
01621       } 
01622       
01623       // intersects top or bottom edge
01624       else { 
01625   
01626   // intersects top first
01627   if ( i0x < ihx ) { 
01628     x1 = i0x;
01629     y1 = 0;
01630   } 
01631   
01632   // intersects bottom first
01633   else { 
01634     x1 = ihx;
01635     y1 = height-1;
01636   }
01637       }
01638     }
01639     
01640     // If right point is active, set starting x and y accordingly.
01641     if ( right_point.isActive() ) {
01642       x2 = right_point.coordX();
01643       y2 = right_point.coordY();
01644     } 
01645     else { // endpoint extends to image edge
01646       if ( iwy >= 0 && iwy < height ) { // intersects right edge
01647   x2 = width-1;
01648   y2 = iwy;
01649       } 
01650       else { // intersects top or bottom edge
01651   if ( i0x > ihx ) { // intersects top last 
01652     x2 = i0x;
01653     y2 = 0;
01654   } 
01655   else { // intersects bottom last
01656     x2 = ihx;
01657     y2 = height-1;
01658   }
01659       }
01660     }
01661   }
01662 }
01663 
01664 void LineData::drawline2d(Sketch<bool>& canvas, int x0, int y0, int x1, int y1) {
01665   int width = canvas->getWidth();
01666   int height = canvas->getHeight();
01667   
01668   //std::cout << "LineData::drawline2D x0=" << x0 << " y0=" << y0 << " x1=" << x1 << " y1=" << y1 << std::endl;
01669 
01670   // code below from free Graphics Gems repository, graphicsgems.org
01671   int dx = x1-x0,  ax = abs(dx)<<1,  sx = signbit(dx) ? -1 : 1;
01672   int dy = y1-y0,  ay = abs(dy)<<1,  sy = signbit(dy) ? -1 : 1;
01673   int d, x=x0, y=y0;
01674   
01675   if ( ax > ay ) {    /* x dominant */
01676     //std::cout << "  x dominant\n";
01677     d = ay-(ax>>1);
01678     for (;;) {
01679       if (x >= 0 && y >= 0 && x < width && y < height)
01680   canvas(x,y) = true;
01681       
01682       if (x==x1)
01683   return;
01684       
01685       if ( d >= 0 ) {
01686   y += sy;
01687   d -= ax;
01688       }
01689       
01690       x += sx;
01691       d += ay;
01692     }
01693   }
01694   else {      /* y dominant */
01695     //std::cout << "  y dominant\n";
01696     d = ax-(ay>>1);
01697     for (;;) {
01698       if (x >= 0 && y >= 0 && x < width && y < height)
01699   canvas(x,y) = true;
01700       
01701       if ( y == y1 )
01702   return;
01703       
01704       if ( d >= 0 ) {
01705   x += sx;
01706   d -= ay;
01707       }
01708       
01709       y += sy;
01710       d += ax;
01711     }
01712   }
01713 }
01714 //}
01715 
01716 
01717 std::vector<Shape<LineData> > LineData::houghTransform(const Sketch<bool>& fat, 
01718             const Sketch<bool>& skinny,
01719             const Sketch<bool>& occlusions,
01720             const size_t num_lines, int minLength)
01721 {
01722   if ( minLength == -1 )
01723     minLength = static_cast<int>(fat->getWidth() * DEFAULT_MIN_LENGTH);
01724   std::vector<Shape<LineData> > result;
01725   ShapeSpace& ShS = fat->getSpace().getDualSpace();
01726 
01727   const int width = fat->getWidth(), height = fat->getHeight();
01728   const int numR = 120, numTheta = 120;
01729   const int numRf = 40, numThetaf = 40;
01730   int hsize = numR*numTheta, hsizef = numRf * numThetaf;
01731   int htab[hsize], hfine[hsizef]; // Hough accumulator table
01732   float minTheta = 0, maxTheta = 2*(float)M_PI; //0.9999*M_PI;
01733   float minR = 0, maxR = std::sqrt((float)width*(float)width+(float)height*(float)height); //240.0;
01734   float thetaSpread = maxTheta - minTheta;
01735   float rSpread = maxR - minR;
01736   //float bestR = 0, bestTheta = 0;
01737 
01738   for (int i = 0; i < hsize; i++)
01739     htab[i] = 0; // zero accumulator table
01740 
01741   // build accumulator table
01742   float theta, r;
01743   int ridx;
01744   for (int x = 0; x < width; x++) {
01745     for (int y = 0; y < height; y++) {
01746       if (fat(x,y) == true) {
01747   for (int tidx = 0; tidx < numTheta ; tidx++) {
01748     theta = minTheta + tidx*thetaSpread/numTheta;
01749     r = (float)x*cos(theta)+(float)y*sin(theta);
01750     ridx = (int)((r-minR)*(float)numR/rSpread); // Is this right calc?
01751     if (ridx >= 0 && ridx < numR) // check ridx bounds
01752       htab[ridx+tidx*numR]++; 
01753   }
01754       }
01755     }
01756   }
01757 
01758 
01759   /*float lineLen = 500;
01760   for (int i = 0; i < numR*numTheta; i++) {
01761     if (htab[i] >= minCount)
01762       {
01763   const float curR = (i%numR)*rSpread/numR + minR;
01764   const float curTheta = (i/numR)*thetaSpread/numTheta + minTheta;
01765   const float x1 = curR*cos(curTheta), y1 = curR*sin(curTheta);
01766   const float x2 = x1+lineLen*cos(curTheta+M_PI/2), y2 = y1+lineLen*sin(curTheta+M_PI/2);
01767   const float x3 = x1-lineLen*cos(curTheta+M_PI/2), y3 = y1-lineLen*sin(curTheta+M_PI/2);
01768 
01769   Point e1(x3,y3), e2(x2,y2);
01770   Shape<LineData> line(ShS,e1,e2);
01771   line->end1Pt().setValid(false);
01772   line->end2Pt().setValid(false);
01773   result.push_back(line);
01774       }
01775       }*/
01776 
01777   // Finely explore the first n best lines in the rough table
01778   int max_val = -1, max_i = 0;
01779   while(result.size() < num_lines) {
01780 
01781     // find maximum value in accumulator table
01782     max_val = -1;
01783     max_i = 0;
01784     for (int i = 0; i < numR*numTheta; i++) {
01785       if (max_val < htab[i]) {
01786   max_val = htab[i];
01787   max_i = i;
01788       }
01789     }
01790 
01791     // Give up if the lines start getting too small
01792     if (max_val < minLength)
01793       break;
01794     
01795 
01796     float bestR = (max_i%numR)*rSpread/numR + minR;
01797     float bestTheta = (max_i/numR)*thetaSpread/numTheta + minTheta;
01798 
01799     // determine parameters for the fine hough iteration
01800     const float fthetaSpread = (float)M_PI/40.f;
01801     const float frSpread = maxR/40.f;
01802     float fminTheta = bestTheta-fthetaSpread/2.f;
01803     float fminR = bestR-frSpread/2.f;
01804 
01805     for (int i = 0; i < hsizef; i++)
01806       hfine[i] = 0; // zero fine table
01807     
01808     // build fine table
01809     float thetaf, rf;
01810     int ridxf;
01811     for (int x = 0; x < width; x++) {
01812       for (int y = 0; y < height; y++) {
01813   if (skinny(x,y) == true) {
01814     for (int tidx = 0; tidx < numThetaf ; tidx++) {
01815       thetaf = fminTheta + tidx*fthetaSpread/numThetaf;
01816       rf = (float)x*cos(thetaf)+(float)y*sin(thetaf);
01817       ridxf = (int)((rf-fminR)*(float)numRf/frSpread); // Is this right calc?
01818       if (ridxf >= 0 && ridxf < numRf) // check ridx bounds
01819         hfine[ridxf+tidx*numRf]++;  
01820     }
01821   }
01822       }
01823     }
01824 
01825     
01826     // Pull the best line out of the fine table
01827     int max_val_f = -1, max_i_f = 0;
01828     for (int i = 0; i < numRf*numThetaf; i++) {
01829       if (max_val_f < hfine[i]) {
01830   max_val_f = hfine[i];
01831   max_i_f = i;
01832       }
01833     }
01834     float bestRf = (max_i_f%numRf)*frSpread/numRf + fminR;
01835     float bestThetaf = (max_i_f/numRf)*fthetaSpread/numThetaf + fminTheta;
01836 
01837     // Add viable segments to the vector
01838     // Step along the line, looking for segments. 
01839     float lineLen = 500;
01840     // Can be simpler
01841     const float x1 = bestRf*cos(bestThetaf), y1 = bestRf*sin(bestThetaf);
01842     const float x2 = x1+lineLen*std::cos(bestThetaf+(float)M_PI_2), y2 = y1+lineLen*std::sin(bestThetaf+(float)M_PI_2);
01843     const float x3 = x1-lineLen*std::cos(bestThetaf+(float)M_PI_2), y3 = y1-lineLen*std::sin(bestThetaf+(float)M_PI_2);
01844     
01845     Point e1(x3,y3), e2(x2,y2);
01846     Shape<LineData> line(ShS,e1,e2);
01847 
01848 
01849     line->setColor(skinny->getColor());
01850     line->setParentId(skinny->getViewableId());
01851 
01852     // m is slope of this line (not the normal line)
01853     float m = (y1 != 0) ? -(x1/y1) : BIG_SLOPE;
01854     float b = y1 - m*x1;
01855     
01856     NEW_SKETCH_N(skelDist,uint,visops::mdist(skinny));
01857     if ( abs(m) <= 1 )  // when |slope| <= 1, scan along x, else scan along y
01858       line->scanHorizForEndPts(skelDist,occlusions,m,b);
01859     else
01860       line->scanVertForEndPts(skelDist,occlusions,m,b);
01861     
01862     //! Check to see if any endpoints are near any edge of the screen.
01863     //! If they are, invalidate them, assuming that line continues beyond screen.
01864     line->end1_pt.checkValidity(width,height,BEG_DIST_THRESH);
01865     line->end2_pt.checkValidity(width,height,BEG_DIST_THRESH);
01866 
01867 
01868     // Experimental: check how much of the line actually lies along edge pixels
01869     // only include lines that are at least 50% above edge pixels. 
01870     
01871     float len = line->getLength();
01872     EndPoint start = line->end1_pt;
01873     EndPoint dx = (line->end2_pt - start)/len;
01874     float onCount = 0;
01875     for (float i=0; i<len; i++) {
01876       EndPoint cur = start+dx*i;
01877       if (skinny((int)(cur.coordX()), (int)(cur.coordY()))) {
01878   onCount++;
01879       }
01880     }
01881 
01882 
01883     std::cout<<"Line with "<<onCount<<" / "<<len<<" pixels";
01884     if (line->isLongerThan(minLength) && (onCount / len) > .5 )
01885       {
01886   std::cout<<" accepted";
01887   result.push_back(line);
01888       }
01889     std::cout<<std::endl;
01890 
01891 
01892     // Empty the corresponding bins in the rough hough table
01893     //std::cout<<max_i%numR<<","<<max_i/numR<<std::endl;
01894     for(int row = -5; row <= 5; row++)
01895       {
01896   for (int col = -(5-abs(row)); col <=5-abs(row); col++)
01897     {
01898       //int index = (max_i - (max_i%numR) + ((max_i+col)%numR)+row*numR + numR*numTheta)%(numR*numTheta);
01899       //std::cout<<index%numR<<","<<index/numR<<" ";
01900       htab[(max_i - (max_i%numR) + ((max_i+col)%numR)+row*numR + numR*numTheta)%(numR*numTheta)] = 0;
01901     }
01902   //std::cout<<std::endl;
01903       }
01904     //std::cout<<std::endl;
01905   }
01906   
01907   return result;
01908 }
01909 
01910 bool LineData::linesParallel(Shape<LineData> l1, Shape<LineData>l2)
01911 {
01912   const float maxDTheta = .15f, minDThetaR = 10.0f;
01913   float dTheta = l1->getOrientation() - l2->getOrientation(); 
01914   if (dTheta > (float)M_PI_2)
01915     dTheta = dTheta - (float)M_PI;
01916   if (std::abs(dTheta) < maxDTheta)
01917     {
01918       if (std::abs (100*dTheta + (l1->rho_norm - l2->rho_norm)) > minDThetaR)
01919   return true;
01920     }
01921   return false;
01922 }
01923 
01924 LineData& LineData::operator=(const LineData& other) {
01925   if (&other == this)
01926     return *this;
01927   BaseData::operator=(other);
01928   end1_pt = other.end1_pt;
01929   end2_pt = other.end2_pt;
01930   rho_norm = other.rho_norm;
01931   theta_norm = other.theta_norm;
01932   orientation = other.orientation;
01933   length = other.length;
01934   return *this;
01935 }
01936 
01937 
01938 // Compute if two points are on the same side of the line
01939 bool LineData::pointsOnSameSide(const Point& p1, const Point& p2)
01940 {
01941   float dx = end2_pt.coordX() - end1_pt.coordX();
01942   float dy = end2_pt.coordY() - end1_pt.coordY();
01943   
01944   float p1val = (p1.coordY() - end1_pt.coordY())*dx - (p1.coordX() - end1_pt.coordX())*dy;
01945   float p2val = (p2.coordY() - end1_pt.coordY())*dx - (p2.coordX() - end1_pt.coordX())*dy;
01946 
01947   return (p1val>0) == (p2val>0);
01948 }
01949 
01950 // Checks if the distance from the line to the point is less than 1 pixel,
01951 // and checks that the point is within the end points of the line.
01952 bool LineData::pointOnLine(const Point& p)
01953 {
01954   const float BOUNDS_EXTEND = 1;
01955   float dx = end2_pt.coordX() - end1_pt.coordX();
01956   float dy = end2_pt.coordY() - end1_pt.coordY();
01957   float val = (p.coordY() - end1_pt.coordY())*dx - (p.coordX() - end1_pt.coordX())*dy;
01958   val /= length;
01959   bool inBounds = (p.coordX() >= (leftPt().coordX() - BOUNDS_EXTEND)) &&
01960     (p.coordX() <= (rightPt().coordX() + BOUNDS_EXTEND)) &&
01961     (p.coordY() >= (topPt().coordY() - BOUNDS_EXTEND)) &&
01962     (p.coordY() <= (bottomPt().coordY() + BOUNDS_EXTEND));
01963   return (val > -1) && (val < 1) && inBounds;
01964 }
01965 
01966 
01967 // Comparison predicates
01968 
01969 bool LineData::LineDataLengthLessThan::operator() (const LineData &line1, const LineData &line2) const {
01970       return line1.getLength() < line2.getLength();
01971 }
01972 
01973 bool LineData::LengthLessThan::operator() (const Shape<LineData> &line1, const Shape<LineData> &line2) const {
01974       return line1->getLength() < line2->getLength();
01975 }
01976 
01977 bool LineData::ParallelTest::operator() (const Shape<LineData> &line1, const Shape<LineData> &line2) const {
01978   return angdist(line1->getOrientation(),line2->getOrientation()) <= tolerance;
01979 }
01980 
01981 bool LineData::PerpendicularTest::operator() (const Shape<LineData> &line1, const Shape<LineData> &line2) const {
01982   return angdist(angdist(line1->getOrientation(),line2->getOrientation()), (orientation_t)M_PI/2) <= tolerance;
01983 }
01984 
01985 bool LineData::ColinearTest::operator() (const Shape<LineData> &line1, const Shape<LineData> &line2) const {
01986   return ParallelTest(ang_tol)(line1,line2) && 
01987     abs( line1->getRhoNorm() - line2->getRhoNorm() ) <= dist_tol;
01988 }
01989 
01990 bool LineData::IsHorizontal::operator() (const Shape<LineData> &line) const {
01991       const AngPi orient = line->getOrientation();
01992       return  (orient <= threshold) || (orient >= M_PI - threshold);
01993     }
01994 
01995 bool LineData::IsVertical::operator() (const Shape<LineData> &line) const {
01996       const AngPi orient = line->getOrientation();
01997       return  (orient >= threshold) && (orient <= M_PI - threshold);
01998     }
01999 
02000 } // namespace

DualCoding 5.1CVS
Generated Sat May 4 06:29:26 2013 by Doxygen 1.6.3