SIFTegomotion.C

Go to the documentation of this file.
00001 /*!@file SIFT/SIFTegomotion.C Calculates egomotion given a set of
00002   correspondence match */
00003 
00004 // //////////////////////////////////////////////////////////////////// //
00005 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00006 // University of Southern California (USC) and the iLab at USC.         //
00007 // See http://iLab.usc.edu for information about this project.          //
00008 // //////////////////////////////////////////////////////////////////// //
00009 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00010 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00011 // in Visual Environments, and Applications'' by Christof Koch and      //
00012 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00013 // pending; application number 09/912,225 filed July 23, 2001; see      //
00014 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00015 // //////////////////////////////////////////////////////////////////// //
00016 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00017 //                                                                      //
00018 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00019 // redistribute it and/or modify it under the terms of the GNU General  //
00020 // Public License as published by the Free Software Foundation; either  //
00021 // version 2 of the License, or (at your option) any later version.     //
00022 //                                                                      //
00023 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00024 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00025 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00026 // PURPOSE.  See the GNU General Public License for more details.       //
00027 //                                                                      //
00028 // You should have received a copy of the GNU General Public License    //
00029 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00030 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00031 // Boston, MA 02111-1307 USA.                                           //
00032 // //////////////////////////////////////////////////////////////////// //
00033 //
00034 // Primary maintainer for this file: Christian Siagian <siagian@usc.edu>
00035 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/SIFT/SIFTegomotion.C $
00036 // $Id: SIFTegomotion.C 9412 2008-03-10 23:10:15Z farhan $
00037 //
00038 #include "SIFT/SIFTegomotion.H"
00039 #include "SIFT/VisualObject.H"
00040 #include "Image/CutPaste.H"
00041 #include "Image/DrawOps.H"
00042 #include "Raster/Raster.H"
00043 #include "Image/LinearAlgebra.H"
00044 #include "Image/MatrixOps.H" // for matrixMult(), matrixInv(), etc
00045 
00046 // Solving Egomotion based on epipolar equation:
00047 // (p^T)(v_hat)(p_dot) + (p^T)(S)(p) = 0    eqn.(1a) - for calibrated case
00048 // (m^T)(W)(m_dot) + (m^T)(C)(m) = 0        eqn.(1b) - for uncalibrated case
00049 
00050 // the diffference is the availability of A (the camera intrinsic parameter matrix)
00051 // that is m = Ap
00052 // below we will deal with the uncalibrated version with some simplification of A
00053 
00054 // eqn.(1a) - for calibrated case
00055 // p is a homogeneous coordinate [p1 p2 p3]^T, p3 = 1
00056 // mdot is an instantaneous optical flow  [p1dot p2dot p3dot]^T, p3dot = 0
00057 // S: 3 by 3 symmetric matrix (6 unknowns: s11, s12, s13, s22, s23, s23)
00058 // v_hat: 3 by 3 anti-symmetric matrix (3 unknowns: v12, v13, v23)
00059 
00060 // eqn.(1b) - for uncalibrated case
00061 // m is a homogeneous coordinate [m1 m2 m3]^T, m3 = 1
00062 // mdot is an instantaneous optical flow  [m1dot m2dot m3dot]^T, m3dot = 0
00063 // C: 3 by 3 symmetric matrix (6 unknowns: c11, c12, c13, c22, c23, c23)
00064 // W: 3 by 3 anti-symmetric matrix (3 unknowns: w12, w13, w23)
00065 
00066 // both eqn(1a. and 1b.) can berearranged to  U*T = 0
00067 // in equation below the solution uses the uncalibrated variable name
00068 // with the following substitution to the calibrated version:
00069 // p is equal to m
00070 // S is equal to C
00071 // v_hat is equal to W
00072 
00073   /* Ui = [
00074            m1*m1
00075            2*m1*m2
00076            2*m1*m3
00077            m2*m2
00078            2*m2*m3
00079            m3*m3
00080            m1*m2dot - m2*m1dot
00081            m1*m3dot - m3*m1dot
00082            m2*m3dot - m3*m2dot
00083           ]
00084      i = 0, 1, ..., 6
00085   */
00086 
00087   /*
00088     T = [
00089           c11
00090           c12
00091           c13
00092           c22
00093           c23
00094           c33
00095           w12
00096           w13
00097           w23
00098         ]
00099   */
00100 
00101 // there is also an additional contraint
00102 // (w^T)(C)(w) = 0            eqn.(2)
00103 // w = [-w23 w13 -w12]^T
00104 // we will deal with it after solving eqn(1) using SVD
00105 
00106 // NOTE: the solution is still to a CONSTANT
00107 // (whether it be negative or positive)
00108 
00109 // ######################################################################
00110 // get a magnitude of a vector
00111 double getMag(Image<double> v)
00112 {
00113   double mag = 0.0;
00114   for(int i = 0; i < v.getHeight(); i++)
00115       mag += v.getVal(0,i)*v.getVal(0,i);
00116 
00117   return sqrt(mag);
00118 }
00119 
00120 // ######################################################################
00121 SIFTegomotion::SIFTegomotion(rutz::shared_ptr<VisualObjectMatch> match,
00122                              rutz::shared_ptr<CameraIntrinsicParam> cip,
00123                              rutz::shared_ptr<XWinManaged> matchWin) :
00124   itsVoMatch(match),
00125   itsCip(cip),
00126   itsMatchWin(matchWin)
00127 {
00128   setCuMatch();
00129   calc();
00130 }
00131 
00132 // ######################################################################
00133 void SIFTegomotion::setCuMatch()
00134 {
00135   for(uint i = 0; i < itsVoMatch->size(); i++)
00136     {
00137       double u1vo = (*itsVoMatch)[i].refkp->getX();
00138       double v1vo = (*itsVoMatch)[i].refkp->getY();
00139       double u2vo = (*itsVoMatch)[i].tstkp->getX();
00140       double v2vo = (*itsVoMatch)[i].tstkp->getY();
00141 
00142       // some points are counted twice because of matches in
00143       // multiple scale-space
00144       bool isIdentical = 0;
00145       for(uint j = 0; j < itsCuMatch.size(); j++)
00146         {
00147           double u1cu = itsCuMatch[j].refkp->getX();
00148           double v1cu = itsCuMatch[j].refkp->getY();
00149           double u2cu = itsCuMatch[j].tstkp->getX();
00150           double v2cu = itsCuMatch[j].tstkp->getY();
00151 
00152           // if the coord. of ref and tst are within .05 in both axis
00153           if(fabs(u1vo - u1cu) < .05 &&
00154              fabs(v1vo - v1cu) < .05 &&
00155              fabs(u2vo - u2cu) < .05 &&
00156              fabs(v2vo - v2cu) < .05 )
00157             {
00158               // it is assumed identical
00159               isIdentical = 1;
00160               // LDEBUG("[%4d] DUP[%10.5f,%10.5f,%10.5f,%10.5f: %10.5f,%10.5f,%10.5f,%10.5f]",
00161               //      i, u1vo, v1vo, u2vo, v2vo, u1cu, v1cu, u2cu, v2cu);
00162             }
00163         }
00164 
00165       if(!isIdentical)
00166         {
00167           //LDEBUG("[%4d]    [%10.5f,%10.5f,%10.5f,%10.5f]",i, u1vo, v1vo, u2vo, v2vo);
00168           itsCuMatch.push_back((*itsVoMatch)[i]);
00169         }
00170     }
00171 
00172   LINFO("there are %"ZU" unique coordinate (%"ZU" duplicates)",
00173         itsCuMatch.size(), itsVoMatch->size() - itsCuMatch.size());
00174   //Raster::waitForKey();
00175 }
00176 
00177 // ######################################################################
00178 Image<double> SIFTegomotion::getU(int nPoint)
00179 {
00180   uint size;
00181   if (nPoint == -1)  size = itsCuMatch.size();
00182   else               size = nPoint;
00183 
00184   Image<double> U(9,size,ZEROS);
00185 
00186   int w = itsMatchWin->getDims().w()/2;
00187   int h = itsMatchWin->getDims().h()/2;
00188   Image< PixRGB<byte> > img(w,2*h,ZEROS);
00189 
00190   // if the camera calibration params are available
00191   Image<double> A; Image<double> Ainv;
00192   if(itsCip.is_valid())
00193     {
00194       A    = itsCip->getCameraMatrix();
00195       Ainv = matrixInv(A);
00196       print(A,    "Camera Matrix");
00197       print(Ainv, "Inverse Camera Matrix");
00198     }
00199 
00200   LINFO("there are %d points in the match", itsVoMatch->size());
00201   //Raster::waitForKey();
00202   Image< PixRGB<byte> > rImg = itsVoMatch->getVoRef()->getImage();
00203   Image< PixRGB<byte> > tImg = itsVoMatch->getVoTest()->getImage();
00204   for(uint i = 0; i < size; i++)
00205     {
00206       // default to uncalibrated first for visual purposes
00207       double tU = itsCuMatch[i].refkp->getX();  // m1
00208       double tV = itsCuMatch[i].refkp->getY();  // m2
00209       double rU = itsCuMatch[i].tstkp->getX();
00210       double rV = itsCuMatch[i].tstkp->getY();
00211       double rUdot = tU - rU;   // m1dot
00212       double rVdot = tV - rV;   // m2dot
00213       LDEBUG("UNCALIB:  (%10.5f,%10.5f) & (%10.5f,%10.5f) -> (%10.5f,%10.5f)",
00214              rU, rV, tU, tV, rUdot, rVdot);
00215 
00216       Point2D<int> locR(int(rU + 0.5F), int(rV + 0.5F));
00217       drawDisk(rImg, locR, 2, PixRGB<byte>(255,0,0));
00218       Point2D<int> locT(int(tU + 0.5F), int(tV + 0.5F));
00219       drawDisk(tImg, locT, 2, PixRGB<byte>(255,0,0));
00220 
00221       drawLine(rImg, locR, locT, PixRGB<byte>(255,255,0));
00222 
00223       // after the visuals we can calculate the calibrated values
00224       if(itsCip.is_valid())
00225         {
00226           Image<double> mR(1,3,ZEROS);
00227           mR.setVal(0, 0, rU); mR.setVal(0, 1, rV); mR.setVal(0, 2, 1.0);
00228           Image<double> pR = matrixMult(Ainv, mR);
00229           //print(mR,"mR"); print(pR,"pR");
00230           rU = pR.getVal(0,0);  // p1
00231           rV = pR.getVal(0,1);  // p2
00232 
00233           Image<double> mT(1,3,ZEROS);
00234           mT.setVal(0, 0, tU); mT.setVal(0, 1, tV); mT.setVal(0, 2, 1.0);
00235           Image<double> pT = matrixMult(Ainv, mT);
00236           tU = pT.getVal(0,0);
00237           tV = pT.getVal(0,1);
00238 
00239           rVdot = tU - rU;   // p1dot
00240           rUdot = tV - rV;   // p2dot
00241 
00242           LDEBUG("CALIB:    (%10.5f,%10.5f) & (%10.5f,%10.5f) -> (%10.5f,%10.5f)",
00243                  rU, rV, tU, tV, rUdot, rVdot);
00244         }
00245 
00246       // we add m3 = 1.0, m3dot = 0.0 below
00247       double m1    = rU;
00248       double m2    = rV;
00249       double m3    = 1.0f;
00250       double m1dot = rUdot;
00251       double m2dot = rVdot;
00252       double m3dot = 0.0f;
00253 
00254       U.setVal(0, i, m1*m1);
00255       U.setVal(1, i, 2*m1*m2);
00256       U.setVal(2, i, 2*m1*m3);
00257       U.setVal(3, i, m2*m2);
00258       U.setVal(4, i, 2*m2*m3);
00259       U.setVal(5, i, m3*m3);
00260       U.setVal(6, i, m1*m2dot - m2*m1dot);
00261       U.setVal(7, i, m1*m3dot - m3*m1dot);
00262       U.setVal(8, i, m2*m3dot - m3*m2dot);
00263 
00264     }
00265 
00266   inplacePaste(img, rImg,   Point2D<int>(0, 0));
00267   inplacePaste(img, tImg,   Point2D<int>(0, h));
00268   itsMatchWin->drawImage(img,w,0);
00269   //Raster::waitForKey();
00270 
00271   return U;
00272 }
00273 
00274 // ######################################################################
00275 void SIFTegomotion::calc()
00276 {
00277   uint techNum = 0;
00278 
00279   if(techNum == 0)
00280     leastSquaresAlgebraic();
00281   else
00282     sevenPointAlgorithm();
00283 }
00284 
00285 // ######################################################################
00286 void SIFTegomotion::leastSquaresAlgebraic()
00287 {
00288   // prepare the matrices
00289   Image<double> U = getU(); print(U, "U");
00290   Image<double> A = matrixMult(transpose(U), U); print(A, "A");
00291   //Raster::waitForKey();
00292 
00293   // perform SVD on A; A = [V1][D][V2]T
00294   // A is [9 x 9] matrix
00295   Image<double> V1;
00296   Image<double>  D;
00297   Image<double> V2;
00298   svd(A, V1, D, V2, SVD_LAPACK, SVD_FULL);
00299   print(V1,"V1"); print(D, "D"); print(V2, "V2");
00300   //Raster::waitForKey();
00301 
00302   // the least sqaures answer is the last column of V2
00303   Image<double> T = crop(V2, Point2D<int>(8,0), Dims(1, 9)); print(T, "T");
00304   //print(transpose(T),"Tt");print(transpose(U),"Ut");
00305   Image<double> ans = matrixMult(U,T); print(ans,"ans");
00306   //Raster::waitForKey();
00307 
00308   //T = constraint2(T);
00309   itsVel   = getVel(T);
00310   itsOmega = getOmega(T);
00311   print(T, "T");  print(itsVel, "vel");  print(itsOmega, "omega");
00312   //Raster::waitForKey();
00313 }
00314 
00315 // ######################################################################
00316 // Seven Point Estimator Algorithm
00317 // for minimizing the Differential Epipolar Constraint
00318 void SIFTegomotion::sevenPointAlgorithm()
00319 {
00320   // prepare the matrices
00321   // get seven random points from the correspondence
00322   Image<double> U = getU(7); print(U, "U");
00323 
00324   // perform SVD on U; U = [V1][D][V2]T
00325   Image<double> V1;
00326   Image<double>  D;
00327   Image<double> V2;
00328   svd(U, V1, D, V2, SVD_LAPACK, SVD_FULL);
00329   print(V1,"V1"); print(D, "D"); print(V2, "V2");
00330 
00331   uint zeig = 0;
00332   for(uint i = 0; i < 7; i++)
00333     {
00334       LINFO("eig[%d] = %20.10f",i+1,D.getVal(i,i));
00335       if(D.getVal(i,i) < .0000001) zeig++;
00336     }
00337   if( zeig > 0)
00338     {
00339       LINFO("> 2 null spaces; points are ill conditioned");
00340       Raster::waitForKey();
00341     }
00342 
00343   // get the last two columns T1 and T2 out of V1
00344   // the null space of U, solve UT = 0
00345   Image<double> T1 = crop(V1, Point2D<int>(7,0), Dims(1, 9)); print(T1, "T1");
00346   Image<double> T2 = crop(V1, Point2D<int>(8,0), Dims(1, 9)); print(T2, "T2");
00347 
00348   // test if it works
00349   //Image<double> UT1 = matrixMult(transpose(U), T1); print(UT1, "UT1");
00350   //Image<double> UT2 = matrixMult(transpose(U), T2); print(UT2, "UT2");
00351 
00352   // solve to take account constraint eqn.(2)
00353   Image<double> T =  constraint2(T1,T2);  print(T,"T");
00354   checkConstraint2(U, T);
00355 }
00356 
00357 // ######################################################################
00358 // check if eqn(2) constraint is fulfilled
00359 void SIFTegomotion::checkConstraint2(Image<double> U, Image<double> T)
00360 {
00361   Image<double> Wtemp(1,3,ZEROS);
00362   Image<double> Ctemp(3,3,ZEROS);
00363   Wtemp.setVal(0,0, -T.getVal(0,8));
00364   Wtemp.setVal(0,1,  T.getVal(0,7));
00365   Wtemp.setVal(0,2, -T.getVal(0,6));
00366 
00367   Ctemp.setVal(0,0, T.getVal(0,0));
00368   Ctemp.setVal(0,1, T.getVal(0,1));
00369   Ctemp.setVal(0,2, T.getVal(0,2));
00370   Ctemp.setVal(1,0, T.getVal(0,1));
00371   Ctemp.setVal(1,1, T.getVal(0,3));
00372   Ctemp.setVal(1,2, T.getVal(0,4));
00373   Ctemp.setVal(2,0, T.getVal(0,2));
00374   Ctemp.setVal(2,1, T.getVal(0,4));
00375   Ctemp.setVal(2,2, T.getVal(0,5));
00376   print(Ctemp,"Ctemp"); print(Wtemp,"Wtemp");
00377   Image<double> wCw = matrixMult(matrixMult(transpose(Wtemp), Ctemp), Wtemp);
00378   print(wCw, "wCw");
00379 }
00380 
00381 // ######################################################################
00382 Image<double> SIFTegomotion::constraint2(Image<double> T1)
00383 {
00384   /*  T1.setVal(0,0, 3.0);
00385   T1.setVal(0,1, 4.0);
00386   T1.setVal(0,2, 5.0);
00387   T1.setVal(0,3, 3.0);
00388   T1.setVal(0,4, 1.0);
00389   T1.setVal(0,5, 5.0);
00390   T1.setVal(0,6, 7.0);
00391   T1.setVal(0,7, 6.0);
00392   T1.setVal(0,8, 2.0);
00393   LINFO("set T1");
00394   Raster::waitForKey();*/
00395 
00396   Image<double> W_hat = getW(T1); print(W_hat, "W_hat");
00397   Image<double> C_hat = getC(T1); print(C_hat, "C_hat");
00398   Raster::waitForKey();
00399 
00400   Image<double> I(3,3, ZEROS);
00401   I.setVal(0,0, 1.0);I.setVal(1,1, 1.0);I.setVal(2,2, 1.0);
00402   Image<double> omega = getOmega(T1); print(omega, "omega");
00403   Raster::waitForKey();
00404 
00405   double magOmega = 1.0;//getMag(omega);
00406   LINFO("magOmega: %f",magOmega);
00407   Image<double> P = I - matrixMult(W_hat,W_hat) * magOmega; print(P, "P");
00408   Raster::waitForKey();
00409 
00410   Image<double> C = C_hat - matrixMult(matrixMult(P,C_hat),P); ;
00411   Image<double> W = W_hat;
00412   double magT = getMag(getThetaCW(C,W));
00413   W = W/magT;
00414   C = C/magT;
00415   Image<double> T = getThetaCW(C,W);
00416       Raster::waitForKey();
00417 
00418   return T;
00419 }
00420 
00421 // ######################################################################
00422 Image<double> SIFTegomotion::constraint2(Image<double> T1, Image<double> T2)
00423 {
00424   /*
00425     // the solution T is linear combination of T1 and T2
00426     T_i = a * T1_i + (1 - a) * T2_i
00427         = T2_i + a(T1_i - T2_i)
00428     solve for 'a'
00429     using the constraint eqn.(2), in algebraic form
00430         w23*w23*c11 - w13*w23*c12 + w12*w23*c13 +
00431        -w23*w13*c12 + w13*w13*c22 - w12*w13*c23 +
00432         w23*w12*c13 - w13*w12*c23 + w12*w12*c33 = 0
00433     Substitute T_i to the equation, e.g. w23 =  T_8
00434 
00435     we have to go one term at a time
00436     the form is:
00437     T_i*T_j*T_k =
00438 
00439     (T2_i + a(T1_i - T2_i))*(T2_j + a(T1_j - T2_j))*(T2_k + a(T1_k - T2_k)) =
00440 
00441     (aa   + a*bb          )*(cc   + a*dd          )*(ee   + a*ff          ) =
00442 
00443     ((aa*cc) + a*(aa*dd) + a*(bb*cc) + a^2*(bb*dd))*(ee   + a*ff          ) =
00444     ((aa*cc*ee) + a*(aa*cc*ff) + a*(aa*dd*ee) + a^2*(aa*dd*ff) +
00445       a*(bb*cc*ee) + a^2*(bb*cc*ff) + a^2*(bb*dd*ee) + a^3*(bb*dd*ff)) =
00446     ( a^3*(bb*dd*ff                      ) +
00447       a^2*(aa*dd*ff + bb*cc*ff + bb*dd*ee) +
00448       a  *(aa*cc*ff + aa*dd*ee + bb*cc*ee) +
00449           (aa*cc*ee                      ) ) =
00450     p_i*a^3 + q_i*a^2 + r_i*a + s_i
00451   */
00452   Image<double> T(1, 9, ZEROS);
00453 
00454   // after accumulating them we end up with the realization that
00455   // eqn.(2) is a cubic constraint: p + q*a + r*a^2 + s*a^3 = 0
00456   double p = 0.0, q = 0.0, r = 0.0, s = 0.0;
00457   double aa, bb, cc, dd, ee, ff;
00458 
00459   // we'll use indI, indJ,indK for theta indexes i,j, and k, respectively
00460   // we have to note the sign in front of the term as well
00461   uint indI, indJ, indK; double sign;
00462 
00463 
00464 
00465 //   T2.setVal(0,0, 5.0);
00466 //   T2.setVal(0,1, 2.0);
00467 //   T2.setVal(0,2, 9.0);
00468 
00469 //   T2.setVal(0,3, 4.0);
00470 //   T2.setVal(0,4, 7.0);
00471 //   T2.setVal(0,5, 3.0);
00472 
00473 //   T2.setVal(0,6, 4.0);
00474 //   T2.setVal(0,7, 8.0);
00475 //   T2.setVal(0,8, 4.0);
00476 //  print(T1, "T1"); print(T2, "T2");
00477   for(uint i = 0; i < 9; i++)
00478     {
00479       if(i == 0)
00480         {
00481           // term 1: w23*w23*c11 = T_i*T_j*T_k, i = 8, j = 8, k = 0
00482           //         sign: positive
00483           indI = 8, indJ = 8, indK = 0; sign = 1.0;
00484         }
00485       else if(i == 1)
00486         {
00487           // term 2:-w13*w23*c12 = T_i*T_j*T_k, i = 7, j = 8, k = 1
00488           //         sign: negative
00489           indI = 7, indJ = 8, indK = 1; sign = -1.0;
00490         }
00491       else if(i == 2)
00492         {
00493           // term 3: w12*w23*c13 = T_i*T_j*T_k, i = 6, j = 8, k = 2
00494           //         sign: positive
00495           indI = 6, indJ = 8, indK = 2; sign = 1.0;
00496         }
00497       else if(i == 3)
00498         {
00499           // term 4:-w23*w13*c12 = T_i*T_j*T_k, i = 8, j = 7, k = 1
00500           //         sign: negative
00501           indI = 8, indJ = 7, indK = 1; sign = -1.0;
00502         }
00503       else if(i == 4)
00504         {
00505           // term 5: w13*w13*c22 = T_i*T_j*T_k, i = 7, j = 7, k = 3
00506           //         sign: positive
00507           indI = 7, indJ = 7, indK = 3; sign = 1.0;
00508         }
00509       else if(i == 5)
00510         {
00511           // term 6:-w12*w13*c23 = T_i*T_j*T_k, i = 6, j = 7, k = 4
00512           //         sign: negative
00513           indI = 6, indJ = 7, indK = 4; sign = -1.0;
00514         }
00515       else if(i == 6)
00516         {
00517           // term 7: w23*w12*c13 = T_i*T_j*T_k, i = 8, j = 6, k = 2
00518           //         sign: positive
00519           indI = 8, indJ = 6, indK = 2; sign = 1.0;
00520         }
00521       else if(i == 7)
00522         {
00523           // term 8:-w13*w12*c23 = T_i*T_j*T_k, i = 7, j = 6, k = 4
00524           //         sign: negative
00525           indI = 7, indJ = 6, indK = 4; sign = -1.0;
00526         }
00527       else
00528         {
00529           // term 9: w12*w12*c33 = T_i*T_j*T_k, i = 6, j = 6, k = 5
00530           //         sign: positive
00531           indI = 6, indJ = 6, indK = 5; sign = 1.0;
00532         }
00533 
00534       aa = T2.getVal(0, indI);
00535       bb = T1.getVal(0, indI) - T2.getVal(0, indI);
00536       cc = T2.getVal(0, indJ);
00537       dd = T1.getVal(0, indJ) - T2.getVal(0, indJ);
00538       ee = T2.getVal(0, indK);
00539       ff = T1.getVal(0, indK) - T2.getVal(0, indK);
00540 
00541       p+= sign * (bb*dd*ff);
00542       q+= sign * (aa*dd*ff + bb*cc*ff + bb*dd*ee);
00543       r+= sign * (aa*cc*ff + aa*dd*ee + bb*cc*ee);
00544       s+= sign * (aa*cc*ee);
00545 
00546       LINFO(" %f*alpha^3 + %f*alpha^2 + %f*alpha + %f",
00547             sign * (bb*dd*ff),
00548             sign * (aa*dd*ff + bb*cc*ff + bb*dd*ee),
00549             sign * (aa*cc*ff + aa*dd*ee + bb*cc*ee),
00550             sign * (aa*cc*ee));
00551 
00552       LINFO("%d:[%d,%d,%d] (%f,%f,%f,%f,%f,%f) sign:(%f) p:%f, q:%f r:%f s:%f",
00553             i, indI, indJ, indK,
00554             aa, bb, cc, dd, ee, ff, sign, p,q,r,s);
00555     }
00556 
00557   // cubic constraint has either 1 or 3 real solutions
00558   std::vector<std::complex<double> > alpha;
00559   uint numSoln = solveCubic(p, q, r, s, alpha);
00560   LINFO("Number of real solutions: %d",numSoln);
00561   LINFO("X1 = %f + %fi, X2 = %f + %fi, X3 = %f + %fi",
00562         alpha[0].real(), alpha[0].imag(),
00563         alpha[1].real(), alpha[1].imag(),
00564         alpha[2].real(), alpha[2].imag() );
00565 
00566   // get the first solution for now
00567   for(uint ns = 0; ns < numSoln; ns++)
00568     {
00569       // calculate the theta
00570       for(uint i= 0; i < 9; i++)
00571         T.setVal(0,i, (alpha[ns].real()*T1.getVal(0,i) +
00572                        (1.0 - alpha[ns].real())*T2.getVal(0,i)));
00573 
00574       // get the camera motion velocities
00575       if(itsCip.is_valid())
00576         {
00577           itsVel   = getVel(T);
00578           itsOmega = getOmega(T);
00579         }
00580       else
00581         getUncalibCW(T);
00582     }
00583   Raster::waitForKey();
00584 
00585   return T;
00586 }
00587 // ######################################################################
00588 Image<double> SIFTegomotion::getThetaCW(Image<double> C, Image<double> W)
00589 {
00590   Image<double> T(1,9, ZEROS);
00591 
00592   T.setVal(0,0, C.getVal(0,0));
00593   T.setVal(0,1, C.getVal(0,1));
00594   T.setVal(0,2, C.getVal(0,2));
00595   T.setVal(0,3, C.getVal(1,1));
00596   T.setVal(0,4, C.getVal(1,2));
00597   T.setVal(0,5, C.getVal(2,2));
00598 
00599   T.setVal(0,6, W.getVal(1,0));
00600   T.setVal(0,7, W.getVal(2,0));
00601   T.setVal(0,8, W.getVal(2,1));
00602 
00603   return T;
00604 }
00605 
00606 // ######################################################################
00607 Image<double> SIFTegomotion::getC(Image<double> T)
00608 {
00609   Image<double> C(3,3, ZEROS);
00610   C.setVal(0,0,  T.getVal(0,0));
00611   C.setVal(0,1,  T.getVal(0,1));
00612   C.setVal(0,2,  T.getVal(0,2));
00613 
00614   C.setVal(1,0,  T.getVal(0,1));
00615   C.setVal(1,1,  T.getVal(0,3));
00616   C.setVal(1,2,  T.getVal(0,4));
00617 
00618   C.setVal(2,0,  T.getVal(0,2));
00619   C.setVal(2,1,  T.getVal(0,4));
00620   C.setVal(2,2,  T.getVal(0,5));
00621   return C;
00622 }
00623 
00624 // ######################################################################
00625 Image<double> SIFTegomotion::getW(Image<double> T)
00626 {
00627   Image<double> W(3,3, ZEROS);
00628   W.setVal(1,0,  T.getVal(0,6));
00629   W.setVal(2,0,  T.getVal(0,7));
00630   W.setVal(2,1,  T.getVal(0,8));
00631   W.setVal(0,1, -T.getVal(0,6));
00632   W.setVal(0,2, -T.getVal(0,7));
00633   W.setVal(1,2, -T.getVal(0,8));
00634   return W;
00635 }
00636 
00637 // ######################################################################
00638 Image<double> SIFTegomotion::getThetaVelOmega
00639   (Image<double> vel, Image<double> omega)
00640 {
00641   Image<double> T(1,9, ZEROS);
00642   /*
00643   Image<double> T(1,9, ZEROS);
00644   T.setVal(0,6, C.getVal(0,));
00645   T.setVal(0,7, C.getVal(0,));
00646   T.setVal(0,8, C.getVal(0,));
00647 
00648   Image<double> CW = matrixMult(C,W); // or S
00649   T.setVal(0,0, CW.getVal(0,0));
00650   T.setVal(0,1, CW.getVal(0,1));
00651   T.setVal(0,2, CW.getVal(0,2));
00652   T.setVal(0,3, CW.getVal(1,1));
00653   T.setVal(0,4, CW.getVal(1,2));
00654   T.setVal(0,5, CW.getVal(2,2));
00655   */
00656   return T;
00657 }
00658 
00659 // ######################################################################
00660 Image<double> SIFTegomotion::getVel(Image<double> T)
00661 {
00662   // in eqn(1b) S (or C in the uncalibrated version):
00663   // v_hat is the translational velocity of the camera in anti-symmetric form
00664   double v1 = -T.getVal(0,8); // -w23
00665   double v2 =  T.getVal(0,7); //  w13
00666   double v3 = -T.getVal(0,6); // -w12
00667 
00668   LINFO("translational velocity (%f,%f,%f)", v1, v2, v3);
00669   Image<double> vel(1,3,ZEROS);
00670   vel.setVal(0,0, v1);
00671   vel.setVal(0,1, v2);
00672   vel.setVal(0,2, v3);
00673   return vel;
00674 }
00675 
00676 // ######################################################################
00677 Image<double> SIFTegomotion::getOmega(Image<double> T)
00678 {
00679   // in eqn(1b) S (or C in the uncalibrated version):
00680   // v_hat is the translational velocity of the camera in anti-symmetric form
00681   double v1 = -T.getVal(0,8); // -w23
00682   double v2 =  T.getVal(0,7); //  w13
00683   double v3 = -T.getVal(0,6); // -w12
00684 
00685   // in eqn(1b) S (or C in the uncalibrated version) = (v_hat)(w_hat)
00686   // v_hat is known, while w_hat is devivable to:
00687   // S = | s11 s12 s13 | = (v_hat)(w_hat) =
00688   //     | s12 s22 s23 |
00689   //     | s13 s23 s33 |
00690   //
00691   // |  0 -v3  v2 ||  0 -w3  w2 | = |(-v3w3 - v2w2)   v2w1            v3w1        |
00692   // | v3   0 -v1 || w3   0 -w1 |   | v1w2           ( -v3w3 - v1w1)  v3w2        |
00693   // |-v2  v1   0 ||-w2  w1   0 |   | v1w3            v2w3          (-v2w2 - v1w1)|
00694   //
00695   // note: wX = omegaX
00696 
00697   double omega1 = 0.0, omega2 = 0.0, omega3 = 0.0;
00698   if(v1 == 0.0 && v2 == 0.0 && v3 == 0.0)
00699     {
00700       // if the camera is stationary we can only assume
00701       // that the angle does not change
00702     }
00703   else if(v1 == 0.0 && v2 == 0.0)
00704     {
00705       omega1 =  T.getVal(0,2)/v3; // s13/v3
00706       omega2 =  T.getVal(0,4)/v3; // s23/v3
00707       omega3 = -T.getVal(0,3)/v3; // s22/v3
00708     }
00709   else if(v1 == 0.0 && v3 == 0.0)
00710     {
00711       omega1 =  T.getVal(0,1)/v2; // s12/v2
00712       omega2 = -T.getVal(0,0)/v2; // s11/v2
00713       omega3 =  T.getVal(0,4)/v2; // s23/v2
00714     }
00715   else if(v2 == 0.0 && v3 == 0.0)
00716     {
00717       omega1 = -T.getVal(0,5)/v1; // s33/v1
00718       omega2 =  T.getVal(0,1)/v1; // s12/v1
00719       omega3 =  T.getVal(0,2)/v1; // s13/v1
00720     }
00721   else if(v1 == 0.0)
00722     {
00723       omega1 =  T.getVal(0,1)/v2; // s12/v2
00724       omega2 =  T.getVal(0,4)/v3; // s23/v3
00725       omega3 =  T.getVal(0,3)/v2; // s23/v2
00726     }
00727   else if(v2 == 0.0)
00728     {
00729       omega1 =  T.getVal(0,2)/v3; // s13/v3
00730       omega2 =  T.getVal(0,4)/v3; // s23/v3
00731       omega3 =  T.getVal(0,3)/v1; // s13/v1
00732     }
00733   else
00734     {
00735       omega1 =  T.getVal(0,1)/v2; // s12/v2
00736       omega2 =  T.getVal(0,1)/v1; // s12/v1
00737       omega3 =  T.getVal(0,2)/v1; // s13/v3
00738     }
00739 
00740   LINFO("angular velocity: (%f,%f,%f)", omega1, omega2, omega3);
00741   Image<double> omega(1,3,ZEROS);
00742   omega.setVal(0,0, omega1);
00743   omega.setVal(0,1, omega2);
00744   omega.setVal(0,2, omega3);
00745   return omega;
00746 }
00747 
00748 // ######################################################################
00749 void SIFTegomotion::getUncalibCW(Image<double> T)
00750 {
00751   // extract the components of W and C
00752   // from eqn.(1) differential Epipolar Constraint
00753   // we now can extract the camera motion up to a scalar
00754   // angular change (3 vals)
00755   // translational change (2 vals)
00756   double c11 = T.getVal(0,0);
00757   double c12 = T.getVal(0,1);
00758   double c13 = T.getVal(0,2);
00759   double c22 = T.getVal(0,3);
00760   double c23 = T.getVal(0,4);
00761   double c33 = T.getVal(0,5);
00762   double w12 = T.getVal(0,6); // = w1
00763   double w13 = T.getVal(0,7); // = w2
00764   double w23 = T.getVal(0,8); // = w3
00765   double w1  = w12;
00766   double w2  = w13;
00767   double w3  = w23;
00768 
00769   //
00770   double delta1 = (2*c12*w2 - (c22 - c11)*w1) / (w1*w1 + w2*w2);
00771   double delta2 = (2*c12*w1 + (c22 - c11)*w2) / (w1*w1 + w2*w2);
00772   double delta3 = (c11*w1*w1 + 2*c12*w1*w2 + c22*w2*w2) / (w3*(w1*w1 + w2*w2));
00773   double psi = (w1*w1 + w2*w2 + w3*w3)*(w2*delta1 + w2*delta2);
00774   double d1 = 2*c13 + w1*delta3;
00775   double d2 = 2*c23 + w2*delta3;
00776   double d3 = c33;
00777   double delta4 = - d3 / (w1*delta1 + w3*delta2);
00778   double delta5 = (1.0/psi) * ((w1*w2*delta1 + (w2*w2 + w3*w3)*delta2)*d1
00779                                - ((w1*w1 + w3*w3)*delta1 + w1*w2*delta2)*d2
00780                                + (w2*w3*delta1 - w1*w3*delta2)*d3);
00781 
00782   LINFO("d1: %f, d2: %f, d3: %f", d1, d2, d3);
00783   LINFO("psi: %f",psi);
00784   LINFO("delta:(1: %f, 2: %f, 3: %f, 4:%f, 5:%f",
00785         delta1, delta2, delta3, delta4, delta5);
00786 
00787   double flen   = sqrt(delta4);
00788   double omega1 = delta1 * sqrt(flen);
00789   double omega2 = delta2 * sqrt(flen);
00790   double omega3 = delta3;
00791   double flen_dot = delta5 * sqrt(flen);
00792 
00793   double v1 = -w1 / flen;
00794   double v2 = -w2 / flen;
00795   double v3 = w3;
00796 
00797   LINFO("focal length: %f, flen_dot: %f",flen, flen_dot);
00798   LINFO("angular velocity: (%f,%f,%f)", omega1, omega2, omega3);
00799   LINFO("translational velocity (%f,%f,%f)", v1, v2, v3);
00800 }
00801 
00802 // ######################################################################
00803 // sign of number
00804 double sgn(double x)
00805 {
00806   if (x < 0.0) return -1.0;
00807   return 1.0;
00808 }
00809 
00810 // ######################################################################
00811 uint SIFTegomotion::solveCubic(double p, double q, double r, double s,
00812                                std::vector<std::complex<double> >& x)
00813 {
00814   // compute real or complex roots of cubic polynomial
00815   // x^3 + A*x^2 + B*x + C = 0
00816   // roots are X1, X2, and X3
00817   // try A = -11.0, B = 49.0, C = -75.0,
00818   //     ans: x1 = 3, x2 = 4+3i, x3 = 4-3i
00819 
00820   // Credit due to:  Stephen R. Schmitt
00821   // http://home.att.net/~srschmitt/script_exact_cubic.html
00822 
00823   double A,B,C;
00824   if(p != 0.0)
00825     {
00826       A = q/p;
00827       B = r/p;
00828       C = s/p;
00829     }
00830   else if(q != 0.0)
00831     {
00832       // this is a quadratic formula
00833       // x = (-r + sqrt(r^2 -4 qs)/2q
00834       // x = (-r - sqrt(r^2 -4 qs)/2q
00835 
00836       double temp = r*r - 4.0*q*s;
00837       if(temp == 0.0)
00838         {
00839           x.push_back(std::complex<double>(-r/(2.0*q), 0.0));
00840           return 1;
00841         }
00842       else if(temp > 0.0)
00843         {
00844           x.push_back(std::complex<double>((-r + sqrt(temp))/(2.0*q), 0.0));
00845           x.push_back(std::complex<double>((-r - sqrt(temp))/(2.0*q), 0.0));
00846           return 2;
00847         }
00848       else
00849         {
00850           x.push_back(std::complex<double>(-r/(2.0*q),  sqrt(-temp)/(2.0*q)));
00851           x.push_back(std::complex<double>(-r/(2.0*q), -sqrt(-temp)/(2.0*q)));
00852           return 0;
00853         }
00854     }
00855   else if(r != 0.0)
00856     {
00857       // this is rx + s = 0
00858       x.push_back(std::complex<double>(-s/r, 0.0));
00859       return 1;
00860     }
00861   else
00862     // this has to be s = 0
00863     return 0;
00864 
00865   double X1, X2, X3, Im;
00866   double Q, R, D, S, T;
00867 
00868   Q = (3*B - (A*A))/9.0;
00869   R = (9*A*B - 27*C - 2*(A*A*A))/54.0;
00870   D = (Q*Q*Q) + (R*R);    // polynomial discriminant
00871 
00872   if (D >= 0)                                 // complex or duplicate roots
00873   {
00874     S = sgn(R + sqrt(D)) * pow(fabs(R + sqrt(D)),(1.0/3.0));
00875     T = sgn(R - sqrt(D)) * pow(fabs(R - sqrt(D)),(1.0/3.0));
00876 
00877     X1 = -A/3.0 + (S + T);                    // real root
00878     X2 = -A/3.0 - (S + T)/2.0;                  // real part of complex root
00879     X3 = -A/3.0 - (S + T)/2.0;                  // real part of complex root
00880     Im = fabs(sqrt(3)*(S - T)/2.0);           // complex part of root pair
00881   }
00882   else                                        // distinct real roots
00883   {
00884     double th = acos(R/sqrt(-pow(Q, 3)));
00885 
00886     X1 = 2*sqrt(-Q) * cos(th/3.0) - A/3.0;
00887     X2 = 2*sqrt(-Q) * cos((th + 2*M_PI)/3) - A/3.0;
00888     X3 = 2*sqrt(-Q) * cos((th + 4*M_PI)/3) - A/3.0;
00889     Im = 0.0;
00890   }
00891 
00892   // 0.0 if real roots
00893   if (Im == 0.0)                              // real roots
00894   {
00895     x.push_back(std::complex<double>(X1, 0.0));
00896     x.push_back(std::complex<double>(X2, 0.0));
00897     x.push_back(std::complex<double>(X3, 0.0));
00898     LDEBUG("X1 = %f, X2 = %f, X3 = %f", X1, X2, X3);
00899     return 3;
00900   }
00901   else                                        // real and complex pair
00902   {
00903     x.push_back(std::complex<double>(X1, 0.0));
00904     x.push_back(std::complex<double>(X2,  Im));
00905     x.push_back(std::complex<double>(X3, -Im));
00906     LDEBUG("X1 = %f, X2 = %f + %fi, X3 = %f - %fi", X1, X2, Im, X3, Im);
00907     return 1;
00908   }
00909 }
00910 
00911 // ######################################################################
00912 void SIFTegomotion::print(Image<double> img, const std::string& name)
00913 {
00914 //   LINFO("%s:  %d by %d", name.c_str(), img.getWidth(),  img.getHeight());
00915 //   for(int j = 0; j < img.getHeight(); j++)
00916 //     {
00917 //       for(int i = 0; i < img.getWidth(); i++)
00918 //         {
00919 //           printf("%13.5f ", img.getVal(i,j));
00920 //         }
00921 //       printf("\n");
00922 //     }
00923 }
00924 
00925 // ######################################################################
00926 /* So things look consistent in everyone's emacs... */
00927 /* Local Variables: */
00928 /* indent-tabs-mode: nil */
00929 /* End: */
Generated on Sun May 8 08:42:17 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3