Program Listing for File stl-helpers.h

Return to documentation for file (aslam_cv2/aslam_cv_common/include/aslam/common/stl-helpers.h)

#ifndef ASLAM_STL_HELPERS_H_
#define ASLAM_STL_HELPERS_H_

#include <algorithm>
#include <chrono>
#include <random>
#include <unordered_set>
#include <vector>

#include <aslam/common/memory.h>
#include <Eigen/Core>
#include <glog/logging.h>

namespace aslam {
namespace common {

// Returns the total number of elements in a nested list, that is,
// num_outer_list_elements * num_inner_list_elements.
template<class ElementType, class Allocator, class NestedAllocator>
size_t countNumberOfElementsInNestedList(
    const std::vector<std::vector<ElementType, Allocator>, NestedAllocator>& nested_list) {
  size_t num_elements = 0u;
  for (const std::vector<ElementType, Allocator>& list : nested_list) {
    num_elements += list.size();
  }
  return num_elements;
}

template<typename RandAccessIter>
double median(RandAccessIter begin, RandAccessIter end) {
  CHECK(begin != end) << "No data provided to calculate the median.";
  size_t size = end - begin;
  size_t middle_idx = size / 2;
  RandAccessIter target_high = begin + middle_idx;
  std::nth_element(begin, target_high, end);

  // Odd number of elements.
  if (size % 2 != 0) {
    return *target_high;
  }
  // Even number of elements.
  double target_high_value = *target_high;
  RandAccessIter target_low = target_high - 1;
  std::nth_element(begin, target_low, end);
  return (target_high_value + *target_low) / 2.0;
}


template<typename RandAccessIter>
double mean(RandAccessIter begin, RandAccessIter end) {
  CHECK(begin != end) << "No data provided to calculate the mean.";
  const size_t n = end - begin;
  double mu = 0.0;
  RandAccessIter element_i = begin;
  while (element_i != end) {
    mu += *element_i;
    ++element_i;
  }
  return mu / n;
}


template<typename RandAccessIter>
double stddev(RandAccessIter begin, RandAccessIter end) {
  CHECK(begin != end) << "No data provided to calculate the standard deviation.";
  const size_t n = end - begin;
  RandAccessIter element_i = begin;
  double mu = aslam::common::mean(begin, end);

  double sum = 0.0;
  element_i = begin;
  while (element_i != end) {
    sum += (*element_i - mu) * (*element_i - mu);
    ++element_i;
  }

  CHECK_GE(sum, 0.0);
  return sqrt(sum / n);
}

template<typename ElementType, typename Allocator>
void drawNRandomElements(const size_t n, const std::vector<ElementType, Allocator>& input,
                         std::vector<ElementType, Allocator>* output,
                         const bool use_fixed_seed) {
  CHECK_NE(&input, output);
  CHECK_NOTNULL(output)->clear();
  CHECK_GT(n, 0u);
  const size_t num_input_elements = input.size();
  if (num_input_elements <= n) {
    *output = input;
    return;
  }

  // Draw random indices.
  const unsigned int seed =
      use_fixed_seed ? 0u : std::random_device{}();

  std::default_random_engine generator(seed);
  std::uniform_int_distribution<size_t> distribution(0u, num_input_elements - 1u);

  std::unordered_set<size_t> random_indices;
  while (random_indices.size() < n) {
    random_indices.insert(distribution(generator));
  }

  // Copy to output.
  output->reserve(n);
  for (const size_t idx : random_indices) {
    CHECK_LT(idx, num_input_elements);
    output->emplace_back(input[idx]);
  }
}

template<typename ElementType, typename Allocator>
void drawNRandomElements(const size_t n, const std::vector<ElementType, Allocator>& input,
                         std::vector<ElementType, Allocator>* output) {
  drawNRandomElements(n, input, output, false);
}

// Remove all elements except the N greatest elements. An optional action can be provided
// that is executed on all removed elements.
template<typename ElementType> struct NullAction { void operator()(const ElementType&) const {} };
template<typename ElementType, typename Allocator, typename CompareFunctor,
         typename RemoveActionFunctor = NullAction<ElementType>>
size_t keepOnlyNSortedElements(size_t max_elements_to_keep,
    const CompareFunctor& sort_compare_functor,
    std::vector<ElementType, Allocator>* container,
    const RemoveActionFunctor& action_on_removed_elements = NullAction<ElementType>()) {
  CHECK_NOTNULL(container);

  // Special case for max_elements_to_keep == 0u: only run the action on all elements.
  if (max_elements_to_keep == 0u) {
    for (const ElementType& element : *container) {
      action_on_removed_elements(element);
    }
    container->clear();
    return 0u;
  }

  // Early exit if the container has less elements than the number to keep.
  const size_t num_elements = container->size();
  if (num_elements <= max_elements_to_keep) {
    return num_elements;
  }

  // Sort up to N greatest elements.
  std::partial_sort(container->begin(), container->begin() + max_elements_to_keep,
                    container->end(), sort_compare_functor);
  CHECK_GE(container->size(), max_elements_to_keep);

  // Run the optional action on removed elements.
  typename std::vector<ElementType, Allocator>::const_iterator it =
      container->begin() + max_elements_to_keep;
  for(; it != container->end(); ++it) {
    action_on_removed_elements(*it);
  }

  // Remove the elements.
  container->erase(container->begin() + max_elements_to_keep, container->end());
  CHECK_LE(container->size(), max_elements_to_keep);
  return container->size();
}

template <int VectorDim>
inline void convertEigenToStlVector(
    const Eigen::template Matrix<double, VectorDim, Eigen::Dynamic>& input,
    Aligned<std::vector, Eigen::template Matrix<double, VectorDim, 1>>*
        output) {
  CHECK_NOTNULL(output);
  size_t num_cols = input.cols();
  output->clear();
  output->reserve(num_cols);

  auto inserter = std::inserter(*output, output->end());
  for (size_t idx = 0; idx < num_cols; ++idx) {
    *inserter++ = input.col(idx);
  }
}

// Solution from:
// http://stackoverflow.com/questions/7571937/how-to-delete-items-from-a-stdvector-given-a-list-of-indices
template<typename ElementType, typename Allocator>
inline std::vector<ElementType, Allocator> eraseIndicesFromVector(
    const std::vector<ElementType, Allocator>& data,
    const std::vector<size_t>& indices_to_delete) {
  if (indices_to_delete.empty()) {
    return data;
  }
  std::vector<size_t> mutable_indices_to_delete = indices_to_delete;
  std::sort(mutable_indices_to_delete.begin(), mutable_indices_to_delete.end());
  CHECK_LT(mutable_indices_to_delete.back(), data.size());

  std::vector<ElementType, Allocator> reduced_vector;
  CHECK_GE(data.size(), mutable_indices_to_delete.size());
  reduced_vector.reserve(data.size() - mutable_indices_to_delete.size());

  // Copy blocks from the input vector to the output vector.
  typename std::vector<ElementType, Allocator>::const_iterator it_block_begin = data.begin();

  for (typename std::vector<size_t>::const_iterator it = mutable_indices_to_delete.begin();
      it != mutable_indices_to_delete.end(); ++it) {
    typename std::vector<ElementType, Allocator>::const_iterator it_block_end = data.begin() + *it;
    if (it_block_begin != it_block_end) {
      std::copy(it_block_begin, it_block_end, std::back_inserter(reduced_vector));
    }
    it_block_begin = it_block_end + 1;
  }

  // Copy the last block.
  if (it_block_begin != data.end()) {
    std::copy(it_block_begin, data.end(), std::back_inserter(reduced_vector));
  }
  return reduced_vector;
}

namespace stl_helpers {

constexpr int kColumns = 0;
constexpr int kRows = 1;

// Helps to pass the supplied dynamic matrix to functions taking containers
// with only one dimension, such as the below eraseIndicesFromContainer().
template <typename ScalarType, int StaticDimension>
struct OneDimensionAdapter {
  typedef Eigen::Matrix<ScalarType, Eigen::Dynamic, Eigen::Dynamic>
  DynamicMatrix;
  DynamicMatrix* matrix;
  const bool allocated_self;

  OneDimensionAdapter(DynamicMatrix* _matrix)
  : matrix(CHECK_NOTNULL(_matrix)), allocated_self(false) {}
  OneDimensionAdapter() : matrix(new DynamicMatrix), allocated_self(true) {}
  ~OneDimensionAdapter() {
    if (allocated_self) {
      delete matrix;
    }
  }

  void swap(OneDimensionAdapter& other) {
    matrix->swap(*other.matrix);
  }
};

// Different implementations are available in the inline header.
template <typename ContainerType>
void eraseIndicesFromContainer(
    const std::vector<size_t>& ordered_indices_to_erase,
    const size_t expected_initial_count, ContainerType* container);

}  // namespace stl_helpers
}  // namespace common
}  // namespace aslam

#include "./stl-helpers-inl.h"

#endif  // ASLAM_STL_HELPERS_H_