gtsam
Loading...
Searching...
No Matches
SmartFactorBase.h
Go to the documentation of this file.
1/* ----------------------------------------------------------------------------
2
3 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
4 * Atlanta, Georgia 30332-0415
5 * All Rights Reserved
6 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7
8 * See LICENSE for the license information
9
10 * -------------------------------------------------------------------------- */
11
21
22#pragma once
23
24#include <gtsam/slam/JacobianFactorQ.h>
25#include <gtsam/slam/JacobianFactorSVD.h>
27
31
32#include <optional>
33#if GTSAM_ENABLE_BOOST_SERIALIZATION
34#include <boost/serialization/optional.hpp>
35#endif
36#include <vector>
37
38namespace gtsam {
39
50template<class CAMERA>
52
53private:
54 typedef NonlinearFactor Base;
55 typedef SmartFactorBase<CAMERA> This;
56 typedef typename CAMERA::Measurement Z;
57 typedef typename CAMERA::MeasurementVector ZVector;
58
59public:
60
61 static const int Dim = traits<CAMERA>::dimension;
62 static const int ZDim = traits<Z>::dimension;
63 typedef Eigen::Matrix<double, ZDim, Dim> MatrixZD; // F blocks (derivatives wrpt camera)
64 typedef std::vector<MatrixZD, Eigen::aligned_allocator<MatrixZD> > FBlocks; // vector of F blocks
65
66protected:
73 SharedIsotropic noiseModel_;
74
80 ZVector measured_;
81
82 std::optional<Pose3>
84
85 // Cache for Fblocks, to avoid a malloc ever time we re-linearize
86 mutable FBlocks Fs;
87
88 public:
90 typedef std::shared_ptr<This> shared_ptr;
91
94
97
99 SmartFactorBase(const SharedNoiseModel& sharedNoiseModel,
100 std::optional<Pose3> body_P_sensor = {},
101 size_t expectedNumberCameras = 10)
102 : body_P_sensor_(body_P_sensor), Fs(expectedNumberCameras) {
103
104 if (!sharedNoiseModel)
105 throw std::runtime_error("SmartFactorBase: sharedNoiseModel is required");
106
107 SharedIsotropic sharedIsotropic = std::dynamic_pointer_cast<
108 noiseModel::Isotropic>(sharedNoiseModel);
109
110 if (!sharedIsotropic)
111 throw std::runtime_error("SmartFactorBase: needs isotropic");
112
113 noiseModel_ = sharedIsotropic;
114 }
115
117 ~SmartFactorBase() override {
118 }
119
125 void add(const Z& measured, const Key& key) {
126 if(std::find(keys_.begin(), keys_.end(), key) != keys_.end()) {
127 throw std::invalid_argument(
128 "SmartFactorBase::add: adding duplicate measurement for key.");
129 }
130 this->measured_.push_back(measured);
131 this->keys_.push_back(key);
132 }
133
135 void add(const ZVector& measurements, const KeyVector& cameraKeys) {
136#ifndef NDEBUG
137 if (measurements.size() != cameraKeys.size()) {
138 throw std::runtime_error("Number of measurements and camera keys do not match");
139 }
140#endif
141 for (size_t i = 0; i < measurements.size(); i++) {
142 this->add(measurements[i], cameraKeys[i]);
143 }
144 }
145
150 template<class SFM_TRACK>
151 void add(const SFM_TRACK& trackToAdd) {
152 for (size_t k = 0; k < trackToAdd.numberMeasurements(); k++) {
153 this->measured_.push_back(trackToAdd.measurements[k].second);
154 this->keys_.push_back(trackToAdd.measurements[k].first);
155 }
156 }
157
159 size_t dim() const override { return ZDim * this->measured_.size(); }
160
162 const ZVector& measured() const { return measured_; }
163
165 virtual Cameras cameras(const Values& values) const {
167 for(const Key& k: this->keys_) {
168 cameras.push_back(values.at<CAMERA>(k));
169 }
170 return cameras;
171 }
172
178 void print(const std::string& s = "", const KeyFormatter& keyFormatter =
179 DefaultKeyFormatter) const override {
180 std::cout << s << "SmartFactorBase, z = \n";
181 for (size_t k = 0; k < measured_.size(); ++k) {
182 std::cout << "measurement " << k<<", px = \n" << measured_[k] << "\n";
183 noiseModel_->print("noise model = ");
184 }
186 body_P_sensor_->print("body_P_sensor_:\n");
187 Base::print("", keyFormatter);
188 }
189
191 bool equals(const NonlinearFactor& p, double tol = 1e-9) const override {
192 if (const This* e = dynamic_cast<const This*>(&p)) {
193 // Check that all measurements are the same.
194 for (size_t i = 0; i < measured_.size(); i++) {
195 if (!traits<Z>::Equals(this->measured_.at(i), e->measured_.at(i), tol))
196 return false;
197 }
198 // If so, check base class.
199 return Base::equals(p, tol);
200 } else {
201 return false;
202 }
203 }
204
210 template <class POINT>
212 const Cameras& cameras, const POINT& point,
213 typename Cameras::FBlocks* Fs = nullptr, //
214 Matrix* E = nullptr) const {
215 // Reproject, with optional derivatives.
216 Vector error = cameras.reprojectionError(point, measured_, Fs, E);
217
218 // Apply chain rule if body_P_sensor_ is given.
219 if (body_P_sensor_ && Fs) {
220 const Pose3 sensor_P_body = body_P_sensor_->inverse();
221 constexpr int camera_dim = traits<CAMERA>::dimension;
222 constexpr int pose_dim = traits<Pose3>::dimension;
223
224 for (size_t i = 0; i < Fs->size(); i++) {
225 const Pose3 world_P_body = cameras[i].pose() * sensor_P_body;
226 Eigen::Matrix<double, camera_dim, camera_dim> J;
227 J.setZero();
228 Eigen::Matrix<double, pose_dim, pose_dim> H;
229 // Call compose to compute Jacobian for camera extrinsics
230 world_P_body.compose(*body_P_sensor_, H);
231 // Assign extrinsics part of the Jacobian
232 J.template block<pose_dim, pose_dim>(0, 0) = H;
233 Fs->at(i) = Fs->at(i) * J;
234 }
235 }
236
237 // Correct the Jacobians in case some measurements are missing.
239
240 return error;
241 }
242
249 template<class POINT, class ...OptArgs, typename = std::enable_if_t<sizeof...(OptArgs)!=0>>
251 const Cameras& cameras, const POINT& point,
252 OptArgs&&... optArgs) const {
253 return unwhitenedError(cameras, point, (&optArgs)...);
254 }
255
262 const Cameras& cameras, Vector& ue,
263 typename Cameras::FBlocks* Fs = nullptr,
264 Matrix* E = nullptr) const {}
265
272 template<class ...OptArgs>
274 const Cameras& cameras, Vector& ue,
275 OptArgs&&... optArgs) const {
276 correctForMissingMeasurements(cameras, ue, (&optArgs)...);
277 }
278
283 template<class POINT>
284 Vector whitenedError(const Cameras& cameras, const POINT& point) const {
285 Vector error = cameras.reprojectionError(point, measured_);
286 if (noiseModel_)
287 noiseModel_->whitenInPlace(error);
288 return error;
289 }
290
299 template<class POINT>
301 const POINT& point) const {
302 Vector error = whitenedError(cameras, point);
303 return 0.5 * error.dot(error);
304 }
305
307 static Matrix PointCov(const Matrix& E) {
308 return (E.transpose() * E).inverse();
309 }
310
317 template<class POINT>
318 void computeJacobians(FBlocks& Fs, Matrix& E, Vector& b,
319 const Cameras& cameras, const POINT& point) const {
320 // Project into Camera set and calculate derivatives
321 // As in expressionFactor, RHS vector b = - (h(x_bar) - z) = z-h(x_bar)
322 // Indeed, nonlinear error |h(x_bar+dx)-z| ~ |h(x_bar) + A*dx - z|
323 // = |A*dx - (z-h(x_bar))|
324 b = -unwhitenedError(cameras, point, &Fs, &E);
325 }
326
332 template<class POINT>
333 void computeJacobiansSVD(FBlocks& Fs, Matrix& Enull,
334 Vector& b, const Cameras& cameras, const POINT& point) const {
335
336 Matrix E;
337 computeJacobians(Fs, E, b, cameras, point);
338
339 static const int N = FixedDimension<POINT>::value; // 2 (Unit3) or 3 (Point3)
340
341 // Do SVD on A.
342 Eigen::JacobiSVD<Matrix> svd(E, Eigen::ComputeFullU);
343 size_t m = this->keys_.size();
344 Enull = svd.matrixU().block(0, N, ZDim * m, ZDim * m - N); // last ZDim*m-N columns
345 }
346
348 // TODO(dellaert): Not used/tested anywhere and not properly whitened.
349 std::shared_ptr<RegularHessianFactor<Dim> > createHessianFactor(
350 const Cameras& cameras, const Point3& point, const double lambda = 0.0,
351 bool diagonalDamping = false) const {
352
353 Matrix E;
354 Vector b;
355 computeJacobians(Fs, E, b, cameras, point);
356
357 // build augmented hessian
358 SymmetricBlockMatrix augmentedHessian = Cameras::SchurComplement(Fs, E, b);
359
360 return std::make_shared<RegularHessianFactor<Dim> >(keys_,
361 augmentedHessian);
362 }
363
369 void updateAugmentedHessian(const Cameras& cameras, const Point3& point,
370 const double lambda, bool diagonalDamping,
371 SymmetricBlockMatrix& augmentedHessian,
372 const KeyVector allKeys) const {
373 Matrix E;
374 Vector b;
375 computeJacobians(Fs, E, b, cameras, point);
376 Cameras::UpdateSchurComplement(Fs, E, b, allKeys, keys_, augmentedHessian);
377 }
378
380 void whitenJacobians(FBlocks& F, Matrix& E, Vector& b) const {
381 noiseModel_->WhitenSystem(E, b);
382 // TODO make WhitenInPlace work with any dense matrix type
383 for (size_t i = 0; i < F.size(); i++)
384 F[i] = noiseModel_->Whiten(F[i]);
385 }
386
388 std::shared_ptr<RegularImplicitSchurFactor<CAMERA> > //
390 double lambda = 0.0, bool diagonalDamping = false) const {
391 Matrix E;
392 Vector b;
393 FBlocks F;
394 computeJacobians(F, E, b, cameras, point);
395 whitenJacobians(F, E, b);
396 Matrix P = Cameras::PointCov(E, lambda, diagonalDamping);
397 return std::make_shared<RegularImplicitSchurFactor<CAMERA> >(keys_, F, E,
398 P, b);
399 }
400
402 std::shared_ptr<JacobianFactorQ<Dim, ZDim> > createJacobianQFactor(
403 const Cameras& cameras, const Point3& point, double lambda = 0.0,
404 bool diagonalDamping = false) const {
405 Matrix E;
406 Vector b;
407 FBlocks F;
408 computeJacobians(F, E, b, cameras, point);
409 const size_t M = b.size();
410 Matrix P = Cameras::PointCov(E, lambda, diagonalDamping);
411 SharedIsotropic n = noiseModel::Isotropic::Sigma(M, noiseModel_->sigma());
412 return std::make_shared<JacobianFactorQ<Dim, ZDim> >(keys_, F, E, P, b, n);
413 }
414
419 std::shared_ptr<JacobianFactor> createJacobianSVDFactor(
420 const Cameras& cameras, const Point3& point, double lambda = 0.0) const {
421 size_t m = this->keys_.size();
422 FBlocks F;
423 Vector b;
424 const size_t M = ZDim * m;
425 Matrix E0(M, M - 3);
426 computeJacobiansSVD(F, E0, b, cameras, point);
427 SharedIsotropic n = noiseModel::Isotropic::Sigma(M - 3,
428 noiseModel_->sigma());
429 return std::make_shared<JacobianFactorSVD<Dim, ZDim> >(keys_, F, E0, b, n);
430 }
431
433 static void FillDiagonalF(const FBlocks& Fs, Matrix& F) {
434 size_t m = Fs.size();
435 F.resize(ZDim * m, Dim * m);
436 F.setZero();
437 for (size_t i = 0; i < m; ++i)
438 F.block<ZDim, Dim>(ZDim * i, Dim * i) = Fs.at(i);
439 }
440
441 // Return sensor pose.
442 Pose3 body_P_sensor() const{
444 return *body_P_sensor_;
445 else
446 return Pose3(); // if unspecified, the transformation is the identity
447 }
448
449private:
450
451#if GTSAM_ENABLE_BOOST_SERIALIZATION
453 friend class boost::serialization::access;
454 template<class ARCHIVE>
455 void serialize(ARCHIVE & ar, const unsigned int /*version*/) {
456 ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
457 ar & BOOST_SERIALIZATION_NVP(noiseModel_);
458 ar & BOOST_SERIALIZATION_NVP(measured_);
459 ar & BOOST_SERIALIZATION_NVP(body_P_sensor_);
460 }
461#endif
462};
463// end class SmartFactorBase
464
465// Definitions need to avoid link errors (above are only declarations)
466template<class CAMERA> const int SmartFactorBase<CAMERA>::Dim;
467template<class CAMERA> const int SmartFactorBase<CAMERA>::ZDim;
468
469} // \ namespace gtsam
Base class to create smart factors on poses or cameras.
Specialized HessianFactor class for regular problems (fixed-size blocks).
Non-linear factor base classes.
A subclass of GaussianFactor specialized to structureless SFM.
Global functions in a separate testing namespace.
Definition chartTesting.h:28
KeyFormatter DefaultKeyFormatter
Assign default key formatter.
Definition Key.cpp:30
FastVector< Key > KeyVector
Define collection type once and for all - also used in wrappers.
Definition Key.h:91
Vector3 Point3
As of GTSAM 4, in order to make GTSAM more lean, it is now possible to just typedef Point3 to Vector3...
Definition Point3.h:38
std::function< std::string(Key)> KeyFormatter
Typedef for a function to format a key, i.e. to convert it to a string.
Definition Key.h:35
void svd(const Matrix &A, Matrix &U, Vector &S, Matrix &V)
SVD computes economy SVD A=U*S*V'.
Definition Matrix.cpp:557
noiseModel::Base::shared_ptr SharedNoiseModel
Aliases.
Definition NoiseModel.h:846
std::uint64_t Key
Integer nonlinear key type.
Definition types.h:43
A manifold defines a space in which there is a notion of a linear tangent space that can be centered ...
Definition Group.h:37
This class stores a dense matrix and allows it to be accessed as a collection of blocks.
Definition SymmetricBlockMatrix.h:80
A set of cameras, all with their own calibration.
Definition CameraSet.h:37
static Matrix PointCov(const Matrix &E, const double lambda=0.0, bool diagonalDamping=false)
Computes Point Covariance P, with lambda parameter, dynamic version.
Definition CameraSet.h:360
static void UpdateSchurComplement(const FBlocks &Fs, const Matrix &E, const Eigen::Matrix< double, N, N > &P, const Vector &b, const KeyVector &allKeys, const KeyVector &keys, SymmetricBlockMatrix &augmentedHessian)
Applies Schur complement (exploiting block structure) to get a smart factor on cameras,...
Definition CameraSet.h:398
static SymmetricBlockMatrix SchurComplement(const std::vector< Eigen::Matrix< double, ZDim, ND >, Eigen::aligned_allocator< Eigen::Matrix< double, ZDim, ND > > > &Fs, const Matrix &E, const Eigen::Matrix< double, N, N > &P, const Vector &b)
Do Schur complement, given Jacobian as Fs,E,P, return SymmetricBlockMatrix G = F' * F - F' * E * P * ...
Definition CameraSet.h:175
A 3D pose (R,t) : (Rot3,Point3).
Definition Pose3.h:42
KeyVector keys_
The keys involved in this factor.
Definition Factor.h:88
An isotropic noise model corresponds to a scaled diagonal covariance To construct,...
Definition NoiseModel.h:581
static shared_ptr Sigma(size_t dim, double sigma, bool smart=true)
An isotropic noise model created by specifying a standard deviation sigma.
Definition NoiseModel.cpp:706
virtual bool equals(const NonlinearFactor &f, double tol=1e-9) const
Check if two factors are equal.
Definition NonlinearFactor.cpp:55
NonlinearFactor()
Default constructor for I/O only.
Definition NonlinearFactor.h:86
void print(const std::string &s="", const KeyFormatter &keyFormatter=DefaultKeyFormatter) const override
print
Definition NonlinearFactor.cpp:45
A non-templated config holding any types of Manifold-group elements.
Definition Values.h:65
const ValueType at(Key j) const
Retrieve a variable by key j.
Definition Values-inl.h:260
void computeJacobians(FBlocks &Fs, Matrix &E, Vector &b, const Cameras &cameras, const POINT &point) const
Compute F, E, and b (called below in both vanilla and SVD versions), where F is a vector of derivativ...
Definition SmartFactorBase.h:318
void add(const Z &measured, const Key &key)
Add a new measurement and pose/camera key.
Definition SmartFactorBase.h:125
~SmartFactorBase() override
Virtual destructor, subclasses from NonlinearFactor.
Definition SmartFactorBase.h:117
std::shared_ptr< JacobianFactor > createJacobianSVDFactor(const Cameras &cameras, const Point3 &point, double lambda=0.0) const
Return Jacobians as JacobianFactorSVD.
Definition SmartFactorBase.h:419
void updateAugmentedHessian(const Cameras &cameras, const Point3 &point, const double lambda, bool diagonalDamping, SymmetricBlockMatrix &augmentedHessian, const KeyVector allKeys) const
Add the contribution of the smart factor to a pre-allocated Hessian, using sparse linear algebra.
Definition SmartFactorBase.h:369
std::shared_ptr< RegularImplicitSchurFactor< CAMERA > > createRegularImplicitSchurFactor(const Cameras &cameras, const Point3 &point, double lambda=0.0, bool diagonalDamping=false) const
Return Jacobians as RegularImplicitSchurFactor with raw access.
Definition SmartFactorBase.h:389
SharedIsotropic noiseModel_
Definition SmartFactorBase.h:73
static const int Dim
Definition SmartFactorBase.h:61
Vector whitenedError(const Cameras &cameras, const POINT &point) const
Calculate vector of re-projection errors [h(x)-z] = [cameras.project(p) - z], with the noise model ap...
Definition SmartFactorBase.h:284
std::shared_ptr< JacobianFactorQ< Dim, ZDim > > createJacobianQFactor(const Cameras &cameras, const Point3 &point, double lambda=0.0, bool diagonalDamping=false) const
Return Jacobians as JacobianFactorQ.
Definition SmartFactorBase.h:402
virtual Cameras cameras(const Values &values) const
Collect all cameras: important that in key order.
Definition SmartFactorBase.h:165
Vector unwhitenedError(const Cameras &cameras, const POINT &point, typename Cameras::FBlocks *Fs=nullptr, Matrix *E=nullptr) const
Compute reprojection errors [h(x)-z] = [cameras.project(p)-z] and derivatives.
Definition SmartFactorBase.h:211
static void FillDiagonalF(const FBlocks &Fs, Matrix &F)
Create BIG block-diagonal matrix F from Fblocks.
Definition SmartFactorBase.h:433
std::shared_ptr< This > shared_ptr
shorthand for a smart pointer to a factor.
Definition SmartFactorBase.h:90
double totalReprojectionError(const Cameras &cameras, const POINT &point) const
Calculate the error of the factor.
Definition SmartFactorBase.h:300
void add(const ZVector &measurements, const KeyVector &cameraKeys)
Add a bunch of measurements, together with the camera keys.
Definition SmartFactorBase.h:135
void computeJacobiansSVD(FBlocks &Fs, Matrix &Enull, Vector &b, const Cameras &cameras, const POINT &point) const
SVD version that produces smaller Jacobian matrices by doing an SVD decomposition on E,...
Definition SmartFactorBase.h:333
ZVector measured_
Definition SmartFactorBase.h:80
std::shared_ptr< RegularHessianFactor< Dim > > createHessianFactor(const Cameras &cameras, const Point3 &point, const double lambda=0.0, bool diagonalDamping=false) const
Linearize to a Hessianfactor.
Definition SmartFactorBase.h:349
SmartFactorBase()
Default Constructor, for serialization.
Definition SmartFactorBase.h:96
void whitenJacobians(FBlocks &F, Matrix &E, Vector &b) const
Whiten the Jacobians computed by computeJacobians using noiseModel_.
Definition SmartFactorBase.h:380
Vector unwhitenedError(const Cameras &cameras, const POINT &point, OptArgs &&... optArgs) const
An overload of unwhitenedError.
Definition SmartFactorBase.h:250
void add(const SFM_TRACK &trackToAdd)
Add an entire SfM_track (collection of cameras observing a single point).
Definition SmartFactorBase.h:151
const ZVector & measured() const
Definition SmartFactorBase.h:162
size_t dim() const override
Return the dimension (number of rows!) of the factor.
Definition SmartFactorBase.h:159
void print(const std::string &s="", const KeyFormatter &keyFormatter=DefaultKeyFormatter) const override
print
Definition SmartFactorBase.h:178
void correctForMissingMeasurements(const Cameras &cameras, Vector &ue, OptArgs &&... optArgs) const
An overload of correctForMissingMeasurements.
Definition SmartFactorBase.h:273
virtual void correctForMissingMeasurements(const Cameras &cameras, Vector &ue, typename Cameras::FBlocks *Fs=nullptr, Matrix *E=nullptr) const
This corrects the Jacobians for the case in which some 2D measurement is missing (nan).
Definition SmartFactorBase.h:261
SmartFactorBase(const SharedNoiseModel &sharedNoiseModel, std::optional< Pose3 > body_P_sensor={}, size_t expectedNumberCameras=10)
Construct with given noise model and optional arguments.
Definition SmartFactorBase.h:99
static const int ZDim
Definition SmartFactorBase.h:62
static Matrix PointCov(const Matrix &E)
Computes Point Covariance P from the "point Jacobian" E.
Definition SmartFactorBase.h:307
std::optional< Pose3 > body_P_sensor_
Definition SmartFactorBase.h:83
CameraSet< CAMERA > Cameras
The CameraSet data structure is used to refer to a set of cameras.
Definition SmartFactorBase.h:93
bool equals(const NonlinearFactor &p, double tol=1e-9) const override
equals
Definition SmartFactorBase.h:191