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: */