Program Listing for File kd-tree-index.h

Return to documentation for file (algorithms/loopclosure/matching-based-loopclosure/include/matching-based-loopclosure/kd-tree-index.h)

#ifndef MATCHING_BASED_LOOPCLOSURE_KD_TREE_INDEX_H_
#define MATCHING_BASED_LOOPCLOSURE_KD_TREE_INDEX_H_

#include <algorithm>
#include <functional>
#include <limits>
#include <queue>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>

#include <Eigen/Core>
#include <aslam/common/memory.h>
#include <descriptor-projection/flags.h>
#include <glog/logging.h>
#include <nabo/nabo.h>

namespace loop_closure {
namespace kd_tree_index {
template <int kDimVectors>
class KDTreeIndex {
 public:
  typedef Eigen::Matrix<float, kDimVectors, 1> DescriptorType;
  typedef Eigen::Matrix<float, kDimVectors, Eigen::Dynamic>
      DescriptorMatrixType;
  typedef Nabo::NearestNeighbourSearch<float> NNSearch;
  // Switch touch statistics (NNSearch::TOUCH_STATISTICS) off for performance.
  static constexpr int kCollectTouchStatistics = 0;
  // Kd-tree search options. ALLOW_SELF_MATCH means that a point which is
  // equal to the query will be returned in the results.
  static constexpr unsigned kSearchOptionsDefault =
      NNSearch::ALLOW_SELF_MATCH | NNSearch::SORT_RESULTS;
  // Epsilon approximation factor for kd-tree backtracking.
  static constexpr float kSearchNNEpsilon = 0.1;

  KDTreeIndex() {}

  inline void Clear() {
    index_data_.resize(Eigen::NoChange, 0);
    index_.reset();
    pending_descriptor_blocks_.clear();
  }

  inline int GetNumDescriptorsInIndex() const {
    int num_descriptors = index_data_.cols();
    for (const std::shared_ptr<Eigen::MatrixXf>& pending_block :
         pending_descriptor_blocks_) {
      num_descriptors += pending_block->cols();
    }
    return num_descriptors;
  }

  // Adds descriptors to an internal waiting list. These descriptors will be
  // added on the next time the index is queried.
  void AddDescriptors(const DescriptorMatrixType& descriptors) {
    pending_descriptor_blocks_.push_back(
        aligned_shared<Eigen::MatrixXf>(descriptors));
  }

  void RefreshIndex() const {
    if (pending_descriptor_blocks_.empty())
      return;

    int total_num_descriptors_to_add = 0;
    for (const std::shared_ptr<Eigen::MatrixXf>& descriptor_block :
         pending_descriptor_blocks_) {
      CHECK(descriptor_block != nullptr);
      total_num_descriptors_to_add += descriptor_block->cols();
    }
    int old_num_descriptors = index_data_.cols();
    int new_num_descriptors =
        total_num_descriptors_to_add + old_num_descriptors;

    index_data_.conservativeResize(kDimVectors, new_num_descriptors);

    int curr_offset = old_num_descriptors;
    for (const std::shared_ptr<Eigen::MatrixXf>& descriptor_block :
         pending_descriptor_blocks_) {
      const DescriptorMatrixType& descriptors = *descriptor_block;
      int num_descriptors = descriptors.cols();
      index_data_.block(0, curr_offset, kDimVectors, num_descriptors) =
          descriptors;
      curr_offset += num_descriptors;
    }

    if (index_data_.cols() == 0) {
      pending_descriptor_blocks_.clear();
      index_.reset();
      return;
    }
    index_.reset(
        NNSearch::createKDTreeLinearHeap(
            index_data_, kDimVectors, kCollectTouchStatistics));
    pending_descriptor_blocks_.clear();
  }

  // Finds the n nearest neighbors for a given query feature.
  // This function is thread-safe.
  inline void GetNNearestNeighbors(
      const Eigen::MatrixXf& query_features, int num_neighbors,
      Eigen::MatrixXi* indices, Eigen::MatrixXf* distances) const {
    CHECK_NOTNULL(indices);
    CHECK_NOTNULL(distances);
    CHECK_EQ(indices->rows(), num_neighbors)
        << "The indices parameter must be pre-allocated to hold all results.";

    CHECK_EQ(distances->rows(), num_neighbors)
        << "The distances parameter must be pre-allocated to hold all results.";

    // Lazy refresh of the index if more data was added in the meantime.
    RefreshIndex();
    if (!index_) {
      indices->setConstant(-1);
      distances->setConstant(std::numeric_limits<float>::infinity());
      LOG(WARNING) << "The kd-tree index is not available.";
      return;
    }
    const NNSearch& index_ref = *index_;
    index_ref.knn(
        query_features, *indices, *distances, num_neighbors, kSearchNNEpsilon,
        kSearchOptionsDefault, FLAGS_lc_knn_max_radius);
  }

 protected:
  mutable std::shared_ptr<NNSearch> index_;
  mutable Eigen::MatrixXf index_data_;
  mutable std::vector<std::shared_ptr<Eigen::MatrixXf> >
      pending_descriptor_blocks_;
};
}  // namespace kd_tree_index
}  // namespace loop_closure

#endif  // MATCHING_BASED_LOOPCLOSURE_KD_TREE_INDEX_H_