// -*- C++ -*- /* Copyright (c) 2008 University of North Carolina at Chapel Hill This file is part of SSBA (Simple Sparse Bundle Adjustment). SSBA is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. SSBA is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with SSBA. If not, see . */ #ifndef V3D_OPTIMIZATION_H #define V3D_OPTIMIZATION_H #include "Math/v3d_linear.h" #include "Math/v3d_mathutilities.h" #include #include namespace V3D { enum { LEVENBERG_OPTIMIZER_TIMEOUT = 0, LEVENBERG_OPTIMIZER_SMALL_UPDATE = 1, LEVENBERG_OPTIMIZER_CONVERGED = 2 }; extern int optimizerVerbosenessLevel; struct LevenbergOptimizerCommon { LevenbergOptimizerCommon() : status(LEVENBERG_OPTIMIZER_TIMEOUT), currentIteration(0), maxIterations(50), tau(1e-3), lambda(1e-3), gradientThreshold(1e-10), updateThreshold(1e-10), _nu(2.0) { } virtual ~LevenbergOptimizerCommon() {} // See Madsen et al., "Methods for non-linear least squares problems." virtual void increaseLambda() { lambda *= _nu; _nu *= 2.0; } virtual void decreaseLambda(double const rho) { double const r = 2*rho - 1.0; lambda *= std::max(1.0/3.0, 1 - r*r*r); if (lambda < 1e-10) lambda = 1e-10; _nu = 2; } bool applyGradientStoppingCriteria(double maxGradient) const { return maxGradient < gradientThreshold; } bool applyUpdateStoppingCriteria(double paramLength, double updateLength) const { return updateLength < updateThreshold * (paramLength + updateThreshold); } int status; int currentIteration, maxIterations; double tau, lambda; double gradientThreshold, updateThreshold; protected: double _nu; }; // end struct LevenbergOptimizerCommon # if defined(V3DLIB_ENABLE_SUITESPARSE) struct SparseLevenbergOptimizer : public LevenbergOptimizerCommon { SparseLevenbergOptimizer(int measurementDimension, int nParametersA, int paramDimensionA, int nParametersB, int paramDimensionB, int paramDimensionC, std::vector const& correspondingParamA, std::vector const& correspondingParamB) : LevenbergOptimizerCommon(), _nMeasurements(correspondingParamA.size()), _measurementDimension(measurementDimension), _nParametersA(nParametersA), _paramDimensionA(paramDimensionA), _nParametersB(nParametersB), _paramDimensionB(paramDimensionB), _paramDimensionC(paramDimensionC), _nNonvaryingA(0), _nNonvaryingB(0), _nNonvaryingC(0), _correspondingParamA(correspondingParamA), _correspondingParamB(correspondingParamB) { assert(correspondingParamA.size() == correspondingParamB.size()); } ~SparseLevenbergOptimizer() { } void setNonvaryingCounts(int nNonvaryingA, int nNonvaryingB, int nNonvaryingC) { _nNonvaryingA = nNonvaryingA; _nNonvaryingB = nNonvaryingB; _nNonvaryingC = nNonvaryingC; } void getNonvaryingCounts(int& nNonvaryingA, int& nNonvaryingB, int& nNonvaryingC) const { nNonvaryingA = _nNonvaryingA; nNonvaryingB = _nNonvaryingB; nNonvaryingC = _nNonvaryingC; } void minimize(); virtual void evalResidual(VectorArray& residuals) = 0; virtual void fillWeights(VectorArray const& residuals, Vector& w) { (void)residuals; std::fill(w.begin(), w.end(), 1.0); } void fillAllJacobians(Vector const& w, MatrixArray& Ak, MatrixArray& Bk, MatrixArray& Ck) { int const nVaryingA = _nParametersA - _nNonvaryingA; int const nVaryingB = _nParametersB - _nNonvaryingB; int const nVaryingC = _paramDimensionC - _nNonvaryingC; for (unsigned k = 0; k < _nMeasurements; ++k) { int const i = _correspondingParamA[k]; int const j = _correspondingParamB[k]; if (i < _nNonvaryingA && j < _nNonvaryingB) continue; fillJacobians(Ak[k], Bk[k], Ck[k], i, j, k); } // end for (k) if (nVaryingA > 0) { for (unsigned k = 0; k < _nMeasurements; ++k) scaleMatrixIP(w[k], Ak[k]); } if (nVaryingB > 0) { for (unsigned k = 0; k < _nMeasurements; ++k) scaleMatrixIP(w[k], Bk[k]); } if (nVaryingC > 0) { for (unsigned k = 0; k < _nMeasurements; ++k) scaleMatrixIP(w[k], Ck[k]); } } // end fillAllJacobians() virtual void setupJacobianGathering() { } virtual void fillJacobians(Matrix& Ak, Matrix& Bk, Matrix& Ck, int i, int j, int k) = 0; virtual double getParameterLength() const = 0; virtual void updateParametersA(VectorArray const& deltaAi) = 0; virtual void updateParametersB(VectorArray const& deltaBj) = 0; virtual void updateParametersC(Vector const& deltaC) = 0; virtual void saveAllParameters() = 0; virtual void restoreAllParameters() = 0; int currentIteration, maxIterations; protected: void serializeNonZerosJtJ(std::vector >& dst) const; void setupSparseJtJ(); void fillSparseJtJ(MatrixArray const& Ui, MatrixArray const& Vj, MatrixArray const& Wk, Matrix const& Z, Matrix const& X, Matrix const& Y); int const _nMeasurements, _measurementDimension; int const _nParametersA, _paramDimensionA; int const _nParametersB, _paramDimensionB; int const _paramDimensionC; int _nNonvaryingA, _nNonvaryingB, _nNonvaryingC; std::vector const& _correspondingParamA; std::vector const& _correspondingParamB; std::vector > _jointNonzerosW; std::vector _jointIndexW; std::vector _JtJ_Lp, _JtJ_Parent, _JtJ_Lnz; std::vector _perm_JtJ, _invPerm_JtJ; CCS_Matrix _JtJ; }; // end struct SparseLevenbergOptimizer struct StdSparseLevenbergOptimizer : public SparseLevenbergOptimizer { StdSparseLevenbergOptimizer(int measurementDimension, int nParametersA, int paramDimensionA, int nParametersB, int paramDimensionB, int paramDimensionC, std::vector const& correspondingParamA, std::vector const& correspondingParamB) : SparseLevenbergOptimizer(measurementDimension, nParametersA, paramDimensionA, nParametersB, paramDimensionB, paramDimensionC, correspondingParamA, correspondingParamB), curParametersA(nParametersA, paramDimensionA), savedParametersA(nParametersA, paramDimensionA), curParametersB(nParametersB, paramDimensionB), savedParametersB(nParametersB, paramDimensionB), curParametersC(paramDimensionC), savedParametersC(paramDimensionC) { } virtual double getParameterLength() const { double res = 0.0; for (int i = 0; i < _nParametersA; ++i) res += sqrNorm_L2(curParametersA[i]); for (int j = 0; j < _nParametersB; ++j) res += sqrNorm_L2(curParametersB[j]); res += sqrNorm_L2(curParametersC); return sqrt(res); } virtual void updateParametersA(VectorArray const& deltaAi) { for (int i = 0; i < _nParametersA; ++i) addVectors(deltaAi[i], curParametersA[i], curParametersA[i]); } virtual void updateParametersB(VectorArray const& deltaBj) { for (int j = 0; j < _nParametersB; ++j) addVectors(deltaBj[j], curParametersB[j], curParametersB[j]); } virtual void updateParametersC(Vector const& deltaC) { addVectors(deltaC, curParametersC, curParametersC); } virtual void saveAllParameters() { for (int i = 0; i < _nParametersA; ++i) savedParametersA[i] = curParametersA[i]; for (int j = 0; j < _nParametersB; ++j) savedParametersB[j] = curParametersB[j]; savedParametersC = curParametersC; } virtual void restoreAllParameters() { for (int i = 0; i < _nParametersA; ++i) curParametersA[i] = savedParametersA[i]; for (int j = 0; j < _nParametersB; ++j) curParametersB[j] = savedParametersB[j]; curParametersC = savedParametersC; } VectorArray curParametersA, savedParametersA; VectorArray curParametersB, savedParametersB; Vector curParametersC, savedParametersC; }; // end struct StdSparseLevenbergOptimizer # endif } // end namespace V3D #endif