gtsam  4.0.0
gtsam
CameraSet.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 
19 #pragma once
20 
21 #include <gtsam/geometry/Point3.h>
22 #include <gtsam/geometry/CalibratedCamera.h> // for Cheirality exception
23 #include <gtsam/base/Testable.h>
25 #include <gtsam/base/FastMap.h>
26 #include <vector>
27 
28 namespace gtsam {
29 
33 template<class CAMERA>
34 class CameraSet: public std::vector<CAMERA> {
35 
36 protected:
37 
42  typedef typename CAMERA::Measurement Z;
43 
44  static const int D = traits<CAMERA>::dimension;
45  static const int ZDim = traits<Z>::dimension;
46 
48  static Vector ErrorVector(const std::vector<Z>& predicted,
49  const std::vector<Z>& measured) {
50 
51  // Check size
52  size_t m = predicted.size();
53  if (measured.size() != m)
54  throw std::runtime_error("CameraSet::errors: size mismatch");
55 
56  // Project and fill error vector
57  Vector b(ZDim * m);
58  for (size_t i = 0, row = 0; i < m; i++, row += ZDim) {
59  b.segment<ZDim>(row) = traits<Z>::Local(measured[i], predicted[i]);
60  }
61  return b;
62  }
63 
64 public:
65 
67  typedef Eigen::Matrix<double, ZDim, D> MatrixZD;
68  typedef std::vector<MatrixZD> FBlocks;
69 
75  virtual void print(const std::string& s = "") const {
76  std::cout << s << "CameraSet, cameras = \n";
77  for (size_t k = 0; k < this->size(); ++k)
78  this->at(k).print(s);
79  }
80 
82  bool equals(const CameraSet& p, double tol = 1e-9) const {
83  if (this->size() != p.size())
84  return false;
85  bool camerasAreEqual = true;
86  for (size_t i = 0; i < this->size(); i++) {
87  if (this->at(i).equals(p.at(i), tol) == false)
88  camerasAreEqual = false;
89  break;
90  }
91  return camerasAreEqual;
92  }
93 
100  template<class POINT>
101  std::vector<Z> project2(const POINT& point, //
102  boost::optional<FBlocks&> Fs = boost::none, //
103  boost::optional<Matrix&> E = boost::none) const {
104 
105  static const int N = FixedDimension<POINT>::value;
106 
107  // Allocate result
108  size_t m = this->size();
109  std::vector<Z> z;
110  z.reserve(m);
111 
112  // Allocate derivatives
113  if (E) E->resize(ZDim * m, N);
114  if (Fs) Fs->resize(m);
115 
116  // Project and fill derivatives
117  for (size_t i = 0; i < m; i++) {
118  MatrixZD Fi;
119  Eigen::Matrix<double, ZDim, N> Ei;
120  z.emplace_back(this->at(i).project2(point, Fs ? &Fi : 0, E ? &Ei : 0));
121  if (Fs) (*Fs)[i] = Fi;
122  if (E) E->block<ZDim, N>(ZDim * i, 0) = Ei;
123  }
124 
125  return z;
126  }
127 
129  template<class POINT>
130  Vector reprojectionError(const POINT& point, const std::vector<Z>& measured,
131  boost::optional<FBlocks&> Fs = boost::none, //
132  boost::optional<Matrix&> E = boost::none) const {
133  return ErrorVector(project2(point, Fs, E), measured);
134  }
135 
142  template<int N> // N = 2 or 3
143  static SymmetricBlockMatrix SchurComplement(const FBlocks& Fs,
144  const Matrix& E, const Eigen::Matrix<double, N, N>& P, const Vector& b) {
145 
146  // a single point is observed in m cameras
147  size_t m = Fs.size();
148 
149  // Create a SymmetricBlockMatrix
150  size_t M1 = D * m + 1;
151  std::vector<DenseIndex> dims(m + 1); // this also includes the b term
152  std::fill(dims.begin(), dims.end() - 1, D);
153  dims.back() = 1;
154  SymmetricBlockMatrix augmentedHessian(dims, Matrix::Zero(M1, M1));
155 
156  // Blockwise Schur complement
157  for (size_t i = 0; i < m; i++) { // for each camera
158 
159  const MatrixZD& Fi = Fs[i];
160  const auto FiT = Fi.transpose();
161  const Eigen::Matrix<double, ZDim, N> Ei_P = //
162  E.block(ZDim * i, 0, ZDim, N) * P;
163 
164  // D = (Dx2) * ZDim
165  augmentedHessian.setOffDiagonalBlock(i, m, FiT * b.segment<ZDim>(ZDim * i) // F' * b
166  - FiT * (Ei_P * (E.transpose() * b))); // D = (DxZDim) * (ZDimx3) * (N*ZDimm) * (ZDimm x 1)
167 
168  // (DxD) = (DxZDim) * ( (ZDimxD) - (ZDimx3) * (3xZDim) * (ZDimxD) )
169  augmentedHessian.setDiagonalBlock(i, FiT
170  * (Fi - Ei_P * E.block(ZDim * i, 0, ZDim, N).transpose() * Fi));
171 
172  // upper triangular part of the hessian
173  for (size_t j = i + 1; j < m; j++) { // for each camera
174  const MatrixZD& Fj = Fs[j];
175 
176  // (DxD) = (Dx2) * ( (2x2) * (2xD) )
177  augmentedHessian.setOffDiagonalBlock(i, j, -FiT
178  * (Ei_P * E.block(ZDim * j, 0, ZDim, N).transpose() * Fj));
179  }
180  } // end of for over cameras
181 
182  augmentedHessian.diagonalBlock(m)(0, 0) += b.squaredNorm();
183  return augmentedHessian;
184  }
185 
187  template<int N> // N = 2 or 3
188  static void ComputePointCovariance(Eigen::Matrix<double, N, N>& P,
189  const Matrix& E, double lambda, bool diagonalDamping = false) {
190 
191  Matrix EtE = E.transpose() * E;
192 
193  if (diagonalDamping) { // diagonal of the hessian
194  EtE.diagonal() += lambda * EtE.diagonal();
195  } else {
196  DenseIndex n = E.cols();
197  EtE += lambda * Eigen::MatrixXd::Identity(n, n);
198  }
199 
200  P = (EtE).inverse();
201  }
202 
204  static Matrix PointCov(const Matrix& E, const double lambda = 0.0,
205  bool diagonalDamping = false) {
206  if (E.cols() == 2) {
207  Matrix2 P2;
208  ComputePointCovariance(P2, E, lambda, diagonalDamping);
209  return P2;
210  } else {
211  Matrix3 P3;
212  ComputePointCovariance(P3, E, lambda, diagonalDamping);
213  return P3;
214  }
215  }
216 
221  static SymmetricBlockMatrix SchurComplement(const FBlocks& Fblocks,
222  const Matrix& E, const Vector& b, const double lambda = 0.0,
223  bool diagonalDamping = false) {
224  if (E.cols() == 2) {
225  Matrix2 P;
226  ComputePointCovariance(P, E, lambda, diagonalDamping);
227  return SchurComplement(Fblocks, E, P, b);
228  } else {
229  Matrix3 P;
230  ComputePointCovariance(P, E, lambda, diagonalDamping);
231  return SchurComplement(Fblocks, E, P, b);
232  }
233  }
234 
239  template<int N> // N = 2 or 3
240  static void UpdateSchurComplement(const FBlocks& Fs, const Matrix& E,
241  const Eigen::Matrix<double, N, N>& P, const Vector& b,
242  const FastVector<Key>& allKeys, const FastVector<Key>& keys,
243  /*output ->*/SymmetricBlockMatrix& augmentedHessian) {
244 
245  assert(keys.size()==Fs.size());
246  assert(keys.size()<=allKeys.size());
247 
248  FastMap<Key, size_t> KeySlotMap;
249  for (size_t slot = 0; slot < allKeys.size(); slot++)
250  KeySlotMap.insert(std::make_pair(allKeys[slot], slot));
251 
252  // Schur complement trick
253  // G = F' * F - F' * E * P * E' * F
254  // g = F' * (b - E * P * E' * b)
255 
256  // a single point is observed in m cameras
257  size_t m = Fs.size(); // cameras observing current point
258  size_t M = (augmentedHessian.rows() - 1) / D; // all cameras in the group
259  assert(allKeys.size()==M);
260 
261  // Blockwise Schur complement
262  for (size_t i = 0; i < m; i++) { // for each camera in the current factor
263 
264  const MatrixZD& Fi = Fs[i];
265  const auto FiT = Fi.transpose();
266  const Eigen::Matrix<double, 2, N> Ei_P = E.template block<ZDim, N>(
267  ZDim * i, 0) * P;
268 
269  // D = (DxZDim) * (ZDim)
270  // allKeys are the list of all camera keys in the group, e.g, (1,3,4,5,7)
271  // we should map those to a slot in the local (grouped) hessian (0,1,2,3,4)
272  // Key cameraKey_i = this->keys_[i];
273  DenseIndex aug_i = KeySlotMap.at(keys[i]);
274 
275  // information vector - store previous vector
276  // vectorBlock = augmentedHessian(aug_i, aug_m).knownOffDiagonal();
277  // add contribution of current factor
278  augmentedHessian.updateOffDiagonalBlock(aug_i, M,
279  FiT * b.segment<ZDim>(ZDim * i) // F' * b
280  - FiT * (Ei_P * (E.transpose() * b))); // D = (DxZDim) * (ZDimx3) * (N*ZDimm) * (ZDimm x 1)
281 
282  // (DxD) += (DxZDim) * ( (ZDimxD) - (ZDimx3) * (3xZDim) * (ZDimxD) )
283  // add contribution of current factor
284  // TODO(gareth): Eigen doesn't let us pass the expression. Call eval() for now...
285  augmentedHessian.updateDiagonalBlock(aug_i,
286  ((FiT * (Fi - Ei_P * E.template block<ZDim, N>(ZDim * i, 0).transpose() * Fi))).eval());
287 
288  // upper triangular part of the hessian
289  for (size_t j = i + 1; j < m; j++) { // for each camera
290  const MatrixZD& Fj = Fs[j];
291 
292  DenseIndex aug_j = KeySlotMap.at(keys[j]);
293 
294  // (DxD) = (DxZDim) * ( (ZDimxZDim) * (ZDimxD) )
295  // off diagonal block - store previous block
296  // matrixBlock = augmentedHessian(aug_i, aug_j).knownOffDiagonal();
297  // add contribution of current factor
298  augmentedHessian.updateOffDiagonalBlock(aug_i, aug_j,
299  -FiT * (Ei_P * E.template block<ZDim, N>(ZDim * j, 0).transpose() * Fj));
300  }
301  } // end of for over cameras
302 
303  augmentedHessian.diagonalBlock(M)(0, 0) += b.squaredNorm();
304  }
305 
306 private:
307 
310  template<class ARCHIVE>
311  void serialize(ARCHIVE & ar, const unsigned int /*version*/) {
312  ar & (*this);
313  }
314 };
315 
316 template<class CAMERA>
317 const int CameraSet<CAMERA>::D;
318 
319 template<class CAMERA>
320 const int CameraSet<CAMERA>::ZDim;
321 
322 template<class CAMERA>
323 struct traits<CameraSet<CAMERA> > : public Testable<CameraSet<CAMERA> > {
324 };
325 
326 template<class CAMERA>
327 struct traits<const CameraSet<CAMERA> > : public Testable<CameraSet<CAMERA> > {
328 };
329 
330 } // \ namespace gtsam
CAMERA::Measurement Z
2D measurement and noise model for each of the m views The order is kept the same as the keys that we...
Definition: CameraSet.h:42
void updateDiagonalBlock(DenseIndex I, const XprType &xpr)
Increment the diagonal block by the values in xpr. Only reads the upper triangular part of xpr...
Definition: SymmetricBlockMatrix.h:217
static void ComputePointCovariance(Eigen::Matrix< double, N, N > &P, const Matrix &E, double lambda, bool diagonalDamping=false)
Computes Point Covariance P, with lambda parameter.
Definition: CameraSet.h:188
void updateOffDiagonalBlock(DenseIndex I, DenseIndex J, const XprType &xpr)
Update an off diagonal block.
Definition: SymmetricBlockMatrix.h:233
virtual void print(const std::string &s="") const
print
Definition: CameraSet.h:75
static SymmetricBlockMatrix SchurComplement(const FBlocks &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&#39; * F - F&#39; * E * P * ...
Definition: CameraSet.h:143
Vector reprojectionError(const POINT &point, const std::vector< Z > &measured, boost::optional< FBlocks & > Fs=boost::none, boost::optional< Matrix & > E=boost::none) const
Calculate vector [project2(point)-z] of re-projection errors.
Definition: CameraSet.h:130
void setDiagonalBlock(DenseIndex I, const XprType &xpr)
Set a diagonal block. Only the upper triangular portion of xpr is evaluated.
Definition: SymmetricBlockMatrix.h:200
Definition: SymmetricBlockMatrix.h:51
Access to matrices via blocks of pre-defined sizes.
friend class boost::serialization::access
Serialization function.
Definition: CameraSet.h:309
A helper that implements the traits interface for GTSAM types.
Definition: Testable.h:150
Calibrated camera for which only pose is unknown.
static const int ZDim
Measurement dimension.
Definition: CameraSet.h:45
ptrdiff_t DenseIndex
The index type for Eigen objects.
Definition: types.h:60
A set of cameras, all with their own calibration.
Definition: CameraSet.h:34
static void UpdateSchurComplement(const FBlocks &Fs, const Matrix &E, const Eigen::Matrix< double, N, N > &P, const Vector &b, const FastVector< Key > &allKeys, const FastVector< Key > &keys, SymmetricBlockMatrix &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.
Definition: CameraSet.h:240
static const int D
Camera dimension.
Definition: CameraSet.h:44
A manifold defines a space in which there is a notion of a linear tangent space that can be centered ...
Definition: concepts.h:30
bool equals(const CameraSet &p, double tol=1e-9) const
equals
Definition: CameraSet.h:82
static SymmetricBlockMatrix SchurComplement(const FBlocks &Fblocks, const Matrix &E, const Vector &b, const double lambda=0.0, bool diagonalDamping=false)
Do Schur complement, given Jacobian as Fs,E,P, return SymmetricBlockMatrix Dynamic version...
Definition: CameraSet.h:221
Give fixed size dimension of a type, fails at compile time if dynamic.
Definition: Manifold.h:164
3D Point
std::vector< Z > project2(const POINT &point, boost::optional< FBlocks & > Fs=boost::none, boost::optional< Matrix & > E=boost::none) const
Project a point (possibly Unit3 at infinity), with derivatives Note that F is a sparse block-diagonal...
Definition: CameraSet.h:101
static Vector ErrorVector(const std::vector< Z > &predicted, const std::vector< Z > &measured)
Make a vector of re-projection errors.
Definition: CameraSet.h:48
DenseIndex rows() const
Row size.
Definition: SymmetricBlockMatrix.h:119
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:204
const MATRIX::ConstRowXpr row(const MATRIX &A, size_t j)
Extracts a row view from a matrix that avoids a copy.
Definition: Matrix.h:224
Eigen::Matrix< double, ZDim, D > MatrixZD
Definitions for blocks of F.
Definition: CameraSet.h:67
A thin wrapper around std::map that uses boost&#39;s fast_pool_allocator.
Eigen::SelfAdjointView< Block, Eigen::Upper > diagonalBlock(DenseIndex J)
Return the J&#39;th diagonal block as a self adjoint view.
Definition: SymmetricBlockMatrix.h:140
void setOffDiagonalBlock(DenseIndex I, DenseIndex J, const XprType &xpr)
Set an off-diagonal block. Only the upper triangular portion of xpr is evaluated. ...
Definition: SymmetricBlockMatrix.h:206
Global functions in a separate testing namespace.
Definition: chartTesting.h:28
Concept check for values that can be used in unit tests.