Checked evaluation and calculation

release/4.3a0
Frank Dellaert 2013-12-23 22:29:09 -05:00
parent ce7cf524ea
commit 92c1398cd2
1 changed files with 57 additions and 22 deletions

View File

@ -6,27 +6,30 @@
*/ */
#include <gtsam/slam/EssentialMatrixFactor.h> #include <gtsam/slam/EssentialMatrixFactor.h>
#include <gtsam/geometry/SimpleCamera.h>
#include <gtsam/base/LieScalar.h> #include <gtsam/base/LieScalar.h>
namespace gtsam { namespace gtsam {
/** /**
* Binary factor that optimizes for E and inverse depth: assumes measurement * Binary factor that optimizes for E and inverse depth: assumes measurement
* in image 1 is perfect, and returns re-projection error in image 2 * in image 2 is perfect, and returns re-projection error in image 1
*/ */
class EssentialMatrixFactor2: public NoiseModelFactor2<EssentialMatrix, class EssentialMatrixFactor2: public NoiseModelFactor2<EssentialMatrix,
LieScalar> { LieScalar> {
Point2 pA_, pB_; ///< Measurements in image A and B Point2 pA_, pB_; ///< Measurements in image A and B
Cal3_S2 K_; ///< Calibration
typedef NoiseModelFactor2<EssentialMatrix, LieScalar> Base; typedef NoiseModelFactor2<EssentialMatrix, LieScalar> Base;
typedef EssentialMatrixFactor2 This;
public: public:
/// Constructor /// Constructor
EssentialMatrixFactor2(Key key1, Key key2, const Point2& pA, const Point2& pB, EssentialMatrixFactor2(Key key1, Key key2, const Point2& pA, const Point2& pB,
const SharedNoiseModel& model) : const Cal3_S2& K, const SharedNoiseModel& model) :
Base(model, key1, key2), pA_(pA), pB_(pB) { Base(model, key1, key2), pA_(pA), pB_(pB), K_(K) {
} }
/// print /// print
@ -42,16 +45,32 @@ public:
Vector evaluateError(const EssentialMatrix& E, const LieScalar& d, Vector evaluateError(const EssentialMatrix& E, const LieScalar& d,
boost::optional<Matrix&> H1 = boost::none, boost::optional<Matrix&> H2 = boost::optional<Matrix&> H1 = boost::none, boost::optional<Matrix&> H2 =
boost::none) const { boost::none) const {
// We have point x,y in image 1
// Given a depth Z, the corresponding 3D point P1 = Z*(x,y,1) = (x,y,1)/d
// We then convert to first camera by 2P = 1R2Õ*(P1-1T2)
// The homogeneous coordinates of can be written as
// 2R1*(P1-1T2) == 2R1*d*(P1-1T2) == 2R1*((x,y,1)-d*1T2)
// Note that this is just a homography for d==0
Point3 dP1(pA_.x(), pA_.y(), 1);
Point3 P2 = E.rotation().unrotate(dP1 - d * E.direction().point3());
// Project to normalized image coordinates, then uncalibrate
const Point2 pn = SimpleCamera::project_to_camera(P2);
const Point2 pi = K_.uncalibrate(pn);
Point2 reprojectionError(pi - pB_);
if (H1) if (H1)
*H1 = zeros(2, 5); *H1 = zeros(2, 5);
if (H2) if (H2)
*H2 = zeros(2, 1); *H2 = zeros(2, 1);
return (Vector(2) << 0,0);
return reprojectionError.vector();
} }
}; };
} // gtsam }
// gtsam
#include <gtsam/slam/dataset.h> #include <gtsam/slam/dataset.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h> #include <gtsam/nonlinear/NonlinearFactorGraph.h>
@ -187,31 +206,47 @@ TEST (EssentialMatrixFactor, fromConstraints) {
//************************************************************************* //*************************************************************************
TEST (EssentialMatrixFactor2, factor) { TEST (EssentialMatrixFactor2, factor) {
EssentialMatrix trueE(aRb, aTb); EssentialMatrix E(aRb, aTb);
LieScalar d(5);
noiseModel::Unit::shared_ptr model = noiseModel::Unit::Create(1); noiseModel::Unit::shared_ptr model = noiseModel::Unit::Create(1);
for (size_t i = 0; i < 5; i++) { for (size_t i = 0; i < 5; i++) {
EssentialMatrixFactor2 factor(100, i, pA(i), pB(i), model); Cal3_S2 K;
EssentialMatrixFactor2 factor(100, i, pA(i), pB(i), K, model);
// Check evaluation // Check evaluation
Vector expected(1); Point3 P1 = data.tracks[i].p, P2 = E.transform_to(P1);
expected << 0; LieScalar d(1.0 / P1.z());
const Point2 pn = SimpleCamera::project_to_camera(P2);
const Point2 pi = K.uncalibrate(pn);
Point2 reprojectionError(pi - pB(i));
Vector expected = reprojectionError.vector();
{
// Check calculations
Point3 dP1(pA(i).x(), pA(i).y(), 1);
EXPECT_DOUBLES_EQUAL(pA(i).x(), P1.x()/P1.z(), 1e-8);
EXPECT_DOUBLES_EQUAL(pA(i).y(), P1.y()/P1.z(), 1e-8);
EXPECT(assert_equal(P1,dP1/d));
Point3 otherP2 = E.rotation().unrotate(
d * P1 - d * E.direction().point3());
EXPECT(assert_equal(P2,otherP2/d));
}
Matrix Hactual1, Hactual2; Matrix Hactual1, Hactual2;
Vector actual = factor.evaluateError(trueE, d, Hactual1, Hactual2); Vector actual = factor.evaluateError(E, d, Hactual1, Hactual2);
EXPECT(assert_equal(expected, actual, 1e-7)); EXPECT(assert_equal(expected, actual, 1e-7));
// Use numerical derivatives to calculate the expected Jacobian // // Use numerical derivatives to calculate the expected Jacobian
Matrix Hexpected1, Hexpected2; // Matrix Hexpected1, Hexpected2;
boost::function<Vector(const EssentialMatrix&, const LieScalar&)> f = // boost::function<Vector(const EssentialMatrix&, const LieScalar&)> f =
boost::bind(&EssentialMatrixFactor2::evaluateError, &factor, _1, _2, // boost::bind(&EssentialMatrixFactor2::evaluateError, &factor, _1, _2,
boost::none, boost::none); // boost::none, boost::none);
Hexpected1 = numericalDerivative21<EssentialMatrix>(f, trueE, d); // Hexpected1 = numericalDerivative21<EssentialMatrix>(f, E, d);
Hexpected2 = numericalDerivative22<EssentialMatrix>(f, trueE, d); // Hexpected2 = numericalDerivative22<EssentialMatrix>(f, E, d);
//
// Verify the Jacobian is correct // // Verify the Jacobian is correct
EXPECT(assert_equal(Hexpected1, Hactual1, 1e-8)); // EXPECT(assert_equal(Hexpected1, Hactual1, 1e-8));
EXPECT(assert_equal(Hexpected2, Hactual2, 1e-8)); // EXPECT(assert_equal(Hexpected2, Hactual2, 1e-8));
} }
} }