From 25ad9ade05d1645ac2d9cebcf65b98695287142a Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 21 Oct 2014 10:42:30 +0200 Subject: [PATCH] Moved AdaptAutoDiff into its own test --- .cproject | 106 ++-- .../nonlinear/tests/testAdaptAutoDiff.cpp | 460 ++++++++++++++++++ .../nonlinear/tests/testExpression.cpp | 381 --------------- 3 files changed, 522 insertions(+), 425 deletions(-) create mode 100644 gtsam_unstable/nonlinear/tests/testAdaptAutoDiff.cpp diff --git a/.cproject b/.cproject index 7223156ff..38c4c66f4 100644 --- a/.cproject +++ b/.cproject @@ -600,6 +600,7 @@ make + tests/testBayesTree.run true false @@ -607,6 +608,7 @@ make + testBinaryBayesNet.run true false @@ -654,6 +656,7 @@ make + testSymbolicBayesNet.run true false @@ -661,6 +664,7 @@ make + tests/testSymbolicFactor.run true false @@ -668,6 +672,7 @@ make + testSymbolicFactorGraph.run true false @@ -683,6 +688,7 @@ make + tests/testBayesTree true false @@ -1042,6 +1048,7 @@ make + testErrors.run true false @@ -1271,6 +1278,46 @@ true true + + make + -j5 + testBTree.run + true + true + true + + + make + -j5 + testDSF.run + true + true + true + + + make + -j5 + testDSFMap.run + true + true + true + + + make + -j5 + testDSFVector.run + true + true + true + + + make + -j5 + testFixedVector.run + true + true + true + make -j2 @@ -1353,7 +1400,6 @@ make - testSimulated2DOriented.run true false @@ -1393,7 +1439,6 @@ make - testSimulated2D.run true false @@ -1401,7 +1446,6 @@ make - testSimulated3D.run true false @@ -1415,46 +1459,6 @@ true true - - make - -j5 - testBTree.run - true - true - true - - - make - -j5 - testDSF.run - true - true - true - - - make - -j5 - testDSFMap.run - true - true - true - - - make - -j5 - testDSFVector.run - true - true - true - - - make - -j5 - testFixedVector.run - true - true - true - make -j5 @@ -1712,6 +1716,7 @@ cpack + -G DEB true false @@ -1719,6 +1724,7 @@ cpack + -G RPM true false @@ -1726,6 +1732,7 @@ cpack + -G TGZ true false @@ -1733,6 +1740,7 @@ cpack + --config CPackSourceConfig.cmake true false @@ -2459,6 +2467,7 @@ make + testGraph.run true false @@ -2466,6 +2475,7 @@ make + testJunctionTree.run true false @@ -2473,6 +2483,7 @@ make + testSymbolicBayesNetB.run true false @@ -2566,6 +2577,14 @@ true true + + make + -j5 + testAdaptAutoDiff.run + true + true + true + make -j2 @@ -2968,7 +2987,6 @@ make - tests/testGaussianISAM2 true false diff --git a/gtsam_unstable/nonlinear/tests/testAdaptAutoDiff.cpp b/gtsam_unstable/nonlinear/tests/testAdaptAutoDiff.cpp new file mode 100644 index 000000000..45267bf81 --- /dev/null +++ b/gtsam_unstable/nonlinear/tests/testAdaptAutoDiff.cpp @@ -0,0 +1,460 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------1------------------------------------------- */ + +/** + * @file testExpression.cpp + * @date September 18, 2014 + * @author Frank Dellaert + * @author Paul Furgale + * @brief unit tests for Block Automatic Differentiation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#undef CHECK +#include + +#include +using boost::assign::list_of; +using boost::assign::map_list_of; + +using namespace std; +using namespace gtsam; + +// The DefaultChart of Camera below is laid out like Snavely's 9-dim vector +typedef PinholeCamera Camera; + +/* ************************************************************************* */ +// Some Ceres Snippets copied for testing +// Copyright 2010, 2011, 2012 Google Inc. All rights reserved. +template inline T &RowMajorAccess(T *base, int rows, int cols, + int i, int j) { + return base[cols * i + j]; +} + +inline double RandDouble() { + double r = static_cast(rand()); + return r / RAND_MAX; +} + +// A structure for projecting a 3x4 camera matrix and a +// homogeneous 3D point, to a 2D inhomogeneous point. +struct Projective { + // Function that takes P and X as separate vectors: + // P, X -> x + template + bool operator()(A const P[12], A const X[4], A x[2]) const { + A PX[3]; + for (int i = 0; i < 3; ++i) { + PX[i] = RowMajorAccess(P, 3, 4, i, 0) * X[0] + + RowMajorAccess(P, 3, 4, i, 1) * X[1] + + RowMajorAccess(P, 3, 4, i, 2) * X[2] + + RowMajorAccess(P, 3, 4, i, 3) * X[3]; + } + if (PX[2] != 0.0) { + x[0] = PX[0] / PX[2]; + x[1] = PX[1] / PX[2]; + return true; + } + return false; + } + + // Adapt to eigen types + Vector2 operator()(const MatrixRowMajor& P, const Vector4& X) const { + Vector2 x; + if (operator()(P.data(), X.data(), x.data())) + return x; + else + throw std::runtime_error("Projective fail"); + } +}; + +// Templated pinhole camera model for used with Ceres. The camera is +// parameterized using 9 parameters: 3 for rotation, 3 for translation, 1 for +// focal length and 2 for radial distortion. The principal point is not modeled +// (i.e. it is assumed be located at the image center). +struct SnavelyProjection { + + template + bool operator()(const T* const camera, const T* const point, + T* predicted) const { + // camera[0,1,2] are the angle-axis rotation. + T p[3]; + ceres::AngleAxisRotatePoint(camera, point, p); + + // camera[3,4,5] are the translation. + p[0] += camera[3]; + p[1] += camera[4]; + p[2] += camera[5]; + + // Compute the center of distortion. The sign change comes from + // the camera model that Noah Snavely's Bundler assumes, whereby + // the camera coordinate system has a negative z axis. + T xp = -p[0] / p[2]; + T yp = -p[1] / p[2]; + + // Apply second and fourth order radial distortion. + const T& l1 = camera[7]; + const T& l2 = camera[8]; + T r2 = xp * xp + yp * yp; + T distortion = T(1.0) + r2 * (l1 + l2 * r2); + + // Compute final projected point position. + const T& focal = camera[6]; + predicted[0] = focal * distortion * xp; + predicted[1] = focal * distortion * yp; + + return true; + } + + // Adapt to GTSAM types + Vector2 operator()(const Vector9& P, const Vector3& X) const { + Vector2 x; + if (operator()(P.data(), X.data(), x.data())) + return x; + else + throw std::runtime_error("Snavely fail"); + } + +}; + +/* ************************************************************************* */ +// is_manifold +TEST(Manifold, _is_manifold) { + using namespace traits; + EXPECT(!is_manifold::value); + EXPECT(is_manifold::value); + EXPECT(is_manifold::value); + EXPECT(is_manifold::value); + EXPECT(is_manifold::value); + EXPECT(is_manifold::value); +} + +/* ************************************************************************* */ +// dimension +TEST(Manifold, _dimension) { + using namespace traits; + EXPECT_LONGS_EQUAL(2, dimension::value); + EXPECT_LONGS_EQUAL(8, dimension::value); + EXPECT_LONGS_EQUAL(1, dimension::value); + EXPECT_LONGS_EQUAL(Eigen::Dynamic, dimension::value); + EXPECT_LONGS_EQUAL(Eigen::Dynamic, dimension::value); +} + +/* ************************************************************************* */ +// charts +TEST(Manifold, DefaultChart) { + + DefaultChart chart1(Point2(0, 0)); + EXPECT(chart1.apply(Point2(1,0))==Vector2(1,0)); + EXPECT(chart1.retract(Vector2(1,0))==Point2(1,0)); + + DefaultChart chart2(Vector2(0, 0)); + EXPECT(chart2.apply(Vector2(1,0))==Vector2(1,0)); + EXPECT(chart2.retract(Vector2(1,0))==Vector2(1,0)); + + DefaultChart chart3(0); + Eigen::Matrix v1; + v1 << 1; + EXPECT(chart3.apply(1)==v1); + EXPECT(chart3.retract(v1)==1); + + // Dynamic does not work yet ! +// Vector z = zero(2), v(2); +// v << 1, 0; +// DefaultChart chart4(z); +// EXPECT(chart4.apply(v)==v); +// EXPECT(chart4.retract(v)==v); +} + +/* ************************************************************************* */ +// zero +TEST(Manifold, _zero) { + EXPECT(assert_equal(Pose3(),traits::zero::value())); + Cal3Bundler cal(0,0,0); + EXPECT(assert_equal(cal,traits::zero::value())); + EXPECT(assert_equal(Camera(Pose3(),cal),traits::zero::value())); +} + +/* ************************************************************************* */ +// charts +TEST(Manifold, Canonical) { + + Canonical chart1; + EXPECT(chart1.apply(Point2(1,0))==Vector2(1,0)); + EXPECT(chart1.retract(Vector2(1,0))==Point2(1,0)); + + Canonical chart2; + EXPECT(chart2.apply(Vector2(1,0))==Vector2(1,0)); + EXPECT(chart2.retract(Vector2(1,0))==Vector2(1,0)); + + Canonical chart3; + Eigen::Matrix v1; + v1 << 1; + EXPECT(chart3.apply(1)==v1); + EXPECT(chart3.retract(v1)==1); + + // Dynamic does not work yet ! +// Vector z = zero(2), v(2); +// v << 1, 0; +// Canonical chart4(z); +// EXPECT(chart4.apply(v)==v); +// EXPECT(chart4.retract(v)==v); +} + +/* ************************************************************************* */ +// New-style numerical derivatives using manifold_traits +template +Matrix numericalDerivative(boost::function h, const X& x, + double delta = 1e-5) { + using namespace traits; + + BOOST_STATIC_ASSERT(is_manifold::value); + static const size_t M = dimension::value; + typedef DefaultChart ChartY; + typedef typename ChartY::vector TangentY; + + BOOST_STATIC_ASSERT(is_manifold::value); + static const size_t N = dimension::value; + typedef DefaultChart ChartX; + typedef typename ChartX::vector TangentX; + + // get chart at x + ChartX chartX(x); + + // get value at x, and corresponding chart + Y hx = h(x); + ChartY chartY(hx); + + // Prepare a tangent vector to perturb x with + TangentX dx; + dx.setZero(); + + // Fill in Jacobian H + Matrix H = zeros(M, N); + double factor = 1.0 / (2.0 * delta); + for (size_t j = 0; j < N; j++) { + dx(j) = delta; + TangentY dy1 = chartY.apply(h(chartX.retract(dx))); + dx(j) = -delta; + TangentY dy2 = chartY.apply(h(chartX.retract(dx))); + H.block(0, j) << (dy1 - dy2) * factor; + dx(j) = 0; + } + return H; +} + +template +Matrix numericalDerivative21(boost::function h, + const X1& x1, const X2& x2, double delta = 1e-5) { + return numericalDerivative(boost::bind(h, _1, x2), x1, delta); +} + +template +Matrix numericalDerivative22(boost::function h, + const X1& x1, const X2& x2, double delta = 1e-5) { + return numericalDerivative(boost::bind(h, x1, _1), x2, delta); +} + +/* ************************************************************************* */ +// Test Ceres AutoDiff +TEST(Expression, AutoDiff) { + using ceres::internal::AutoDiff; + + // Instantiate function + Projective projective; + + // Make arguments + typedef Eigen::Matrix M; + M P; + P << 1, 0, 0, 0, 0, 1, 0, 5, 0, 0, 1, 0; + Vector4 X(10, 0, 5, 1); + + // Apply the mapping, to get image point b_x. + Vector expected = Vector2(2, 1); + Vector2 actual = projective(P, X); + EXPECT(assert_equal(expected,actual,1e-9)); + + // Get expected derivatives + Matrix E1 = numericalDerivative21(Projective(), P, X); + Matrix E2 = numericalDerivative22(Projective(), P, X); + + // Get derivatives with AutoDiff + Vector2 actual2; + MatrixRowMajor H1(2, 12), H2(2, 4); + double *parameters[] = { P.data(), X.data() }; + double *jacobians[] = { H1.data(), H2.data() }; + CHECK( + (AutoDiff::Differentiate( projective, parameters, 2, actual2.data(), jacobians))); + EXPECT(assert_equal(E1,H1,1e-8)); + EXPECT(assert_equal(E2,H2,1e-8)); +} + +/* ************************************************************************* */ +// Test Ceres AutoDiff on Snavely +TEST(Expression, AutoDiff2) { + using ceres::internal::AutoDiff; + + // Instantiate function + SnavelyProjection snavely; + + // Make arguments + Vector9 P; // zero rotation, (0,5,0) translation, focal length 1 + P << 0, 0, 0, 0, 5, 0, 1, 0, 0; + Vector3 X(10, 0, -5); // negative Z-axis convention of Snavely! + + // Apply the mapping, to get image point b_x. + Vector expected = Vector2(2, 1); + Vector2 actual = snavely(P, X); + EXPECT(assert_equal(expected,actual,1e-9)); + + // Get expected derivatives + Matrix E1 = numericalDerivative21( + SnavelyProjection(), P, X); + Matrix E2 = numericalDerivative22( + SnavelyProjection(), P, X); + + // Get derivatives with AutoDiff + Vector2 actual2; + MatrixRowMajor H1(2, 9), H2(2, 3); + double *parameters[] = { P.data(), X.data() }; + double *jacobians[] = { H1.data(), H2.data() }; + CHECK( + (AutoDiff::Differentiate( snavely, parameters, 2, actual2.data(), jacobians))); + EXPECT(assert_equal(E1,H1,1e-8)); + EXPECT(assert_equal(E2,H2,1e-8)); +} + +/* ************************************************************************* */ +// Adapt ceres-style autodiff +template +class AdaptAutoDiff { + + static const int N = traits::dimension::value; + static const int M1 = traits::dimension::value; + static const int M2 = traits::dimension::value; + + typedef Eigen::Matrix RowMajor1; + typedef Eigen::Matrix RowMajor2; + + typedef Canonical CanonicalT; + typedef Canonical Canonical1; + typedef Canonical Canonical2; + + typedef typename CanonicalT::vector VectorT; + typedef typename Canonical1::vector Vector1; + typedef typename Canonical2::vector Vector2; + + // Instantiate function and charts + CanonicalT chartT; + Canonical1 chart1; + Canonical2 chart2; + F f; + +public: + + typedef Eigen::Matrix JacobianTA1; + typedef Eigen::Matrix JacobianTA2; + + T operator()(const A1& a1, const A2& a2, boost::optional H1 = + boost::none, boost::optional H2 = boost::none) { + + using ceres::internal::AutoDiff; + + // Make arguments + Vector1 v1 = chart1.apply(a1); + Vector2 v2 = chart2.apply(a2); + + bool success; + VectorT result; + + if (H1 || H2) { + + // Get derivatives with AutoDiff + double *parameters[] = { v1.data(), v2.data() }; + double rowMajor1[N * M1], rowMajor2[N * M2]; // om the stack + double *jacobians[] = { rowMajor1, rowMajor2 }; + success = AutoDiff::Differentiate(f, parameters, 2, + result.data(), jacobians); + + // Convert from row-major to columnn-major + // TODO: if this is a bottleneck (probably not!) fix Autodiff to be Column-Major + *H1 = Eigen::Map(rowMajor1); + *H2 = Eigen::Map(rowMajor2); + + } else { + // Apply the mapping, to get result + success = f(v1.data(), v2.data(), result.data()); + } + if (!success) + throw std::runtime_error( + "AdaptAutoDiff: function call resulted in failure"); + return chartT.retract(result); + } + +}; + +/* ************************************************************************* */ +// Test AutoDiff wrapper Snavely +TEST(Expression, AutoDiff3) { + + // Make arguments + Camera P(Pose3(Rot3(), Point3(0, 5, 0)), Cal3Bundler(1, 0, 0)); + Point3 X(10, 0, -5); // negative Z-axis convention of Snavely! + + typedef AdaptAutoDiff Adaptor; + Adaptor snavely; + + // Apply the mapping, to get image point b_x. + Point2 expected(2, 1); + Point2 actual = snavely(P, X); + EXPECT(assert_equal(expected,actual,1e-9)); + +// // Get expected derivatives + Matrix E1 = numericalDerivative21(Adaptor(), P, X); + Matrix E2 = numericalDerivative22(Adaptor(), P, X); + + // Get derivatives with AutoDiff, not gives RowMajor results! + Matrix29 H1; + Matrix23 H2; + Point2 actual2 = snavely(P, X, H1, H2); + EXPECT(assert_equal(expected,actual,1e-9)); + EXPECT(assert_equal(E1,H1,1e-8)); + EXPECT(assert_equal(E2,H2,1e-8)); +} + +/* ************************************************************************* */ +// Test AutoDiff wrapper in an expression +TEST(Expression, Snavely) { + Expression P(1); + Expression X(2); + typedef AdaptAutoDiff Adaptor; + Expression expression(Adaptor(), P, X); + set expected = list_of(1)(2); + EXPECT(expected == expression.keys()); +} + +/* ************************************************************************* */ +int main() { + TestResult tr; + return TestRegistry::runAllTests(tr); +} +/* ************************************************************************* */ + diff --git a/gtsam_unstable/nonlinear/tests/testExpression.cpp b/gtsam_unstable/nonlinear/tests/testExpression.cpp index 68765ecc0..1997bdb53 100644 --- a/gtsam_unstable/nonlinear/tests/testExpression.cpp +++ b/gtsam_unstable/nonlinear/tests/testExpression.cpp @@ -18,17 +18,11 @@ */ #include -#include #include -#include #include #include #include -#include -#include - -#undef CHECK #include #include @@ -229,381 +223,6 @@ TEST(Expression, ternary) { EXPECT(expected == ABC.keys()); } -/* ************************************************************************* */ -// Some Ceres Snippets copied for testing -// Copyright 2010, 2011, 2012 Google Inc. All rights reserved. -template inline T &RowMajorAccess(T *base, int rows, int cols, - int i, int j) { - return base[cols * i + j]; -} - -inline double RandDouble() { - double r = static_cast(rand()); - return r / RAND_MAX; -} - -// A structure for projecting a 3x4 camera matrix and a -// homogeneous 3D point, to a 2D inhomogeneous point. -struct Projective { - // Function that takes P and X as separate vectors: - // P, X -> x - template - bool operator()(A const P[12], A const X[4], A x[2]) const { - A PX[3]; - for (int i = 0; i < 3; ++i) { - PX[i] = RowMajorAccess(P, 3, 4, i, 0) * X[0] - + RowMajorAccess(P, 3, 4, i, 1) * X[1] - + RowMajorAccess(P, 3, 4, i, 2) * X[2] - + RowMajorAccess(P, 3, 4, i, 3) * X[3]; - } - if (PX[2] != 0.0) { - x[0] = PX[0] / PX[2]; - x[1] = PX[1] / PX[2]; - return true; - } - return false; - } - - // Adapt to eigen types - Vector2 operator()(const MatrixRowMajor& P, const Vector4& X) const { - Vector2 x; - if (operator()(P.data(), X.data(), x.data())) - return x; - else - throw std::runtime_error("Projective fail"); - } -}; - -// Templated pinhole camera model for used with Ceres. The camera is -// parameterized using 9 parameters: 3 for rotation, 3 for translation, 1 for -// focal length and 2 for radial distortion. The principal point is not modeled -// (i.e. it is assumed be located at the image center). -struct SnavelyProjection { - - template - bool operator()(const T* const camera, const T* const point, - T* predicted) const { - // camera[0,1,2] are the angle-axis rotation. - T p[3]; - ceres::AngleAxisRotatePoint(camera, point, p); - - // camera[3,4,5] are the translation. - p[0] += camera[3]; - p[1] += camera[4]; - p[2] += camera[5]; - - // Compute the center of distortion. The sign change comes from - // the camera model that Noah Snavely's Bundler assumes, whereby - // the camera coordinate system has a negative z axis. - T xp = -p[0] / p[2]; - T yp = -p[1] / p[2]; - - // Apply second and fourth order radial distortion. - const T& l1 = camera[7]; - const T& l2 = camera[8]; - T r2 = xp * xp + yp * yp; - T distortion = T(1.0) + r2 * (l1 + l2 * r2); - - // Compute final projected point position. - const T& focal = camera[6]; - predicted[0] = focal * distortion * xp; - predicted[1] = focal * distortion * yp; - - return true; - } - - // Adapt to GTSAM types - Vector2 operator()(const Vector9& P, const Vector3& X) const { - Vector2 x; - if (operator()(P.data(), X.data(), x.data())) - return x; - else - throw std::runtime_error("Snavely fail"); - } - -}; - -/* ************************************************************************* */ - -// is_manifold -TEST(Expression, is_manifold) { - using namespace traits; - EXPECT(!is_manifold::value); - EXPECT(is_manifold::value); - EXPECT(is_manifold::value); - EXPECT(is_manifold::value); - EXPECT(is_manifold::value); - EXPECT(is_manifold::value); -} - -// dimension -TEST(Expression, dimension) { - using namespace traits; - EXPECT_LONGS_EQUAL(2, dimension::value); - EXPECT_LONGS_EQUAL(8, dimension::value); - EXPECT_LONGS_EQUAL(1, dimension::value); - EXPECT_LONGS_EQUAL(Eigen::Dynamic, dimension::value); - EXPECT_LONGS_EQUAL(Eigen::Dynamic, dimension::value); -} - -// charts -TEST(Expression, Charts) { - - DefaultChart chart1(Point2(0, 0)); - EXPECT(chart1.apply(Point2(1,0))==Vector2(1,0)); - EXPECT(chart1.retract(Vector2(1,0))==Point2(1,0)); - - DefaultChart chart2(Vector2(0, 0)); - EXPECT(chart2.apply(Vector2(1,0))==Vector2(1,0)); - EXPECT(chart2.retract(Vector2(1,0))==Vector2(1,0)); - - DefaultChart chart3(0); - Eigen::Matrix v1; - v1 << 1; - EXPECT(chart3.apply(1)==v1); - EXPECT(chart3.retract(v1)==1); - - // Dynamic does not work yet ! -// Vector z = zero(2), v(2); -// v << 1, 0; -// DefaultChart chart4(z); -// EXPECT(chart4.apply(v)==v); -// EXPECT(chart4.retract(v)==v); -} - -/* ************************************************************************* */ -// New-style numerical derivatives using manifold_traits -template -Matrix numericalDerivative(boost::function h, const X& x, - double delta = 1e-5) { - using namespace traits; - - BOOST_STATIC_ASSERT(is_manifold::value); - static const size_t M = dimension::value; - typedef DefaultChart ChartY; - typedef typename ChartY::vector TangentY; - - BOOST_STATIC_ASSERT(is_manifold::value); - static const size_t N = dimension::value; - typedef DefaultChart ChartX; - typedef typename ChartX::vector TangentX; - - // get chart at x - ChartX chartX(x); - - // get value at x, and corresponding chart - Y hx = h(x); - ChartY chartY(hx); - - // Prepare a tangent vector to perturb x with - TangentX dx; - dx.setZero(); - - // Fill in Jacobian H - Matrix H = zeros(M, N); - double factor = 1.0 / (2.0 * delta); - for (size_t j = 0; j < N; j++) { - dx(j) = delta; - TangentY dy1 = chartY.apply(h(chartX.retract(dx))); - dx(j) = -delta; - TangentY dy2 = chartY.apply(h(chartX.retract(dx))); - H.block(0, j) << (dy1 - dy2) * factor; - dx(j) = 0; - } - return H; -} - -template -Matrix numericalDerivative21(boost::function h, - const X1& x1, const X2& x2, double delta = 1e-5) { - return numericalDerivative(boost::bind(h, _1, x2), x1, delta); -} - -template -Matrix numericalDerivative22(boost::function h, - const X1& x1, const X2& x2, double delta = 1e-5) { - return numericalDerivative(boost::bind(h, x1, _1), x2, delta); -} - -/* ************************************************************************* */ -// Test Ceres AutoDiff -TEST(Expression, AutoDiff) { - using ceres::internal::AutoDiff; - - // Instantiate function - Projective projective; - - // Make arguments - typedef Eigen::Matrix M; - M P; - P << 1, 0, 0, 0, 0, 1, 0, 5, 0, 0, 1, 0; - Vector4 X(10, 0, 5, 1); - - // Apply the mapping, to get image point b_x. - Vector expected = Vector2(2, 1); - Vector2 actual = projective(P, X); - EXPECT(assert_equal(expected,actual,1e-9)); - - // Get expected derivatives - Matrix E1 = numericalDerivative21(Projective(), P, X); - Matrix E2 = numericalDerivative22(Projective(), P, X); - - // Get derivatives with AutoDiff - Vector2 actual2; - MatrixRowMajor H1(2, 12), H2(2, 4); - double *parameters[] = { P.data(), X.data() }; - double *jacobians[] = { H1.data(), H2.data() }; - CHECK( - (AutoDiff::Differentiate( projective, parameters, 2, actual2.data(), jacobians))); - EXPECT(assert_equal(E1,H1,1e-8)); - EXPECT(assert_equal(E2,H2,1e-8)); -} - -/* ************************************************************************* */ -// Test Ceres AutoDiff on Snavely -TEST(Expression, AutoDiff2) { - using ceres::internal::AutoDiff; - - // Instantiate function - SnavelyProjection snavely; - - // Make arguments - Vector9 P; // zero rotation, (0,5,0) translation, focal length 1 - P << 0, 0, 0, 0, 5, 0, 1, 0, 0; - Vector3 X(10, 0, -5); // negative Z-axis convention of Snavely! - - // Apply the mapping, to get image point b_x. - Vector expected = Vector2(2, 1); - Vector2 actual = snavely(P, X); - EXPECT(assert_equal(expected,actual,1e-9)); - - // Get expected derivatives - Matrix E1 = numericalDerivative21( - SnavelyProjection(), P, X); - Matrix E2 = numericalDerivative22( - SnavelyProjection(), P, X); - - // Get derivatives with AutoDiff - Vector2 actual2; - MatrixRowMajor H1(2, 9), H2(2, 3); - double *parameters[] = { P.data(), X.data() }; - double *jacobians[] = { H1.data(), H2.data() }; - CHECK( - (AutoDiff::Differentiate( snavely, parameters, 2, actual2.data(), jacobians))); - EXPECT(assert_equal(E1,H1,1e-8)); - EXPECT(assert_equal(E2,H2,1e-8)); -} - -/* ************************************************************************* */ -// Adapt ceres-style autodiff -template -class AdaptAutoDiff { - - static const int N = traits::dimension::value; - static const int M1 = traits::dimension::value; - static const int M2 = traits::dimension::value; - - typedef Eigen::Matrix RowMajor1; - typedef Eigen::Matrix RowMajor2; - - typedef Canonical CanonicalT; - typedef Canonical Canonical1; - typedef Canonical Canonical2; - - typedef typename CanonicalT::vector VectorT; - typedef typename Canonical1::vector Vector1; - typedef typename Canonical2::vector Vector2; - - // Instantiate function and charts - CanonicalT chartT; - Canonical1 chart1; - Canonical2 chart2; - F f; - -public: - - typedef Eigen::Matrix JacobianTA1; - typedef Eigen::Matrix JacobianTA2; - - T operator()(const A1& a1, const A2& a2, boost::optional H1 = - boost::none, boost::optional H2 = boost::none) { - - using ceres::internal::AutoDiff; - - // Make arguments - Vector1 v1 = chart1.apply(a1); - Vector2 v2 = chart2.apply(a2); - - bool success; - VectorT result; - - if (H1 || H2) { - - // Get derivatives with AutoDiff - double *parameters[] = { v1.data(), v2.data() }; - double rowMajor1[N * M1], rowMajor2[N * M2]; // om the stack - double *jacobians[] = { rowMajor1, rowMajor2 }; - success = AutoDiff::Differentiate(f, parameters, 2, - result.data(), jacobians); - - // Convert from row-major to columnn-major - // TODO: if this is a bottleneck (probably not!) fix Autodiff to be Column-Major - *H1 = Eigen::Map(rowMajor1); - *H2 = Eigen::Map(rowMajor2); - - } else { - // Apply the mapping, to get result - success = f(v1.data(), v2.data(), result.data()); - } - if (!success) - throw std::runtime_error( - "AdaptAutoDiff: function call resulted in failure"); - return chartT.retract(result); - } - -}; - -// The DefaultChart of Camera below is laid out like Snavely's 9-dim vector -typedef PinholeCamera Camera; - -/* ************************************************************************* */ -// Test AutoDiff wrapper Snavely -TEST(Expression, AutoDiff3) { - - // Make arguments - Camera P(Pose3(Rot3(), Point3(0, 5, 0)), Cal3Bundler(1, 0, 0)); - Point3 X(10, 0, -5); // negative Z-axis convention of Snavely! - - typedef AdaptAutoDiff Adaptor; - Adaptor snavely; - - // Apply the mapping, to get image point b_x. - Point2 expected(2, 1); - Point2 actual = snavely(P, X); - EXPECT(assert_equal(expected,actual,1e-9)); - -// // Get expected derivatives - Matrix E1 = numericalDerivative21(Adaptor(), P, X); - Matrix E2 = numericalDerivative22(Adaptor(), P, X); - - // Get derivatives with AutoDiff, not gives RowMajor results! - Matrix29 H1; - Matrix23 H2; - Point2 actual2 = snavely(P, X, H1, H2); - EXPECT(assert_equal(expected,actual,1e-9)); - EXPECT(assert_equal(E1,H1,1e-8)); - EXPECT(assert_equal(E2,H2,1e-8)); -} - -TEST(Expression, Snavely) { - Expression P(1); - Expression X(2); - typedef AdaptAutoDiff Adaptor; - Expression expression(Adaptor(), P, X); - set expected = list_of(1)(2); - EXPECT(expected == expression.keys()); -} - /* ************************************************************************* */ int main() { TestResult tr;