GTSAM  4.0.2
C++ library for smoothing and mapping (SAM)
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 
22 #pragma once
23 
24 #include <gtsam/slam/JacobianFactorQ.h>
25 #include <gtsam/slam/JacobianFactorSVD.h>
27 
31 
32 #include <optional>
33 #ifdef GTSAM_ENABLE_BOOST_SERIALIZATION
34 #include <boost/serialization/optional.hpp>
35 #endif
36 #include <vector>
37 
38 namespace gtsam {
39 
50 template<class CAMERA>
52 
53 private:
54  typedef NonlinearFactor Base;
56  typedef typename CAMERA::Measurement Z;
57  typedef typename CAMERA::MeasurementVector ZVector;
58 
59 public:
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 
66 protected:
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 
92  typedef std::shared_ptr<This> shared_ptr;
93 
96 
99 
101  SmartFactorBase(const SharedNoiseModel& sharedNoiseModel,
102  std::optional<Pose3> body_P_sensor = {},
103  size_t expectedNumberCameras = 10)
104  : body_P_sensor_(body_P_sensor), Fs(expectedNumberCameras) {
105 
106  if (!sharedNoiseModel)
107  throw std::runtime_error("SmartFactorBase: sharedNoiseModel is required");
108 
109  SharedIsotropic sharedIsotropic = std::dynamic_pointer_cast<
110  noiseModel::Isotropic>(sharedNoiseModel);
111 
112  if (!sharedIsotropic)
113  throw std::runtime_error("SmartFactorBase: needs isotropic");
114 
115  noiseModel_ = sharedIsotropic;
116  }
117 
119  ~SmartFactorBase() override {
120  }
121 
127  void add(const Z& measured, const Key& key) {
128  if(std::find(keys_.begin(), keys_.end(), key) != keys_.end()) {
129  throw std::invalid_argument(
130  "SmartFactorBase::add: adding duplicate measurement for key.");
131  }
132  this->measured_.push_back(measured);
133  this->keys_.push_back(key);
134  }
135 
137  void add(const ZVector& measurements, const KeyVector& cameraKeys) {
138  assert(measurements.size() == cameraKeys.size());
139  for (size_t i = 0; i < measurements.size(); i++) {
140  this->add(measurements[i], cameraKeys[i]);
141  }
142  }
143 
148  template<class SFM_TRACK>
149  void add(const SFM_TRACK& trackToAdd) {
150  for (size_t k = 0; k < trackToAdd.numberMeasurements(); k++) {
151  this->measured_.push_back(trackToAdd.measurements[k].second);
152  this->keys_.push_back(trackToAdd.measurements[k].first);
153  }
154  }
155 
157  size_t dim() const override { return ZDim * this->measured_.size(); }
158 
160  const ZVector& measured() const { return measured_; }
161 
163  virtual Cameras cameras(const Values& values) const {
164  Cameras cameras;
165  for(const Key& k: this->keys_)
166  cameras.push_back(values.at<CAMERA>(k));
167  return cameras;
168  }
169 
175  void print(const std::string& s = "", const KeyFormatter& keyFormatter =
176  DefaultKeyFormatter) const override {
177  std::cout << s << "SmartFactorBase, z = \n";
178  for (size_t k = 0; k < measured_.size(); ++k) {
179  std::cout << "measurement " << k<<", px = \n" << measured_[k] << "\n";
180  noiseModel_->print("noise model = ");
181  }
182  if(body_P_sensor_)
183  body_P_sensor_->print("body_P_sensor_:\n");
184  Base::print("", keyFormatter);
185  }
186 
188  bool equals(const NonlinearFactor& p, double tol = 1e-9) const override {
189  if (const This* e = dynamic_cast<const This*>(&p)) {
190  // Check that all measurements are the same.
191  for (size_t i = 0; i < measured_.size(); i++) {
192  if (!traits<Z>::Equals(this->measured_.at(i), e->measured_.at(i), tol))
193  return false;
194  }
195  // If so, check base class.
196  return Base::equals(p, tol);
197  } else {
198  return false;
199  }
200  }
201 
207  template <class POINT>
209  const Cameras& cameras, const POINT& point,
210  typename Cameras::FBlocks* Fs = nullptr, //
211  Matrix* E = nullptr) const {
212  // Reproject, with optional derivatives.
213  Vector error = cameras.reprojectionError(point, measured_, Fs, E);
214 
215  // Apply chain rule if body_P_sensor_ is given.
216  if (body_P_sensor_ && Fs) {
217  const Pose3 sensor_P_body = body_P_sensor_->inverse();
218  constexpr int camera_dim = traits<CAMERA>::dimension;
219  constexpr int pose_dim = traits<Pose3>::dimension;
220 
221  for (size_t i = 0; i < Fs->size(); i++) {
222  const Pose3 world_P_body = cameras[i].pose() * sensor_P_body;
223  Eigen::Matrix<double, camera_dim, camera_dim> J;
224  J.setZero();
225  Eigen::Matrix<double, pose_dim, pose_dim> H;
226  // Call compose to compute Jacobian for camera extrinsics
227  world_P_body.compose(*body_P_sensor_, H);
228  // Assign extrinsics part of the Jacobian
229  J.template block<pose_dim, pose_dim>(0, 0) = H;
230  Fs->at(i) = Fs->at(i) * J;
231  }
232  }
233 
234  // Correct the Jacobians in case some measurements are missing.
235  correctForMissingMeasurements(cameras, error, Fs, E);
236 
237  return error;
238  }
239 
246  template<class POINT, class ...OptArgs, typename = std::enable_if_t<sizeof...(OptArgs)!=0>>
248  const Cameras& cameras, const POINT& point,
249  OptArgs&&... optArgs) const {
250  return unwhitenedError(cameras, point, (&optArgs)...);
251  }
252 
259  const Cameras& cameras, Vector& ue,
260  typename Cameras::FBlocks* Fs = nullptr,
261  Matrix* E = nullptr) const {}
262 
269  template<class ...OptArgs>
271  const Cameras& cameras, Vector& ue,
272  OptArgs&&... optArgs) const {
273  correctForMissingMeasurements(cameras, ue, (&optArgs)...);
274  }
275 
280  template<class POINT>
281  Vector whitenedError(const Cameras& cameras, const POINT& point) const {
282  Vector error = cameras.reprojectionError(point, measured_);
283  if (noiseModel_)
284  noiseModel_->whitenInPlace(error);
285  return error;
286  }
287 
296  template<class POINT>
297  double totalReprojectionError(const Cameras& cameras,
298  const POINT& point) const {
299  Vector error = whitenedError(cameras, point);
300  return 0.5 * error.dot(error);
301  }
302 
304  static Matrix PointCov(const Matrix& E) {
305  return (E.transpose() * E).inverse();
306  }
307 
314  template<class POINT>
315  void computeJacobians(FBlocks& Fs, Matrix& E, Vector& b,
316  const Cameras& cameras, const POINT& point) const {
317  // Project into Camera set and calculate derivatives
318  // As in expressionFactor, RHS vector b = - (h(x_bar) - z) = z-h(x_bar)
319  // Indeed, nonlinear error |h(x_bar+dx)-z| ~ |h(x_bar) + A*dx - z|
320  // = |A*dx - (z-h(x_bar))|
321  b = -unwhitenedError(cameras, point, &Fs, &E);
322  }
323 
329  template<class POINT>
330  void computeJacobiansSVD(FBlocks& Fs, Matrix& Enull,
331  Vector& b, const Cameras& cameras, const POINT& point) const {
332 
333  Matrix E;
334  computeJacobians(Fs, E, b, cameras, point);
335 
336  static const int N = FixedDimension<POINT>::value; // 2 (Unit3) or 3 (Point3)
337 
338  // Do SVD on A.
339  Eigen::JacobiSVD<Matrix> svd(E, Eigen::ComputeFullU);
340  size_t m = this->keys_.size();
341  Enull = svd.matrixU().block(0, N, ZDim * m, ZDim * m - N); // last ZDim*m-N columns
342  }
343 
345  // TODO(dellaert): Not used/tested anywhere and not properly whitened.
346  std::shared_ptr<RegularHessianFactor<Dim> > createHessianFactor(
347  const Cameras& cameras, const Point3& point, const double lambda = 0.0,
348  bool diagonalDamping = false) const {
349 
350  Matrix E;
351  Vector b;
352  computeJacobians(Fs, E, b, cameras, point);
353 
354  // build augmented hessian
355  SymmetricBlockMatrix augmentedHessian = Cameras::SchurComplement(Fs, E, b);
356 
357  return std::make_shared<RegularHessianFactor<Dim> >(keys_,
358  augmentedHessian);
359  }
360 
366  void updateAugmentedHessian(const Cameras& cameras, const Point3& point,
367  const double lambda, bool diagonalDamping,
368  SymmetricBlockMatrix& augmentedHessian,
369  const KeyVector allKeys) const {
370  Matrix E;
371  Vector b;
372  computeJacobians(Fs, E, b, cameras, point);
373  Cameras::UpdateSchurComplement(Fs, E, b, allKeys, keys_, augmentedHessian);
374  }
375 
377  void whitenJacobians(FBlocks& F, Matrix& E, Vector& b) const {
378  noiseModel_->WhitenSystem(E, b);
379  // TODO make WhitenInPlace work with any dense matrix type
380  for (size_t i = 0; i < F.size(); i++)
381  F[i] = noiseModel_->Whiten(F[i]);
382  }
383 
385  std::shared_ptr<RegularImplicitSchurFactor<CAMERA> > //
386  createRegularImplicitSchurFactor(const Cameras& cameras, const Point3& point,
387  double lambda = 0.0, bool diagonalDamping = false) const {
388  Matrix E;
389  Vector b;
390  FBlocks F;
391  computeJacobians(F, E, b, cameras, point);
392  whitenJacobians(F, E, b);
393  Matrix P = Cameras::PointCov(E, lambda, diagonalDamping);
394  return std::make_shared<RegularImplicitSchurFactor<CAMERA> >(keys_, F, E,
395  P, b);
396  }
397 
399  std::shared_ptr<JacobianFactorQ<Dim, ZDim> > createJacobianQFactor(
400  const Cameras& cameras, const Point3& point, double lambda = 0.0,
401  bool diagonalDamping = false) const {
402  Matrix E;
403  Vector b;
404  FBlocks F;
405  computeJacobians(F, E, b, cameras, point);
406  const size_t M = b.size();
407  Matrix P = Cameras::PointCov(E, lambda, diagonalDamping);
408  SharedIsotropic n = noiseModel::Isotropic::Sigma(M, noiseModel_->sigma());
409  return std::make_shared<JacobianFactorQ<Dim, ZDim> >(keys_, F, E, P, b, n);
410  }
411 
416  std::shared_ptr<JacobianFactor> createJacobianSVDFactor(
417  const Cameras& cameras, const Point3& point, double lambda = 0.0) const {
418  size_t m = this->keys_.size();
419  FBlocks F;
420  Vector b;
421  const size_t M = ZDim * m;
422  Matrix E0(M, M - 3);
423  computeJacobiansSVD(F, E0, b, cameras, point);
424  SharedIsotropic n = noiseModel::Isotropic::Sigma(M - 3,
425  noiseModel_->sigma());
426  return std::make_shared<JacobianFactorSVD<Dim, ZDim> >(keys_, F, E0, b, n);
427  }
428 
430  static void FillDiagonalF(const FBlocks& Fs, Matrix& F) {
431  size_t m = Fs.size();
432  F.resize(ZDim * m, Dim * m);
433  F.setZero();
434  for (size_t i = 0; i < m; ++i)
435  F.block<ZDim, Dim>(ZDim * i, Dim * i) = Fs.at(i);
436  }
437 
438  // Return sensor pose.
439  Pose3 body_P_sensor() const{
440  if(body_P_sensor_)
441  return *body_P_sensor_;
442  else
443  return Pose3(); // if unspecified, the transformation is the identity
444  }
445 
446 private:
447 
448 #ifdef GTSAM_ENABLE_BOOST_SERIALIZATION
449  friend class boost::serialization::access;
451  template<class ARCHIVE>
452  void serialize(ARCHIVE & ar, const unsigned int /*version*/) {
453  ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
454  ar & BOOST_SERIALIZATION_NVP(noiseModel_);
455  ar & BOOST_SERIALIZATION_NVP(measured_);
456  ar & BOOST_SERIALIZATION_NVP(body_P_sensor_);
457  }
458 #endif
459 };
460 // end class SmartFactorBase
461 
462 // Definitions need to avoid link errors (above are only declarations)
463 template<class CAMERA> const int SmartFactorBase<CAMERA>::Dim;
464 template<class CAMERA> const int SmartFactorBase<CAMERA>::ZDim;
465 
466 } // \ namespace gtsam
virtual Cameras cameras(const Values &values) const
Collect all cameras: important that in key order.
Definition: SmartFactorBase.h:163
void computeJacobians(FBlocks &Fs, Matrix &E, Vector &b, const Cameras &cameras, const POINT &point) const
Definition: SmartFactorBase.h:315
typename std::enable_if< B, T >::type enable_if_t
An shorthand alias for accessing the ::type inside std::enable_if that can be used in a template dire...
Definition: make_shared.h:30
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:352
void add(const SFM_TRACK &trackToAdd)
Definition: SmartFactorBase.h:149
double totalReprojectionError(const Cameras &cameras, const POINT &point) const
Definition: SmartFactorBase.h:297
GTSAM_MAKE_ALIGNED_OPERATOR_NEW typedef std::shared_ptr< This > shared_ptr
shorthand for a smart pointer to a factor.
Definition: SmartFactorBase.h:92
std::string serialize(const T &input)
serializes to a string
Definition: serialization.h:113
bool equals(const NonlinearFactor &p, double tol=1e-9) const override
equals
Definition: SmartFactorBase.h:188
const ValueType at(Key j) const
Definition: Values-inl.h:204
Base class to create smart factors on poses or cameras.
Definition: Factor.h:69
Give fixed size dimension of a type, fails at compile time if dynamic.
Definition: Manifold.h:161
Vector reprojectionError(const POINT &point, const ZVector &measured, FBlocks *Fs=nullptr, Matrix *E=nullptr) const
Calculate vector [project2(point)-z] of re-projection errors.
Definition: CameraSet.h:146
virtual bool equals(const NonlinearFactor &f, double tol=1e-9) const
Definition: Group.h:43
Definition: NonlinearFactor.h:68
SmartFactorBase()
Default Constructor, for serialization.
Definition: SmartFactorBase.h:98
CameraSet< CAMERA > Cameras
The CameraSet data structure is used to refer to a set of cameras.
Definition: SmartFactorBase.h:95
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:346
virtual void correctForMissingMeasurements(const Cameras &cameras, Vector &ue, typename Cameras::FBlocks *Fs=nullptr, Matrix *E=nullptr) const
Definition: SmartFactorBase.h:258
KeyVector keys_
The keys involved in this factor.
Definition: Factor.h:87
void print(const std::string &s="", const KeyFormatter &keyFormatter=DefaultKeyFormatter) const override
SharedIsotropic noiseModel_
Definition: SmartFactorBase.h:73
void print(const std::string &s="", const KeyFormatter &keyFormatter=DefaultKeyFormatter) const override
Definition: SmartFactorBase.h:175
~SmartFactorBase() override
Virtual destructor, subclasses from NonlinearFactor.
Definition: SmartFactorBase.h:119
A set of cameras, all with their own calibration.
Definition: CameraSet.h:36
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)
Definition: CameraSet.h:171
Vector unwhitenedError(const Cameras &cameras, const POINT &point, OptArgs &&... optArgs) const
Definition: SmartFactorBase.h:247
Definition: SymmetricBlockMatrix.h:53
Base class for smart factors. This base class has no internal point, but it has a measurement...
Definition: SmartFactorBase.h:51
ZVector measured_
Definition: SmartFactorBase.h:80
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:386
void computeJacobiansSVD(FBlocks &Fs, Matrix &Enull, Vector &b, const Cameras &cameras, const POINT &point) const
Definition: SmartFactorBase.h:330
static void FillDiagonalF(const FBlocks &Fs, Matrix &F)
Create BIG block-diagonal matrix F from Fblocks.
Definition: SmartFactorBase.h:430
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:101
HessianFactor class with constant sized blocks.
Definition: Values.h:65
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
Vector whitenedError(const Cameras &cameras, const POINT &point) const
Definition: SmartFactorBase.h:281
void add(const ZVector &measurements, const KeyVector &cameraKeys)
Add a bunch of measurements, together with the camera keys.
Definition: SmartFactorBase.h:137
std::shared_ptr< JacobianFactor > createJacobianSVDFactor(const Cameras &cameras, const Point3 &point, double lambda=0.0) const
Definition: SmartFactorBase.h:416
const ZVector & measured() const
Return the 2D measurements (ZDim, in general).
Definition: SmartFactorBase.h:160
Definition: chartTesting.h:28
FastVector< Key > KeyVector
Define collection type once and for all - also used in wrappers.
Definition: Key.h:86
A subclass of GaussianFactor specialized to structureless SFM.
Non-linear factor base classes.
size_t dim() const override
Return the dimension (number of rows!) of the factor.
Definition: SmartFactorBase.h:157
void add(const Z &measured, const Key &key)
Definition: SmartFactorBase.h:127
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:399
void correctForMissingMeasurements(const Cameras &cameras, Vector &ue, OptArgs &&... optArgs) const
Definition: SmartFactorBase.h:270
GTSAM_EXPORT void svd(const Matrix &A, Matrix &U, Vector &S, Matrix &V)
static const int Dim
Camera dimension.
Definition: SmartFactorBase.h:61
void updateAugmentedHessian(const Cameras &cameras, const Point3 &point, const double lambda, bool diagonalDamping, SymmetricBlockMatrix &augmentedHessian, const KeyVector allKeys) const
Definition: SmartFactorBase.h:366
#define GTSAM_MAKE_ALIGNED_OPERATOR_NEW
Definition: types.h:284
Vector3 Point3
Definition: Point3.h:38
static Matrix PointCov(const Matrix &E)
Computes Point Covariance P from the "point Jacobian" E.
Definition: SmartFactorBase.h:304
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)
Definition: CameraSet.h:390
void whitenJacobians(FBlocks &F, Matrix &E, Vector &b) const
Whiten the Jacobians computed by computeJacobians using noiseModel_.
Definition: SmartFactorBase.h:377
Definition: NoiseModel.h:527
Definition: Pose3.h:37
std::optional< Pose3 > body_P_sensor_
Pose of the camera in the body frame.
Definition: SmartFactorBase.h:83
std::uint64_t Key
Integer nonlinear key type.
Definition: types.h:102
static const int ZDim
Measurement dimension.
Definition: SmartFactorBase.h:62
noiseModel::Base::shared_ptr SharedNoiseModel
Definition: NoiseModel.h:741
static shared_ptr Sigma(size_t dim, double sigma, bool smart=true)
Vector unwhitenedError(const Cameras &cameras, const POINT &point, typename Cameras::FBlocks *Fs=nullptr, Matrix *E=nullptr) const
Definition: SmartFactorBase.h:208