Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

Quad.cc

Go to the documentation of this file.
00001 #include "Vision/AprilTags/FloatImage.h"
00002 #include "Vision/AprilTags/MathUtil.h"
00003 #include "Vision/AprilTags/GLine2D.h"
00004 #include "Vision/AprilTags/Quad.h"
00005 #include "Vision/AprilTags/Segment.h"
00006 
00007 namespace AprilTags {
00008   
00009 const float Quad::maxQuadAspectRatio = 32;
00010 
00011 Quad::Quad(const std::vector< std::pair<float,float> >& p, const std::pair<float,float>& opticalCenter)
00012   : quadPoints(p), segments(), observedPerimeter(), homography(opticalCenter) {
00013   homography.addCorrespondence(-1, -1, quadPoints[0].first, quadPoints[0].second);
00014   homography.addCorrespondence( 1, -1, quadPoints[1].first, quadPoints[1].second);
00015   homography.addCorrespondence( 1,  1, quadPoints[2].first, quadPoints[2].second);
00016   homography.addCorrespondence(-1,  1, quadPoints[3].first, quadPoints[3].second);
00017 }
00018 
00019 std::pair<float,float> Quad::interpolate(float x, float y) {
00020   return homography.project(x,y);
00021 }
00022 
00023 std::pair<float,float> Quad::interpolate01(float x, float y) {
00024   return interpolate(2*x-1, 2*y-1);
00025 }
00026 
00027 void Quad::search(const FloatImage& fImage, std::vector<Segment*>& path,
00028       Segment& parent, int depth, std::vector<Quad>& quads) {
00029   // cout << "Searching segment " << parent.getId() << ", depth=" << depth << ", #children=" << parent.children.size() << endl;
00030   // terminal depth occurs when we've found four segments.
00031   if (depth == 4) {
00032     // cout << "Entered terminal depth" << endl; // debug code
00033 
00034     // Is the first segment the same as the last segment (i.e., a loop?)
00035     if (path[4] == path[0]) {
00036       // the 4 corners of the quad as computed by the intersection of segments.
00037       std::vector< std::pair<float,float> > p(4);
00038       float calculatedPerimeter = 0;
00039       bool bad = false;
00040       for (int i = 0; i < 4; i++) {
00041   // compute intersections between all the lines. This will give us 
00042   // sub-pixel accuracy for the corners of the quad.
00043   GLine2D linea(std::make_pair(path[i]->getX0(),path[i]->getY0()),
00044           std::make_pair(path[i]->getX1(),path[i]->getY1()));
00045   GLine2D lineb(std::make_pair(path[i+1]->getX0(),path[i+1]->getY0()),
00046           std::make_pair(path[i+1]->getX1(),path[i+1]->getY1()));
00047 
00048   p[i] = linea.intersectionWith(lineb);
00049   calculatedPerimeter += path[i]->getLength();
00050 
00051   // no intersection? Occurs when the lines are almost parallel.
00052   if (p[i].first == -1)
00053     bad = true;
00054       }
00055       // cout << "bad = " << bad << endl;
00056       // eliminate quads that don't form a simply connected loop, i.e., those 
00057       // that form an hour glass, or wind the wrong way.
00058       if (!bad) {
00059     float t0 = std::atan2(p[1].second-p[0].second, p[1].first-p[0].first);
00060   float t1 = std::atan2(p[2].second-p[1].second, p[2].first-p[1].first);
00061   float t2 = std::atan2(p[3].second-p[2].second, p[3].first-p[2].first);
00062   float t3 = std::atan2(p[0].second-p[3].second, p[0].first-p[3].first);
00063 
00064   //  double ttheta = fmod(t1-t0, 2*M_PI) + fmod(t2-t1, 2*M_PI) +
00065   //    fmod(t3-t2, 2*M_PI) + fmod(t0-t3, 2*M_PI);
00066   float ttheta = MathUtil::mod2pi(t1-t0) + MathUtil::mod2pi(t2-t1) +
00067     MathUtil::mod2pi(t3-t2) + MathUtil::mod2pi(t0-t3);
00068   // cout << "ttheta=" << ttheta << endl;
00069   // the magic value is -2*PI. It should be exact, 
00070   // but we allow for (lots of) numeric imprecision.
00071   if (ttheta < -7 || ttheta > -5)
00072     bad = true;
00073       }
00074 
00075       if (!bad) {
00076   float d0 = MathUtil::distance2D(p[0], p[1]);
00077   float d1 = MathUtil::distance2D(p[1], p[2]);
00078   float d2 = MathUtil::distance2D(p[2], p[3]);
00079   float d3 = MathUtil::distance2D(p[3], p[0]);
00080   float d4 = MathUtil::distance2D(p[0], p[2]);
00081   float d5 = MathUtil::distance2D(p[1], p[3]);
00082 
00083   // check sizes
00084   if (d0 < Quad::minimumEdgeLength || d1 < Quad::minimumEdgeLength || d2 < Quad::minimumEdgeLength ||
00085       d3 < Quad::minimumEdgeLength || d4 < Quad::minimumEdgeLength || d5 < Quad::minimumEdgeLength) {
00086     bad = true;
00087     // cout << "tagsize too small" << endl;
00088   }
00089 
00090   // check aspect ratio
00091   float dmax = max(max(d0,d1), max(d2,d3));
00092   float dmin = min(min(d0,d1), min(d2,d3));
00093 
00094   if (dmax > dmin*Quad::maxQuadAspectRatio) {
00095     bad = true;
00096     // cout << "aspect ratio too extreme" << endl;
00097   }
00098       }
00099 
00100       if (!bad) {
00101   std::pair<float,float> opticalCenter(fImage.getWidth()/2, fImage.getHeight()/2);
00102   Quad q(p, opticalCenter);
00103   q.segments=path;
00104   q.observedPerimeter = calculatedPerimeter;
00105   quads.push_back(q);
00106       }
00107     }
00108     return;
00109   }
00110 
00111   //  if (depth >= 1) // debug code
00112   //cout << "depth: " << depth << endl;
00113 
00114   // Not terminal depth. Recurse on any children that obey the correct handedness.
00115   for (unsigned int i = 0; i < parent.children.size(); i++) {
00116     Segment &child = *parent.children[i];
00117     //    cout << "  Child " << child.getId() << ":  ";
00118     // (handedness was checked when we created the children)
00119     
00120     // we could rediscover each quad 4 times (starting from
00121     // each corner). If we had an arbitrary ordering over
00122     // points, we can eliminate the redundant detections by
00123     // requiring that the first corner have the lowest
00124     // value. We're arbitrarily going to use theta...
00125     if ( child.getTheta() > path[0]->getTheta() ) {
00126       // cout << "theta failed: " << child.getTheta() << " > " << path[0]->getTheta() << endl;
00127       continue;
00128     }
00129     path[depth+1] = &child;
00130     search(fImage, path, child, depth+1, quads);
00131   }
00132 }
00133 
00134 } // namespace

Tekkotsu v5.1CVS
Generated Fri Mar 16 05:26:50 2012 by Doxygen 1.6.3