Program Listing for File descriptor-projection.h

Return to documentation for file (algorithms/loopclosure/descriptor-projection/include/descriptor-projection/descriptor-projection.h)

#ifndef DESCRIPTOR_PROJECTION_DESCRIPTOR_PROJECTION_H_
#define DESCRIPTOR_PROJECTION_DESCRIPTOR_PROJECTION_H_

#include <memory>
#include <utility>
#include <vector>

#include <Eigen/Core>
#include <aslam/common/feature-descriptor-ref.h>
#include <aslam/common/memory.h>
#include <aslam/common/unique-id.h>
#include <descriptor-projection/flags.h>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <loopclosure-common/types.h>
#include <maplab-common/macros.h>
#include <vi-map/unique-id.h>

#include "descriptor-projection/projected_image.pb.h"

namespace loop_closure {
typedef vi_map::MissionId DatasetId;
typedef vi_map::LandmarkId PointLandmarkId;

struct ProjectedImage {
  MAPLAB_POINTER_TYPEDEFS(ProjectedImage);
  int64_t timestamp_nanoseconds;
  KeyframeId keyframe_id;
  DatasetId dataset_id;
  Eigen::MatrixXf projected_descriptors;
  Eigen::Matrix2Xd measurements;
  std::vector<PointLandmarkId> landmarks;

  void serialize(proto::ProjectedImage* projected_image) const;
  void deserialize(const proto::ProjectedImage& projected_image);
};
typedef std::vector<ProjectedImage::Ptr> ProjectedImagePtrList;

}  // namespace loop_closure

namespace descriptor_projection {
typedef Eigen::Matrix<float, Eigen::Dynamic, 1> ProjectedDescriptorType;
typedef Eigen::aligned_allocator<ProjectedDescriptorType> FeatureAllocator;
typedef Aligned<std::vector, ProjectedDescriptorType> DescriptorVector;

typedef std::pair<unsigned int, unsigned int> DescriptorMatch;

typedef Aligned<std::vector, Eigen::Vector3f> CoordinateVector;

template <typename DerivedOut>
void DescriptorToEigenMatrix(
    const aslam::common::FeatureDescriptorConstRef& descriptor,
    const Eigen::MatrixBase<DerivedOut>& matrix_const) {
  EIGEN_STATIC_ASSERT(
      !(Eigen::internal::traits<DerivedOut>::Flags & Eigen::RowMajorBit),
      "This method is only valid for column major matrices");
  Eigen::MatrixBase<DerivedOut>& matrix =
      const_cast<Eigen::MatrixBase<DerivedOut>&>(matrix_const);
  const int num_descriptor_bytes = descriptor.size();
  const int num_descriptor_bits = num_descriptor_bytes * 8;
  CHECK_EQ(matrix.rows(), num_descriptor_bits)
      << "The matrix passed must be preallocated to match the descriptor "
         "length in bits, which is "
      << num_descriptor_bits << ".";

  CHECK_EQ(num_descriptor_bytes % 16, 0);

// Define a set of macros to NEON and SSE instructions so we can use the same
// code further down for both platforms.
#ifdef __ARM_NEON
#define VECTOR_SET vdupq_n_u8       // Set a vector from a single uint8.
#define VECTOR_LOAD(x) vld1q_u8(x)  // Set a vector from a mem location.
#define VECTOR_TYPE uint8x16_t      // The type of the vector element.
#define VECTOR_AND vandq_u8         // The vector AND instruction.
#define VECTOR_EXTRACT(x, i) vgetq_lane_u8(x, i)  // Get element from vector.
#else
#define VECTOR_SET _mm_set1_epi8
#define VECTOR_LOAD(x) _mm_load_si128(reinterpret_cast<const __m128i*>(x))
#define VECTOR_TYPE __m128i
#define VECTOR_AND _mm_and_si128
// Could use _mm_extract_epi8, but this requires SSE4.1.
#define VECTOR_EXTRACT(x, i) reinterpret_cast<const char*>(&x)[i]
#endif  // __ARM_NEON

  VECTOR_TYPE mask[8];
  mask[0] = VECTOR_SET((1 << 0));
  mask[1] = VECTOR_SET((1 << 1));
  mask[2] = VECTOR_SET((1 << 2));
  mask[3] = VECTOR_SET((1 << 3));
  mask[4] = VECTOR_SET((1 << 4));
  mask[5] = VECTOR_SET((1 << 5));
  mask[6] = VECTOR_SET((1 << 6));
  mask[7] = VECTOR_SET((1 << 7));

  float* matrix_ref = matrix.derived().data();

  for (int pack = 0; pack < num_descriptor_bytes / 16; ++pack) {
    VECTOR_TYPE value = VECTOR_LOAD(descriptor.data() + pack * 16);
    const int pack128 = pack << 7;
    for (int i = 0; i < 8; ++i) {  // Checks 16 bits at once with SSE/NEON.
      // Masks the i'th bit of the 16 uint8s.
      VECTOR_TYPE xmm1 = VECTOR_AND(value, mask[i]);
      matrix_ref[pack128 + i + 0] = VECTOR_EXTRACT(xmm1, 0) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 8] = VECTOR_EXTRACT(xmm1, 1) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 16] = VECTOR_EXTRACT(xmm1, 2) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 24] = VECTOR_EXTRACT(xmm1, 3) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 32] = VECTOR_EXTRACT(xmm1, 4) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 40] = VECTOR_EXTRACT(xmm1, 5) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 48] = VECTOR_EXTRACT(xmm1, 6) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 56] = VECTOR_EXTRACT(xmm1, 7) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 64] = VECTOR_EXTRACT(xmm1, 8) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 72] = VECTOR_EXTRACT(xmm1, 9) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 80] = VECTOR_EXTRACT(xmm1, 10) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 88] = VECTOR_EXTRACT(xmm1, 11) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 96] = VECTOR_EXTRACT(xmm1, 12) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 104] = VECTOR_EXTRACT(xmm1, 13) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 112] = VECTOR_EXTRACT(xmm1, 14) ? 1.f : 0.f;
      matrix_ref[pack128 + i + 120] = VECTOR_EXTRACT(xmm1, 15) ? 1.f : 0.f;
    }
  }
}

template <typename DerivedIn, typename DerivedOut>
void DescriptorToEigenMatrix(
    const Eigen::MatrixBase<DerivedIn>& descriptor,
    const Eigen::MatrixBase<DerivedOut>& matrix_const) {
  EIGEN_STATIC_ASSERT(
      !(Eigen::internal::traits<DerivedOut>::Flags & Eigen::RowMajorBit),
      "This method is only valid for column major matrices");
  CHECK_EQ(descriptor.cols(), 1);
  Eigen::MatrixBase<DerivedOut>& matrix =
      const_cast<Eigen::MatrixBase<DerivedOut>&>(matrix_const);
  const int num_descriptor_bytes = descriptor.rows();
  const int num_descriptor_bits = num_descriptor_bytes * 8;
  CHECK_EQ(matrix.rows(), num_descriptor_bits)
      << "The matrix passed must be preallocated to match the descriptor "
         "length in bits, which is "
      << num_descriptor_bits << ".";
  matrix.setZero();

  CHECK_EQ(num_descriptor_bytes % 16, 0);

  VECTOR_TYPE mask[8];
  mask[0] = VECTOR_SET((1 << 0));
  mask[1] = VECTOR_SET((1 << 1));
  mask[2] = VECTOR_SET((1 << 2));
  mask[3] = VECTOR_SET((1 << 3));
  mask[4] = VECTOR_SET((1 << 4));
  mask[5] = VECTOR_SET((1 << 5));
  mask[6] = VECTOR_SET((1 << 6));
  mask[7] = VECTOR_SET((1 << 7));

  float* matrix_ref = matrix.derived().data();

  CHECK_EQ(descriptor.derived().cols(), 1);

  const unsigned char* descriptor_data = &descriptor.derived().coeffRef(0, 0);

  for (int pack = 0; pack < num_descriptor_bytes / 16; ++pack) {
    VECTOR_TYPE value = VECTOR_LOAD(descriptor_data + pack * 16);
    const int pack128 = pack << 7;
    for (int i = 0; i < 8; ++i) {  // Checks 16 bits at once with SSE/NEON.
      // Masks the i'th bit of the 16 uint8s.
      VECTOR_TYPE xmm1 = VECTOR_AND(value, mask[i]);
      if (VECTOR_EXTRACT(xmm1, 0))
        matrix_ref[pack128 + i + 0] = 1;
      if (VECTOR_EXTRACT(xmm1, 1))
        matrix_ref[pack128 + i + 8] = 1;
      if (VECTOR_EXTRACT(xmm1, 2))
        matrix_ref[pack128 + i + 16] = 1;
      if (VECTOR_EXTRACT(xmm1, 3))
        matrix_ref[pack128 + i + 24] = 1;
      if (VECTOR_EXTRACT(xmm1, 4))
        matrix_ref[pack128 + i + 32] = 1;
      if (VECTOR_EXTRACT(xmm1, 5))
        matrix_ref[pack128 + i + 40] = 1;
      if (VECTOR_EXTRACT(xmm1, 6))
        matrix_ref[pack128 + i + 48] = 1;
      if (VECTOR_EXTRACT(xmm1, 7))
        matrix_ref[pack128 + i + 56] = 1;
      if (VECTOR_EXTRACT(xmm1, 8))
        matrix_ref[pack128 + i + 64] = 1;
      if (VECTOR_EXTRACT(xmm1, 9))
        matrix_ref[pack128 + i + 72] = 1;
      if (VECTOR_EXTRACT(xmm1, 10))
        matrix_ref[pack128 + i + 80] = 1;
      if (VECTOR_EXTRACT(xmm1, 11))
        matrix_ref[pack128 + i + 88] = 1;
      if (VECTOR_EXTRACT(xmm1, 12))
        matrix_ref[pack128 + i + 96] = 1;
      if (VECTOR_EXTRACT(xmm1, 13))
        matrix_ref[pack128 + i + 104] = 1;
      if (VECTOR_EXTRACT(xmm1, 14))
        matrix_ref[pack128 + i + 112] = 1;
      if (VECTOR_EXTRACT(xmm1, 15))
        matrix_ref[pack128 + i + 120] = 1;
    }
  }
}

template <typename DerivedIn, typename DerivedOut>
inline typename std::enable_if<
    std::is_floating_point<typename DerivedIn::Scalar>::value, void>::type
ProjectDescriptor(
    const Eigen::MatrixBase<DerivedIn>& descriptor,
    const Eigen::MatrixXf& projection_matrix, int target_dimensions,
    const Eigen::MatrixBase<DerivedOut>& projected_descriptor_const) {
  static_assert(
      std::is_same<typename DerivedIn::Scalar, float>::value,
      "This method is only valid for float matrices.");
  Eigen::MatrixBase<DerivedOut>& projected_descriptor =
      const_cast<Eigen::MatrixBase<DerivedOut>&>(projected_descriptor_const);
  CHECK_EQ(projected_descriptor.rows(), target_dimensions);
  projected_descriptor =
      projection_matrix.block(
          0, 0, target_dimensions, projection_matrix.cols()) *
      descriptor.block(0, 0, projection_matrix.cols(), 1);
}

template <typename DerivedIn, typename DerivedOut>
inline typename std::enable_if<
    !std::is_floating_point<typename DerivedIn::Scalar>::value, void>::type
ProjectDescriptor(
    const Eigen::MatrixBase<DerivedIn>& raw_descriptor,
    const Eigen::MatrixXf& projection_matrix, int target_dimensions,
    const Eigen::MatrixBase<DerivedOut>& projected_descriptor_const) {
  static_assert(
      std::is_same<typename DerivedIn::Scalar, unsigned char>::value,
      "This method is only valid for unsigned char matrices.");
  Eigen::MatrixBase<DerivedOut>& projected_descriptor =
      const_cast<Eigen::MatrixBase<DerivedOut>&>(projected_descriptor_const);
  CHECK_EQ(raw_descriptor.cols(), 1);
  Eigen::Matrix<float, Eigen::Dynamic, 1> descriptor;
  const int descriptor_bits = raw_descriptor.rows() * 8;
  descriptor.resize(descriptor_bits, Eigen::NoChange);
  DescriptorToEigenMatrix(raw_descriptor, descriptor);
  CHECK_EQ(projected_descriptor.rows(), target_dimensions);
  projected_descriptor =
      projection_matrix.block(
          0, 0, target_dimensions, projection_matrix.cols()) *
      descriptor.block(0, 0, projection_matrix.cols(), 1);
}

template <typename DerivedOut>
void ProjectDescriptor(
    const aslam::common::FeatureDescriptorConstRef& raw_descriptor,
    const Eigen::MatrixXf& projection_matrix, int target_dimensions,
    const Eigen::MatrixBase<DerivedOut>& projected_descriptor_const) {
  Eigen::MatrixBase<DerivedOut>& projected_descriptor =
      const_cast<Eigen::MatrixBase<DerivedOut>&>(projected_descriptor_const);
  Eigen::Matrix<float, Eigen::Dynamic, 1> descriptor;
  const int descriptor_bits = raw_descriptor.size() * 8;
  descriptor.resize(descriptor_bits, Eigen::NoChange);
  DescriptorToEigenMatrix(raw_descriptor, descriptor);
  ProjectDescriptor(
      descriptor, projection_matrix, target_dimensions, projected_descriptor);
}

void ProjectDescriptorBlock(
    const Eigen::Matrix<unsigned char, Eigen::Dynamic, Eigen::Dynamic>&
        raw_descriptors,
    const Eigen::MatrixXf& projection_matrix, int target_dimensions,
    Eigen::MatrixXf* projected_descriptors);

void ProjectDescriptorBlock(
    const std::vector<aslam::common::FeatureDescriptorConstRef>&
        raw_descriptors,
    const Eigen::MatrixXf& projection_matrix, int target_dimensions,
    Eigen::MatrixXf* projected_descriptors);

template <typename DerivedIn>
inline void ProjectDescriptor(
    const Eigen::MatrixBase<DerivedIn>& descriptor,
    const Eigen::MatrixXf& projection_matrix, int target_dimensions,
    std::vector<float>* projected_descriptor) {
  CHECK_NOTNULL(projected_descriptor);
  projected_descriptor->resize(target_dimensions);
  Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, 1> > descriptor_map(
      projected_descriptor->data(), target_dimensions, 1);

  descriptor_map =
      projection_matrix.block(0, 0, target_dimensions, descriptor.rows()) *
      descriptor;

  Eigen::MatrixXf proj_tmp =
      projection_matrix.block(
          0, 0, target_dimensions, projection_matrix.cols()) *
      descriptor.block(0, 0, projection_matrix.cols(), 1);
}

bool LoadprojectionMatrix(Eigen::MatrixXf* projection_matrix);
}  // namespace descriptor_projection
#endif  // DESCRIPTOR_PROJECTION_DESCRIPTOR_PROJECTION_H_