Program Listing for File triangulation-fixture.h

Return to documentation for file (aslam_cv2/aslam_cv_triangulation/include/aslam/triangulation/test/triangulation-fixture.h)

#ifndef ASLAM_TEST_TRIANGULATION_FIXTURE_H_
#define ASLAM_TEST_TRIANGULATION_FIXTURE_H_

#include <Eigen/Eigen>
#include <gtest/gtest.h>

#include <aslam/common/memory.h>
#include <aslam/common/pose-types.h>
#include <aslam/triangulation/triangulation.h>
#include <eigen-checks/gtest.h>

constexpr double kDoubleTolerance = 1e-9;
#if !defined(DOXYGEN_SHOULD_SKIP_THIS)
const Eigen::Vector3d kGPoint(0, 0, 5);
#endif // DOXYGEN_SHOULD_SKIP_THIS

typedef Aligned<std::vector, Eigen::Vector2d> Vector2dList;

void fillObservations(
    size_t n_observations,
    const aslam::Transformation& T_I_C,
    Eigen::Matrix3Xd* C_bearing_vectors,
    Aligned<std::vector, aslam::Transformation>* T_G_I) {
  CHECK_NOTNULL(C_bearing_vectors);
  CHECK_NOTNULL(T_G_I)->clear();

  Eigen::Vector3d position_start(-2,-2,-1);
  Eigen::Vector3d position_end(2,2,1);

  Eigen::Vector3d position_step((position_end - position_start) / (n_observations - 1));

  C_bearing_vectors->resize(3, n_observations);
  // Move along line from position_start to position_end.
  for(size_t i = 0; i < n_observations; ++i) {
    Eigen::Vector3d test_pos(position_start + i * position_step);
    aslam::Transformation T_G_I_current(test_pos, Eigen::Quaterniond::Identity());
    T_G_I->push_back(T_G_I_current);

    aslam::Transformation T_C_G = (T_G_I_current * T_I_C).inverse();
    C_bearing_vectors->block<3, 1>(0, i) = T_C_G.transform(kGPoint);
  }
}

void fillObservations(
    size_t n_observations, const aslam::Transformation& T_I_C,
    Aligned<std::vector, Eigen::Vector2d>* measurements,
    Aligned<std::vector, aslam::Transformation>* T_G_I) {
  CHECK_NOTNULL(measurements);
  CHECK_NOTNULL(T_G_I);
  Eigen::Matrix3Xd C_bearing_vectors;
  fillObservations(n_observations, T_I_C, &C_bearing_vectors, T_G_I);
  measurements->resize(C_bearing_vectors.cols());
  for (int i = 0; i < C_bearing_vectors.cols(); ++i) {
    (*measurements)[i] = C_bearing_vectors.block<2, 1>(0, i) / C_bearing_vectors(2, i);
  }
}

template <typename MeasurementsType>
class TriangulationFixture : public testing::Test {
 protected:
  virtual void SetUp() {
    T_I_C_.setIdentity();
  }

  void fillMeasurements(
      size_t n_observations) {
    fillObservations(n_observations, T_I_C_, &measurements_, &T_G_I_);
  }

  aslam::TriangulationResult triangulate(Eigen::Vector3d* result) const;

  void setNMeasurements(const size_t n) {
    C_bearing_measurements_.resize(3, n);
    T_G_I_.resize(n);
  }

  void setLandmark(const Eigen::Vector3d& p_G) {
    p_G_ = p_G;
  }

  void inferMeasurements(double angle_noise = 0.) {
    for (size_t i = 0; i < T_G_I_.size(); ++i) {
      // Ignoring IMU to camera transformation (set to identity in SetUp()).
      C_bearing_measurements_.block<3, 1>(0, i) =
          T_G_I_[i].inverse().transform(p_G_);
      if (angle_noise > 0.) {
        aslam::Transformation perturbation;
        perturbation.setRandom(0., angle_noise);
        C_bearing_measurements_.block<3, 1>(0, i) = perturbation *
            C_bearing_measurements_.block<3, 1>(0, i);
      }
    }
    setMeasurements(C_bearing_measurements_);
  }

  void setMeasurements(const Eigen::Matrix3Xd& measurements);

  void expectSuccess() {
    Eigen::Vector3d p_G_estimate;
    EXPECT_TRUE(triangulate(&p_G_estimate).wasTriangulationSuccessful());
    EXPECT_TRUE(EIGEN_MATRIX_NEAR(p_G_, p_G_estimate, kDoubleTolerance));
  }

  void expectFailue() {
    Eigen::Vector3d p_G_estimate;
    EXPECT_FALSE(triangulate(&p_G_estimate).wasTriangulationSuccessful());
  }

  aslam::Transformation T_I_C_;
  MeasurementsType measurements_;
  Eigen::Matrix3Xd C_bearing_measurements_;
  Aligned<std::vector, aslam::Transformation> T_G_I_;
  Eigen::Vector3d p_G_;

 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};

template<>
aslam::TriangulationResult TriangulationFixture<Vector2dList>::triangulate(
    Eigen::Vector3d* result) const {
  return aslam::linearTriangulateFromNViews(measurements_, T_G_I_, T_I_C_, result);
}

template<>
aslam::TriangulationResult TriangulationFixture<Eigen::Matrix3Xd>::triangulate(
    Eigen::Vector3d* result) const {
  Eigen::Matrix3Xd G_measurements(3, measurements_.cols()),
      p_G_C(3, measurements_.cols());
  for (int i = 0; i < measurements_.cols(); ++i) {
    G_measurements.block<3, 1>(0, i) = T_G_I_[i].getRotationMatrix() *
        T_I_C_.getRotationMatrix() * measurements_.block<3, 1>(0, i);
    p_G_C.block<3, 1>(0, i) = T_G_I_[i] * T_I_C_.getPosition();
  }
  return aslam::linearTriangulateFromNViews(G_measurements, p_G_C, result);
}

template <>
void TriangulationFixture<Vector2dList>::setMeasurements(const Eigen::Matrix3Xd& measurements) {
  measurements_.resize(measurements.cols());
  for (int i = 0; i < measurements.cols(); ++i) {
    measurements_[i] = measurements.block<2, 1>(0, i) / measurements(2, i);
  }
}

template <>
void TriangulationFixture<Eigen::Matrix3Xd>::setMeasurements(const Eigen::Matrix3Xd& measurements) {
  measurements_ = measurements;
}

typedef ::testing::Types<Vector2dList, Eigen::Matrix3Xd>
TestTypes;
TYPED_TEST_CASE(TriangulationFixture, TestTypes);

#endif  // ASLAM_TEST_TRIANGULATION_FIXTURE_H_