rename of computeJacobians overloads to better reflect functionality

release/4.3a0
dellaert 2015-02-25 19:30:17 +01:00
parent c5394da29e
commit 850470ef06
6 changed files with 121 additions and 137 deletions

View File

@ -9,7 +9,7 @@
namespace gtsam { namespace gtsam {
/** /**
* JacobianFactor for Schur complement that uses Q noise model * JacobianFactor for Schur complement
*/ */
template<size_t D, size_t ZDim> template<size_t D, size_t ZDim>
class JacobianFactorSVD: public RegularJacobianFactor<D> { class JacobianFactorSVD: public RegularJacobianFactor<D> {
@ -38,7 +38,14 @@ public:
JacobianFactor::fillTerms(QF, zeroVector, model); JacobianFactor::fillTerms(QF, zeroVector, model);
} }
/// Constructor /**
* @brief Constructor
* Takes the CameraSet derivatives (as ZDim*D blocks of block-diagonal F)
* and a reduced point derivative, Enull
* and creates a reduced-rank Jacobian factor on the CameraSet
*
* @Fblocks:
*/
JacobianFactorSVD(const std::vector<KeyMatrixZD>& Fblocks, JacobianFactorSVD(const std::vector<KeyMatrixZD>& Fblocks,
const Matrix& Enull, const Vector& b, // const Matrix& Enull, const Vector& b, //
const SharedDiagonal& model = SharedDiagonal()) : const SharedDiagonal& model = SharedDiagonal()) :

View File

@ -81,6 +81,7 @@ protected:
boost::optional<Pose3> body_P_sensor_; boost::optional<Pose3> body_P_sensor_;
// check that noise model is isotropic and the same // check that noise model is isotropic and the same
// TODO, this is hacky, we should just do this via constructor, not add
void maybeSetNoiseModel(const SharedNoiseModel& sharedNoiseModel) { void maybeSetNoiseModel(const SharedNoiseModel& sharedNoiseModel) {
if (!sharedNoiseModel) if (!sharedNoiseModel)
return; return;
@ -213,6 +214,36 @@ public:
&& body_P_sensor_->equals(*e->body_P_sensor_))); && body_P_sensor_->equals(*e->body_P_sensor_)));
} }
/// Compute reprojection errors
Vector reprojectionError(const Cameras& cameras, const Point3& point) const {
return cameras.reprojectionError(point, measured_);
}
/**
* Compute reprojection errors and derivatives
* TODO: the treatment of body_P_sensor_ is weird: the transformation
* is applied in the caller, but the derivatives are computed here.
*/
Vector reprojectionError(const Cameras& cameras, const Point3& point,
typename Cameras::FBlocks& F, Matrix& E) const {
Vector b = cameras.reprojectionError(point, measured_, F, E);
// Apply sensor chain rule if needed TODO: no simpler way ??
if (body_P_sensor_) {
size_t m = keys_.size();
for (size_t i = 0; i < m; i++) {
const Pose3& pose_i = cameras[i].pose();
Pose3 w_Pose_body = pose_i.compose(body_P_sensor_->inverse());
Matrix66 J;
Pose3 world_P_body = w_Pose_body.compose(*body_P_sensor_, J);
F[i].leftCols(6) *= J;
}
}
return b;
}
/// Calculate vector of re-projection errors, noise model applied /// Calculate vector of re-projection errors, noise model applied
Vector whitenedErrors(const Cameras& cameras, const Point3& point) const { Vector whitenedErrors(const Cameras& cameras, const Point3& point) const {
Vector b = cameras.reprojectionError(point, measured_); Vector b = cameras.reprojectionError(point, measured_);
@ -251,36 +282,6 @@ public:
return 0.5 * b.dot(b); return 0.5 * b.dot(b);
} }
/// Compute reprojection errors
Vector reprojectionError(const Cameras& cameras, const Point3& point) const {
return cameras.reprojectionError(point, measured_);
}
/**
* Compute reprojection errors and derivatives
* TODO: the treatment of body_P_sensor_ is weird: the transformation
* is applied in the caller, but the derivatives are computed here.
*/
Vector reprojectionError(const Cameras& cameras, const Point3& point,
typename Cameras::FBlocks& F, Matrix& E) const {
Vector b = cameras.reprojectionError(point, measured_, F, E);
// Apply sensor chain rule if needed TODO: no simpler way ??
if (body_P_sensor_) {
size_t m = keys_.size();
for (size_t i = 0; i < m; i++) {
const Pose3& pose_i = cameras[i].pose();
Pose3 w_Pose_body = pose_i.compose(body_P_sensor_->inverse());
Matrix66 J;
Pose3 world_P_body = w_Pose_body.compose(*body_P_sensor_, J);
F[i].leftCols(6) *= J;
}
}
return b;
}
/// Computes Point Covariance P from E /// Computes Point Covariance P from E
static Matrix3 PointCov(Matrix& E) { static Matrix3 PointCov(Matrix& E) {
return (E.transpose() * E).inverse(); return (E.transpose() * E).inverse();
@ -371,7 +372,6 @@ public:
Eigen::JacobiSVD<Matrix> svd(E, Eigen::ComputeFullU); Eigen::JacobiSVD<Matrix> svd(E, Eigen::ComputeFullU);
Vector s = svd.singularValues(); Vector s = svd.singularValues();
size_t m = this->keys_.size(); size_t m = this->keys_.size();
// Enull = zeros(ZDim * m, ZDim * m - 3);
Enull = svd.matrixU().block(0, 3, ZDim * m, ZDim * m - 3); // last ZDim*m-3 columns Enull = svd.matrixU().block(0, 3, ZDim * m, ZDim * m - 3); // last ZDim*m-3 columns
return f; return f;
@ -661,7 +661,7 @@ public:
Matrix E; Matrix E;
Vector b; Vector b;
computeJacobians(F, E, b, cameras, point); computeJacobians(F, E, b, cameras, point);
noiseModel_->WhitenSystem(E,b); noiseModel_->WhitenSystem(E, b);
Matrix3 P = PointCov(E, lambda, diagonalDamping); Matrix3 P = PointCov(E, lambda, diagonalDamping);
// TODO make WhitenInPlace work with any dense matrix type // TODO make WhitenInPlace work with any dense matrix type
BOOST_FOREACH(KeyMatrix2D& Fblock,F) BOOST_FOREACH(KeyMatrix2D& Fblock,F)
@ -675,13 +675,15 @@ public:
boost::shared_ptr<JacobianFactorQ<Dim, ZDim> > createJacobianQFactor( boost::shared_ptr<JacobianFactorQ<Dim, ZDim> > createJacobianQFactor(
const Cameras& cameras, const Point3& point, double lambda = 0.0, const Cameras& cameras, const Point3& point, double lambda = 0.0,
bool diagonalDamping = false) const { bool diagonalDamping = false) const {
std::vector<KeyMatrix2D> Fblocks; std::vector<KeyMatrix2D> F;
Matrix E; Matrix E;
Vector b; Vector b;
computeJacobians(Fblocks, E, b, cameras, point); computeJacobians(F, E, b, cameras, point);
const size_t M = b.size();
std::cout << M << std::endl;
Matrix3 P = PointCov(E, lambda, diagonalDamping); Matrix3 P = PointCov(E, lambda, diagonalDamping);
return boost::make_shared<JacobianFactorQ<Dim, ZDim> > // SharedIsotropic n = noiseModel::Isotropic::Sigma(M, noiseModel_->sigma());
(Fblocks, E, P, b, noiseModel_); return boost::make_shared<JacobianFactorQ<Dim, ZDim> >(F, E, P, b, n);
} }
/** /**
@ -690,13 +692,15 @@ public:
*/ */
boost::shared_ptr<JacobianFactor> createJacobianSVDFactor( boost::shared_ptr<JacobianFactor> createJacobianSVDFactor(
const Cameras& cameras, const Point3& point, double lambda = 0.0) const { const Cameras& cameras, const Point3& point, double lambda = 0.0) const {
size_t numKeys = this->keys_.size(); size_t m = this->keys_.size();
std::vector<KeyMatrix2D> Fblocks; std::vector<KeyMatrix2D> F;
Vector b; Vector b;
Matrix Enull(ZDim * numKeys, ZDim * numKeys - 3); const size_t M = ZDim * m;
computeJacobiansSVD(Fblocks, Enull, b, cameras, point); Matrix E0(M, M - 3);
return boost::make_shared<JacobianFactorSVD<Dim, ZDim> > // computeJacobiansSVD(F, E0, b, cameras, point);
(Fblocks, Enull, b, noiseModel_); std::cout << M << std::endl;
SharedIsotropic n = noiseModel::Isotropic::Sigma(M, noiseModel_->sigma());
return boost::make_shared<JacobianFactorSVD<Dim, ZDim> >(F, E0, b, n);
} }
private: private:

View File

@ -368,7 +368,12 @@ public:
// ================================================================== // ==================================================================
Matrix F, E; Matrix F, E;
Vector b; Vector b;
double f = computeJacobians(F, E, b, cameras); double f;
{
std::vector<typename Base::KeyMatrix2D> Fblocks;
f = computeJacobiansWithTriangulatedPoint(Fblocks, E, b, cameras);
Base::FillDiagonalF(Fblocks,F); // expensive !
}
// Schur complement trick // Schur complement trick
// Frank says: should be possible to do this more efficiently? // Frank says: should be possible to do this more efficiently?
@ -486,21 +491,12 @@ public:
return nonDegenerate; return nonDegenerate;
} }
/// Version that takes values, and creates the point
bool computeJacobians(std::vector<typename Base::KeyMatrix2D>& Fblocks,
Matrix& E, Vector& b, const Values& values) const {
Cameras cameras;
bool nonDegenerate = computeCamerasAndTriangulate(values, cameras);
if (nonDegenerate)
computeJacobians(Fblocks, E, b, cameras);
return nonDegenerate;
}
/// Compute F, E only (called below in both vanilla and SVD versions) /// Compute F, E only (called below in both vanilla and SVD versions)
/// Assumes the point has been computed /// Assumes the point has been computed
/// Note E can be 2m*3 or 2m*2, in case point is degenerate /// Note E can be 2m*3 or 2m*2, in case point is degenerate
double computeJacobians(std::vector<typename Base::KeyMatrix2D>& Fblocks, double computeJacobiansWithTriangulatedPoint(
Matrix& E, Vector& b, const Cameras& cameras) const { std::vector<typename Base::KeyMatrix2D>& Fblocks, Matrix& E, Vector& b,
const Cameras& cameras) const {
if (this->degenerate_) { if (this->degenerate_) {
std::cout << "manage degeneracy " << manageDegeneracy_ << std::endl; std::cout << "manage degeneracy " << manageDegeneracy_ << std::endl;
@ -515,9 +511,9 @@ public:
} }
// TODO replace all this by Call to CameraSet // TODO replace all this by Call to CameraSet
int numKeys = this->keys_.size(); int m = this->keys_.size();
E = zeros(2 * numKeys, 2); E = zeros(2 * m, 2);
b = zero(2 * numKeys); b = zero(2 * m);
double f = 0; double f = 0;
for (size_t i = 0; i < this->measured_.size(); i++) { for (size_t i = 0; i < this->measured_.size(); i++) {
if (i == 0) { // first pose if (i == 0) { // first pose
@ -541,35 +537,27 @@ public:
} // end else } // end else
} }
/// Version that takes values, and creates the point
bool triangulateAndComputeJacobians(std::vector<typename Base::KeyMatrix2D>& Fblocks,
Matrix& E, Vector& b, const Values& values) const {
Cameras cameras;
bool nonDegenerate = computeCamerasAndTriangulate(values, cameras);
if (nonDegenerate)
computeJacobiansWithTriangulatedPoint(Fblocks, E, b, cameras);
return nonDegenerate;
}
/// takes values /// takes values
bool computeJacobiansSVD(std::vector<typename Base::KeyMatrix2D>& Fblocks, bool triangulateAndComputeJacobiansSVD(
Matrix& Enull, Vector& b, const Values& values) const { std::vector<typename Base::KeyMatrix2D>& Fblocks, Matrix& Enull,
Vector& b, const Values& values) const {
typename Base::Cameras cameras; typename Base::Cameras cameras;
double good = computeCamerasAndTriangulate(values, cameras); double good = computeCamerasAndTriangulate(values, cameras);
if (good) if (good)
computeJacobiansSVD(Fblocks, Enull, b, cameras); Base::computeJacobiansSVD(Fblocks, Enull, b, cameras, point_);
return true; return true;
} }
/// SVD version
double computeJacobiansSVD(std::vector<typename Base::KeyMatrix2D>& Fblocks,
Matrix& Enull, Vector& b, const Cameras& cameras) const {
return Base::computeJacobiansSVD(Fblocks, Enull, b, cameras, point_);
}
/// Returns Matrix, TODO: maybe should not exist -> not sparse !
// TODO should there be a lambda?
double computeJacobiansSVD(Matrix& F, Matrix& Enull, Vector& b,
const Cameras& cameras) const {
return Base::computeJacobiansSVD(F, Enull, b, cameras, point_);
}
/// Returns Matrix, TODO: maybe should not exist -> not sparse !
double computeJacobians(Matrix& F, Matrix& E, Vector& b,
const Cameras& cameras) const {
return Base::computeJacobians(F, E, b, cameras, point_);
}
/// Calculate vector of re-projection errors, before applying noise model /// Calculate vector of re-projection errors, before applying noise model
Vector reprojectionErrorAfterTriangulation(const Values& values) const { Vector reprojectionErrorAfterTriangulation(const Values& values) const {
Cameras cameras; Cameras cameras;
@ -652,6 +640,7 @@ public:
inline bool isPointBehindCamera() const { inline bool isPointBehindCamera() const {
return cheiralityException_; return cheiralityException_;
} }
/** return cheirality verbosity */ /** return cheirality verbosity */
inline bool verboseCheirality() const { inline bool verboseCheirality() const {
return verboseCheirality_; return verboseCheirality_;

View File

@ -754,17 +754,17 @@ TEST( SmartProjectionCameraFactor, computeImplicitJacobian ) {
cameras.push_back(level_camera_right); cameras.push_back(level_camera_right);
factor1->error(values); // this is important to have a triangulation of the point factor1->error(values); // this is important to have a triangulation of the point
Point3 expectedPoint; Point3 point;
if (factor1->point()) if (factor1->point())
expectedPoint = *(factor1->point()); point = *(factor1->point());
factor1->computeJacobians(expectedF, expectedE, expectedb, cameras); factor1->computeJacobians(expectedF, expectedE, expectedb, cameras, point);
NonlinearFactorGraph generalGraph; NonlinearFactorGraph generalGraph;
SFMFactor sfm1(level_uv, unit2, c1, l1); SFMFactor sfm1(level_uv, unit2, c1, l1);
SFMFactor sfm2(level_uv_right, unit2, c2, l1); SFMFactor sfm2(level_uv_right, unit2, c2, l1);
generalGraph.push_back(sfm1); generalGraph.push_back(sfm1);
generalGraph.push_back(sfm2); generalGraph.push_back(sfm2);
values.insert(l1, expectedPoint); // note: we get rid of possible errors in the triangulation values.insert(l1, point); // note: we get rid of possible errors in the triangulation
Matrix actualFullHessian = generalGraph.linearize(values)->hessian().first; Matrix actualFullHessian = generalGraph.linearize(values)->hessian().first;
Matrix actualVinv = (actualFullHessian.block(18, 18, 3, 3)).inverse(); Matrix actualVinv = (actualFullHessian.block(18, 18, 3, 3)).inverse();

View File

@ -153,7 +153,7 @@ TEST_UNSAFE( SmartProjectionPoseFactor, noiseless ) {
// Calculate using computeJacobians // Calculate using computeJacobians
Vector b; Vector b;
vector<SmartFactor::KeyMatrix2D> Fblocks; vector<SmartFactor::KeyMatrix2D> Fblocks;
double actualError3 = factor.computeJacobians(Fblocks, E, b, cameras); double actualError3 = factor.computeJacobians(Fblocks, E, b, cameras, *point);
EXPECT(assert_equal(expectedE, E, 1e-7)); EXPECT(assert_equal(expectedE, E, 1e-7));
EXPECT_DOUBLES_EQUAL(expectedError, actualError3, 1e-8); EXPECT_DOUBLES_EQUAL(expectedError, actualError3, 1e-8);
} }
@ -364,19 +364,19 @@ TEST( SmartProjectionPoseFactor, smartFactorWithSensorBodyTransform ) {
// Calculate expected derivative for point (easiest to check) // Calculate expected derivative for point (easiest to check)
SmartFactor::Cameras cameras = smartFactor1->cameras(values); SmartFactor::Cameras cameras = smartFactor1->cameras(values);
boost::function<Vector(Point3)> f = // boost::function<Vector(Point3)> f = //
boost::bind(&SmartFactor::whitenedErrors, *smartFactor1, cameras, _1); boost::bind(&SmartFactor::reprojectionError, *smartFactor1, cameras, _1);
boost::optional<Point3> point = smartFactor1->point(); boost::optional<Point3> point = smartFactor1->point();
CHECK(point); CHECK(point);
// Note ! After refactor the noiseModel is only in the factors, not these matrices // TODO, this is really a test of CameraSet
Matrix expectedE = sigma * numericalDerivative11<Vector, Point3>(f, *point); Matrix expectedE = numericalDerivative11<Vector, Point3>(f, *point);
// Calculate using computeEP // Calculate using computeEP
Matrix actualE, PointCov; Matrix actualE, PointCov;
smartFactor1->computeEP(actualE, PointCov, values); smartFactor1->computeEP(actualE, PointCov, values);
EXPECT(assert_equal(expectedE, actualE, 1e-7)); EXPECT(assert_equal(expectedE, actualE, 1e-7));
// Calculate using whitenedError // Calculate using reprojectionError
Matrix E; Matrix E;
SmartFactor::Cameras::FBlocks F; SmartFactor::Cameras::FBlocks F;
Vector actualErrors = smartFactor1->reprojectionError(cameras, *point, F, E); Vector actualErrors = smartFactor1->reprojectionError(cameras, *point, F, E);
@ -472,7 +472,7 @@ TEST( SmartProjectionPoseFactor, Factors ) {
E(2, 2) = 10; E(2, 2) = 10;
E(3, 1) = 100; E(3, 1) = 100;
const vector<pair<Key, Matrix26> > Fblocks = list_of<pair<Key, Matrix> > // const vector<pair<Key, Matrix26> > Fblocks = list_of<pair<Key, Matrix> > //
(make_pair(x1, 10 * F1))(make_pair(x2, 10 * F2)); (make_pair(x1, F1))(make_pair(x2, F2));
Matrix3 P = (E.transpose() * E).inverse(); Matrix3 P = (E.transpose() * E).inverse();
Vector4 b; Vector4 b;
b.setZero(); b.setZero();
@ -483,10 +483,11 @@ TEST( SmartProjectionPoseFactor, Factors ) {
boost::shared_ptr<RegularImplicitSchurFactor<6> > actual = boost::shared_ptr<RegularImplicitSchurFactor<6> > actual =
smartFactor1->createRegularImplicitSchurFactor(cameras, 0.0); smartFactor1->createRegularImplicitSchurFactor(cameras, 0.0);
CHECK(actual); CHECK(actual);
CHECK(assert_equal(expected, *actual)); EXPECT(assert_equal(expected, *actual));
// createJacobianQFactor // createJacobianQFactor
JacobianFactorQ<6, 2> expectedQ(Fblocks, E, P, b); SharedIsotropic n = noiseModel::Isotropic::Sigma(4, sigma);
JacobianFactorQ<6, 2> expectedQ(Fblocks, E, P, b, n);
boost::shared_ptr<JacobianFactorQ<6, 2> > actualQ = boost::shared_ptr<JacobianFactorQ<6, 2> > actualQ =
smartFactor1->createJacobianQFactor(cameras, 0.0); smartFactor1->createJacobianQFactor(cameras, 0.0);
@ -890,8 +891,10 @@ TEST( SmartProjectionPoseFactor, CheckHessian) {
using namespace vanillaPose; using namespace vanillaPose;
// Two slightly different cameras // Two slightly different cameras
Pose3 pose2 = level_pose * Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0)); Pose3 pose2 = level_pose
Pose3 pose3 = level_pose * Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0)); * Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0));
Pose3 pose3 = level_pose
* Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0));
Camera cam2(pose2, sharedK); Camera cam2(pose2, sharedK);
Camera cam3(pose3, sharedK); Camera cam3(pose3, sharedK);
@ -970,8 +973,10 @@ TEST( SmartProjectionPoseFactor, 3poses_2land_rotation_only_smart_projection_fac
views.push_back(x3); views.push_back(x3);
// Two different cameras // Two different cameras
Pose3 pose2 = level_pose * Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0)); Pose3 pose2 = level_pose
Pose3 pose3 = level_pose * Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0)); * Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0));
Pose3 pose3 = level_pose
* Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0));
Camera cam2(pose2, sharedK2); Camera cam2(pose2, sharedK2);
Camera cam3(pose3, sharedK2); Camera cam3(pose3, sharedK2);
@ -1050,8 +1055,10 @@ TEST( SmartProjectionPoseFactor, 3poses_rotation_only_smart_projection_factor )
views.push_back(x3); views.push_back(x3);
// Two different cameras // Two different cameras
Pose3 pose2 = level_pose * Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0)); Pose3 pose2 = level_pose
Pose3 pose3 = level_pose * Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0)); * Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0));
Pose3 pose3 = level_pose
* Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0));
Camera cam2(pose2, sharedK); Camera cam2(pose2, sharedK);
Camera cam3(pose3, sharedK); Camera cam3(pose3, sharedK);
@ -1390,8 +1397,10 @@ TEST( SmartProjectionPoseFactor, Cal3BundlerRotationOnly ) {
views.push_back(x3); views.push_back(x3);
// Two different cameras // Two different cameras
Pose3 pose2 = level_pose * Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0)); Pose3 pose2 = level_pose
Pose3 pose3 = level_pose * Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0)); * Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0));
Pose3 pose3 = level_pose
* Pose3(Rot3::RzRyRx(-0.05, 0.0, -0.05), Point3(0, 0, 0));
Camera cam2(pose2, sharedBundlerK); Camera cam2(pose2, sharedBundlerK);
Camera cam3(pose3, sharedBundlerK); Camera cam3(pose3, sharedBundlerK);

View File

@ -582,41 +582,16 @@ public:
} // end else } // end else
} }
// /// Version that computes PointCov, with optional lambda parameter /// takes values
// double computeJacobians(std::vector<typename Base::KeyMatrix2D>& Fblocks, bool triangulateAndComputeJacobiansSVD(
// Matrix& E, Matrix& PointCov, Vector& b, const Cameras& cameras, std::vector<typename Base::KeyMatrix2D>& Fblocks, Matrix& Enull,
// const double lambda = 0.0) const { Vector& b, const Values& values) const {
// typename Base::Cameras cameras;
// double f = computeJacobians(Fblocks, E, b, cameras); double good = computeCamerasAndTriangulate(values, cameras);
// if (good)
// // Point covariance inv(E'*E) return Base::computeJacobiansSVD(Fblocks, Enull, b, cameras, point_);
// PointCov.noalias() = (E.transpose() * E + lambda * eye(E.cols())).inverse(); return true;
// }
// return f;
// }
//
// /// takes values
// bool computeJacobiansSVD(std::vector<typename Base::KeyMatrix2D>& Fblocks,
// Matrix& Enull, Vector& b, const Values& values) const {
// typename Base::Cameras cameras;
// double good = computeCamerasAndTriangulate(values, cameras);
// if (good)
// computeJacobiansSVD(Fblocks, Enull, b, cameras);
// return true;
// }
//
// /// SVD version
// double computeJacobiansSVD(std::vector<typename Base::KeyMatrix2D>& Fblocks,
// Matrix& Enull, Vector& b, const Cameras& cameras) const {
// return Base::computeJacobiansSVD(Fblocks, Enull, b, cameras, point_);
// }
//
// /// Returns Matrix, TODO: maybe should not exist -> not sparse !
// // TODO should there be a lambda?
// double computeJacobiansSVD(Matrix& F, Matrix& Enull, Vector& b,
// const Cameras& cameras) const {
// return Base::computeJacobiansSVD(F, Enull, b, cameras, point_);
// }
/// Returns Matrix, TODO: maybe should not exist -> not sparse ! /// Returns Matrix, TODO: maybe should not exist -> not sparse !
double computeJacobians(Matrix& F, Matrix& E, Vector& b, double computeJacobians(Matrix& F, Matrix& E, Vector& b,