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 
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:
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:
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;
224  J.setZero();
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.
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);
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.
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
const gtsam::Symbol key('X', 0)
Matrix3f m
virtual Cameras cameras(const Values &values) const
Collect all cameras: important that in key order.
void computeJacobians(FBlocks &Fs, Matrix &E, Vector &b, const Cameras &cameras, const POINT &point) const
virtual double error(const Values &c) const
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)
Matrix< RealScalar, Dynamic, Dynamic > M
Definition: bench_gemm.cpp:51
EIGEN_DEVICE_FUNC Matrix< _Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols > & setZero(Index size)
Pose3 body_P_sensor() const
double totalReprojectionError(const Cameras &cameras, const POINT &point) const
Key F(std::uint64_t j)
CAMERA::Measurement Z
GTSAM_MAKE_ALIGNED_OPERATOR_NEW typedef std::shared_ptr< This > shared_ptr
shorthand for a smart pointer to a factor.
std::string serialize(const T &input)
serializes to a string
bool equals(const NonlinearFactor &p, double tol=1e-9) const override
equals
const ValueType at(Key j) const
Definition: Values-inl.h:204
Base class to create smart factors on poses or cameras.
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
int n
Eigen::MatrixXd Matrix
Definition: base/Matrix.h:39
std::vector< MatrixZD, Eigen::aligned_allocator< MatrixZD > > FBlocks
leaf::MyValues values
SmartFactorBase()
Default Constructor, for serialization.
CameraSet< CAMERA > Cameras
The CameraSet data structure is used to refer to a set of cameras.
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.
virtual void correctForMissingMeasurements(const Cameras &cameras, Vector &ue, typename Cameras::FBlocks *Fs=nullptr, Matrix *E=nullptr) const
KeyVector keys_
The keys involved in this factor.
Definition: Factor.h:87
noiseModel::Isotropic::shared_ptr SharedIsotropic
Definition: NoiseModel.h:745
set noclip points set clip one set noclip two set bar set border lt lw set xdata set ydata set zdata set x2data set y2data set boxwidth set dummy y set format x g set format y g set format x2 g set format y2 g set format z g set angles radians set nogrid set key title set key left top Right noreverse box linetype linewidth samplen spacing width set nolabel set noarrow set nologscale set logscale x set set pointsize set encoding default set nopolar set noparametric set set set set surface set nocontour set clabel set mapping cartesian set nohidden3d set cntrparam order set cntrparam linear set cntrparam levels auto set cntrparam points set size set set xzeroaxis lt lw set x2zeroaxis lt lw set yzeroaxis lt lw set y2zeroaxis lt lw set tics in set ticslevel set tics set mxtics default set mytics default set mx2tics default set my2tics default set xtics border mirror norotate autofreq set ytics border mirror norotate autofreq set ztics border nomirror norotate autofreq set nox2tics set noy2tics set timestamp bottom norotate set rrange [*:*] noreverse nowriteback set trange [*:*] noreverse nowriteback set urange [*:*] noreverse nowriteback set vrange [*:*] noreverse nowriteback set xlabel matrix size set x2label set timefmt d m y n H
#define N
Definition: gksort.c:12
SharedIsotropic noiseModel_
void print(const std::string &s="", const KeyFormatter &keyFormatter=DefaultKeyFormatter) const override
CAMERA::MeasurementVector ZVector
void print(const std::string &s="", const KeyFormatter &keyFormatter=DefaultKeyFormatter) const override
~SmartFactorBase() override
Virtual destructor, subclasses from NonlinearFactor.
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
static const KeyFormatter DefaultKeyFormatter
Definition: Key.h:43
EIGEN_DEVICE_FUNC const InverseReturnType inverse() const
Vector unwhitenedError(const Cameras &cameras, const POINT &point, OptArgs &&... optArgs) const
virtual bool equals(const NonlinearFactor &f, double tol=1e-9) const
Base class for smart factors. This base class has no internal point, but it has a measurement...
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.
Eigen::VectorXd Vector
Definition: Vector.h:38
void computeJacobiansSVD(FBlocks &Fs, Matrix &Enull, Vector &b, const Cameras &cameras, const POINT &point) const
static void FillDiagonalF(const FBlocks &Fs, Matrix &F)
Create BIG block-diagonal matrix F from Fblocks.
GenericMeasurement< Point2, Point2 > Measurement
Definition: simulated2D.h:278
SmartFactorBase(const SharedNoiseModel &sharedNoiseModel, std::optional< Pose3 > body_P_sensor={}, size_t expectedNumberCameras=10)
Construct with given noise model and optional arguments.
Array< double, 1, 3 > e(1./3., 0.5, 2.)
RealScalar s
HessianFactor class with constant sized blocks.
std::vector< MatrixZD, Eigen::aligned_allocator< MatrixZD > > FBlocks
Definition: CameraSet.h:78
JacobiRotation< float > J
void svd(const Matrix &A, Matrix &U, Vector &S, Matrix &V)
Definition: Matrix.cpp:558
Class compose(const Class &g) const
Definition: Lie.h:48
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
const G & b
Definition: Group.h:86
Vector whitenedError(const Cameras &cameras, const POINT &point) const
void add(const ZVector &measurements, const KeyVector &cameraKeys)
Add a bunch of measurements, together with the camera keys.
std::shared_ptr< JacobianFactor > createJacobianSVDFactor(const Cameras &cameras, const Point3 &point, double lambda=0.0) const
cout<< "The eigenvalues of A are:"<< endl<< ces.eigenvalues()<< endl;cout<< "The matrix of eigenvectors, V, is:"<< endl<< ces.eigenvectors()<< endl<< endl;complex< float > lambda
const ZVector & measured() const
Return the 2D measurements (ZDim, in general).
traits
Definition: chartTesting.h:28
A subclass of GaussianFactor specialized to structureless SFM.
DiscreteKey E(5, 2)
Non-linear factor base classes.
size_t dim() const override
Return the dimension (number of rows!) of the factor.
void add(const Z &measured, const Key &key)
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.
void correctForMissingMeasurements(const Cameras &cameras, Vector &ue, OptArgs &&... optArgs) const
static const int Dim
Camera dimension.
void updateAugmentedHessian(const Cameras &cameras, const Point3 &point, const double lambda, bool diagonalDamping, SymmetricBlockMatrix &augmentedHessian, const KeyVector allKeys) const
Two-sided Jacobi SVD decomposition of a rectangular matrix.
float * p
typename std::enable_if< B, T >::type enable_if_t
from cpp_future import (convenient aliases from C++14/17)
SmartFactorBase< CAMERA > This
#define GTSAM_MAKE_ALIGNED_OPERATOR_NEW
Definition: types.h:284
const G double tol
Definition: Group.h:86
Vector3 Point3
Definition: Point3.h:38
Eigen::Matrix< double, ZDim, Dim > MatrixZD
static Matrix PointCov(const Matrix &E)
Computes Point Covariance P from the "point Jacobian" E.
FastVector< Key > KeyVector
Define collection type once and for all - also used in wrappers.
Definition: Key.h:86
The matrix class, also used for vectors and row-vectors.
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_.
NonlinearFactor Base
std::optional< Pose3 > body_P_sensor_
Pose of the camera in the body frame.
std::uint64_t Key
Integer nonlinear key type.
Definition: types.h:102
static const int ZDim
Measurement dimension.
noiseModel::Base::shared_ptr SharedNoiseModel
Definition: NoiseModel.h:741
static shared_ptr Sigma(size_t dim, double sigma, bool smart=true)
Definition: NoiseModel.cpp:594
Vector unwhitenedError(const Cameras &cameras, const POINT &point, typename Cameras::FBlocks *Fs=nullptr, Matrix *E=nullptr) const


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:35:50