00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00024
00025 #include <QTime>
00026 #include <qvsfm/qvgea/geaoptimization.h>
00027 #include <QVSparseBlockMatrix>
00028 #include <qvsfm/qvgea/so3EssentialEvaluation.h>
00029 #include <qvsfm/qvgea/quaternionEssentialEvaluation.h>
00030
00031 QList< QHash< int, QPointF > > storePointMatchingsInTrackingContainer(const QVDirectedGraph< QVector<QPointFMatching> > &matchingLists)
00032 {
00033 QList< QHash< int, QPointF > > result;
00034
00035 foreach(QVGraphLink link, matchingLists.keys())
00036 {
00037 const QVector<QPointFMatching> &matchings = matchingLists[link];
00038 for(int i = 0; i < matchings.count(); i++)
00039 {
00040 QHash< int, QPointF > tracking;
00041 tracking[link.x()] = matchings[i].first;
00042 tracking[link.y()] = matchings[i].second;
00043 result << tracking;
00044 }
00045 }
00046 return result;
00047 }
00048
00049 QVDirectedGraph< QList<QPointFMatching> > getPointMatchingsLists(const QList<QHash<int, QPointF> > pointProjections, const int numCams, const int minPointCorrespondences)
00050 {
00051 QTime time;
00052 QHash<int, QPointF> projections;
00053
00054
00055
00056 time.start();
00057 QVector< int > count(numCams*numCams, 0);
00058
00059 foreach(projections, pointProjections)
00060 {
00061 const QVector<int> indexes = projections.keys().toVector();
00062 foreach(int i, indexes)
00063 foreach(int j, indexes)
00064 count[i * numCams + j]++;
00065 }
00066
00067
00068
00069
00070
00071
00072
00073 QVector< QList<QPointFMatching> > v(numCams*numCams);
00074 foreach(projections, pointProjections)
00075 {
00076 const QVector<int> indexes = projections.keys().toVector();
00077
00078
00079
00080 foreach(int i, indexes)
00081 foreach(int j, indexes)
00082 if ( (i < j) and (count[i*numCams+j] >= minPointCorrespondences) )
00083 v[i * numCams + j] << QPointFMatching(projections[i], projections[j]);
00084 }
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107 QVDirectedGraph< QList<QPointFMatching> > result;
00108 for(int i = 0; i < numCams; i++)
00109 for(int j = 0; j < numCams; j++)
00110 {
00111 const QList<QPointFMatching> & list = v[i*numCams+j];
00112 if (list.count() > 0)
00113 result[ QVGraphLink(i,j) ] = list;
00114 }
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124 return result;
00125 }
00126
00127 QVDirectedGraph< QList<QPointFMatching> > pointMatchingsListOld(const QList<QHash<int, QPointF> > pointProjections, const int numCams)
00128 {
00129 Q_UNUSED(numCams);
00130
00131 QVDirectedGraph< QList<QPointFMatching> > result;
00132
00133 QHash<int, QPointF> projections;
00134 foreach(projections, pointProjections)
00135 foreach(int i, projections.keys())
00136 foreach(int j, projections.keys())
00137 if (i < j)
00138 result[ QVGraphLink(i,j) ] << QPointFMatching(projections[i], projections[j]);
00139 return result;
00140 }
00141
00142 QVDirectedGraph<QVMatrix> getReducedMatrices( const QVDirectedGraph< QList<QPointFMatching> > &pointLists,
00143 const bool normalize,
00144 const TGEA_decomposition_method decomposition_method,
00145 const bool gsl,
00146 const double choleskyLambda,
00147 const int minPointCorrespondences)
00148 {
00149
00150 QVDirectedGraph<QVMatrix> reducedMatricesGraph;
00151
00152
00153 foreach(QPoint p, pointLists.keys())
00154 {
00155 const QList<QPointFMatching> &pointList = pointLists[p];
00156 if (pointList.count() < minPointCorrespondences)
00157 continue;
00158
00159
00160 reducedMatricesGraph[p] = getReduced8PointsCoefficientsMatrix(pointList, decomposition_method, normalize, gsl, choleskyLambda);
00161 }
00162
00163 return reducedMatricesGraph;
00164 }
00165
00166 QVDirectedGraph<QVMatrix> getReducedMatrices( const QVDirectedGraph< QVector<QPointFMatching> > &pointLists,
00167 const bool normalize,
00168 const TGEA_decomposition_method decomposition_method,
00169 const bool gsl,
00170 const double choleskyLambda,
00171 const int minPointCorrespondences)
00172 {
00173
00174 QVDirectedGraph<QVMatrix> reducedMatricesGraph;
00175
00176
00177 foreach(QPoint p, pointLists.keys())
00178 {
00179 const QVector<QPointFMatching> &pointList = pointLists[p];
00180 if (pointList.count() < minPointCorrespondences)
00181 continue;
00182
00183
00184 reducedMatricesGraph[p] = getReduced8PointsCoefficientsMatrix(pointList.toList(), decomposition_method, normalize, gsl, choleskyLambda);
00185 }
00186
00187 return reducedMatricesGraph;
00188 }
00189
00190
00191
00192
00193
00194 QVMatrix get8PointsCoefficientMatrix(const QVector<QPointFMatching> &matchings, const bool normalize)
00195 {
00196 QVMatrix A(matchings.size(),9);
00197 double *aptr = A.getWriteData();
00198
00199 foreach(QPointFMatching matching,matchings)
00200 {
00201
00202
00203
00204
00205
00206
00207
00208 const QPointF sourcePoint = matching.first, destPoint = matching.second;
00209 const double x = sourcePoint.x(), y = sourcePoint.y(),
00210 x_p = destPoint.x(), y_p = destPoint.y(),
00211 normalizer = normalize?1.0/sqrt((x*x + y*y +1)*(x_p*x_p+y_p*y_p+1)):1.0,
00212 x_ = x * normalizer, y_ = y * normalizer;
00213
00214 *aptr = x_*x_p; aptr++;
00215 *aptr = y_*x_p; aptr++;
00216 *aptr = normalizer*x_p; aptr++;
00217 *aptr = x_*y_p; aptr++;
00218 *aptr = y_*y_p; aptr++;
00219 *aptr = normalizer*y_p; aptr++;
00220 *aptr = x_; aptr++;
00221 *aptr = y_; aptr++;
00222 *aptr = normalizer; aptr++;
00223 }
00224 return A;
00225 }
00226
00227 QVMatrix getTransposeProductOf8PointsCoefficientMatrix(const QVector<QPointFMatching> &matchings, const bool normalize)
00228 {
00229 QVMatrix A(9,9, 0.0);
00230 double *ptrA = A.getWriteData();
00231
00232 foreach(QPointFMatching matching, matchings)
00233 {
00234 double v[9];
00235
00236 const QPointF sourcePoint = matching.first, destPoint = matching.second;
00237 const double x = sourcePoint.x(), y = sourcePoint.y(),
00238 x_p = destPoint.x(), y_p = destPoint.y(),
00239 normalizer = normalize?1.0/sqrt((x*x + y*y +1)*(x_p*x_p+y_p*y_p+1)):1.0,
00240 x_ = x * normalizer, y_ = y * normalizer;
00241
00242
00243 v[0] = x_*x_p;
00244 v[1] = y_*x_p;
00245 v[2] = normalizer * x_p;
00246 v[3] = x_*y_p;
00247 v[4] = y_*y_p;
00248 v[5] = normalizer * y_p;
00249 v[6] = x_;
00250 v[7] = y_;
00251 v[8] = normalizer;
00252
00253 for(int i = 0; i < 9; i++)
00254 for(int j = 0; j <= i; j++)
00255 ptrA[9*i+j] += v[i] * v[j];
00256 }
00257
00258
00259 for(int i = 0; i < 9; i++)
00260 for(int j = i+1; j < 9; j++)
00261 ptrA[9*i+j] = ptrA[9*j+i];
00262
00263 return A;
00264 }
00265
00266
00267 QVMatrix getReduced8PointsCoefficientsMatrix( const QVector<QPointFMatching> &matchingsList,
00268 const TGEA_decomposition_method decomposition_method,
00269 const bool normalize,
00270 const bool use_gsl,
00271 const double choleskyLambda)
00272 {
00273 if (matchingsList.count() > 9)
00274 {
00275 QVMatrix MtM = getTransposeProductOf8PointsCoefficientMatrix(matchingsList, normalize);
00276
00277 switch(decomposition_method)
00278 {
00279 case GEA_DO_NOT_DECOMPOSE:
00280 return MtM;
00281 break;
00282
00283 case GEA_CHOLESKY_DECOMPOSITION:
00284 {
00285 QVMatrix L;
00286
00287
00288
00289 if (choleskyLambda != 0.0)
00290 {
00291 const int n = MtM.getCols();
00292 double *data = MtM.getWriteData();
00293 for(int i = 0, idx = 0; i < n; i++, idx += n+1)
00294 data[idx] += choleskyLambda;
00295 }
00296
00297 CholeskyDecomposition(MtM, L, use_gsl?GSL_CHOLESKY:LAPACK_CHOLESKY_DPOTRF);
00298
00299 return L;
00300 }
00301 break;
00302
00303 case GEA_EIGEN_DECOMPOSITION:
00304 {
00305 QVMatrix Q;
00306 QVVector lambda;
00307
00308 eigenDecomposition(MtM, lambda, Q, use_gsl?GSL_EIGENSYMM:LAPACK_DSYEV);
00309
00310
00311 double *dataLambda = lambda.data();
00312 for(int i=0; i<lambda.size(); i++)
00313 dataLambda[i] = sqrt(fabs(dataLambda[i]));
00314
00315
00316 double *dataQ = Q.getWriteData();
00317 const int cols = Q.getCols(), rows = Q.getRows();
00318 for(int i = 0, idx = 0; i < rows; i++)
00319 for(int j = 0; j < cols; j++, idx++)
00320 dataQ[idx] *= dataLambda[j];
00321
00322 return Q;
00323 }
00324 break;
00325 }
00326 }
00327 return get8PointsCoefficientMatrix(matchingsList, normalize).transpose();
00328 }
00329
00330 QVDirectedGraph< QVector<QPointFMatching> > getPointMatchingsListsVec(const QList<QHash<int, QPointF> > pointProjections, const int numCams, const int minPointCorrespondences)
00331 {
00332 QTime time;
00333 QHash<int, QPointF> projections;
00334
00335
00336
00337
00338 QVector< int > count(numCams*numCams, 0);
00339 foreach(projections, pointProjections)
00340 {
00341 const QVector<int> indexes = projections.keys().toVector();
00342 foreach(int i, indexes)
00343 foreach(int j, indexes)
00344 count[i * numCams + j]++;
00345 }
00346
00347
00348
00349 QVector< QVector<QPointFMatching> > vX(numCams*numCams);
00350 for(int i = 0; i < numCams; i++)
00351 for(int j = 0; j < numCams; j++)
00352 {
00353 const int c = count[i*numCams+j];
00354 if (c > 0)
00355 vX[i*numCams+j].reserve(c);
00356 }
00357
00358
00359
00360
00361 time.start();
00362 foreach(projections, pointProjections)
00363 {
00364 const QVector<int> indexes = projections.keys().toVector();
00365
00366
00367
00368 foreach(int i, indexes)
00369 foreach(int j, indexes)
00370 if ( (i < j) and (count[i*numCams+j] >= minPointCorrespondences) )
00371 {
00372 QVector<QPointFMatching> &vector = vX[i * numCams + j];
00373 vector << QPointFMatching(projections[i], projections[j]);
00374
00375
00376
00377
00378
00379 }
00380 }
00381
00382
00383
00384 QVDirectedGraph< QVector<QPointFMatching> > result2;
00385 for(int i = 0; i < numCams; i++)
00386 for(int j = 0; j < numCams; j++)
00387 {
00388 const QVector<QPointFMatching> & list = vX[i*numCams+j];
00389 if (list.count() > 0)
00390 result2.insert(i,j,list);
00391 }
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402 return result2;
00403 }
00404
00405
00406
00407
00408
00409
00410
00411 bool evaluateGEAJacobianAndResidual(
00412 const int numCameras,
00413
00414 const int DOF,
00415
00416 const QVDirectedGraph<QVMatrix> &reducedMatricesGraph,
00417
00418 const QList<QVGraphLink> &links,
00419
00420 const QVVector &x,
00421
00422 QVSparseBlockMatrix &estimatedJ,
00423
00424 QVVector &residuals
00425 )
00426 {
00427 const int numLinks = links.count();
00428 estimatedJ = QVSparseBlockMatrix(numLinks, numCameras, 9, DOF);
00429 residuals = QVVector(numLinks*9, 0.0);
00430
00431
00432 const double *xData = x.constData();
00433
00434 #ifdef DEBUG
00435 if (x.containsNaN())
00436 {
00437 std::cout << "[evaluateGEAJacobianAndResidual] Error: NaN value found in reconstruction" << std::endl;
00438 return false;
00439 }
00440 #endif
00441
00442
00443
00444 int linkIndex = 0;
00445 foreach(QVGraphLink link, links)
00446 {
00447
00448 const int xIndex = link.x(),
00449 yIndex = link.y();
00450
00451
00452 if (xIndex > yIndex)
00453 {
00454 std::cout << "[evaluateGEAHessianAndObjectiveVector] Error: provided invalid order for indexes 'x' and 'y'." << std::endl;
00455 exit(0);
00456 }
00457
00458 if (xIndex >= numCameras)
00459 {
00460 std::cout << "[evaluateGEAHessianAndObjectiveVector] Error: malformed coefficient matrices graph: index 'x' out of bounds." << std::endl;
00461 exit(0);
00462 }
00463
00464 if (yIndex >= numCameras)
00465 {
00466 std::cout << "[evaluateGEAHessianAndObjectiveVector] Error: malformed coefficient matrices graph: index 'y' out of bounds." << std::endl;
00467 exit(0);
00468 }
00469
00470
00471
00472 const int jacRows = 9, jacCols = DOF*2+1;
00473
00474
00475
00476
00477 double D1D2v[jacRows*jacCols];
00478
00479
00480 #ifdef SO3_PARAMETRIZATION
00481 so3EssentialEvaluation(xData + DOF*xIndex, xData + DOF*yIndex, D1D2v, 1e-32);
00482 #else
00483 quaternionEssentialEvaluation(xData + DOF*xIndex, xData + DOF*yIndex, D1D2v);
00484 #endif
00485
00486
00487
00488 const QVMatrix &reducedM = reducedMatricesGraph.value(xIndex, yIndex);
00489 const int reducedMCols = reducedM.getCols();
00490
00491 #ifdef DEBUG
00492 if (reducedM.containsNaN())
00493 {
00494 std::cout << "[evaluateGEAJacobianAndResidual] Error: NaN value found in reduced matrix for views " << xIndex << ", " << yIndex << std::endl;
00495 return false;
00496 }
00497 #endif
00498
00499
00500
00501
00502 double J1J2e[jacRows*jacCols];
00503 cblas_dgemm(CblasRowMajor, CblasTrans, CblasNoTrans, reducedMCols, jacCols, jacRows, 1.0, reducedM.getReadData(), reducedMCols, D1D2v, jacCols, 0.0, J1J2e, jacCols);
00504 const QVMatrix mJ1J2e(jacRows, jacCols, J1J2e);
00505
00506 #ifdef DEBUG
00507 if (mJ1J2e.containsNaN())
00508 {
00509 std::cout << "[evaluateGEAJacobianAndResidual] Error: NaN value found in block jacobian for views " << xIndex << ", " << yIndex << std::endl;
00510 return false;
00511 }
00512 #endif
00513
00514 QVMatrix mJ1(9, DOF, 0.0), mJ2(9, DOF, 0.0);
00515 QVVector r(9, 0.0);
00516 for(int i = 0; i < reducedMCols; i++)
00517 {
00518 r[i] = mJ1J2e(i, DOF*2);
00519 for(int j = 0; j < DOF; j++)
00520 {
00521 mJ1(i,j) = mJ1J2e(i,j);
00522 mJ2(i,j) = mJ1J2e(i,DOF+j);
00523 }
00524 }
00525
00526 estimatedJ.setBlock(linkIndex, xIndex, mJ1);
00527 estimatedJ.setBlock(linkIndex, yIndex, mJ2);
00528 for(int i = 0; i < 9; i++)
00529 residuals[linkIndex*9+i] = mJ1J2e(i, jacCols-1);
00530
00531 linkIndex++;
00532 }
00533
00534 return true;
00535 }
00536
00537
00538
00539
00540
00541 bool evaluateGEAHessianAndObjectiveVector(
00542
00543 const int numCameras,
00544
00545 const int DOF,
00546
00547 const QVDirectedGraph<QVMatrix> &reducedMatricesGraph,
00548
00549 const QVector<bool> &freeCamera,
00550
00551 const QVVector &x,
00552
00553 QVSparseBlockMatrix &estimatedH,
00554
00555 QVVector &objectives
00556 )
00557 {
00558 Q_ASSERT_X(not x.containsNaN(), "[evaluateGEAHessianAndObjectiveVector2]", "NaN value found in state vector");
00559
00560
00561 objectives = QVVector(DOF*numCameras, 0.0);
00562 estimatedH = QVSparseBlockMatrix(numCameras, numCameras, DOF, DOF);
00563
00564
00565
00566
00567
00568 double diags[numCameras*DOF*DOF];
00569 for(int i = 0; i < numCameras*DOF*DOF; i++)
00570 diags[i] = 0.0;
00571
00572
00573 const double *xData = x.constData();
00574 double *objectivesData = objectives.data();
00575
00576 #ifdef DEBUG // -----------------------------------------------------------------
00577 bool errorFound = false;
00578 int jacobianEvaluations = 0;
00579 #endif // ------------------------ DEBUG -----------------------------------------
00580
00581
00582
00583 QMapIterator<QVGraphLink, QVMatrix> iterator(reducedMatricesGraph);
00584 while (iterator.hasNext())
00585 {
00586 iterator.next();
00587
00588
00589 const int xIndex = iterator.key().x(),
00590 yIndex = iterator.key().y();
00591
00592 if (not freeCamera[xIndex] and not freeCamera[yIndex])
00593 continue;
00594
00595
00596 if (xIndex > yIndex)
00597 {
00598 std::cout << "[evaluateGEAHessianAndObjectiveVector] Error: provided invalid order for indexes 'x' and 'y'." << std::endl;
00599 exit(0);
00600 }
00601
00602 if (xIndex >= numCameras)
00603 {
00604 std::cout << "[evaluateGEAHessianAndObjectiveVector] Error: malformed coefficient matrices graph: index 'x' out of bounds." << std::endl;
00605 exit(0);
00606 }
00607
00608 if (yIndex >= numCameras)
00609 {
00610 std::cout << "[evaluateGEAHessianAndObjectiveVector] Error: malformed coefficient matrices graph: index 'y' out of bounds." << std::endl;
00611 exit(0);
00612 }
00613
00614
00615
00616
00617
00618
00619
00620 QVMatrix J1J2(DOF,DOF);
00621 double temp1[DOF], temp2[DOF];
00622 double J1J1Data[DOF*DOF], J2J2Data[DOF*DOF];
00623
00624
00625
00626 const int jacRows = 9,
00627 jacCols = DOF*2+1;
00628
00629
00630
00631
00632
00633
00634
00635 double D1D2v[jacRows*jacCols];
00636
00637
00638 #ifdef SO3_PARAMETRIZATION
00639 so3EssentialEvaluation(xData + DOF*xIndex, xData + DOF*yIndex, D1D2v, 1e-32);
00640 #else
00641 quaternionEssentialEvaluation(xData + DOF*xIndex, xData + DOF*yIndex, D1D2v);
00642 #endif
00643
00644
00645
00646 const QVMatrix &reducedM = iterator.value();
00647 const int reducedMCols = reducedM.getCols();
00648 Q_ASSERT(reducedM.getRows() == 9);
00649 Q_ASSERT(reducedM.getCols() <= 9);
00650 Q_ASSERT(reducedM.getCols() > 0);
00651
00652
00653
00654
00655 double J1J2e[jacRows*jacCols];
00656 cblas_dgemm(CblasRowMajor, CblasTrans, CblasNoTrans, reducedMCols, jacCols, jacRows, 1.0, reducedM.getReadData(), reducedMCols, D1D2v, jacCols, 0.0, J1J2e, jacCols);
00657
00658 if (freeCamera[xIndex])
00659 {
00660
00661 cblas_dgemv(CblasRowMajor, CblasTrans, reducedMCols, DOF, 1.0, J1J2e, jacCols, J1J2e+jacCols-1, jacCols, 0.0, temp1, 1);
00662 cblas_daxpy(DOF, 1.0, temp1, 1, objectivesData + DOF*xIndex, 1);
00663
00664
00665 cblas_dgemm(CblasRowMajor, CblasTrans, CblasNoTrans, DOF, DOF, reducedMCols, 1.0, J1J2e, jacCols, J1J2e, jacCols, 0.0, J1J1Data, DOF);
00666
00667 cblas_daxpy(DOF*DOF, 1.0, J1J1Data, 1, diags + xIndex*DOF*DOF, 1);
00668 }
00669
00670 if (freeCamera[yIndex])
00671 {
00672
00673 cblas_dgemv(CblasRowMajor, CblasTrans, reducedMCols, DOF, 1.0, J1J2e+DOF, jacCols, J1J2e+jacCols-1, jacCols, 0.0, temp2, 1);
00674 cblas_daxpy(DOF, 1.0, temp2, 1, objectivesData + DOF*yIndex, 1);
00675
00676
00677 cblas_dgemm(CblasRowMajor, CblasTrans, CblasNoTrans, DOF, DOF, reducedMCols, 1.0, J1J2e+DOF, jacCols, J1J2e+DOF, jacCols, 0.0, J2J2Data, DOF);
00678
00679 cblas_daxpy(DOF*DOF, 1.0, J2J2Data, 1, diags + yIndex*DOF*DOF, 1);
00680 }
00681
00682 if (freeCamera[xIndex] and freeCamera[yIndex])
00683 {
00684
00685
00686 cblas_dgemm(CblasRowMajor, CblasTrans, CblasNoTrans, DOF, DOF, reducedMCols, 1.0, J1J2e, jacCols, J1J2e+DOF, jacCols, 0.0, J1J2.getWriteData(), DOF);
00687
00688 estimatedH.setBlock(xIndex, yIndex, J1J2);
00689 }
00690
00691 #ifdef DEBUG // -----------------------------------------------------------------
00692 jacobianEvaluations++;
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713 if (QVVector(jacRows*jacCols, D1D2v).containsNaN() and not errorFound)
00714 {
00715 errorFound = true;
00716
00717 std::cout << "[globalEpipolarAdjustment] Error: NaN value found in Jacobian matrix for views (" << xIndex << ", " << yIndex << ")." << std::endl;
00718
00719
00720 const QVVector view1(DOF, xData + DOF*xIndex), view2(DOF, xData + DOF*yIndex);
00721
00722 if (view1 == view2)
00723 std::cout << "[globalEpipolarAdjustment] Reason: poses for views " << xIndex << " and " << yIndex << " are numerically EQUAL." << std::endl;
00724 else {
00725 std::cout << "[globalEpipolarAdjustment] View 1 pose vector:" << std::endl << QVEuclideanMapping3(view1).toRotationTranslationMatrix() << std::endl;
00726 std::cout << "[globalEpipolarAdjustment] View 2 pose vector:" << std::endl << QVEuclideanMapping3(view2).toRotationTranslationMatrix() << std::endl;
00727 std::cout << "[globalEpipolarAdjustment] Values for Jacobian matrix:" << std::endl << QVVector(jacRows*jacCols, D1D2v) << std::endl;
00728 }
00729 }
00730
00731 if (reducedM.containsNaN())
00732 {
00733 errorFound = true;
00734 std::cout << "[globalEpipolarAdjustment] Error: NaN value found in reduced matrix for views (" << xIndex << ", " << yIndex << ")." << std::endl;
00735 }
00736
00737
00738 if (errorFound)
00739
00740 break;
00741 #endif // ------------------------ DEBUG -----------------------------------------
00742 }
00743
00744
00745 for(int i = 0; i < numCameras; i++)
00746 {
00747 const QVMatrix diagMatrix(DOF, DOF, diags + i*DOF*DOF);
00748 estimatedH.setBlock(i,i, diagMatrix);
00749 }
00750
00751 #ifdef DEBUG // -----------------------------------------------------------------
00752
00753 if (errorFound)
00754 return false;
00755 #endif // ------------------------ DEBUG -----------------------------------------
00756
00757 return true;
00758 }
00759
00760
00761
00762
00763 void addTrace(const double value, QVSparseBlockMatrix &H)
00764 {
00765 for(int k = 0; k < H.getMajorRows(); k++)
00766 {
00767 QVMatrix &M = H[k][k];
00768 for (int j = 0; j < M.getCols(); j++)
00769 M(j,j) += value;
00770 }
00771 }
00772
00773
00774
00775 QList<QVCameraPose> globalEpipolarAdjustment(
00776 const int numIterations,
00777 const QList<QVCameraPose> &initialCameraPoses,
00778 const QVDirectedGraph<QVMatrix> &reducedMatricesGraph,
00779 const double lambda,
00780 const bool adaptativeLambda,
00781 const TQVSparseSolve_Method solveMethod,
00782 const int secondLevelIterations
00783 )
00784 {
00785 QVector<bool> freeCameras(initialCameraPoses.count(), true);
00786 freeCameras[0] = false;
00787
00788 return globalEpipolarAdjustment(numIterations, initialCameraPoses, reducedMatricesGraph, freeCameras, lambda, adaptativeLambda, solveMethod, secondLevelIterations);
00789 }
00790
00791 int gea_time_eval = 0, gea_time_solve = 0;
00792 QList<QVCameraPose> globalEpipolarAdjustment(
00793 const int numIterations,
00794 const QList<QVCameraPose> &initialCameraPoses,
00795 const QVDirectedGraph<QVMatrix> &reducedMatricesGraph,
00796 const QVector<bool> &freeCameras,
00797 const double lambda,
00798 const bool adaptativeLambda,
00799 const TQVSparseSolve_Method solveMethod,
00800 const int secondLevelIterations
00801 )
00802 {
00803 #ifdef DEBUG
00804 std::cout << "[globalEpipolarAdjustment] Number of terms in the GEA cost error function = " << reducedMatricesGraph.count() << std::endl;
00805 #endif // DEBUG
00806
00807 const int numCameras = initialCameraPoses.count();
00808
00809
00810
00811
00812 #ifdef SO3_PARAMETRIZATION
00813 const int DOF = 6;
00814 #else
00815 const int DOF = 7;
00816 #endif
00817
00818 QTime time;
00819
00820
00821
00822 time.start();
00823 QVVector x;
00824 foreach(QVCameraPose cameraPose, initialCameraPoses)
00825 #ifdef SO3_PARAMETRIZATION
00826 x << lnSO3(cameraPose.getOrientation()) << cameraPose.getCenter();
00827 #else
00828 x << QVVector(cameraPose);
00829 #endif
00830
00831
00832 gea_time_eval = 0;
00833 gea_time_solve = 0;
00834
00835
00836
00837
00838 QVVector xInc(numCameras * DOF, 0.0);
00839 for(int iteration = 0; iteration < numIterations; iteration++)
00840 {
00841
00842 time.start();
00843 QVSparseBlockMatrix H;
00844 QVVector b;
00845 if ( not evaluateGEAHessianAndObjectiveVector(numCameras, DOF, reducedMatricesGraph, freeCameras, x, H, b) )
00846 {
00847 std::cout << "[globalEpipolarAdjustment] Stopping optimization at iteration " << iteration << std::endl;
00848 break;
00849 }
00850
00851
00852
00853 const double factor = adaptativeLambda? lambda * H.trace() / (numCameras*DOF): lambda;
00854 addTrace(factor, H);
00855
00856 gea_time_eval += time.elapsed();
00857
00858
00859
00860 time.start();
00861 sparseSolve(H, xInc, b, true, true, solveMethod, true, true, secondLevelIterations);
00862 gea_time_solve += time.elapsed();
00863
00864
00865 x = x - xInc;
00866
00867 #ifdef DEBUG // -----------------------------------------------------------------
00868 if (iteration == 0)
00869 std::cout << "[globalEpipolarAdjustment] Increment for diagonal of H at iteration " << iteration << " is " << factor << std::endl;
00870
00871
00872 if (xInc.containsNaN())
00873 {
00874 std::cout << "[globalEpipolarAdjustment] Error: NaN value found in increment vector at iteration " << iteration << ". Stopping." << std::endl;
00875 break;
00876 }
00877
00878
00879 foreach(int ib, H.keys())
00880 {
00881 const QMap<int, QVMatrix> &majorRow = H[ib];
00882 foreach(int jb, majorRow.keys())
00883 {
00884
00885 if (ib == jb)
00886 {
00887 if (not majorRow[ib].isDiagonal() and not freeCameras[ib])
00888 std::cout << "[globalEpipolarAdjustment] Error: Diagonal Hessian block " << ib << " is not diagonal." << std::endl;
00889 continue;
00890 }
00891 if ( not freeCameras[ib] and majorRow.contains(jb) )
00892 std::cout << "[globalEpipolarAdjustment] Error: Hessian block " << ib << ", " << jb << " is not zero: " << majorRow[jb] << " while camera " << ib << " is fixed." << std::endl;
00893 if ( not freeCameras[jb] and majorRow.contains(jb) )
00894 std::cout << "[globalEpipolarAdjustment] Error: Hessian block " << ib << ", " << jb << " is not zero: " << majorRow[jb] << " while camera " << jb << " is fixed." << std::endl;
00895 }
00896 }
00897
00898
00899 if (xInc.count() != numCameras * DOF)
00900 std::cout << "[globalEpipolarAdjustment] Error: size of increment vector should be " << (numCameras * DOF) << "(" << numCameras << " cameras, and " << DOF << " DOF's) but is " << xInc.count() << std::endl;
00901
00902
00903 for(int i = 0, idx = 0; i < numCameras; i++)
00904 for(int j = 0; j < DOF; j++, idx++)
00905 if (not freeCameras[i] and xInc[idx] != 0.0)
00906 std::cout << "[globalEpipolarAdjustment] Error: increment " << j << " for fixed camera " << i << " is not zero." << std::endl;
00907
00908 #endif // ------------------------ DEBUG -----------------------------------------
00909 }
00910
00911
00912 QList<QVCameraPose> optimizedCameraPoses;
00913 for(int i = 0; i < numCameras; i++)
00914 #ifdef SO3_PARAMETRIZATION
00915 optimizedCameraPoses << QVCameraPose(QVQuaternion(expSO3(QV3DPointF(x.mid(DOF*i, 3)))), QV3DPointF(x.mid(DOF*i+3,3)));
00916 #else
00917 optimizedCameraPoses << QVCameraPose(QVQuaternion(x.mid(DOF*i, 4)), QV3DPointF(x.mid(DOF*i+4,3)) );
00918 #endif
00919
00920
00921 return optimizedCameraPoses;
00922 }
00923
00924 QList<QVCameraPose> incrementalGEA( const int numIterations,
00925 const QList<QVCameraPose> &initialCameraPoses,
00926 const QVDirectedGraph<QVMatrix> &reducedMatricesGraph,
00927 const int numFreeCameras,
00928 const double lambda,
00929 const bool adaptativeLambda,
00930 const TQVSparseSolve_Method solveMethod,
00931 const int secondLevelIterations
00932 )
00933 {
00934 std::cout << "WARNING: 'incrementalGEA' deprecated. Use function 'globalEpipolarAdjustment' to perform incremental GEA optimization." << std::endl;
00935
00936 const int numCameras = initialCameraPoses.count(),
00937 freeCameras = MIN(numFreeCameras, numCameras),
00938 fixedCameras = numCameras - freeCameras,
00939 firstFreeCameraIndex = fixedCameras;
00940
00941
00942 QList<QPoint> incrementalLinks;
00943 foreach(QPoint link, reducedMatricesGraph.keys())
00944 if (link.x() >= firstFreeCameraIndex or link.y() >= firstFreeCameraIndex)
00945 incrementalLinks << link;
00946
00947
00948
00949
00950 #ifdef SO3_PARAMETRIZATION
00951 const int DOF = 6;
00952 #else
00953 const int DOF = 7;
00954 #endif
00955
00956 QTime time;
00957
00958
00959
00960 time.start();
00961 QVVector x;
00962 foreach(QVCameraPose cameraPose, initialCameraPoses)
00963 #ifdef SO3_PARAMETRIZATION
00964 x << lnSO3(cameraPose.getOrientation()) << cameraPose.getCenter();
00965 #else
00966 x << QVVector(cameraPose);
00967 #endif
00968
00969
00970
00971 gea_time_eval = 0;
00972 gea_time_solve = 0;
00973
00974
00975
00976
00977
00978 for(int i = 0; i < numIterations; i++)
00979 {
00980
00981 time.start();
00982 QVSparseBlockMatrix J;
00983 QVVector r;
00984 evaluateGEAJacobianAndResidual(numCameras, DOF, reducedMatricesGraph, incrementalLinks, x, J, r);
00985 QVSparseBlockMatrix H = J.dotProduct(J, true, false);
00986 QVVector b = J.dotProduct(r, true);
00987
00988
00989 QVSparseBlockMatrix simplifiedJ = QVSparseBlockMatrix(J.getMajorRows(), freeCameras, J.getMinorRows(), J.getMinorCols());
00990 foreach(int ib, J.keys())
00991 {
00992 const QMap<int, QVMatrix> &majorRow = J[ib];
00993 foreach(int jb, majorRow.keys())
00994 if (jb >= firstFreeCameraIndex)
00995 simplifiedJ.setBlock(ib, jb - firstFreeCameraIndex, majorRow[jb]);
00996 }
00997
00998 H = simplifiedJ.dotProduct(simplifiedJ, true, false);
00999 b = simplifiedJ.dotProduct(r, true);
01000 gea_time_eval += time.elapsed();
01001
01002
01003
01004 QVSparseBlockMatrix H2(H.getMajorRows(), H.getMajorCols(), H.getMinorRows(), H.getMinorCols());
01005 foreach(int ib, H.keys())
01006 {
01007 QMap<int, QVMatrix> &majorRow = H[ib];
01008 foreach(int jb, majorRow.keys())
01009 if (ib <= jb)
01010 H2.setBlock(ib, jb, majorRow[jb]);
01011 }
01012
01013
01014
01015
01016
01017
01018
01019 const double factor = adaptativeLambda? lambda * H2.trace() / (numCameras*DOF): lambda;
01020
01021 #ifdef DEBUG
01022 std::cout << "[globalEpipolarAdjustment] Increment for diagonal of H at iteration " << i << " is " << factor << std::endl;
01023 #endif
01024
01025 addTrace(factor, H2);
01026
01027 time.start();
01028 QVVector xIncPartial;
01029 sparseSolve(H2, xIncPartial, b, true, true, solveMethod, true, true, secondLevelIterations);
01030
01031 QVVector xInc(fixedCameras * DOF, 0.0);
01032 xInc << xIncPartial;
01033 gea_time_solve += time.elapsed();
01034
01035
01036 if (xInc.containsNaN())
01037 {
01038 std::cout << "[globalEpipolarAdjustment] Error: NaN value found in 'x' increment at iteration " << i << ". Stopping." << std::endl;
01039 break;
01040 }
01041
01042
01043 x = x - xInc;
01044 }
01045
01046 #ifdef DEBUG
01047 std::cout << "[globalEpipolarAdjustment] Number of Jacobian evaluations = " << reducedMatricesGraph.getLinks().count() << std::endl;
01048 #endif
01049
01050
01051 QList<QVCameraPose> optimizedCameraPoses;
01052 for(int i = 0; i < numCameras; i++)
01053 #ifdef SO3_PARAMETRIZATION
01054 optimizedCameraPoses << QVCameraPose(QVQuaternion(expSO3(QV3DPointF(x.mid(DOF*i, 3)))), QV3DPointF(x.mid(DOF*i+3,3)));
01055 #else
01056 optimizedCameraPoses << QVCameraPose(QVQuaternion(x.mid(DOF*i, 4)), QV3DPointF(x.mid(DOF*i+4,3)) );
01057 #endif
01058
01059
01060 return optimizedCameraPoses;
01061 }
01062