Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

TagDetection.cc

Go to the documentation of this file.
00001 #include "TagDetection.h"
00002 #include "MathUtil.h"
00003 #include "Shared/mathutils.h" // for isnan fix
00004 using namespace std;
00005 namespace AprilTags {
00006 
00007 TagDetection::TagDetection() 
00008   : good(false), obsCode(), code(), id(), hammingDistance(), rotation(), p(),
00009     cxy(), observedPerimeter(), homography(), hxy() {}
00010 
00011 TagDetection::TagDetection(int _id)
00012   : good(false), obsCode(), code(), id(_id), hammingDistance(), rotation(), p(),
00013     cxy(), observedPerimeter(), homography(), hxy() {}
00014 
00015 AngSignPi TagDetection::getXYOrientation() const {
00016   // Original code said:
00017   //   Because the order of segments in a quad is arbitrary, so is the
00018   //   homography's rotation, so we can't determine orientation directly
00019   //   from the homography.  Instead, use the homography to find two
00020   //   bottom corners of a properly oriented tag in pixel coordinates,
00021   //   and then compute orientation from that.
00022   //   std::pair<float,float> p0 = interpolate(-1,-1);   // lower left corner of tag
00023   //   std::pair<float,float> p1 = interpolate(1,-1);    // lower right corner of tag
00024   // But we're not computing orientation until the tag is decoded, so
00025   // the points ARE in the right order.  Using the points instead of the
00026   // homography avoids occasional buggy behavior with the homography.  -- DST
00027   const std::pair<float,float> &p0 = p[0];
00028   const std::pair<float,float> &p1 = p[1];
00029   AngSignPi orient = std::atan2(p1.second - p0.second, p1.first - p0.first);
00030   return ! std::isnan(float(orient)) ? orient : AngSignPi(0);
00031 }
00032 
00033 fmat::Matrix<4,4> TagDetection::getRotMatrix() const {
00034   // This code was taken in April 2014 from the latest AprilTags
00035   // Java source, but it does not appear to work correctly.  -- DST
00036   float s = (7*25.4) / 1000;
00037   float cx = 640/2, cy = 480/2;
00038   float fx = 500, fy = 500;
00039 
00040   fmat::Matrix<4,4> M;
00041   M(0,0) = (homography(0,0)-cx*homography(2,0)) / fx;
00042   M(0,1) = (homography(0,1)-cx*homography(2,1)) / fx;
00043   M(0,3) = (homography(0,2)-cx*homography(2,2)) / fx;
00044   M(1,0) = (homography(1,0)-cy*homography(2,0)) / fy;
00045   M(1,1) = (homography(1,1)-cy*homography(2,1)) / fy;
00046   M(1,3) = (homography(1,2)-cy*homography(2,2)) / fy;
00047   M(2,0) = s * homography(2,0);
00048   M(2,1) = s * homography(2,1);
00049   M(2,3) = s * homography(2,2);
00050 
00051   float scale0 = sqrt(M(0,0)*M(0,0) + M(1,0)*M(1,0) + M(2,0)*M(2,0));
00052   float scale1 = sqrt(M(0,1)*M(0,1) + M(1,1)*M(1,1) + M(2,1)*M(2,1));
00053   float scale = sqrt(scale0 * scale1);
00054 
00055   M = M / scale;
00056   if ( M(2,3) > 0 )
00057     M = M * -1.0;
00058 
00059   M(3,0) = M(3,1) = M(3,2) = 0;
00060   M(3,3) = 1;
00061 
00062   // Recover third rotation vector by cross product.
00063   fmat::Column<3> a = fmat::pack(M(0,0), M(1,0), M(2,0));
00064   fmat::Column<3> b = fmat::pack(M(0,1), M(1,1), M(2,1));
00065   fmat::Column<3> ab = fmat::crossProduct(a,b);
00066   M(0,2) = ab[0];
00067   M(1,2) = ab[1];
00068   M(2,2) = ab[2];
00069 
00070   // Pull out just the rotation component so we can normalize it.
00071   fmat::Matrix<3,3> R = fmat::SubMatrix<3,3>(M,0,0);
00072 
00073   // Polar decomposition, R = (UV')(VSV')
00074   NEWMAT::Matrix tm(3,3), tmu(3,3), tmv(3,3);
00075   NEWMAT::DiagonalMatrix tmd;
00076   R.exportToNewmat(tm);
00077   NEWMAT::SVD(tm, tmd, tmu, tmv, true, true);
00078   NEWMAT::Matrix MR = tmu * tmv.t(); 
00079   R.importFromNewmat(MR);
00080   fmat::SubMatrix<3,3>(M,0,0) = R;
00081   return M;
00082 }
00083 
00084 /*
00085       // ***** OLD CODE HERE
00086 
00087       double tagSize = 0.200;
00088       double f = 500.0;
00089 
00090       // cout << "homography: " << endl << homography.fmt() << endl;
00091 
00092 //     double F[][] = LinAlg.identity(3);
00093 //     F[1][1] = -1;
00094 //     F[2][2] = -1;
00095         
00096         fmat::Matrix<3,3> identMatrix(fmat::Matrix<3,3>::identity());
00097         identMatrix(1, 1) = -1;
00098         identMatrix(2, 2) = -1;
00099         // cout << identMatrix.fmt() << endl;
00100         
00101 //     h = LinAlg.matrixAB(F, h);
00102         fmat::Matrix<3,3> h = identMatrix * homography;
00103         // cout << h.fmt() << endl;
00104 
00105 //     double M[][] = new double[4][4];
00106 //     M[0][0] =  h[0][0] / f;
00107 //     M[0][1] =  h[0][1] / f;
00108 //     M[0][3] =  h[0][2] / f;
00109 //     M[1][0] =  h[1][0] / f;
00110 //     M[1][1] =  h[1][1] / f;
00111 //     M[1][3] =  h[1][2] / f;
00112 //     M[2][0] =  h[2][0];
00113 //     M[2][1] =  h[2][1];
00114 //     M[2][3] =  h[2][2];
00115 
00116         fmat::Matrix<4,4> m;
00117 
00118         // cout << m.fmt() << endl;
00119         
00120         m(0,0) = h(0,0) / f;
00121         m(0,1) = h(0,1) / f;
00122         m(0,3) = h(0,2) / f;
00123         m(1,0) = h(1,0) / f;
00124         m(1,1) = h(1,1) / f;
00125         m(1,3) = h(1,2) / f;
00126         m(2,0) = h(2,0);
00127         m(2,1) = h(2,1);
00128         m(2,3) = h(2,2);
00129 
00130         // cout << m.fmt() << endl;
00131 
00132 //     // Compute the scale. The columns of M should be made to be
00133 //     // unit vectors. This is over-determined, so we take the
00134 //     // geometric average.
00135 //     double scale0 = Math.sqrt(sq(M[0][0]) + sq(M[1][0]) + sq(M[2][0]));
00136 //     double scale1 = Math.sqrt(sq(M[0][1]) + sq(M[1][1]) + sq(M[2][1]));
00137 //     double scale = Math.sqrt(scale0*scale1);
00138 
00139         double scale0 = sqrt(MathUtil::square(m(0,0)) + MathUtil::square(m(1,0)) + MathUtil::square(m(2,0)));
00140         double scale1 = sqrt(MathUtil::square(m(0,1)) + MathUtil::square(m(1,1)) + MathUtil::square(m(2,1)));
00141         double scale = sqrt(scale0 * scale1);
00142 
00143 //     M = LinAlg.scale(M, 1.0/scale);
00144 
00145         m = m * (1.0/scale);
00146 
00147         // cout << "Scaled" << m.fmt() << endl;
00148 
00149   // recover sign of scale factor by noting that observations must occur in front of the camera.
00150 //     if (M[2][3] > 0)
00151 //         M = LinAlg.scale(M, -1);
00152 
00153         if (m(2,3) > 0)
00154             m = m * -1;
00155 
00156         //     // The bottom row should always be [0 0 0 1].  We reset the
00157 //     // first three elements, even though they must be zero, in
00158 //     // order to make sure that they are +0. (We could have -0 due
00159 //     // to the sign flip above. This is theoretically harmless but
00160 //     // annoying in practice.)
00161 //     M[3][0] = 0;
00162 //     M[3][1] = 0;
00163 //     M[3][2] = 0;
00164 //     M[3][3] = 1;
00165 
00166         m(3,0) = 0;
00167         m(3,1) = 0;
00168         m(3,2) = 0;
00169         m(3,3) = 1;
00170 
00171         //     // recover third rotation vector by crossproduct of the other two rotation vectors.
00172 //     double a[] = new double[] { M[0][0], M[1][0], M[2][0] };
00173 //     double b[] = new double[] { M[0][1], M[1][1], M[2][1] };
00174 //     double ab[] = LinAlg.crossProduct(a,b);
00175 
00176 //     M[0][2] = ab[0];
00177 //     M[1][2] = ab[1];
00178 //     M[2][2] = ab[2];
00179 
00180         fmat::Column<3> a = fmat::pack(m(0,0), m(1,0), m(2,0));
00181         fmat::Column<3> b = fmat::pack(m(0,1), m(1,1), m(2,1));
00182         fmat::Column<3> ab = fmat::crossProduct(a,b);
00183 
00184         // cout << ab.fmt() << endl;
00185 
00186         m(0,2) = ab[0];
00187         m(1,2) = ab[1];
00188         m(2,2) = ab[2];
00189         
00190         //     // pull out just the rotation component so we can normalize it.
00191 //     double R[][] = new double[3][3];
00192 //     for (int i = 0; i < 3; i++)
00193 //         for (int j = 0; j < 3; j++)
00194 //             R[i][j] = M[i][j];
00195 
00196         fmat::Matrix<3,3> m2;
00197         for (int i = 0; i < 3; i++)
00198             for (int j = 0; j < 3; j++)
00199                 m2(i,j) = m(i,j);
00200 
00201         // cout << m2 << endl;
00202 
00203         //     SingularValueDecomposition svd = new SingularValueDecomposition(new Matrix(R));
00204 //     // polar decomposition, R = (UV')(VSV')
00205 //     Matrix MR = svd.getU().times(svd.getV().transpose());
00206 //     for (int i = 0; i < 3; i++)
00207 //         for (int j = 0; j < 3; j++)
00208 //             M[i][j] = MR.get(i,j);
00209 
00210         NEWMAT::Matrix tm(3,3);
00211         m2.exportToNewmat(tm);
00212         NEWMAT::Matrix tmu(3,3);
00213         NEWMAT::Matrix tmv(3,3);
00214         NEWMAT::DiagonalMatrix tmd;
00215         NEWMAT::SVD(tm, tmd, tmu, tmv, true, true);
00216         NEWMAT::Matrix tm2 = tmu * tmv.t();
00217         fmat::Matrix<3,3> m3;
00218         m3.importFromNewmat(tm2);
00219 
00220         // cout << "imported" << endl << m3.fmt() << endl;
00221 
00222         for (int i = 0; i < 3; i++)
00223             for (int j = 0; j < 3; j++)
00224                 m(i,j) = m3(i,j);
00225         
00226 
00227   // Scale the results based on the scale in the homography. The
00228   // homography assumes that tags span from -1 to +1, i.e., that
00229   // they are two units wide (and tall).
00230 //     for (int i = 0; i < 3; i++)
00231 //         M[i][3] *= tagSize / 2;
00232         for (int i = 0; i < 3; i++)
00233              m(i,3) *= tagSize / 2;
00234 
00235         // cout << "final" << endl << m << endl;
00236         return m;
00237     }
00238 
00239 */
00240 
00241 std::pair<float,float> TagDetection::interpolate(float x, float y) const {
00242   float z = homography(2,0)*x + homography(2,1)*y + homography(2,2);
00243   if ( z == 0 )
00244     return std::pair<float,float>(0,0);  // prevents returning a pair with a -NaN, for which gcc 4.4 flubs isnan
00245   float newx = (homography(0,0)*x + homography(0,1)*y + homography(0,2))/z + hxy.first;
00246   float newy = (homography(1,0)*x + homography(1,1)*y + homography(1,2))/z + hxy.second;
00247   return std::pair<float,float>(newx,newy);
00248 }
00249 
00250 bool TagDetection::overlapsTooMuch(const TagDetection &other) const {
00251   // Compute a sort of "radius" of the two targets. We'll do this by
00252   // computing the average length of the edges of the quads (in
00253   // pixels).
00254   float radius =
00255     ( MathUtil::distance2D(p[0], p[1]) +
00256       MathUtil::distance2D(p[1], p[2]) +
00257       MathUtil::distance2D(p[2], p[3]) +
00258       MathUtil::distance2D(p[3], p[0]) +
00259       MathUtil::distance2D(other.p[0], other.p[1]) +
00260       MathUtil::distance2D(other.p[1], other.p[2]) +
00261       MathUtil::distance2D(other.p[2], other.p[3]) +
00262       MathUtil::distance2D(other.p[3], other.p[0]) ) / 16.0f;
00263 
00264   // distance (in pixels) between two tag centers
00265   float dist = MathUtil::distance2D(cxy, other.cxy);
00266 
00267   // reject pairs where the distance between centroids is smaller than
00268   // the "radius" of one of the tags.
00269   if ( dist < radius )
00270     std::cout << "AprilTags: rejecting AprilTag with dist=" << dist << "  radius=" << radius << std::endl;
00271   return ( dist < radius );
00272 }
00273 
00274 } // namespace

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:51 2016 by Doxygen 1.6.3