From 83d0bd414db0bce122271f793473f6564caa2094 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 12:06:16 +0100 Subject: [PATCH 01/18] Introduced TriangulationResult --- gtsam/geometry/triangulation.h | 144 ++++++++++++++++++++------------- 1 file changed, 90 insertions(+), 54 deletions(-) diff --git a/gtsam/geometry/triangulation.h b/gtsam/geometry/triangulation.h index 15721e81c..a67f26bf2 100644 --- a/gtsam/geometry/triangulation.h +++ b/gtsam/geometry/triangulation.h @@ -316,12 +316,57 @@ struct TriangulationParameters { _landmarkDistanceThreshold), dynamicOutlierRejectionThreshold( _dynamicOutlierRejectionThreshold) { } + + // stream to output + friend std::ostream &operator<<(std::ostream &os, + const TriangulationParameters& p) { + os << "rankTolerance = " << p.rankTolerance << std::endl; + os << "enableEPI = " << p.enableEPI << std::endl; + os << "landmarkDistanceThreshold = " << p.landmarkDistanceThreshold + << std::endl; + os << "dynamicOutlierRejectionThreshold = " + << p.dynamicOutlierRejectionThreshold << std::endl; + return os; + } }; -struct TriangulationResult { - Point3 point; - bool degenerate; - bool cheiralityException; +/** + * TriangulationResult is an optional point, along with the reasons why it is invalid. + */ +class TriangulationResult: public boost::optional { + enum Status { + VALID, DEGENERATE, BEHIND_CAMERA + }; + Status status_; + TriangulationResult(Status s) : + status_(s) { + } +public: + TriangulationResult(const Point3& p) : + status_(VALID) { + reset(p); + } + static TriangulationResult Degenerate() { + return TriangulationResult(DEGENERATE); + } + static TriangulationResult BehindCamera() { + return TriangulationResult(BEHIND_CAMERA); + } + bool degenerate() const { + return status_ == DEGENERATE; + } + bool behindCamera() const { + return status_ == BEHIND_CAMERA; + } + // stream to output + friend std::ostream &operator<<(std::ostream &os, + const TriangulationResult& result) { + if (result) + os << "point = " << *result << std::endl; + else + os << "no point, status = " << result.status_ << std::endl; + return os; + } }; /// triangulateSafe: extensive checking of the outcome @@ -330,62 +375,53 @@ TriangulationResult triangulateSafe(const std::vector& cameras, const std::vector& measured, const TriangulationParameters& params) { - TriangulationResult result; - size_t m = cameras.size(); // if we have a single pose the corresponding factor is uninformative - if (m < 2) { - result.degenerate = true; - return result; - } + if (m < 2) + return TriangulationResult::Degenerate(); + else + // We triangulate the 3D position of the landmark + try { + Point3 point = triangulatePoint3(cameras, measured, + params.rankTolerance, params.enableEPI); - // We triangulate the 3D position of the landmark - try { - // std::cout << "triangulatePoint3 i \n" << rankTolerance << std::endl; - result.point = triangulatePoint3(cameras, measured, - params.rankTolerance, params.enableEPI); - result.degenerate = false; - result.cheiralityException = false; + // Check landmark distance and reprojection errors to avoid outliers + size_t i = 0; + double totalReprojError = 0.0; + BOOST_FOREACH(const CAMERA& camera, cameras) { + // we discard smart factors corresponding to points that are far away + Point3 cameraTranslation = camera.pose().translation(); + if (cameraTranslation.distance(point) > params.landmarkDistanceThreshold) + return TriangulationResult::Degenerate(); + // Also flag if point is behind any of the cameras + try { + const Point2& zi = measured.at(i); + Point2 reprojectionError(camera.project(point) - zi); + totalReprojError += reprojectionError.vector().norm(); + } catch (CheiralityException) { + return TriangulationResult::BehindCamera(); + } + i += 1; + } + // we discard smart factors that have large reprojection error + if (params.dynamicOutlierRejectionThreshold > 0 + && totalReprojError / m > params.dynamicOutlierRejectionThreshold) + return TriangulationResult::Degenerate(); - // Check landmark distance and reprojection errors to avoid outliers - double totalReprojError = 0.0; - size_t i = 0; - BOOST_FOREACH(const CAMERA& camera, cameras) { - Point3 cameraTranslation = camera.pose().translation(); - // we discard smart factors corresponding to points that are far away - if (cameraTranslation.distance(result.point) - > params.landmarkDistanceThreshold) { - result.degenerate = true; - break; - } - const Point2& zi = measured.at(i); - try { - Point2 reprojectionError(camera.project(result.point) - zi); - totalReprojError += reprojectionError.vector().norm(); - } catch (CheiralityException) { - result.cheiralityException = true; - } - i += 1; + // all good! + return TriangulationResult(point); + } catch (TriangulationUnderconstrainedException&) { + // if TriangulationUnderconstrainedException can be + // 1) There is a single pose for triangulation - this should not happen because we checked the number of poses before + // 2) The rank of the matrix used for triangulation is < 3: rotation-only, parallel cameras (or motion towards the landmark) + // in the second case we want to use a rotation-only smart factor + return TriangulationResult::Degenerate(); + } catch (TriangulationCheiralityException&) { + // point is behind one of the cameras: can be the case of close-to-parallel cameras or may depend on outliers + // we manage this case by either discarding the smart factor, or imposing a rotation-only constraint + return TriangulationResult::BehindCamera(); } - // we discard smart factors that have large reprojection error - if (params.dynamicOutlierRejectionThreshold > 0 - && totalReprojError / m > params.dynamicOutlierRejectionThreshold) - result.degenerate = true; - - } catch (TriangulationUnderconstrainedException&) { - // if TriangulationUnderconstrainedException can be - // 1) There is a single pose for triangulation - this should not happen because we checked the number of poses before - // 2) The rank of the matrix used for triangulation is < 3: rotation-only, parallel cameras (or motion towards the landmark) - // in the second case we want to use a rotation-only smart factor - result.degenerate = true; - result.cheiralityException = false; - } catch (TriangulationCheiralityException&) { - // point is behind one of the cameras: can be the case of close-to-parallel cameras or may depend on outliers - // we manage this case by either discarding the smart factor, or imposing a rotation-only constraint - result.cheiralityException = true; - } - return result; } } // \namespace gtsam From bc6069a94bc8eeb3f6ced3eca7f42349c0b8ef14 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 12:06:26 +0100 Subject: [PATCH 02/18] Now using TriangulationResult --- gtsam/slam/SmartProjectionFactor.h | 355 +++++++++-------------------- 1 file changed, 105 insertions(+), 250 deletions(-) diff --git a/gtsam/slam/SmartProjectionFactor.h b/gtsam/slam/SmartProjectionFactor.h index 5b061f612..13d17f683 100644 --- a/gtsam/slam/SmartProjectionFactor.h +++ b/gtsam/slam/SmartProjectionFactor.h @@ -35,14 +35,8 @@ namespace gtsam { * Structure for storing some state memory, used to speed up optimization * @addtogroup SLAM */ -class SmartProjectionFactorState { +struct SmartProjectionFactorState { -protected: - -public: - - SmartProjectionFactorState() { - } // Hessian representation (after Schur complement) bool calculatedHessian; Matrix H; @@ -68,38 +62,31 @@ private: protected: - // Some triangulation parameters - const double rankTolerance_; ///< threshold to decide whether triangulation is degenerate_ + /// @name Caching triangulation + /// @{ + const TriangulationParameters parameters_; + mutable TriangulationResult result_; ///< result from triangulateSafe + const double retriangulationThreshold_; ///< threshold to decide whether to re-triangulate mutable std::vector cameraPosesTriangulation_; ///< current triangulation poses + /// @} + /// @name Parameters governing how triangulation result is treated + /// @{ const bool manageDegeneracy_; ///< if set to true will use the rotation-only version for degenerate cases + const bool throwCheirality_; ///< If true, rethrows Cheirality exceptions (default: false) + const bool verboseCheirality_; ///< If true, prints text for Cheirality exceptions (default: false) + /// @} - const bool enableEPI_; ///< if set to true, will refine triangulation using LM + /// @name Caching linearization + /// @{ + /// shorthand for smart projection factor state variable + typedef boost::shared_ptr SmartFactorStatePtr; + SmartFactorStatePtr state_; ///< cached linearization const double linearizationThreshold_; ///< threshold to decide whether to re-linearize mutable std::vector cameraPosesLinearization_; ///< current linearization poses - - mutable Point3 point_; ///< Current estimate of the 3D point - - mutable bool degenerate_; - mutable bool cheiralityException_; - - // verbosity handling for Cheirality Exceptions - const bool throwCheirality_; ///< If true, rethrows Cheirality exceptions (default: false) - const bool verboseCheirality_; ///< If true, prints text for Cheirality exceptions (default: false) - - boost::shared_ptr state_; - - /// shorthand for smart projection factor state variable - typedef boost::shared_ptr SmartFactorStatePtr; - - double landmarkDistanceThreshold_; // if the landmark is triangulated at a - // distance larger than that the factor is considered degenerate - - double dynamicOutlierRejectionThreshold_; // if this is nonnegative the factor will check if the - // average reprojection error is smaller than this threshold after triangulation, - // and the factor is disregarded if the error is large + /// @} public: @@ -117,17 +104,18 @@ public: * otherwise the factor is simply neglected * @param enableEPI if set to true linear triangulation is refined with embedded LM iterations */ - SmartProjectionFactor(const double rankTol, const double linThreshold, + SmartProjectionFactor(const double rankTolerance, const double linThreshold, const bool manageDegeneracy, const bool enableEPI, double landmarkDistanceThreshold = 1e10, double dynamicOutlierRejectionThreshold = -1, SmartFactorStatePtr state = SmartFactorStatePtr(new SmartProjectionFactorState())) : - rankTolerance_(rankTol), retriangulationThreshold_(1e-5), manageDegeneracy_( - manageDegeneracy), enableEPI_(enableEPI), linearizationThreshold_( - linThreshold), degenerate_(false), cheiralityException_(false), throwCheirality_( - false), verboseCheirality_(false), state_(state), landmarkDistanceThreshold_( - landmarkDistanceThreshold), dynamicOutlierRejectionThreshold_( - dynamicOutlierRejectionThreshold) { + parameters_(rankTolerance, enableEPI, landmarkDistanceThreshold, + dynamicOutlierRejectionThreshold), // + result_(TriangulationResult::Degenerate()), // + retriangulationThreshold_(1e-5), // + manageDegeneracy_(manageDegeneracy), // + throwCheirality_(false), verboseCheirality_(false), // + state_(state), linearizationThreshold_(linThreshold) { } /** Virtual destructor */ @@ -141,24 +129,23 @@ public: */ void print(const std::string& s = "", const KeyFormatter& keyFormatter = DefaultKeyFormatter) const { - std::cout << s << "SmartProjectionFactor, z = \n"; - std::cout << "rankTolerance_ = " << rankTolerance_ << std::endl; - std::cout << "degenerate_ = " << degenerate_ << std::endl; - std::cout << "cheiralityException_ = " << cheiralityException_ << std::endl; + std::cout << s << "SmartProjectionFactor\n"; + std::cout << "triangulationParameters:\n" << parameters_ << std::endl; + std::cout << "result:\n" << result_ << std::endl; Base::print("", keyFormatter); } - /// Check if the new linearization point_ is the same as the one used for previous triangulation + /// Check if the new linearization point is the same as the one used for previous triangulation bool decideIfTriangulate(const Cameras& cameras) const { - // several calls to linearize will be done from the same linearization point_, hence it is not needed to re-triangulate + // several calls to linearize will be done from the same linearization point, hence it is not needed to re-triangulate // Note that this is not yet "selecting linearization", that will come later, and we only check if the - // current linearization is the "same" (up to tolerance) w.r.t. the last time we triangulated the point_ + // current linearization is the "same" (up to tolerance) w.r.t. the last time we triangulated the point size_t m = cameras.size(); bool retriangulate = false; - // if we do not have a previous linearization point_ or the new linearization point_ includes more poses + // if we do not have a previous linearization point or the new linearization point includes more poses if (cameraPosesTriangulation_.empty() || cameras.size() != cameraPosesTriangulation_.size()) retriangulate = true; @@ -181,19 +168,19 @@ public: cameraPosesTriangulation_.push_back(cameras[i].pose()); } - return retriangulate; // if we arrive to this point_ all poses are the same and we don't need re-triangulation + return retriangulate; // if we arrive to this point all poses are the same and we don't need re-triangulation } - /// This function checks if the new linearization point_ is 'close' to the previous one used for linearization + /// This function checks if the new linearization point is 'close' to the previous one used for linearization bool decideIfLinearize(const Cameras& cameras) const { // "selective linearization" // The function evaluates how close are the old and the new poses, transformed in the ref frame of the first pose // (we only care about the "rigidity" of the poses, not about their absolute pose) - if (this->linearizationThreshold_ < 0) //by convention if linearizationThreshold is negative we always relinearize + if (linearizationThreshold_ < 0) //by convention if linearizationThreshold is negative we always relinearize return true; - // if we do not have a previous linearization point_ or the new linearization point_ includes more poses + // if we do not have a previous linearization point or the new linearization point includes more poses if (cameraPosesLinearization_.empty() || (cameras.size() != cameraPosesLinearization_.size())) return true; @@ -211,100 +198,29 @@ public: Pose3 localCameraPose = firstCameraPose.between(cameras[i].pose()); Pose3 localCameraPoseOld = firstCameraPoseOld.between( cameraPosesLinearization_[i]); - if (!localCameraPose.equals(localCameraPoseOld, - this->linearizationThreshold_)) + if (!localCameraPose.equals(localCameraPoseOld, linearizationThreshold_)) return true; // at least two "relative" poses are different, hence we re-linearize } - return false; // if we arrive to this point_ all poses are the same and we don't need re-linearize + return false; // if we arrive to this point all poses are the same and we don't need re-linearize } /// triangulateSafe - size_t triangulateSafe(const Values& values) const { - return triangulateSafe(this->cameras(values)); - } - - /// triangulateSafe - size_t triangulateSafe(const Cameras& cameras) const { + TriangulationResult triangulateSafe(const Cameras& cameras) const { size_t m = cameras.size(); - if (m < 2) { // if we have a single pose the corresponding factor is uninformative - degenerate_ = true; - return m; - } + if (m < 2) // if we have a single pose the corresponding factor is uninformative + return TriangulationResult::Degenerate(); + bool retriangulate = decideIfTriangulate(cameras); - - if (retriangulate) { - // We triangulate the 3D position of the landmark - try { - // std::cout << "triangulatePoint3 i \n" << rankTolerance << std::endl; - point_ = triangulatePoint3(cameras, this->measured_, - rankTolerance_, enableEPI_); - degenerate_ = false; - cheiralityException_ = false; - - // Check landmark distance and reprojection errors to avoid outliers - double totalReprojError = 0.0; - size_t i = 0; - BOOST_FOREACH(const CAMERA& camera, cameras) { - Point3 cameraTranslation = camera.pose().translation(); - // we discard smart factors corresponding to points that are far away - if (cameraTranslation.distance(point_) > landmarkDistanceThreshold_) { - degenerate_ = true; - break; - } - const Point2& zi = this->measured_.at(i); - try { - Point2 reprojectionError(camera.project(point_) - zi); - totalReprojError += reprojectionError.vector().norm(); - } catch (CheiralityException) { - cheiralityException_ = true; - } - i += 1; - } - // we discard smart factors that have large reprojection error - if (dynamicOutlierRejectionThreshold_ > 0 - && totalReprojError / m > dynamicOutlierRejectionThreshold_) - degenerate_ = true; - - } catch (TriangulationUnderconstrainedException&) { - // if TriangulationUnderconstrainedException can be - // 1) There is a single pose for triangulation - this should not happen because we checked the number of poses before - // 2) The rank of the matrix used for triangulation is < 3: rotation-only, parallel cameras (or motion towards the landmark) - // in the second case we want to use a rotation-only smart factor - degenerate_ = true; - cheiralityException_ = false; - } catch (TriangulationCheiralityException&) { - // point is behind one of the cameras: can be the case of close-to-parallel cameras or may depend on outliers - // we manage this case by either discarding the smart factor, or imposing a rotation-only constraint - cheiralityException_ = true; - } - } - return m; + if (retriangulate) + result_ = gtsam::triangulateSafe(cameras, this->measured_, parameters_); + return result_; } /// triangulate bool triangulateForLinearize(const Cameras& cameras) const { - - bool isDebug = false; - size_t nrCameras = this->triangulateSafe(cameras); - - if (nrCameras < 2 - || (!this->manageDegeneracy_ - && (this->cheiralityException_ || this->degenerate_))) { - if (isDebug) { - std::cout - << "createRegularImplicitSchurFactor: degenerate configuration" - << std::endl; - } - return false; - } else { - - // instead, if we want to manage the exception.. - if (this->cheiralityException_ || this->degenerate_) { // if we want to manage the exceptions with rotation-only factors - this->degenerate_ = true; - } - return true; - } + triangulateSafe(cameras); // imperative, might reset result_ + return (manageDegeneracy_ || result_); } /// linearize returns a Hessianfactor that is an approximation of error(p) @@ -324,12 +240,10 @@ public: exit(1); } - this->triangulateSafe(cameras); + triangulateSafe(cameras); - if (numKeys < 2 - || (!this->manageDegeneracy_ - && (this->cheiralityException_ || this->degenerate_))) { - // std::cout << "In linearize: exception" << std::endl; + if (!manageDegeneracy_ && !result_) { + // put in "empty" Hessian BOOST_FOREACH(Matrix& m, Gs) m = zeros(Base::Dim, Base::Dim); BOOST_FOREACH(Vector& v, gs) @@ -338,23 +252,19 @@ public: Gs, gs, 0.0); } - // instead, if we want to manage the exception.. - if (this->cheiralityException_ || this->degenerate_) { // if we want to manage the exceptions with rotation-only factors - this->degenerate_ = true; - } - + // decide whether to re-linearize bool doLinearize = this->decideIfLinearize(cameras); - if (this->linearizationThreshold_ >= 0 && doLinearize) // if we apply selective relinearization and we need to relinearize + if (linearizationThreshold_ >= 0 && doLinearize) // if we apply selective relinearization and we need to relinearize for (size_t i = 0; i < cameras.size(); i++) this->cameraPosesLinearization_[i] = cameras[i].pose(); if (!doLinearize) { // return the previous Hessian factor std::cout << "=============================" << std::endl; std::cout << "doLinearize " << doLinearize << std::endl; - std::cout << "this->linearizationThreshold_ " - << this->linearizationThreshold_ << std::endl; - std::cout << "this->degenerate_ " << this->degenerate_ << std::endl; + std::cout << "linearizationThreshold_ " << linearizationThreshold_ + << std::endl; + std::cout << "valid: " << isValid() << std::endl; std::cout << "something wrong in SmartProjectionHessianFactor: selective relinearization should be disabled" << std::endl; @@ -404,7 +314,7 @@ public: } } // ================================================================== - if (this->linearizationThreshold_ >= 0) { // if we do not use selective relinearization we don't need to store these variables + if (linearizationThreshold_ >= 0) { // if we do not use selective relinearization we don't need to store these variables this->state_->Gs = Gs; this->state_->gs = gs; this->state_->f = f; @@ -417,7 +327,7 @@ public: boost::shared_ptr > createRegularImplicitSchurFactor( const Cameras& cameras, double lambda) const { if (triangulateForLinearize(cameras)) - return Base::createRegularImplicitSchurFactor(cameras, point_, lambda); + return Base::createRegularImplicitSchurFactor(cameras, *result_, lambda); else return boost::shared_ptr >(); } @@ -426,7 +336,7 @@ public: boost::shared_ptr > createJacobianQFactor( const Cameras& cameras, double lambda) const { if (triangulateForLinearize(cameras)) - return Base::createJacobianQFactor(cameras, point_, lambda); + return Base::createJacobianQFactor(cameras, *result_, lambda); else return boost::make_shared >(this->keys_); } @@ -434,63 +344,27 @@ public: /// Create a factor, takes values boost::shared_ptr > createJacobianQFactor( const Values& values, double lambda) const { - Cameras cameras; - // TODO triangulate twice ?? - bool nonDegenerate = computeCamerasAndTriangulate(values, cameras); - if (nonDegenerate) - return createJacobianQFactor(cameras, lambda); - else - return boost::make_shared >(this->keys_); + return createJacobianQFactor(this->cameras(values),lambda); } /// different (faster) way to compute Jacobian factor boost::shared_ptr createJacobianSVDFactor( const Cameras& cameras, double lambda) const { if (triangulateForLinearize(cameras)) - return Base::createJacobianSVDFactor(cameras, point_, lambda); + return Base::createJacobianSVDFactor(cameras, *result_, lambda); else return boost::make_shared >(this->keys_); } - /// Returns true if nonDegenerate - bool computeCamerasAndTriangulate(const Values& values, - Cameras& cameras) const { - Values valuesFactor; - - // Select only the cameras - BOOST_FOREACH(const Key key, this->keys_) - valuesFactor.insert(key, values.at(key)); - - cameras = this->cameras(valuesFactor); - size_t nrCameras = this->triangulateSafe(cameras); - - if (nrCameras < 2 - || (!this->manageDegeneracy_ - && (this->cheiralityException_ || this->degenerate_))) - return false; - - // instead, if we want to manage the exception.. - if (this->cheiralityException_ || this->degenerate_) // if we want to manage the exceptions with rotation-only factors - this->degenerate_ = true; - - if (this->degenerate_) { - std::cout << "SmartProjectionFactor: this is not ready" << std::endl; - std::cout << "this->cheiralityException_ " << this->cheiralityException_ - << std::endl; - std::cout << "this->degenerate_ " << this->degenerate_ << std::endl; - } - return true; - } - /** * Triangulate and compute derivative of error with respect to point * @return whether triangulation worked */ bool triangulateAndComputeE(Matrix& E, const Values& values) const { - Cameras cameras; - bool nonDegenerate = computeCamerasAndTriangulate(values, cameras); + Cameras cameras = this->cameras(values); + bool nonDegenerate = triangulateForLinearize(cameras); if (nonDegenerate) - cameras.project2(point_, boost::none, E); + cameras.project2(*result_, boost::none, E); return nonDegenerate; } @@ -501,31 +375,18 @@ public: std::vector& Fblocks, Matrix& E, Vector& b, const Cameras& cameras) const { - if (this->degenerate_) { - std::cout << "manage degeneracy " << manageDegeneracy_ << std::endl; - std::cout << "point " << point_ << std::endl; - std::cout - << "SmartProjectionFactor: Management of degeneracy is disabled - not ready to be used" - << std::endl; - if (Base::Dim > 6) { - std::cout - << "Management of degeneracy is not yet ready when one also optimizes for the calibration " - << std::endl; - } - + if (!result_) { + // TODO Luca clarify whether this works or not + result_ = TriangulationResult( + cameras[0].backprojectPointAtInfinity(this->measured_.at(0))); // TODO replace all this by Call to CameraSet int m = this->keys_.size(); E = zeros(2 * m, 2); b = zero(2 * m); double f = 0; for (size_t i = 0; i < this->measured_.size(); i++) { - if (i == 0) { // first pose - this->point_ = cameras[i].backprojectPointAtInfinity( - this->measured_.at(i)); - // 3D parametrization of point at infinity: [px py 1] - } Matrix Fi, Ei; - Vector bi = -(cameras[i].projectPointAtInfinity(this->point_, Fi, Ei) + Vector bi = -(cameras[i].projectPointAtInfinity(*result_, Fi, Ei) - this->measured_.at(i)).vector(); f += bi.squaredNorm(); @@ -535,17 +396,17 @@ public: } return f; } else { - // nondegenerate: just return Base version - return Base::computeJacobians(Fblocks, E, b, cameras, point_); - } // end else + // valid result: just return Base version + return Base::computeJacobians(Fblocks, E, b, cameras, *result_); + } } /// Version that takes values, and creates the point bool triangulateAndComputeJacobians( std::vector& Fblocks, Matrix& E, Vector& b, const Values& values) const { - Cameras cameras; - bool nonDegenerate = computeCamerasAndTriangulate(values, cameras); + Cameras cameras = this->cameras(values); + bool nonDegenerate = triangulateForLinearize(cameras); if (nonDegenerate) computeJacobiansWithTriangulatedPoint(Fblocks, E, b, cameras); return nonDegenerate; @@ -555,19 +416,19 @@ public: bool triangulateAndComputeJacobiansSVD( std::vector& Fblocks, Matrix& Enull, Vector& b, const Values& values) const { - typename Base::Cameras cameras; - double good = computeCamerasAndTriangulate(values, cameras); - if (good) - Base::computeJacobiansSVD(Fblocks, Enull, b, cameras, point_); - return true; + Cameras cameras = this->cameras(values); + bool nonDegenerate = triangulateForLinearize(cameras); + if (nonDegenerate) + Base::computeJacobiansSVD(Fblocks, Enull, b, cameras, *result_); + return nonDegenerate; } /// Calculate vector of re-projection errors, before applying noise model Vector reprojectionErrorAfterTriangulation(const Values& values) const { - Cameras cameras; - bool nonDegenerate = computeCamerasAndTriangulate(values, cameras); + Cameras cameras = this->cameras(values); + bool nonDegenerate = triangulateForLinearize(cameras); if (nonDegenerate) - return Base::reprojectionError(cameras, point_); + return Base::reprojectionError(cameras, *result_); else return zero(cameras.size() * 2); } @@ -581,65 +442,59 @@ public: double totalReprojectionError(const Cameras& cameras, boost::optional externalPoint = boost::none) const { - size_t nrCameras; - if (externalPoint) { - nrCameras = this->keys_.size(); - point_ = *externalPoint; - degenerate_ = false; - cheiralityException_ = false; - } else { - nrCameras = this->triangulateSafe(cameras); - } + if (externalPoint) + result_ = TriangulationResult(*externalPoint); - if (nrCameras < 2 - || (!this->manageDegeneracy_ - && (this->cheiralityException_ || this->degenerate_))) { - // if we don't want to manage the exceptions we discard the factor - // std::cout << "In error evaluation: exception" << std::endl; + // if we don't want to manage the exceptions we discard the factor + if (!manageDegeneracy_ && !result_) return 0.0; - } - if (this->cheiralityException_) { // if we want to manage the exceptions with rotation-only factors + if (isPointBehindCamera()) { // if we want to manage the exceptions with rotation-only factors std::cout << "SmartProjectionHessianFactor: cheirality exception (this should not happen if CheiralityException is disabled)!" << std::endl; - this->degenerate_ = true; } - if (this->degenerate_) { + if (isDegenerate()) { // return 0.0; // TODO: this maybe should be zero? std::cout << "SmartProjectionHessianFactor: trying to manage degeneracy (this should not happen is manageDegeneracy is disabled)!" << std::endl; // 3D parameterization of point at infinity - const Point2& zi = this->measured_.at(0); - this->point_ = cameras.front().backprojectPointAtInfinity(zi); - return Base::totalReprojectionErrorAtInfinity(cameras, this->point_); + const Point2& z0 = this->measured_.at(0); + result_ = TriangulationResult( + cameras.front().backprojectPointAtInfinity(z0)); + return Base::totalReprojectionErrorAtInfinity(cameras, *result_); } else { // Just use version in base class - return Base::totalReprojectionError(cameras, point_); + return Base::totalReprojectionError(cameras, *result_); } } /** return the landmark */ - boost::optional point() const { - return point_; + TriangulationResult point() const { + return result_; } /** COMPUTE the landmark */ - boost::optional point(const Values& values) const { - triangulateSafe(values); - return point_; + TriangulationResult point(const Values& values) const { + Cameras cameras = this->cameras(values); + return triangulateSafe(cameras); + } + + /// Is result valid? + inline bool isValid() const { + return result_; } /** return the degenerate state */ inline bool isDegenerate() const { - return (cheiralityException_ || degenerate_); + return result_.degenerate(); } /** return the cheirality status flag */ inline bool isPointBehindCamera() const { - return cheiralityException_; + return result_.behindCamera(); } /** return cheirality verbosity */ From 2cc0223624fc6b33bc74264ad6e1d850de38dda8 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 12:06:46 +0100 Subject: [PATCH 03/18] Made tests compile with TriangulationResult changes --- .../tests/testSmartProjectionCameraFactor.cpp | 96 ++++++------------- .../tests/testSmartProjectionPoseFactor.cpp | 2 +- 2 files changed, 29 insertions(+), 69 deletions(-) diff --git a/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp b/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp index ba7b7bf51..750751935 100644 --- a/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp +++ b/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp @@ -27,7 +27,7 @@ using namespace boost::assign; -static bool isDebugTest = false; +static bool isDebugTest = true; static double rankTol = 1.0; static double linThreshold = -1.0; @@ -133,9 +133,8 @@ TEST( SmartProjectionCameraFactor, noisy ) { using namespace vanilla; - // 1. Project two landmarks into two cameras and triangulate - Point2 pixelError(0.2, 0.2); - Point2 level_uv = level_camera.project(landmark1) + pixelError; + // Project one landmark into two cameras and add noise on first + Point2 level_uv = level_camera.project(landmark1) + Point2(0.2, 0.2); Point2 level_uv_right = level_camera_right.project(landmark1); Values values; @@ -165,7 +164,6 @@ TEST( SmartProjectionCameraFactor, noisy ) { factor2->add(measurements, views, noises); double actualError2 = factor2->error(values); - DOUBLES_EQUAL(actualError1, actualError2, 1e-7); } @@ -174,68 +172,58 @@ TEST( SmartProjectionCameraFactor, perturbPoseAndOptimize ) { using namespace vanilla; + // Project three landmarks into three cameras vector measurements_cam1, measurements_cam2, measurements_cam3; - - // 1. Project three landmarks into three cameras and triangulate projectToMultipleCameras(cam1, cam2, cam3, landmark1, measurements_cam1); projectToMultipleCameras(cam1, cam2, cam3, landmark2, measurements_cam2); projectToMultipleCameras(cam1, cam2, cam3, landmark3, measurements_cam3); + // Create and fill smartfactors + SmartFactor::shared_ptr smartFactor1(new SmartFactor()); + SmartFactor::shared_ptr smartFactor2(new SmartFactor()); + SmartFactor::shared_ptr smartFactor3(new SmartFactor()); vector views; views.push_back(c1); views.push_back(c2); views.push_back(c3); - - SmartFactor::shared_ptr smartFactor1(new SmartFactor()); smartFactor1->add(measurements_cam1, views, unit2); - - SmartFactor::shared_ptr smartFactor2(new SmartFactor()); smartFactor2->add(measurements_cam2, views, unit2); - - SmartFactor::shared_ptr smartFactor3(new SmartFactor()); smartFactor3->add(measurements_cam3, views, unit2); - const SharedDiagonal noisePrior = noiseModel::Isotropic::Sigma(6 + 5, 1e-5); - + // Create factor graph and add priors on two cameras NonlinearFactorGraph graph; graph.push_back(smartFactor1); graph.push_back(smartFactor2); graph.push_back(smartFactor3); + const SharedDiagonal noisePrior = noiseModel::Isotropic::Sigma(6 + 5, 1e-5); graph.push_back(PriorFactor(c1, cam1, noisePrior)); graph.push_back(PriorFactor(c2, cam2, noisePrior)); - Values values; - values.insert(c1, cam1); - values.insert(c2, cam2); - values.insert(c3, perturbCameraPose(cam3)); + // Create initial estimate + Values initialEstimate; + initialEstimate.insert(c1, cam1); + initialEstimate.insert(c2, cam2); + initialEstimate.insert(c3, perturbCameraPose(cam3)); if (isDebugTest) - values.at(c3).print("Smart: Pose3 before optimization: "); + initialEstimate.at(c3).print("Smart: Pose3 before optimization: "); + // Optimize LevenbergMarquardtParams params; - if (isDebugTest) + if (isDebugTest) { params.verbosityLM = LevenbergMarquardtParams::TRYLAMBDA; - if (isDebugTest) params.verbosity = NonlinearOptimizerParams::ERROR; + } + LevenbergMarquardtOptimizer optimizer(graph, initialEstimate, params); + Values result = optimizer.optimize(); - Values result; - gttic_(SmartProjectionCameraFactor); - LevenbergMarquardtOptimizer optimizer(graph, values, params); - result = optimizer.optimize(); - gttoc_(SmartProjectionCameraFactor); - tictoc_finishedIteration_(); - - // GaussianFactorGraph::shared_ptr GFG = graph.linearize(values); + // GaussianFactorGraph::shared_ptr GFG = graph.linearize(initialEstimate); // VectorValues delta = GFG->optimize(); if (isDebugTest) result.at(c3).print("Smart: Pose3 after optimization: "); EXPECT(assert_equal(cam1, result.at(c1))); EXPECT(assert_equal(cam2, result.at(c2))); - EXPECT(assert_equal(result.at(c3).pose(), cam3.pose(), 1e-4)); - EXPECT( - assert_equal(result.at(c3).calibration(), cam3.calibration(), 2)); - if (isDebugTest) - tictoc_print_(); + EXPECT(assert_equal(cam3, result.at(c3), 1e-4)); } /* *************************************************************************/ @@ -243,8 +231,8 @@ TEST( SmartProjectionCameraFactor, perturbPoseAndOptimizeFromSfM_tracks ) { using namespace vanilla; + // Project three landmarks into three cameras vector measurements_cam1, measurements_cam2, measurements_cam3; - // 1. Project three landmarks into three cameras and triangulate projectToMultipleCameras(cam1, cam2, cam3, landmark1, measurements_cam1); projectToMultipleCameras(cam1, cam2, cam3, landmark2, measurements_cam2); projectToMultipleCameras(cam1, cam2, cam3, landmark3, measurements_cam3); @@ -300,11 +288,8 @@ TEST( SmartProjectionCameraFactor, perturbPoseAndOptimizeFromSfM_tracks ) { params.verbosity = NonlinearOptimizerParams::ERROR; Values result; - gttic_(SmartProjectionCameraFactor); LevenbergMarquardtOptimizer optimizer(graph, values, params); result = optimizer.optimize(); - gttoc_(SmartProjectionCameraFactor); - tictoc_finishedIteration_(); // GaussianFactorGraph::shared_ptr GFG = graph.linearize(values); // VectorValues delta = GFG->optimize(); @@ -313,11 +298,7 @@ TEST( SmartProjectionCameraFactor, perturbPoseAndOptimizeFromSfM_tracks ) { result.at(c3).print("Smart: Pose3 after optimization: "); EXPECT(assert_equal(cam1, result.at(c1))); EXPECT(assert_equal(cam2, result.at(c2))); - EXPECT(assert_equal(result.at(c3).pose(), cam3.pose(), 1e-4)); - EXPECT( - assert_equal(result.at(c3).calibration(), cam3.calibration(), 2)); - if (isDebugTest) - tictoc_print_(); + EXPECT(assert_equal(cam3, result.at(c3), 1e-4)); } /* *************************************************************************/ @@ -383,11 +364,8 @@ TEST( SmartProjectionCameraFactor, perturbCamerasAndOptimize ) { params.verbosity = NonlinearOptimizerParams::ERROR; Values result; - gttic_(SmartProjectionCameraFactor); LevenbergMarquardtOptimizer optimizer(graph, values, params); result = optimizer.optimize(); - gttoc_(SmartProjectionCameraFactor); - tictoc_finishedIteration_(); // GaussianFactorGraph::shared_ptr GFG = graph.linearize(values); // VectorValues delta = GFG->optimize(); @@ -396,11 +374,7 @@ TEST( SmartProjectionCameraFactor, perturbCamerasAndOptimize ) { result.at(c3).print("Smart: Pose3 after optimization: "); EXPECT(assert_equal(cam1, result.at(c1))); EXPECT(assert_equal(cam2, result.at(c2))); - EXPECT(assert_equal(result.at(c3).pose(), cam3.pose(), 1e-1)); - EXPECT( - assert_equal(result.at(c3).calibration(), cam3.calibration(), 20)); - if (isDebugTest) - tictoc_print_(); + EXPECT(assert_equal(cam3, result.at(c3), 1e-4)); } /* *************************************************************************/ @@ -465,21 +439,14 @@ TEST( SmartProjectionCameraFactor, Cal3Bundler ) { params.verbosity = NonlinearOptimizerParams::ERROR; Values result; - gttic_(SmartProjectionCameraFactor); LevenbergMarquardtOptimizer optimizer(graph, values, params); result = optimizer.optimize(); - gttoc_(SmartProjectionCameraFactor); - tictoc_finishedIteration_(); if (isDebugTest) result.at(c3).print("Smart: Pose3 after optimization: "); EXPECT(assert_equal(cam1, result.at(c1), 1e-4)); EXPECT(assert_equal(cam2, result.at(c2), 1e-4)); - EXPECT(assert_equal(result.at(c3).pose(), cam3.pose(), 1e-1)); - EXPECT( - assert_equal(result.at(c3).calibration(), cam3.calibration(), 1)); - if (isDebugTest) - tictoc_print_(); + EXPECT(assert_equal(cam3, result.at(c3), 1e-4)); } /* *************************************************************************/ @@ -544,21 +511,14 @@ TEST( SmartProjectionCameraFactor, Cal3Bundler2 ) { params.verbosity = NonlinearOptimizerParams::ERROR; Values result; - gttic_(SmartProjectionCameraFactor); LevenbergMarquardtOptimizer optimizer(graph, values, params); result = optimizer.optimize(); - gttoc_(SmartProjectionCameraFactor); - tictoc_finishedIteration_(); if (isDebugTest) result.at(c3).print("Smart: Pose3 after optimization: "); EXPECT(assert_equal(cam1, result.at(c1), 1e-4)); EXPECT(assert_equal(cam2, result.at(c2), 1e-4)); - EXPECT(assert_equal(result.at(c3).pose(), cam3.pose(), 1e-1)); - EXPECT( - assert_equal(result.at(c3).calibration(), cam3.calibration(), 2)); - if (isDebugTest) - tictoc_print_(); + EXPECT(assert_equal(cam3, result.at(c3), 1e-4)); } /* *************************************************************************/ diff --git a/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp b/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp index b235d8e2b..d03db7ab1 100644 --- a/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp +++ b/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp @@ -289,7 +289,7 @@ TEST( SmartProjectionPoseFactor, Factors ) { cameras.push_back(cam2); // Make sure triangulation works - LONGS_EQUAL(2, smartFactor1->triangulateSafe(cameras)); + CHECK(smartFactor1->triangulateSafe(cameras)); CHECK(!smartFactor1->isDegenerate()); CHECK(!smartFactor1->isPointBehindCamera()); boost::optional p = smartFactor1->point(); From 47ea3834dfe3e03f5a14131658b9a9a6241b112e Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 13:13:31 +0100 Subject: [PATCH 04/18] Extra tests --- .../tests/testSmartProjectionCameraFactor.cpp | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp b/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp index ba7b7bf51..4a27f7724 100644 --- a/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp +++ b/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp @@ -147,7 +147,24 @@ TEST( SmartProjectionCameraFactor, noisy ) { factor1->add(level_uv, c1, unit2); factor1->add(level_uv_right, c2, unit2); + // check point before triangulation + EXPECT(factor1->point()); + EXPECT(assert_equal(Point3(),*factor1->point(), 1e-7)); + + double expectedError = 58640; double actualError1 = factor1->error(values); + EXPECT_DOUBLES_EQUAL(expectedError, actualError1, 1); + + // Check triangulated point + EXPECT(assert_equal(Point3(13.7587, 1.43851, -1.14274),*factor1->point(), 1e-4)); + + // Check whitened errors + Vector expected(4); + expected << -7, 235, 58, -242; + SmartFactor::Cameras cameras1 = factor1->cameras(values); + Point3 point1 = *factor1->point(); + Vector actual = factor1->whitenedErrors(cameras1, point1); + EXPECT(assert_equal(expected, actual, 1)); SmartFactor::shared_ptr factor2(new SmartFactor()); vector measurements; @@ -165,8 +182,7 @@ TEST( SmartProjectionCameraFactor, noisy ) { factor2->add(measurements, views, noises); double actualError2 = factor2->error(values); - - DOUBLES_EQUAL(actualError1, actualError2, 1e-7); + EXPECT_DOUBLES_EQUAL(expectedError, actualError2, 1); } /* *************************************************************************/ @@ -211,6 +227,30 @@ TEST( SmartProjectionCameraFactor, perturbPoseAndOptimize ) { if (isDebugTest) values.at(c3).print("Smart: Pose3 before optimization: "); + EXPECT(smartFactor1->point()); + EXPECT(smartFactor2->point()); + EXPECT(smartFactor3->point()); + + EXPECT(assert_equal(Point3(),*smartFactor1->point(), 1e-7)); + EXPECT(assert_equal(Point3(),*smartFactor2->point(), 1e-7)); + EXPECT(assert_equal(Point3(),*smartFactor3->point(), 1e-7)); + + EXPECT_DOUBLES_EQUAL(75711, smartFactor1->error(values), 1); + EXPECT_DOUBLES_EQUAL(58524, smartFactor2->error(values), 1); + EXPECT_DOUBLES_EQUAL(77564, smartFactor3->error(values), 1); + + EXPECT(assert_equal(Point3(2.57696, -0.182566, 1.04085),*smartFactor1->point(), 1e-4)); + EXPECT(assert_equal(Point3(2.80114, -0.702153, 1.06594),*smartFactor2->point(), 1e-4)); + EXPECT(assert_equal(Point3(1.82593, -0.289569, 2.13438),*smartFactor3->point(), 1e-4)); + + // Check whitened errors + Vector expected(6); + expected << 256, 29, -26, 29, -206, -202; + SmartFactor::Cameras cameras1 = smartFactor1->cameras(values); + Point3 point1 = *smartFactor1->point(); + Vector actual = smartFactor1->whitenedErrors(cameras1, point1); + EXPECT(assert_equal(expected, actual, 1)); + LevenbergMarquardtParams params; if (isDebugTest) params.verbosityLM = LevenbergMarquardtParams::TRYLAMBDA; @@ -224,6 +264,10 @@ TEST( SmartProjectionCameraFactor, perturbPoseAndOptimize ) { gttoc_(SmartProjectionCameraFactor); tictoc_finishedIteration_(); + EXPECT(assert_equal(landmark1,*smartFactor1->point(), 1e-7)); + EXPECT(assert_equal(landmark2,*smartFactor2->point(), 1e-7)); + EXPECT(assert_equal(landmark3,*smartFactor3->point(), 1e-7)); + // GaussianFactorGraph::shared_ptr GFG = graph.linearize(values); // VectorValues delta = GFG->optimize(); From e15231fb3ed49ac6b452c371af722f73b952d81b Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 13:48:17 +0100 Subject: [PATCH 05/18] Fixed bug (restored omitted triangulateSafe call) --- gtsam/slam/SmartProjectionFactor.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gtsam/slam/SmartProjectionFactor.h b/gtsam/slam/SmartProjectionFactor.h index 13d17f683..4c0d1f4a2 100644 --- a/gtsam/slam/SmartProjectionFactor.h +++ b/gtsam/slam/SmartProjectionFactor.h @@ -344,7 +344,7 @@ public: /// Create a factor, takes values boost::shared_ptr > createJacobianQFactor( const Values& values, double lambda) const { - return createJacobianQFactor(this->cameras(values),lambda); + return createJacobianQFactor(this->cameras(values), lambda); } /// different (faster) way to compute Jacobian factor @@ -444,6 +444,8 @@ public: if (externalPoint) result_ = TriangulationResult(*externalPoint); + else + result_ = triangulateSafe(cameras); // if we don't want to manage the exceptions we discard the factor if (!manageDegeneracy_ && !result_) From 754e8447b16c553f63bbb08b145725019cfc0637 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 13:48:47 +0100 Subject: [PATCH 06/18] Made tests compile and succeed --- .../tests/testSmartProjectionCameraFactor.cpp | 82 +++++++++++-------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp b/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp index 782f31c49..a4ed72f4b 100644 --- a/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp +++ b/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp @@ -27,7 +27,7 @@ using namespace boost::assign; -static bool isDebugTest = true; +static bool isDebugTest = false; static double rankTol = 1.0; static double linThreshold = -1.0; @@ -146,15 +146,15 @@ TEST( SmartProjectionCameraFactor, noisy ) { factor1->add(level_uv, c1, unit2); factor1->add(level_uv_right, c2, unit2); - // check point before triangulation - EXPECT(factor1->point()); - EXPECT(assert_equal(Point3(),*factor1->point(), 1e-7)); + // Point is now uninitialized before a triangulation event + EXPECT(!factor1->point()); double expectedError = 58640; double actualError1 = factor1->error(values); EXPECT_DOUBLES_EQUAL(expectedError, actualError1, 1); // Check triangulated point + CHECK(factor1->point()); EXPECT(assert_equal(Point3(13.7587, 1.43851, -1.14274),*factor1->point(), 1e-4)); // Check whitened errors @@ -217,24 +217,26 @@ TEST( SmartProjectionCameraFactor, perturbPoseAndOptimize ) { graph.push_back(PriorFactor(c2, cam2, noisePrior)); // Create initial estimate - Values initialEstimate; - initialEstimate.insert(c1, cam1); - initialEstimate.insert(c2, cam2); - initialEstimate.insert(c3, perturbCameraPose(cam3)); + Values initial; + initial.insert(c1, cam1); + initial.insert(c2, cam2); + initial.insert(c3, perturbCameraPose(cam3)); if (isDebugTest) - initialEstimate.at(c3).print("Smart: Pose3 before optimization: "); + initial.at(c3).print("Smart: Pose3 before optimization: "); - EXPECT(smartFactor1->point()); - EXPECT(smartFactor2->point()); - EXPECT(smartFactor3->point()); + // Points are now uninitialized before a triangulation event + EXPECT(!smartFactor1->point()); + EXPECT(!smartFactor2->point()); + EXPECT(!smartFactor3->point()); - EXPECT(assert_equal(Point3(),*smartFactor1->point(), 1e-7)); - EXPECT(assert_equal(Point3(),*smartFactor2->point(), 1e-7)); - EXPECT(assert_equal(Point3(),*smartFactor3->point(), 1e-7)); + EXPECT_DOUBLES_EQUAL(75711, smartFactor1->error(initial), 1); + EXPECT_DOUBLES_EQUAL(58524, smartFactor2->error(initial), 1); + EXPECT_DOUBLES_EQUAL(77564, smartFactor3->error(initial), 1); - EXPECT_DOUBLES_EQUAL(75711, smartFactor1->error(values), 1); - EXPECT_DOUBLES_EQUAL(58524, smartFactor2->error(values), 1); - EXPECT_DOUBLES_EQUAL(77564, smartFactor3->error(values), 1); + // Error should trigger this and initialize the points, abort if not so + CHECK(smartFactor1->point()); + CHECK(smartFactor2->point()); + CHECK(smartFactor3->point()); EXPECT(assert_equal(Point3(2.57696, -0.182566, 1.04085),*smartFactor1->point(), 1e-4)); EXPECT(assert_equal(Point3(2.80114, -0.702153, 1.06594),*smartFactor2->point(), 1e-4)); @@ -243,7 +245,7 @@ TEST( SmartProjectionCameraFactor, perturbPoseAndOptimize ) { // Check whitened errors Vector expected(6); expected << 256, 29, -26, 29, -206, -202; - SmartFactor::Cameras cameras1 = smartFactor1->cameras(values); + SmartFactor::Cameras cameras1 = smartFactor1->cameras(initial); Point3 point1 = *smartFactor1->point(); Vector actual = smartFactor1->whitenedErrors(cameras1, point1); EXPECT(assert_equal(expected, actual, 1)); @@ -254,29 +256,25 @@ TEST( SmartProjectionCameraFactor, perturbPoseAndOptimize ) { params.verbosityLM = LevenbergMarquardtParams::TRYLAMBDA; params.verbosity = NonlinearOptimizerParams::ERROR; } - LevenbergMarquardtOptimizer optimizer(graph, initialEstimate, params); + LevenbergMarquardtOptimizer optimizer(graph, initial, params); Values result = optimizer.optimize(); - - Values result; - gttic_(SmartProjectionCameraFactor); - LevenbergMarquardtOptimizer optimizer(graph, values, params); - result = optimizer.optimize(); - gttoc_(SmartProjectionCameraFactor); - tictoc_finishedIteration_(); - EXPECT(assert_equal(landmark1,*smartFactor1->point(), 1e-7)); EXPECT(assert_equal(landmark2,*smartFactor2->point(), 1e-7)); EXPECT(assert_equal(landmark3,*smartFactor3->point(), 1e-7)); - // GaussianFactorGraph::shared_ptr GFG = graph.linearize(values); + // GaussianFactorGraph::shared_ptr GFG = graph.linearize(initial); // VectorValues delta = GFG->optimize(); if (isDebugTest) result.at(c3).print("Smart: Pose3 after optimization: "); EXPECT(assert_equal(cam1, result.at(c1))); EXPECT(assert_equal(cam2, result.at(c2))); - EXPECT(assert_equal(cam3, result.at(c3), 1e-4)); + EXPECT(assert_equal(result.at(c3).pose(), cam3.pose(), 1e-4)); + EXPECT( + assert_equal(result.at(c3).calibration(), cam3.calibration(), 2)); + if (isDebugTest) + tictoc_print_(); } /* *************************************************************************/ @@ -351,7 +349,11 @@ TEST( SmartProjectionCameraFactor, perturbPoseAndOptimizeFromSfM_tracks ) { result.at(c3).print("Smart: Pose3 after optimization: "); EXPECT(assert_equal(cam1, result.at(c1))); EXPECT(assert_equal(cam2, result.at(c2))); - EXPECT(assert_equal(cam3, result.at(c3), 1e-4)); + EXPECT(assert_equal(result.at(c3).pose(), cam3.pose(), 1e-4)); + EXPECT( + assert_equal(result.at(c3).calibration(), cam3.calibration(), 2)); + if (isDebugTest) + tictoc_print_(); } /* *************************************************************************/ @@ -427,7 +429,11 @@ TEST( SmartProjectionCameraFactor, perturbCamerasAndOptimize ) { result.at(c3).print("Smart: Pose3 after optimization: "); EXPECT(assert_equal(cam1, result.at(c1))); EXPECT(assert_equal(cam2, result.at(c2))); - EXPECT(assert_equal(cam3, result.at(c3), 1e-4)); + EXPECT(assert_equal(result.at(c3).pose(), cam3.pose(), 1e-1)); + EXPECT( + assert_equal(result.at(c3).calibration(), cam3.calibration(), 20)); + if (isDebugTest) + tictoc_print_(); } /* *************************************************************************/ @@ -499,7 +505,11 @@ TEST( SmartProjectionCameraFactor, Cal3Bundler ) { result.at(c3).print("Smart: Pose3 after optimization: "); EXPECT(assert_equal(cam1, result.at(c1), 1e-4)); EXPECT(assert_equal(cam2, result.at(c2), 1e-4)); - EXPECT(assert_equal(cam3, result.at(c3), 1e-4)); + EXPECT(assert_equal(result.at(c3).pose(), cam3.pose(), 1e-1)); + EXPECT( + assert_equal(result.at(c3).calibration(), cam3.calibration(), 1)); + if (isDebugTest) + tictoc_print_(); } /* *************************************************************************/ @@ -571,7 +581,11 @@ TEST( SmartProjectionCameraFactor, Cal3Bundler2 ) { result.at(c3).print("Smart: Pose3 after optimization: "); EXPECT(assert_equal(cam1, result.at(c1), 1e-4)); EXPECT(assert_equal(cam2, result.at(c2), 1e-4)); - EXPECT(assert_equal(cam3, result.at(c3), 1e-4)); + EXPECT(assert_equal(result.at(c3).pose(), cam3.pose(), 1e-1)); + EXPECT( + assert_equal(result.at(c3).calibration(), cam3.calibration(), 2)); + if (isDebugTest) + tictoc_print_(); } /* *************************************************************************/ From 8fe612ca719d85918b2c74698f8f8bd17aa4d802 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 14:25:32 +0100 Subject: [PATCH 07/18] whitenJacobians --- gtsam/slam/SmartFactorBase.h | 14 ++++++++++---- gtsam/slam/SmartProjectionFactor.h | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/gtsam/slam/SmartFactorBase.h b/gtsam/slam/SmartFactorBase.h index 417ba1354..59a267278 100644 --- a/gtsam/slam/SmartFactorBase.h +++ b/gtsam/slam/SmartFactorBase.h @@ -487,6 +487,15 @@ public: updateSparseSchurComplement(Fblocks, E, P, b, f, allKeys, augmentedHessian); // augmentedHessian.matrix().block (i1,i2) = ... } + /// Whiten the Jacobians computed by computeJacobians using noiseModel_ + void whitenJacobians(std::vector& F, Matrix& E, + Vector& b) const { + noiseModel_->WhitenSystem(E, b); + // TODO make WhitenInPlace work with any dense matrix type + BOOST_FOREACH(KeyMatrix2D& Fblock,F) + Fblock.second = noiseModel_->Whiten(Fblock.second); + } + /** * Return Jacobians as RegularImplicitSchurFactor with raw access */ @@ -497,11 +506,8 @@ public: Matrix E; Vector b; computeJacobians(F, E, b, cameras, point); - noiseModel_->WhitenSystem(E, b); + whitenJacobians(F, E, b); Matrix3 P = PointCov(E, lambda, diagonalDamping); - // TODO make WhitenInPlace work with any dense matrix type - BOOST_FOREACH(KeyMatrix2D& Fblock,F) - Fblock.second = noiseModel_->Whiten(Fblock.second); return boost::make_shared >(F, E, P, b); } diff --git a/gtsam/slam/SmartProjectionFactor.h b/gtsam/slam/SmartProjectionFactor.h index 4c0d1f4a2..51f892a6d 100644 --- a/gtsam/slam/SmartProjectionFactor.h +++ b/gtsam/slam/SmartProjectionFactor.h @@ -280,6 +280,7 @@ public: { std::vector Fblocks; f = computeJacobiansWithTriangulatedPoint(Fblocks, E, b, cameras); + Base::whitenJacobians(Fblocks,E,b); Base::FillDiagonalF(Fblocks, F); // expensive ! } From 575a06b0427aafa40d6d38d9dd0b7344092ce60f Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 14:27:37 +0100 Subject: [PATCH 08/18] Added my name --- gtsam/slam/SmartProjectionCameraFactor.h | 1 + gtsam/slam/tests/testSmartProjectionCameraFactor.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/gtsam/slam/SmartProjectionCameraFactor.h b/gtsam/slam/SmartProjectionCameraFactor.h index f10681ab8..3afd04188 100644 --- a/gtsam/slam/SmartProjectionCameraFactor.h +++ b/gtsam/slam/SmartProjectionCameraFactor.h @@ -15,6 +15,7 @@ * @author Luca Carlone * @author Chris Beall * @author Zsolt Kira + * @author Frank Dellaert */ #pragma once diff --git a/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp b/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp index a4ed72f4b..56ff47c3e 100644 --- a/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp +++ b/gtsam/slam/tests/testSmartProjectionCameraFactor.cpp @@ -15,6 +15,7 @@ * @author Chris Beall * @author Luca Carlone * @author Zsolt Kira + * @author Frank Dellaert * @date Sept 2013 */ From 6a024259e59826ca0d88ef43bd1728c8bc99597b Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 14:28:13 +0100 Subject: [PATCH 09/18] Switched to (i,j) instead of (i1,i2) --- gtsam/slam/SmartFactorBase.h | 73 +++++++++---------- .../tests/testSmartProjectionPoseFactor.cpp | 1 + 2 files changed, 36 insertions(+), 38 deletions(-) diff --git a/gtsam/slam/SmartFactorBase.h b/gtsam/slam/SmartFactorBase.h index 59a267278..5a757d998 100644 --- a/gtsam/slam/SmartFactorBase.h +++ b/gtsam/slam/SmartFactorBase.h @@ -367,28 +367,27 @@ public: size_t numKeys = this->keys_.size(); // Blockwise Schur complement - for (size_t i1 = 0; i1 < numKeys; i1++) { // for each camera + for (size_t i = 0; i < numKeys; i++) { // for each camera - const Matrix2D& Fi1 = Fblocks.at(i1).second; - const Matrix23 Ei1_P = E.block(ZDim * i1, 0) * P; + const Matrix2D& Fi = Fblocks.at(i).second; + const Matrix23 Ei_P = E.block(ZDim * i, 0) * P; // Dim = (Dx2) * (2) - // (augmentedHessian.matrix()).block (i1,numKeys+1) = Fi1.transpose() * b.segment < 2 > (2 * i1); // F' * b - augmentedHessian(i1, numKeys) = Fi1.transpose() - * b.segment(ZDim * i1) // F' * b - - Fi1.transpose() * (Ei1_P * (E.transpose() * b)); // Dim = (DxZDim) * (ZDimx3) * (3*ZDimm) * (ZDimm x 1) + // (augmentedHessian.matrix()).block (i,numKeys+1) = Fi.transpose() * b.segment < 2 > (2 * i); // F' * b + augmentedHessian(i, numKeys) = Fi.transpose() * b.segment(ZDim * i) // F' * b + - Fi.transpose() * (Ei_P * (E.transpose() * b)); // Dim = (DxZDim) * (ZDimx3) * (3*ZDimm) * (ZDimm x 1) // (DxD) = (DxZDim) * ( (ZDimxD) - (ZDimx3) * (3xZDim) * (ZDimxD) ) - augmentedHessian(i1, i1) = Fi1.transpose() - * (Fi1 - Ei1_P * E.block(ZDim * i1, 0).transpose() * Fi1); + augmentedHessian(i, i) = Fi.transpose() + * (Fi - Ei_P * E.block(ZDim * i, 0).transpose() * Fi); // upper triangular part of the hessian - for (size_t i2 = i1 + 1; i2 < numKeys; i2++) { // for each camera - const Matrix2D& Fi2 = Fblocks.at(i2).second; + for (size_t j = i + 1; j < numKeys; j++) { // for each camera + const Matrix2D& Fj = Fblocks.at(j).second; // (DxD) = (Dx2) * ( (2x2) * (2xD) ) - augmentedHessian(i1, i2) = -Fi1.transpose() - * (Ei1_P * E.block(ZDim * i2, 0).transpose() * Fi2); + augmentedHessian(i, j) = -Fi.transpose() + * (Ei_P * E.block(ZDim * j, 0).transpose() * Fj); } } // end of for over cameras } @@ -417,50 +416,48 @@ public: size_t aug_numKeys = (augmentedHessian.rows() - 1) / Dim; // all cameras in the group // Blockwise Schur complement - for (size_t i1 = 0; i1 < numKeys; i1++) { // for each camera in the current factor + for (size_t i = 0; i < numKeys; i++) { // for each camera in the current factor - const Matrix2D& Fi1 = Fblocks.at(i1).second; - const Matrix23 Ei1_P = E.block(ZDim * i1, 0) * P; + const Matrix2D& Fi = Fblocks.at(i).second; + const Matrix23 Ei_P = E.block(ZDim * i, 0) * P; // Dim = (DxZDim) * (ZDim) // allKeys are the list of all camera keys in the group, e.g, (1,3,4,5,7) // we should map those to a slot in the local (grouped) hessian (0,1,2,3,4) - // Key cameraKey_i1 = this->keys_[i1]; - DenseIndex aug_i1 = KeySlotMap[this->keys_[i1]]; + // Key cameraKey_i = this->keys_[i]; + DenseIndex aug_i = KeySlotMap[this->keys_[i]]; // information vector - store previous vector - // vectorBlock = augmentedHessian(aug_i1, aug_numKeys).knownOffDiagonal(); + // vectorBlock = augmentedHessian(aug_i, aug_numKeys).knownOffDiagonal(); // add contribution of current factor - augmentedHessian(aug_i1, aug_numKeys) = augmentedHessian(aug_i1, + augmentedHessian(aug_i, aug_numKeys) = augmentedHessian(aug_i, aug_numKeys).knownOffDiagonal() - + Fi1.transpose() * b.segment(ZDim * i1) // F' * b - - Fi1.transpose() * (Ei1_P * (E.transpose() * b)); // Dim = (DxZDim) * (ZDimx3) * (3*ZDimm) * (ZDimm x 1) + + Fi.transpose() * b.segment(ZDim * i) // F' * b + - Fi.transpose() * (Ei_P * (E.transpose() * b)); // Dim = (DxZDim) * (ZDimx3) * (3*ZDimm) * (ZDimm x 1) // (DxD) = (DxZDim) * ( (ZDimxD) - (ZDimx3) * (3xZDim) * (ZDimxD) ) // main block diagonal - store previous block - matrixBlock = augmentedHessian(aug_i1, aug_i1); + matrixBlock = augmentedHessian(aug_i, aug_i); // add contribution of current factor - augmentedHessian(aug_i1, aug_i1) = - matrixBlock - + (Fi1.transpose() - * (Fi1 - - Ei1_P * E.block(ZDim * i1, 0).transpose() * Fi1)); + augmentedHessian(aug_i, aug_i) = matrixBlock + + (Fi.transpose() + * (Fi - Ei_P * E.block(ZDim * i, 0).transpose() * Fi)); // upper triangular part of the hessian - for (size_t i2 = i1 + 1; i2 < numKeys; i2++) { // for each camera - const Matrix2D& Fi2 = Fblocks.at(i2).second; + for (size_t j = i + 1; j < numKeys; j++) { // for each camera + const Matrix2D& Fj = Fblocks.at(j).second; - //Key cameraKey_i2 = this->keys_[i2]; - DenseIndex aug_i2 = KeySlotMap[this->keys_[i2]]; + //Key cameraKey_j = this->keys_[j]; + DenseIndex aug_j = KeySlotMap[this->keys_[j]]; // (DxD) = (DxZDim) * ( (ZDimxZDim) * (ZDimxD) ) // off diagonal block - store previous block - // matrixBlock = augmentedHessian(aug_i1, aug_i2).knownOffDiagonal(); + // matrixBlock = augmentedHessian(aug_i, aug_j).knownOffDiagonal(); // add contribution of current factor - augmentedHessian(aug_i1, aug_i2) = - augmentedHessian(aug_i1, aug_i2).knownOffDiagonal() - - Fi1.transpose() - * (Ei1_P * E.block(ZDim * i2, 0).transpose() * Fi2); + augmentedHessian(aug_i, aug_j) = + augmentedHessian(aug_i, aug_j).knownOffDiagonal() + - Fi.transpose() + * (Ei_P * E.block(ZDim * j, 0).transpose() * Fj); } } // end of for over cameras @@ -484,7 +481,7 @@ public: Vector b; double f = computeJacobians(Fblocks, E, b, cameras, point); Matrix3 P = PointCov(E, lambda, diagonalDamping); - updateSparseSchurComplement(Fblocks, E, P, b, f, allKeys, augmentedHessian); // augmentedHessian.matrix().block (i1,i2) = ... + updateSparseSchurComplement(Fblocks, E, P, b, f, allKeys, augmentedHessian); // augmentedHessian.matrix().block (i,j) = ... } /// Whiten the Jacobians computed by computeJacobians using noiseModel_ diff --git a/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp b/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp index d03db7ab1..0e1c3ff46 100644 --- a/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp +++ b/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp @@ -15,6 +15,7 @@ * @author Chris Beall * @author Luca Carlone * @author Zsolt Kira + * @author Frank Dellaert * @date Sept 2013 */ From cee64e385393f09738f45d23bf14da5df7042ae8 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 14:28:31 +0100 Subject: [PATCH 10/18] Made sure all refer to the same information matrix --- gtsam/slam/tests/testSmartProjectionPoseFactor.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp b/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp index 0e1c3ff46..b0c310a36 100644 --- a/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp +++ b/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp @@ -301,6 +301,7 @@ TEST( SmartProjectionPoseFactor, Factors ) { Matrix16 A1, A2; A1 << -1000, 0, 0, 0, 100, 0; A2 << 1000, 0, 100, 0, -100, 0; + Matrix expectedInformation; // filled below { // createHessianFactor Matrix66 G11 = 0.5 * A1.transpose() * A1; @@ -315,10 +316,11 @@ TEST( SmartProjectionPoseFactor, Factors ) { double f = 0; RegularHessianFactor<6> expected(x1, x2, G11, G12, g1, G22, g2, f); + expectedInformation = expected.information(); boost::shared_ptr > actual = smartFactor1->createHessianFactor(cameras, 0.0); - EXPECT(assert_equal(expected.information(), actual->information(), 1e-8)); + EXPECT(assert_equal(expectedInformation, actual->information(), 1e-8)); EXPECT(assert_equal(expected, *actual, 1e-8)); } @@ -356,16 +358,19 @@ TEST( SmartProjectionPoseFactor, Factors ) { boost::shared_ptr > actual = smartFactor1->createRegularImplicitSchurFactor(cameras, 0.0); CHECK(actual); + EXPECT(assert_equal(expectedInformation, expected.information(), 1e-8)); + EXPECT(assert_equal(expectedInformation, actual->information(), 1e-8)); EXPECT(assert_equal(expected, *actual)); // createJacobianQFactor SharedIsotropic n = noiseModel::Isotropic::Sigma(4, sigma); JacobianFactorQ<6, 2> expectedQ(Fblocks, E, P, b, n); + EXPECT(assert_equal(expectedInformation, expectedQ.information(), 1e-8)); boost::shared_ptr > actualQ = smartFactor1->createJacobianQFactor(cameras, 0.0); CHECK(actual); - EXPECT(assert_equal(expectedQ.information(), actualQ->information(), 1e-8)); + EXPECT(assert_equal(expectedInformation, actualQ->information(), 1e-8)); EXPECT(assert_equal(expectedQ, *actualQ)); } @@ -375,11 +380,12 @@ TEST( SmartProjectionPoseFactor, Factors ) { b.setZero(); double s = sin(M_PI_4); JacobianFactor expected(x1, s * A1, x2, s * A2, b); + EXPECT(assert_equal(expectedInformation, expected.information(), 1e-8)); boost::shared_ptr actual = smartFactor1->createJacobianSVDFactor(cameras, 0.0); CHECK(actual); - EXPECT(assert_equal(expected.information(), actual->information(), 1e-8)); + EXPECT(assert_equal(expectedInformation, actual->information(), 1e-8)); EXPECT(assert_equal(expected, *actual)); } } From b94279f37fbec5b53001ee35fdbf9a9be885f0ba Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 14:35:54 +0100 Subject: [PATCH 11/18] numKeys -> m --- gtsam/slam/SmartFactorBase.h | 43 +++++++++++++++++------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/gtsam/slam/SmartFactorBase.h b/gtsam/slam/SmartFactorBase.h index 5a757d998..9a53957a5 100644 --- a/gtsam/slam/SmartFactorBase.h +++ b/gtsam/slam/SmartFactorBase.h @@ -360,21 +360,20 @@ public: const Matrix& E, const Matrix3& P /*Point Covariance*/, const Vector& b, /*output ->*/SymmetricBlockMatrix& augmentedHessian) const { // Schur complement trick - // Gs = F' * F - F' * E * P * E' * F - // gs = F' * (b - E * P * E' * b) + // G = F' * F - F' * E * P * E' * F + // g = F' * (b - E * P * E' * b) - // a single point is observed in numKeys cameras - size_t numKeys = this->keys_.size(); + // a single point is observed in m cameras + size_t m = this->keys_.size(); // Blockwise Schur complement - for (size_t i = 0; i < numKeys; i++) { // for each camera + for (size_t i = 0; i < m; i++) { // for each camera const Matrix2D& Fi = Fblocks.at(i).second; const Matrix23 Ei_P = E.block(ZDim * i, 0) * P; // Dim = (Dx2) * (2) - // (augmentedHessian.matrix()).block (i,numKeys+1) = Fi.transpose() * b.segment < 2 > (2 * i); // F' * b - augmentedHessian(i, numKeys) = Fi.transpose() * b.segment(ZDim * i) // F' * b + augmentedHessian(i, m) = Fi.transpose() * b.segment(ZDim * i) // F' * b - Fi.transpose() * (Ei_P * (E.transpose() * b)); // Dim = (DxZDim) * (ZDimx3) * (3*ZDimm) * (ZDimm x 1) // (DxD) = (DxZDim) * ( (ZDimxD) - (ZDimx3) * (3xZDim) * (ZDimxD) ) @@ -382,7 +381,7 @@ public: * (Fi - Ei_P * E.block(ZDim * i, 0).transpose() * Fi); // upper triangular part of the hessian - for (size_t j = i + 1; j < numKeys; j++) { // for each camera + for (size_t j = i + 1; j < m; j++) { // for each camera const Matrix2D& Fj = Fblocks.at(j).second; // (DxD) = (Dx2) * ( (2x2) * (2xD) ) @@ -411,12 +410,12 @@ public: for (size_t slot = 0; slot < allKeys.size(); slot++) KeySlotMap.insert(std::make_pair(allKeys[slot], slot)); - // a single point is observed in numKeys cameras - size_t numKeys = this->keys_.size(); // cameras observing current point - size_t aug_numKeys = (augmentedHessian.rows() - 1) / Dim; // all cameras in the group + // a single point is observed in m cameras + size_t m = this->keys_.size(); // cameras observing current point + size_t aug_m = (augmentedHessian.rows() - 1) / Dim; // all cameras in the group // Blockwise Schur complement - for (size_t i = 0; i < numKeys; i++) { // for each camera in the current factor + for (size_t i = 0; i < m; i++) { // for each camera in the current factor const Matrix2D& Fi = Fblocks.at(i).second; const Matrix23 Ei_P = E.block(ZDim * i, 0) * P; @@ -428,15 +427,15 @@ public: DenseIndex aug_i = KeySlotMap[this->keys_[i]]; // information vector - store previous vector - // vectorBlock = augmentedHessian(aug_i, aug_numKeys).knownOffDiagonal(); + // vectorBlock = augmentedHessian(aug_i, aug_m).knownOffDiagonal(); // add contribution of current factor - augmentedHessian(aug_i, aug_numKeys) = augmentedHessian(aug_i, - aug_numKeys).knownOffDiagonal() - + Fi.transpose() * b.segment(ZDim * i) // F' * b - - Fi.transpose() * (Ei_P * (E.transpose() * b)); // Dim = (DxZDim) * (ZDimx3) * (3*ZDimm) * (ZDimm x 1) + augmentedHessian(aug_i, aug_m) = + augmentedHessian(aug_i, aug_m).knownOffDiagonal() + + Fi.transpose() * b.segment(ZDim * i) // F' * b + - Fi.transpose() * (Ei_P * (E.transpose() * b)); // Dim = (DxZDim) * (ZDimx3) * (3*ZDimm) * (ZDimm x 1) - // (DxD) = (DxZDim) * ( (ZDimxD) - (ZDimx3) * (3xZDim) * (ZDimxD) ) - // main block diagonal - store previous block + // (DxD) = (DxZDim) * ( (ZDimxD) - (ZDimx3) * (3xZDim) * (ZDimxD) ) + // main block diagonal - store previous block matrixBlock = augmentedHessian(aug_i, aug_i); // add contribution of current factor augmentedHessian(aug_i, aug_i) = matrixBlock @@ -444,7 +443,7 @@ public: * (Fi - Ei_P * E.block(ZDim * i, 0).transpose() * Fi)); // upper triangular part of the hessian - for (size_t j = i + 1; j < numKeys; j++) { // for each camera + for (size_t j = i + 1; j < m; j++) { // for each camera const Matrix2D& Fj = Fblocks.at(j).second; //Key cameraKey_j = this->keys_[j]; @@ -461,7 +460,7 @@ public: } } // end of for over cameras - augmentedHessian(aug_numKeys, aug_numKeys)(0, 0) += f; + augmentedHessian(aug_m, aug_m)(0, 0) += f; } /** @@ -474,8 +473,6 @@ public: SymmetricBlockMatrix& augmentedHessian, const FastVector allKeys) const { - // int numKeys = this->keys_.size(); - std::vector Fblocks; Matrix E; Vector b; From d732a01e0376351b340f37828173ee3703b8c550 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 15:03:18 +0100 Subject: [PATCH 12/18] Convert to static functions --- gtsam/slam/SmartFactorBase.h | 43 ++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/gtsam/slam/SmartFactorBase.h b/gtsam/slam/SmartFactorBase.h index 9a53957a5..9542a4067 100644 --- a/gtsam/slam/SmartFactorBase.h +++ b/gtsam/slam/SmartFactorBase.h @@ -356,15 +356,15 @@ public: * Do Schur complement, given Jacobian as F,E,P, return SymmetricBlockMatrix * Fast version - works on with sparsity */ - void sparseSchurComplement(const std::vector& Fblocks, + static void sparseSchurComplement(const std::vector& Fblocks, const Matrix& E, const Matrix3& P /*Point Covariance*/, const Vector& b, - /*output ->*/SymmetricBlockMatrix& augmentedHessian) const { + /*output ->*/SymmetricBlockMatrix& augmentedHessian) { // Schur complement trick // G = F' * F - F' * E * P * E' * F // g = F' * (b - E * P * E' * b) // a single point is observed in m cameras - size_t m = this->keys_.size(); + size_t m = Fblocks.size(); // Blockwise Schur complement for (size_t i = 0; i < m; i++) { // for each camera @@ -395,23 +395,20 @@ public: * Applies Schur complement (exploiting block structure) to get a smart factor on cameras, * and adds the contribution of the smart factor to a pre-allocated augmented Hessian. */ - void updateSparseSchurComplement(const std::vector& Fblocks, - const Matrix& E, const Matrix3& P /*Point Covariance*/, const Vector& b, - const double f, const FastVector allKeys, - /*output ->*/SymmetricBlockMatrix& augmentedHessian) const { + static void updateSparseSchurComplement( + const std::vector& Fblocks, const Matrix& E, + const Matrix3& P /*Point Covariance*/, const Vector& b, const double f, + const FastVector& keys, const FastMap& KeySlotMap, + /*output ->*/SymmetricBlockMatrix& augmentedHessian) { // Schur complement trick - // Gs = F' * F - F' * E * P * E' * F - // gs = F' * (b - E * P * E' * b) + // G = F' * F - F' * E * P * E' * F + // g = F' * (b - E * P * E' * b) MatrixDD matrixBlock; typedef SymmetricBlockMatrix::Block Block; ///< A block from the Hessian matrix - FastMap KeySlotMap; - for (size_t slot = 0; slot < allKeys.size(); slot++) - KeySlotMap.insert(std::make_pair(allKeys[slot], slot)); - // a single point is observed in m cameras - size_t m = this->keys_.size(); // cameras observing current point + size_t m = Fblocks.size(); // cameras observing current point size_t aug_m = (augmentedHessian.rows() - 1) / Dim; // all cameras in the group // Blockwise Schur complement @@ -424,7 +421,7 @@ public: // allKeys are the list of all camera keys in the group, e.g, (1,3,4,5,7) // we should map those to a slot in the local (grouped) hessian (0,1,2,3,4) // Key cameraKey_i = this->keys_[i]; - DenseIndex aug_i = KeySlotMap[this->keys_[i]]; + DenseIndex aug_i = KeySlotMap.at(keys[i]); // information vector - store previous vector // vectorBlock = augmentedHessian(aug_i, aug_m).knownOffDiagonal(); @@ -447,7 +444,7 @@ public: const Matrix2D& Fj = Fblocks.at(j).second; //Key cameraKey_j = this->keys_[j]; - DenseIndex aug_j = KeySlotMap[this->keys_[j]]; + DenseIndex aug_j = KeySlotMap.at(keys[j]); // (DxD) = (DxZDim) * ( (ZDimxZDim) * (ZDimxD) ) // off diagonal block - store previous block @@ -463,6 +460,20 @@ public: augmentedHessian(aug_m, aug_m)(0, 0) += f; } + /** + * Applies Schur complement (exploiting block structure) to get a smart factor on cameras, + * and adds the contribution of the smart factor to a pre-allocated augmented Hessian. + */ + void updateSparseSchurComplement(const std::vector& Fblocks, + const Matrix& E, const Matrix3& P /*Point Covariance*/, const Vector& b, + const double f, const FastVector& allKeys, + /*output ->*/SymmetricBlockMatrix& augmentedHessian) const { + FastMap KeySlotMap; + for (size_t slot = 0; slot < allKeys.size(); slot++) + KeySlotMap.insert(std::make_pair(allKeys[slot], slot)); + updateSparseSchurComplement(Fblocks, E, P, b, f, augmentedHessian); + } + /** * Add the contribution of the smart factor to a pre-allocated Hessian, * using sparse linear algebra. More efficient than the creation of the From e30919bc2755371aac5d494c0678619828e763f6 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 15:42:21 +0100 Subject: [PATCH 13/18] Moved static functions here, templated on measurement dimension Z --- .cproject | 24 +--- gtsam/slam/RegularImplicitSchurFactor.h | 164 ++++++++++++++++++++---- 2 files changed, 144 insertions(+), 44 deletions(-) diff --git a/.cproject b/.cproject index 94e8c3a50..7111b0a6b 100644 --- a/.cproject +++ b/.cproject @@ -1309,6 +1309,7 @@ make + testSimulated2DOriented.run true false @@ -1348,6 +1349,7 @@ make + testSimulated2D.run true false @@ -1355,6 +1357,7 @@ make + testSimulated3D.run true false @@ -1458,7 +1461,6 @@ make - testErrors.run true false @@ -1536,10 +1538,10 @@ true true - + make - -j5 - testImplicitSchurFactor.run + -j4 + testRegularImplicitSchurFactor.run true true true @@ -1793,7 +1795,6 @@ cpack - -G DEB true false @@ -1801,7 +1802,6 @@ cpack - -G RPM true false @@ -1809,7 +1809,6 @@ cpack - -G TGZ true false @@ -1817,7 +1816,6 @@ cpack - --config CPackSourceConfig.cmake true false @@ -2009,6 +2007,7 @@ make + tests/testGaussianISAM2 true false @@ -2160,7 +2159,6 @@ make - tests/testBayesTree.run true false @@ -2168,7 +2166,6 @@ make - testBinaryBayesNet.run true false @@ -2216,7 +2213,6 @@ make - testSymbolicBayesNet.run true false @@ -2224,7 +2220,6 @@ make - tests/testSymbolicFactor.run true false @@ -2232,7 +2227,6 @@ make - testSymbolicFactorGraph.run true false @@ -2248,7 +2242,6 @@ make - tests/testBayesTree true false @@ -3392,7 +3385,6 @@ make - testGraph.run true false @@ -3400,7 +3392,6 @@ make - testJunctionTree.run true false @@ -3408,7 +3399,6 @@ make - testSymbolicBayesNetB.run true false diff --git a/gtsam/slam/RegularImplicitSchurFactor.h b/gtsam/slam/RegularImplicitSchurFactor.h index 731db479f..24033678d 100644 --- a/gtsam/slam/RegularImplicitSchurFactor.h +++ b/gtsam/slam/RegularImplicitSchurFactor.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -17,7 +18,7 @@ namespace gtsam { /** * RegularImplicitSchurFactor */ -template // +template // class RegularImplicitSchurFactor: public GaussianFactor { public: @@ -26,12 +27,12 @@ public: protected: - typedef Eigen::Matrix Matrix2D; ///< type of an F block + typedef Eigen::Matrix Matrix2D; ///< type of an F block typedef Eigen::Matrix MatrixDD; ///< camera hessian typedef std::pair KeyMatrix2D; ///< named F block - const std::vector Fblocks_; ///< All 2*D F blocks (one for each camera) - const Matrix3 PointCovariance_; ///< the 3*3 matrix P = inv(E'E) (2*2 if degenerate) + const std::vector Fblocks_; ///< All Z*D F blocks (one for each camera) + const Matrix3 PointCovariance_; ///< the 3*3 matrix P = inv(E'E) (Z*Z if degenerate) const Matrix E_; ///< The 2m*3 E Jacobian with respect to the point const Vector b_; ///< 2m-dimensional RHS vector @@ -122,6 +123,115 @@ public: "RegularImplicitSchurFactor::jacobian non implemented"); return std::make_pair(Matrix(), Vector()); } + + /** + * Do Schur complement, given Jacobian as F,E,P, return SymmetricBlockMatrix + * Fast version - works on with sparsity + */ + static void sparseSchurComplement(const std::vector& Fblocks, + const Matrix& E, const Matrix3& P /*Point Covariance*/, const Vector& b, + /*output ->*/SymmetricBlockMatrix& augmentedHessian) { + // Schur complement trick + // G = F' * F - F' * E * P * E' * F + // g = F' * (b - E * P * E' * b) + + // a single point is observed in m cameras + size_t m = Fblocks.size(); + + // Blockwise Schur complement + for (size_t i = 0; i < m; i++) { // for each camera + + const Matrix2D& Fi = Fblocks.at(i).second; + const Matrix23 Ei_P = E.block(Z * i, 0) * P; + + // D = (Dx2) * (Z) + augmentedHessian(i, m) = Fi.transpose() * b.segment(Z * i) // F' * b + - Fi.transpose() * (Ei_P * (E.transpose() * b)); // D = (DxZDim) * (ZDimx3) * (3*ZDimm) * (ZDimm x 1) + + // (DxD) = (DxZDim) * ( (ZDimxD) - (ZDimx3) * (3xZDim) * (ZDimxD) ) + augmentedHessian(i, i) = Fi.transpose() + * (Fi - Ei_P * E.block(Z * i, 0).transpose() * Fi); + + // upper triangular part of the hessian + for (size_t j = i + 1; j < m; j++) { // for each camera + const Matrix2D& Fj = Fblocks.at(j).second; + + // (DxD) = (Dx2) * ( (2x2) * (2xD) ) + augmentedHessian(i, j) = -Fi.transpose() + * (Ei_P * E.block(Z * j, 0).transpose() * Fj); + } + } // end of for over cameras + } + + /** + * Applies Schur complement (exploiting block structure) to get a smart factor on cameras, + * and adds the contribution of the smart factor to a pre-allocated augmented Hessian. + */ + static void updateSparseSchurComplement( + const std::vector& Fblocks, const Matrix& E, + const Matrix3& P /*Point Covariance*/, const Vector& b, const double f, + const FastVector& keys, const FastMap& KeySlotMap, + /*output ->*/SymmetricBlockMatrix& augmentedHessian) { + // Schur complement trick + // G = F' * F - F' * E * P * E' * F + // g = F' * (b - E * P * E' * b) + + MatrixDD matrixBlock; + typedef SymmetricBlockMatrix::Block Block; ///< A block from the Hessian matrix + + // a single point is observed in m cameras + size_t m = Fblocks.size(); // cameras observing current point + size_t aug_m = (augmentedHessian.rows() - 1) / D; // all cameras in the group + + // Blockwise Schur complement + for (size_t i = 0; i < m; i++) { // for each camera in the current factor + + const Matrix2D& Fi = Fblocks.at(i).second; + const Matrix23 Ei_P = E.block(Z * i, 0) * P; + + // D = (DxZDim) * (Z) + // allKeys are the list of all camera keys in the group, e.g, (1,3,4,5,7) + // we should map those to a slot in the local (grouped) hessian (0,1,2,3,4) + // Key cameraKey_i = this->keys_[i]; + DenseIndex aug_i = KeySlotMap.at(keys[i]); + + // information vector - store previous vector + // vectorBlock = augmentedHessian(aug_i, aug_m).knownOffDiagonal(); + // add contribution of current factor + augmentedHessian(aug_i, aug_m) = + augmentedHessian(aug_i, aug_m).knownOffDiagonal() + + Fi.transpose() * b.segment(Z * i) // F' * b + - Fi.transpose() * (Ei_P * (E.transpose() * b)); // D = (DxZDim) * (ZDimx3) * (3*ZDimm) * (ZDimm x 1) + + // (DxD) = (DxZDim) * ( (ZDimxD) - (ZDimx3) * (3xZDim) * (ZDimxD) ) + // main block diagonal - store previous block + matrixBlock = augmentedHessian(aug_i, aug_i); + // add contribution of current factor + augmentedHessian(aug_i, aug_i) = matrixBlock + + (Fi.transpose() + * (Fi - Ei_P * E.block(Z * i, 0).transpose() * Fi)); + + // upper triangular part of the hessian + for (size_t j = i + 1; j < m; j++) { // for each camera + const Matrix2D& Fj = Fblocks.at(j).second; + + //Key cameraKey_j = this->keys_[j]; + DenseIndex aug_j = KeySlotMap.at(keys[j]); + + // (DxD) = (DxZDim) * ( (ZDimxZDim) * (ZDimxD) ) + // off diagonal block - store previous block + // matrixBlock = augmentedHessian(aug_i, aug_j).knownOffDiagonal(); + // add contribution of current factor + augmentedHessian(aug_i, aug_j) = + augmentedHessian(aug_i, aug_j).knownOffDiagonal() + - Fi.transpose() + * (Ei_P * E.block(Z * j, 0).transpose() * Fj); + } + } // end of for over cameras + + augmentedHessian(aug_m, aug_m)(0, 0) += f; + } + virtual Matrix augmentedInformation() const { throw std::runtime_error( "RegularImplicitSchurFactor::augmentedInformation non implemented"); @@ -142,10 +252,10 @@ public: Key j = keys_[pos]; // Calculate Fj'*Ej for the current camera (observing a single point) - // D x 3 = (D x 2) * (2 x 3) + // D x 3 = (D x Z) * (Z x 3) const Matrix2D& Fj = Fblocks_[pos].second; Eigen::Matrix FtE = Fj.transpose() - * E_.block<2, 3>(2 * pos, 0); + * E_.block(Z * pos, 0); Eigen::Matrix dj; for (size_t k = 0; k < D; ++k) { // for each diagonal element of the camera hessian @@ -174,10 +284,10 @@ public: Key j = keys_[pos]; // Calculate Fj'*Ej for the current camera (observing a single point) - // D x 3 = (D x 2) * (2 x 3) + // D x 3 = (D x Z) * (Z x 3) const Matrix2D& Fj = Fblocks_[pos].second; Eigen::Matrix FtE = Fj.transpose() - * E_.block<2, 3>(2 * pos, 0); + * E_.block(Z * pos, 0); DVector dj; for (size_t k = 0; k < D; ++k) { // for each diagonal element of the camera hessian @@ -195,28 +305,28 @@ public: // F'*(I - E*P*E')*F for (size_t pos = 0; pos < size(); ++pos) { Key j = keys_[pos]; - // F'*F - F'*E*P*E'*F (9*2)*(2*9) - (9*2)*(2*3)*(3*3)*(3*2)*(2*9) + // F'*F - F'*E*P*E'*F e.g. (9*2)*(2*9) - (9*2)*(2*3)*(3*3)*(3*2)*(2*9) const Matrix2D& Fj = Fblocks_[pos].second; // Eigen::Matrix FtE = Fj.transpose() - // * E_.block<2, 3>(2 * pos, 0); + // * E_.block(Z * pos, 0); // blocks[j] = Fj.transpose() * Fj // - FtE * PointCovariance_ * FtE.transpose(); - const Matrix23& Ej = E_.block<2, 3>(2 * pos, 0); + const Matrix23& Ej = E_.block(Z * pos, 0); blocks[j] = Fj.transpose() * (Fj - Ej * PointCovariance_ * Ej.transpose() * Fj); // F'*(I - E*P*E')*F, TODO: this should work, but it does not :-( - // static const Eigen::Matrix I2 = eye(2); + // static const Eigen::Matrix I2 = eye(Z); // Matrix2 Q = // - // I2 - E_.block<2, 3>(2 * pos, 0) * PointCovariance_ * E_.block<2, 3>(2 * pos, 0).transpose(); + // I2 - E_.block(Z * pos, 0) * PointCovariance_ * E_.block(Z * pos, 0).transpose(); // blocks[j] = Fj.transpose() * Q * Fj; } return blocks; } virtual GaussianFactor::shared_ptr clone() const { - return boost::make_shared >(Fblocks_, + return boost::make_shared >(Fblocks_, PointCovariance_, E_, b_); throw std::runtime_error( "RegularImplicitSchurFactor::clone non implemented"); @@ -226,7 +336,7 @@ public: } virtual GaussianFactor::shared_ptr negate() const { - return boost::make_shared >(Fblocks_, + return boost::make_shared >(Fblocks_, PointCovariance_, E_, b_); throw std::runtime_error( "RegularImplicitSchurFactor::negate non implemented"); @@ -247,23 +357,23 @@ public: typedef std::vector Error2s; /** - * @brief Calculate corrected error Q*(e-2*b) = (I - E*P*E')*(e-2*b) + * @brief Calculate corrected error Q*(e-Z*b) = (I - E*P*E')*(e-Z*b) */ void projectError2(const Error2s& e1, Error2s& e2) const { - // d1 = E.transpose() * (e1-2*b) = (3*2m)*2m + // d1 = E.transpose() * (e1-Z*b) = (3*2m)*2m Vector3 d1; d1.setZero(); for (size_t k = 0; k < size(); k++) - d1 += E_.block<2, 3>(2 * k, 0).transpose() - * (e1[k] - 2 * b_.segment<2>(k * 2)); + d1 += E_.block(Z * k, 0).transpose() + * (e1[k] - Z * b_.segment(k * Z)); // d2 = E.transpose() * e1 = (3*2m)*2m Vector3 d2 = PointCovariance_ * d1; // e3 = alpha*(e1 - E*d2) = 1*[2m-(2m*3)*3] for (size_t k = 0; k < size(); k++) - e2[k] = e1[k] - 2 * b_.segment<2>(k * 2) - E_.block<2, 3>(2 * k, 0) * d2; + e2[k] = e1[k] - Z * b_.segment(k * Z) - E_.block(Z * k, 0) * d2; } /* @@ -305,7 +415,7 @@ public: // e1 = F * x - b = (2m*dm)*dm for (size_t k = 0; k < size(); ++k) - e1[k] = Fblocks_[k].second * x.at(keys_[k]) - b_.segment<2>(k * 2); + e1[k] = Fblocks_[k].second * x.at(keys_[k]) - b_.segment(k * Z); projectError(e1, e2); double result = 0; @@ -324,14 +434,14 @@ public: Vector3 d1; d1.setZero(); for (size_t k = 0; k < size(); k++) - d1 += E_.block<2, 3>(2 * k, 0).transpose() * e1[k]; + d1 += E_.block(Z * k, 0).transpose() * e1[k]; // d2 = E.transpose() * e1 = (3*2m)*2m Vector3 d2 = PointCovariance_ * d1; // e3 = alpha*(e1 - E*d2) = 1*[2m-(2m*3)*3] for (size_t k = 0; k < size(); k++) - e2[k] = e1[k] - E_.block<2, 3>(2 * k, 0) * d2; + e2[k] = e1[k] - E_.block(Z * k, 0) * d2; } /// Scratch space for multiplyHessianAdd @@ -426,7 +536,7 @@ public: e1.resize(size()); e2.resize(size()); for (size_t k = 0; k < size(); k++) - e1[k] = b_.segment<2>(2 * k); + e1[k] = b_.segment(Z * k); projectError(e1, e2); // g = F.transpose()*e2 @@ -453,7 +563,7 @@ public: e1.resize(size()); e2.resize(size()); for (size_t k = 0; k < size(); k++) - e1[k] = b_.segment<2>(2 * k); + e1[k] = b_.segment(Z * k); projectError(e1, e2); for (size_t k = 0; k < size(); ++k) { // for each camera in the factor @@ -472,8 +582,8 @@ public: // end class RegularImplicitSchurFactor // traits -template struct traits > : public Testable< - RegularImplicitSchurFactor > { +template struct traits > : public Testable< + RegularImplicitSchurFactor > { }; } From 94c70dd7bced2b920cbb1800ea98f54c18bcbb0a Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 15:43:53 +0100 Subject: [PATCH 14/18] Now a function --- gtsam/geometry/tests/testPinholeSet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtsam/geometry/tests/testPinholeSet.cpp b/gtsam/geometry/tests/testPinholeSet.cpp index 5de2b5f4d..1e5426f33 100644 --- a/gtsam/geometry/tests/testPinholeSet.cpp +++ b/gtsam/geometry/tests/testPinholeSet.cpp @@ -129,7 +129,7 @@ TEST(PinholeSet, Pinhole) { // Instantiate triangulateSafe TriangulationParameters params; TriangulationResult actual = set.triangulateSafe(z,params); - CHECK(actual.degenerate); + CHECK(actual.degenerate()); } /* ************************************************************************* */ From bb58814f1c0f58ac0b9baf2dfc50ccf15fd0ee4b Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 15:48:11 +0100 Subject: [PATCH 15/18] Moved static functions to RegularImplicit and now use templates --- gtsam/slam/SmartFactorBase.h | 120 ++--------------------------------- 1 file changed, 7 insertions(+), 113 deletions(-) diff --git a/gtsam/slam/SmartFactorBase.h b/gtsam/slam/SmartFactorBase.h index 9542a4067..1a78f1050 100644 --- a/gtsam/slam/SmartFactorBase.h +++ b/gtsam/slam/SmartFactorBase.h @@ -352,114 +352,6 @@ public: augmentedHessian); } - /** - * Do Schur complement, given Jacobian as F,E,P, return SymmetricBlockMatrix - * Fast version - works on with sparsity - */ - static void sparseSchurComplement(const std::vector& Fblocks, - const Matrix& E, const Matrix3& P /*Point Covariance*/, const Vector& b, - /*output ->*/SymmetricBlockMatrix& augmentedHessian) { - // Schur complement trick - // G = F' * F - F' * E * P * E' * F - // g = F' * (b - E * P * E' * b) - - // a single point is observed in m cameras - size_t m = Fblocks.size(); - - // Blockwise Schur complement - for (size_t i = 0; i < m; i++) { // for each camera - - const Matrix2D& Fi = Fblocks.at(i).second; - const Matrix23 Ei_P = E.block(ZDim * i, 0) * P; - - // Dim = (Dx2) * (2) - augmentedHessian(i, m) = Fi.transpose() * b.segment(ZDim * i) // F' * b - - Fi.transpose() * (Ei_P * (E.transpose() * b)); // Dim = (DxZDim) * (ZDimx3) * (3*ZDimm) * (ZDimm x 1) - - // (DxD) = (DxZDim) * ( (ZDimxD) - (ZDimx3) * (3xZDim) * (ZDimxD) ) - augmentedHessian(i, i) = Fi.transpose() - * (Fi - Ei_P * E.block(ZDim * i, 0).transpose() * Fi); - - // upper triangular part of the hessian - for (size_t j = i + 1; j < m; j++) { // for each camera - const Matrix2D& Fj = Fblocks.at(j).second; - - // (DxD) = (Dx2) * ( (2x2) * (2xD) ) - augmentedHessian(i, j) = -Fi.transpose() - * (Ei_P * E.block(ZDim * j, 0).transpose() * Fj); - } - } // end of for over cameras - } - - /** - * Applies Schur complement (exploiting block structure) to get a smart factor on cameras, - * and adds the contribution of the smart factor to a pre-allocated augmented Hessian. - */ - static void updateSparseSchurComplement( - const std::vector& Fblocks, const Matrix& E, - const Matrix3& P /*Point Covariance*/, const Vector& b, const double f, - const FastVector& keys, const FastMap& KeySlotMap, - /*output ->*/SymmetricBlockMatrix& augmentedHessian) { - // Schur complement trick - // G = F' * F - F' * E * P * E' * F - // g = F' * (b - E * P * E' * b) - - MatrixDD matrixBlock; - typedef SymmetricBlockMatrix::Block Block; ///< A block from the Hessian matrix - - // a single point is observed in m cameras - size_t m = Fblocks.size(); // cameras observing current point - size_t aug_m = (augmentedHessian.rows() - 1) / Dim; // all cameras in the group - - // Blockwise Schur complement - for (size_t i = 0; i < m; i++) { // for each camera in the current factor - - const Matrix2D& Fi = Fblocks.at(i).second; - const Matrix23 Ei_P = E.block(ZDim * i, 0) * P; - - // Dim = (DxZDim) * (ZDim) - // allKeys are the list of all camera keys in the group, e.g, (1,3,4,5,7) - // we should map those to a slot in the local (grouped) hessian (0,1,2,3,4) - // Key cameraKey_i = this->keys_[i]; - DenseIndex aug_i = KeySlotMap.at(keys[i]); - - // information vector - store previous vector - // vectorBlock = augmentedHessian(aug_i, aug_m).knownOffDiagonal(); - // add contribution of current factor - augmentedHessian(aug_i, aug_m) = - augmentedHessian(aug_i, aug_m).knownOffDiagonal() - + Fi.transpose() * b.segment(ZDim * i) // F' * b - - Fi.transpose() * (Ei_P * (E.transpose() * b)); // Dim = (DxZDim) * (ZDimx3) * (3*ZDimm) * (ZDimm x 1) - - // (DxD) = (DxZDim) * ( (ZDimxD) - (ZDimx3) * (3xZDim) * (ZDimxD) ) - // main block diagonal - store previous block - matrixBlock = augmentedHessian(aug_i, aug_i); - // add contribution of current factor - augmentedHessian(aug_i, aug_i) = matrixBlock - + (Fi.transpose() - * (Fi - Ei_P * E.block(ZDim * i, 0).transpose() * Fi)); - - // upper triangular part of the hessian - for (size_t j = i + 1; j < m; j++) { // for each camera - const Matrix2D& Fj = Fblocks.at(j).second; - - //Key cameraKey_j = this->keys_[j]; - DenseIndex aug_j = KeySlotMap.at(keys[j]); - - // (DxD) = (DxZDim) * ( (ZDimxZDim) * (ZDimxD) ) - // off diagonal block - store previous block - // matrixBlock = augmentedHessian(aug_i, aug_j).knownOffDiagonal(); - // add contribution of current factor - augmentedHessian(aug_i, aug_j) = - augmentedHessian(aug_i, aug_j).knownOffDiagonal() - - Fi.transpose() - * (Ei_P * E.block(ZDim * j, 0).transpose() * Fj); - } - } // end of for over cameras - - augmentedHessian(aug_m, aug_m)(0, 0) += f; - } - /** * Applies Schur complement (exploiting block structure) to get a smart factor on cameras, * and adds the contribution of the smart factor to a pre-allocated augmented Hessian. @@ -471,7 +363,8 @@ public: FastMap KeySlotMap; for (size_t slot = 0; slot < allKeys.size(); slot++) KeySlotMap.insert(std::make_pair(allKeys[slot], slot)); - updateSparseSchurComplement(Fblocks, E, P, b, f, augmentedHessian); + RegularImplicitSchurFactor::updateSparseSchurComplement(Fblocks, E, P, + b, f, this->keys_, KeySlotMap, augmentedHessian); } /** @@ -504,16 +397,17 @@ public: /** * Return Jacobians as RegularImplicitSchurFactor with raw access */ - boost::shared_ptr > createRegularImplicitSchurFactor( - const Cameras& cameras, const Point3& point, double lambda = 0.0, - bool diagonalDamping = false) const { + boost::shared_ptr > // + createRegularImplicitSchurFactor(const Cameras& cameras, const Point3& point, + double lambda = 0.0, bool diagonalDamping = false) const { std::vector F; Matrix E; Vector b; computeJacobians(F, E, b, cameras, point); whitenJacobians(F, E, b); Matrix3 P = PointCov(E, lambda, diagonalDamping); - return boost::make_shared >(F, E, P, b); + return boost::make_shared >(F, E, P, + b); } /** From 05fcbe6556ae7b72b4fff099990a5cbfbd140cf2 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 1 Mar 2015 17:15:42 +0100 Subject: [PATCH 16/18] Augmented and regular information now implemented --- gtsam/slam/RegularImplicitSchurFactor.h | 33 ++++++++++++++----- .../tests/testRegularImplicitSchurFactor.cpp | 12 +++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/gtsam/slam/RegularImplicitSchurFactor.h b/gtsam/slam/RegularImplicitSchurFactor.h index 24033678d..88fb4b6e1 100644 --- a/gtsam/slam/RegularImplicitSchurFactor.h +++ b/gtsam/slam/RegularImplicitSchurFactor.h @@ -18,7 +18,7 @@ namespace gtsam { /** * RegularImplicitSchurFactor */ -template // +template // class RegularImplicitSchurFactor: public GaussianFactor { public: @@ -170,8 +170,12 @@ public: static void updateSparseSchurComplement( const std::vector& Fblocks, const Matrix& E, const Matrix3& P /*Point Covariance*/, const Vector& b, const double f, - const FastVector& keys, const FastMap& KeySlotMap, + const FastVector& allKeys, const FastVector& keys, /*output ->*/SymmetricBlockMatrix& augmentedHessian) { + + FastMap KeySlotMap; + for (size_t slot = 0; slot < allKeys.size(); slot++) + KeySlotMap.insert(std::make_pair(allKeys[slot], slot)); // Schur complement trick // G = F' * F - F' * E * P * E' * F // g = F' * (b - E * P * E' * b) @@ -232,15 +236,28 @@ public: augmentedHessian(aug_m, aug_m)(0, 0) += f; } + /// *Compute* full augmented information matrix virtual Matrix augmentedInformation() const { - throw std::runtime_error( - "RegularImplicitSchurFactor::augmentedInformation non implemented"); - return Matrix(); + + // Create a SymmetricBlockMatrix + int m = this->keys_.size(); + size_t M1 = D * m + 1; + std::vector dims(m + 1); // this also includes the b term + std::fill(dims.begin(), dims.end() - 1, D); + dims.back() = 1; + SymmetricBlockMatrix augmentedHessian(dims, Matrix::Zero(M1, M1)); + + // Do the Schur complement + sparseSchurComplement(Fblocks_, E_, PointCovariance_, b_, augmentedHessian); + return augmentedHessian.matrix(); } + + /// *Compute* full information matrix virtual Matrix information() const { - throw std::runtime_error( - "RegularImplicitSchurFactor::information non implemented"); - return Matrix(); + Matrix augmented = augmentedInformation(); + int m = this->keys_.size(); + size_t M = D * m; + return augmented.block(0,0,M,M); } /// Return the diagonal of the Hessian for this factor diff --git a/gtsam/slam/tests/testRegularImplicitSchurFactor.cpp b/gtsam/slam/tests/testRegularImplicitSchurFactor.cpp index 8571a345d..3575a0286 100644 --- a/gtsam/slam/tests/testRegularImplicitSchurFactor.cpp +++ b/gtsam/slam/tests/testRegularImplicitSchurFactor.cpp @@ -255,6 +255,18 @@ TEST(regularImplicitSchurFactor, hessianDiagonal) EXPECT(assert_equal(F0t*(I2-E0*P*E0.transpose())*F0,actualBD[0])); EXPECT(assert_equal(F1.transpose()*F1-FtE1*P*FtE1.transpose(),actualBD[1])); EXPECT(assert_equal(F3.transpose()*F3-FtE3*P*FtE3.transpose(),actualBD[3])); + + // augmentedInformation (test just checks diagonals) + Matrix actualInfo = factor.augmentedInformation(); + EXPECT(assert_equal(actualBD[0],actualInfo.block<6,6>(0,0))); + EXPECT(assert_equal(actualBD[1],actualInfo.block<6,6>(6,6))); + EXPECT(assert_equal(actualBD[3],actualInfo.block<6,6>(12,12))); + + // information (test just checks diagonals) + Matrix actualInfo2 = factor.information(); + EXPECT(assert_equal(actualBD[0],actualInfo2.block<6,6>(0,0))); + EXPECT(assert_equal(actualBD[1],actualInfo2.block<6,6>(6,6))); + EXPECT(assert_equal(actualBD[3],actualInfo2.block<6,6>(12,12))); } /* ************************************************************************* */ From 11a6ba412ca1eb0d5abc6db96d31a447f081a7b4 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 2 Mar 2015 08:52:35 -0800 Subject: [PATCH 17/18] Smore more refactoring to RegularImplicit.. --- gtsam/slam/SmartFactorBase.h | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/gtsam/slam/SmartFactorBase.h b/gtsam/slam/SmartFactorBase.h index 1a78f1050..d1c0d76e2 100644 --- a/gtsam/slam/SmartFactorBase.h +++ b/gtsam/slam/SmartFactorBase.h @@ -337,14 +337,14 @@ public: double f = computeJacobians(Fblocks, E, b, cameras, point); Matrix3 P = PointCov(E, lambda, diagonalDamping); - // we create directly a SymmetricBlockMatrix + // Create a SymmetricBlockMatrix size_t M1 = Dim * m + 1; std::vector dims(m + 1); // this also includes the b term std::fill(dims.begin(), dims.end() - 1, Dim); dims.back() = 1; + SymmetricBlockMatrix augmentedHessian(dims, Matrix::Zero(M1, M1)); // build augmented hessian - SymmetricBlockMatrix augmentedHessian(dims, Matrix::Zero(M1, M1)); sparseSchurComplement(Fblocks, E, P, b, augmentedHessian); augmentedHessian(m, m)(0, 0) = f; @@ -352,21 +352,6 @@ public: augmentedHessian); } - /** - * Applies Schur complement (exploiting block structure) to get a smart factor on cameras, - * and adds the contribution of the smart factor to a pre-allocated augmented Hessian. - */ - void updateSparseSchurComplement(const std::vector& Fblocks, - const Matrix& E, const Matrix3& P /*Point Covariance*/, const Vector& b, - const double f, const FastVector& allKeys, - /*output ->*/SymmetricBlockMatrix& augmentedHessian) const { - FastMap KeySlotMap; - for (size_t slot = 0; slot < allKeys.size(); slot++) - KeySlotMap.insert(std::make_pair(allKeys[slot], slot)); - RegularImplicitSchurFactor::updateSparseSchurComplement(Fblocks, E, P, - b, f, this->keys_, KeySlotMap, augmentedHessian); - } - /** * Add the contribution of the smart factor to a pre-allocated Hessian, * using sparse linear algebra. More efficient than the creation of the @@ -376,13 +361,13 @@ public: const double lambda, bool diagonalDamping, SymmetricBlockMatrix& augmentedHessian, const FastVector allKeys) const { - - std::vector Fblocks; Matrix E; Vector b; + std::vector Fblocks; double f = computeJacobians(Fblocks, E, b, cameras, point); Matrix3 P = PointCov(E, lambda, diagonalDamping); - updateSparseSchurComplement(Fblocks, E, P, b, f, allKeys, augmentedHessian); // augmentedHessian.matrix().block (i,j) = ... + RegularImplicitSchurFactor::updateSparseSchurComplement(Fblocks, + E, P, b, f, allKeys, keys_, augmentedHessian); } /// Whiten the Jacobians computed by computeJacobians using noiseModel_ @@ -400,9 +385,9 @@ public: boost::shared_ptr > // createRegularImplicitSchurFactor(const Cameras& cameras, const Point3& point, double lambda = 0.0, bool diagonalDamping = false) const { - std::vector F; Matrix E; Vector b; + std::vector F; computeJacobians(F, E, b, cameras, point); whitenJacobians(F, E, b); Matrix3 P = PointCov(E, lambda, diagonalDamping); @@ -416,12 +401,11 @@ public: boost::shared_ptr > createJacobianQFactor( const Cameras& cameras, const Point3& point, double lambda = 0.0, bool diagonalDamping = false) const { - std::vector F; Matrix E; Vector b; + std::vector F; computeJacobians(F, E, b, cameras, point); const size_t M = b.size(); - std::cout << M << std::endl; Matrix3 P = PointCov(E, lambda, diagonalDamping); SharedIsotropic n = noiseModel::Isotropic::Sigma(M, noiseModel_->sigma()); return boost::make_shared >(F, E, P, b, n); From ca18c13cd2da1a46a46fea872cc9a71abe647621 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 2 Mar 2015 08:53:07 -0800 Subject: [PATCH 18/18] Most factors now work out for sigma=1 and sigma=0.1 --- .../tests/testSmartProjectionPoseFactor.cpp | 86 ++++++++++--------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp b/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp index b0c310a36..fae17b9a2 100644 --- a/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp +++ b/gtsam/slam/tests/testSmartProjectionPoseFactor.cpp @@ -39,7 +39,7 @@ static const bool manageDegeneracy = true; // Create a noise model for the pixel error static const double sigma = 0.1; -static SharedNoiseModel model(noiseModel::Isotropic::Sigma(2, sigma)); +static SharedIsotropic model(noiseModel::Isotropic::Sigma(2, sigma)); // Convenience for named keys using symbol_shorthand::X; @@ -299,8 +299,10 @@ TEST( SmartProjectionPoseFactor, Factors ) { // After eliminating the point, A1 and A2 contain 2-rank information on cameras: Matrix16 A1, A2; - A1 << -1000, 0, 0, 0, 100, 0; - A2 << 1000, 0, 100, 0, -100, 0; + A1 << -10, 0, 0, 0, 1, 0; + A2 << 10, 0, 1, 0, -1, 0; + A1 *= 10. / sigma; + A2 *= 10. / sigma; Matrix expectedInformation; // filled below { // createHessianFactor @@ -339,21 +341,38 @@ TEST( SmartProjectionPoseFactor, Factors ) { F2(1, 0) = 100; F2(1, 2) = 10; F2(1, 4) = -10; - Matrix43 E; + Matrix E(4, 3); E.setZero(); - E(0, 0) = 100; - E(1, 1) = 100; - E(2, 0) = 100; - E(2, 2) = 10; - E(3, 1) = 100; - const vector > Fblocks = list_of > // + E(0, 0) = 10; + E(1, 1) = 10; + E(2, 0) = 10; + E(2, 2) = 1; + E(3, 1) = 10; + vector > Fblocks = list_of > // (make_pair(x1, F1))(make_pair(x2, F2)); - Matrix3 P = (E.transpose() * E).inverse(); - Vector4 b; + Vector b(4); b.setZero(); + // createJacobianQFactor + SharedIsotropic n = noiseModel::Isotropic::Sigma(4, sigma); + Matrix3 P = (E.transpose() * E).inverse(); + JacobianFactorQ<6, 2> expectedQ(Fblocks, E, P, b, n); + EXPECT(assert_equal(expectedInformation, expectedQ.information(), 1e-8)); + + boost::shared_ptr > actualQ = + smartFactor1->createJacobianQFactor(cameras, 0.0); + CHECK(actualQ); + EXPECT(assert_equal(expectedInformation, actualQ->information(), 1e-8)); + EXPECT(assert_equal(expectedQ, *actualQ)); + + // Whiten for RegularImplicitSchurFactor (does not have noise model) + model->WhitenSystem(E, b); + Matrix3 whiteP = (E.transpose() * E).inverse(); + BOOST_FOREACH(SmartFactor::KeyMatrix2D& Fblock,Fblocks) + Fblock.second = model->Whiten(Fblock.second); + // createRegularImplicitSchurFactor - RegularImplicitSchurFactor<6> expected(Fblocks, E, P, b); + RegularImplicitSchurFactor<6> expected(Fblocks, E, whiteP, b); boost::shared_ptr > actual = smartFactor1->createRegularImplicitSchurFactor(cameras, 0.0); @@ -361,17 +380,6 @@ TEST( SmartProjectionPoseFactor, Factors ) { EXPECT(assert_equal(expectedInformation, expected.information(), 1e-8)); EXPECT(assert_equal(expectedInformation, actual->information(), 1e-8)); EXPECT(assert_equal(expected, *actual)); - - // createJacobianQFactor - SharedIsotropic n = noiseModel::Isotropic::Sigma(4, sigma); - JacobianFactorQ<6, 2> expectedQ(Fblocks, E, P, b, n); - EXPECT(assert_equal(expectedInformation, expectedQ.information(), 1e-8)); - - boost::shared_ptr > actualQ = - smartFactor1->createJacobianQFactor(cameras, 0.0); - CHECK(actual); - EXPECT(assert_equal(expectedInformation, actualQ->information(), 1e-8)); - EXPECT(assert_equal(expectedQ, *actualQ)); } { @@ -983,7 +991,7 @@ TEST( SmartProjectionPoseFactor, 3poses_rotation_only_smart_projection_factor ) values.insert(x1, cam2); values.insert(x2, cam2); // initialize third pose with some noise, we expect it to move back to original pose_above - values.insert(x3, Camera(pose_above * noise_pose,sharedK)); + values.insert(x3, Camera(pose_above * noise_pose, sharedK)); if (isDebugTest) values.at(x3).print("Smart: Pose3 before optimization: "); @@ -1077,9 +1085,9 @@ TEST( SmartProjectionPoseFactor, HessianWithRotation ) { Pose3 poseDrift = Pose3(Rot3::ypr(-M_PI / 2, 0., -M_PI / 2), Point3(0, 0, 0)); Values rotValues; - rotValues.insert(x1, Camera(poseDrift.compose(level_pose),sharedK)); - rotValues.insert(x2, Camera(poseDrift.compose(pose_right),sharedK)); - rotValues.insert(x3, Camera(poseDrift.compose(pose_above),sharedK)); + rotValues.insert(x1, Camera(poseDrift.compose(level_pose), sharedK)); + rotValues.insert(x2, Camera(poseDrift.compose(pose_right), sharedK)); + rotValues.insert(x3, Camera(poseDrift.compose(pose_above), sharedK)); boost::shared_ptr hessianFactorRot = smartFactorInstance->linearize(rotValues); @@ -1093,9 +1101,9 @@ TEST( SmartProjectionPoseFactor, HessianWithRotation ) { Point3(10, -4, 5)); Values tranValues; - tranValues.insert(x1, Camera(poseDrift2.compose(level_pose),sharedK)); - tranValues.insert(x2, Camera(poseDrift2.compose(pose_right),sharedK)); - tranValues.insert(x3, Camera(poseDrift2.compose(pose_above),sharedK)); + tranValues.insert(x1, Camera(poseDrift2.compose(level_pose), sharedK)); + tranValues.insert(x2, Camera(poseDrift2.compose(pose_right), sharedK)); + tranValues.insert(x3, Camera(poseDrift2.compose(pose_above), sharedK)); boost::shared_ptr hessianFactorRotTran = smartFactorInstance->linearize(tranValues); @@ -1137,9 +1145,9 @@ TEST( SmartProjectionPoseFactor, HessianWithRotationDegenerate ) { Pose3 poseDrift = Pose3(Rot3::ypr(-M_PI / 2, 0., -M_PI / 2), Point3(0, 0, 0)); Values rotValues; - rotValues.insert(x1, Camera(poseDrift.compose(level_pose),sharedK2)); - rotValues.insert(x2, Camera(poseDrift.compose(pose_right),sharedK2)); - rotValues.insert(x3, Camera(poseDrift.compose(pose_above),sharedK2)); + rotValues.insert(x1, Camera(poseDrift.compose(level_pose), sharedK2)); + rotValues.insert(x2, Camera(poseDrift.compose(pose_right), sharedK2)); + rotValues.insert(x3, Camera(poseDrift.compose(pose_above), sharedK2)); boost::shared_ptr hessianFactorRot = smartFactor->linearize( rotValues); @@ -1155,9 +1163,9 @@ TEST( SmartProjectionPoseFactor, HessianWithRotationDegenerate ) { Point3(10, -4, 5)); Values tranValues; - tranValues.insert(x1, Camera(poseDrift2.compose(level_pose),sharedK2)); - tranValues.insert(x2, Camera(poseDrift2.compose(pose_right),sharedK2)); - tranValues.insert(x3, Camera(poseDrift2.compose(pose_above),sharedK2)); + tranValues.insert(x1, Camera(poseDrift2.compose(level_pose), sharedK2)); + tranValues.insert(x2, Camera(poseDrift2.compose(pose_right), sharedK2)); + tranValues.insert(x3, Camera(poseDrift2.compose(pose_above), sharedK2)); boost::shared_ptr hessianFactorRotTran = smartFactor->linearize(tranValues); @@ -1237,7 +1245,7 @@ TEST( SmartProjectionPoseFactor, Cal3Bundler ) { values.insert(x1, cam2); values.insert(x2, cam2); // initialize third pose with some noise, we expect it to move back to original pose_above - values.insert(x3, Camera(pose_above * noise_pose,sharedBundlerK)); + values.insert(x3, Camera(pose_above * noise_pose, sharedBundlerK)); if (isDebugTest) values.at(x3).print("Smart: Pose3 before optimization: "); @@ -1343,7 +1351,7 @@ TEST( SmartProjectionPoseFactor, Cal3BundlerRotationOnly ) { values.insert(x1, cam2); values.insert(x2, cam2); // initialize third pose with some noise, we expect it to move back to original pose_above - values.insert(x3, Camera(pose_above * noise_pose,sharedBundlerK)); + values.insert(x3, Camera(pose_above * noise_pose, sharedBundlerK)); if (isDebugTest) values.at(x3).print("Smart: Pose3 before optimization: ");