ChainApproximation.cpp
Go to the documentation of this file.
1 #include "ChainApproximation.h"
2 
3 #include <algorithm>
4 #include <cstddef>
5 #include <iterator>
6 #include <ostream>
7 #include <string>
8 #include <vector>
9 
10 #include <Eigen/Geometry>
11 
12 #include <SimoxUtility/algorithm/apply.hpp>
13 
15 
16 #include "range/v3/algorithm/find.hpp"
17 #include "range/v3/numeric/iota.hpp"
18 
20 {
21 
22  ChainApproximation::ChainApproximation(const Points& points, const Params& params) :
23  points(points), params(params)
24  {
25  // fill indices
26  indices.resize(points.size());
27  ranges::iota(indices, 0);
28  }
29 
32  {
33 
34  int iterations = 0;
35 
36  const auto maxIterConditionReached = [&]()
37  {
38  // inactive?
39  if (params.maxIterations <= 0)
40  {
41  return false;
42  }
43 
44  return iterations >= params.maxIterations;
45  };
46 
47  while (true)
48  {
49  if (maxIterConditionReached())
50  {
51  return ApproximationResult{
53  .iterations = iterations};
54  }
55 
56  if (not approximateStep())
57  {
58  return ApproximationResult{
60  .iterations = iterations,
61  .reductionFactor = 1.F - static_cast<float>(indices.size()) /
62  static_cast<float>(points.size())};
63  }
64 
65  iterations++;
66  }
67  }
68 
69  ChainApproximation::Triplets
70  ChainApproximation::getTriplets() const
71  {
72  const int nIndices = static_cast<int>(indices.size());
73 
74  if (nIndices < 3)
75  {
76  return {};
77  }
78 
79  Triplets triplets;
80  triplets.reserve(indices.size());
81 
82  // Here, we iterate over all elements under consideration.
83  // The aim is to create a view - a sliding window - over the
84  // indices. i will always point to the centered element.
85 
86  // the first element
87  triplets.emplace_back(indices.back(), indices.front(), indices.at(1));
88 
89  // intermediate elements
90  for (int i = 1; i < (nIndices - 1); i++)
91  {
92  triplets.emplace_back(indices.at(i - 1), indices.at(i), indices.at(i + 1));
93  }
94 
95  // the last element
96  triplets.emplace_back(indices.back(), indices.front(), indices.at(1));
97 
98  return triplets;
99  }
100 
101  std::vector<float>
102  ChainApproximation::computeDistances(const ChainApproximation::Triplets& triplets)
103  {
104  std::vector<float> distances;
105  distances.reserve(triplets.size());
106 
107  std::transform(triplets.begin(),
108  triplets.end(),
109  std::back_inserter(distances),
110  [&](const auto& triplet) { return computeDistance(triplet); });
111 
112  return distances;
113  }
114 
115  float
116  ChainApproximation::computeDistance(const ChainApproximation::Triplet& triplet) const
117  {
118  using Line = Eigen::ParametrizedLine<float, 2>;
119 
120  const Eigen::Vector2f& ptBefore = points.at(triplet.a);
121  const Eigen::Vector2f& ptPivot = points.at(triplet.b);
122  const Eigen::Vector2f& ptAfter = points.at(triplet.c);
123 
124  const auto line = Line::Through(ptBefore, ptAfter);
125  return line.distance(ptPivot);
126  }
127 
128  bool
129  ChainApproximation::approximateStep()
130  {
131  const size_t nIndices = indices.size();
132  if (nIndices <= 3)
133  {
134  return false;
135  }
136 
137  const Triplets triplets = getTriplets();
138  const std::vector<float> distances = computeDistances(triplets);
139 
140  ARMARX_CHECK_EQUAL(triplets.size(), distances.size());
141  const int n = static_cast<int>(triplets.size());
142 
143  std::vector<int> indicesToBeRemoved;
144 
145  // TODO(fabian.reister): consider boundaries
146  for (int i = 1; i < n - 1; i++)
147  {
148  const auto& distance = distances.at(i);
149 
150  // check distance criterion (necessary conditio)
151  if (distance >= params.distanceTh)
152  {
153  continue;
154  }
155 
156  // better remove this element than those left and right (sufficient condition)
157  if (distance <= std::min(distances.at(i - 1), distances.at(i + 1)))
158  {
159  indicesToBeRemoved.emplace_back(triplets.at(i).b);
160  }
161  }
162 
163  // termination condition
164  if (indicesToBeRemoved.empty())
165  {
166  return false;
167  }
168 
169  const auto isMatch = [&](const int& idx) -> bool
170  { return ranges::find(indicesToBeRemoved, idx) != indicesToBeRemoved.end(); };
171 
172  indices.erase(std::remove_if(indices.begin(), indices.end(), isMatch), indices.end());
173 
174  return true;
175  }
176 
179  {
180  return simox::alg::apply(indices, [&](const auto& idx) { return points.at(idx); });
181  }
182 
183  std::ostream&
184  detail::operator<<(std::ostream& str, const ApproximationResult& res)
185  {
186  using TerminationCondition = ApproximationResult::TerminationCondition;
187 
188  const std::string condStr = [&res]() -> std::string
189  {
190  std::string repr;
191 
192  switch (res.condition)
193  {
194  case TerminationCondition::Converged:
195  repr = "Converged";
196  break;
197  case TerminationCondition::IterationLimit:
198  repr = "IterationLimit";
199  break;
200  }
201  return repr;
202  }();
203 
204  str << "ApproximationResult: ["
205  << "condition: " << condStr << " | "
206  << "iterations: " << res.iterations << " | "
207  << "reduction: " << res.reductionFactor * 100 << "%]";
208 
209  return str;
210  }
211 } // namespace armarx::navigation::algorithm
ChainApproximation.h
str
std::string str(const T &t)
Definition: UserAssistedSegmenterGuiWidgetController.cpp:43
armarx::navigation::algorithm::detail::ChainApproximationParams::maxIterations
int maxIterations
Definition: ChainApproximation.h:39
armarx::navigation::algorithm::detail::ChainApproximationParams
Definition: ChainApproximation.h:35
armarx::navigation::algorithm::ChainApproximation::approximate
ApproximationResult approximate()
Definition: ChainApproximation.cpp:31
armarx::navigation::algorithm::detail::ApproximationResult::condition
TerminationCondition condition
Definition: ChainApproximation.h:49
armarx::navigation::algorithm::ChainApproximation::approximatedChain
Points approximatedChain() const
Definition: ChainApproximation.cpp:178
armarx::navigation::algorithm::detail::ApproximationResult::TerminationCondition::IterationLimit
@ IterationLimit
armarx::navigation::algorithm::detail::ApproximationResult::iterations
int iterations
Definition: ChainApproximation.h:51
armarx::navigation::algorithm::ChainApproximation::Points
std::vector< Point > Points
Definition: ChainApproximation.h:63
armarx::navigation::algorithm
This file is part of ArmarX.
Definition: AStarPlanner.cpp:32
armarx::navigation::algorithm::detail::operator<<
std::ostream & operator<<(std::ostream &str, const ApproximationResult &res)
Definition: ChainApproximation.cpp:184
armarx::navigation::algorithm::ChainApproximation::ChainApproximation
ChainApproximation(const Points &points, const Params &params)
Definition: ChainApproximation.cpp:22
armarx::transform
auto transform(const Container< InputT, Alloc > &in, OutputT(*func)(InputT const &)) -> Container< OutputT, typename std::allocator_traits< Alloc >::template rebind_alloc< OutputT >>
Convenience function (with less typing) to transform a container of type InputT into the same contain...
Definition: algorithm.h:351
ExpressionException.h
armarx::navigation::algorithm::detail::ChainApproximationParams::distanceTh
float distanceTh
Definition: ChainApproximation.h:37
armarx::navigation::algorithm::detail::ApproximationResult::TerminationCondition::Converged
@ Converged
distance
double distance(const Point &a, const Point &b)
Definition: point.hpp:95
min
T min(T t1, T t2)
Definition: gdiam.h:44
ARMARX_CHECK_EQUAL
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
Definition: ExpressionException.h:130
armarx::navigation::algorithm::detail::ApproximationResult::reductionFactor
float reductionFactor
Definition: ChainApproximation.h:53
armarx::navigation::algorithm::detail::ApproximationResult::TerminationCondition
TerminationCondition
Definition: ChainApproximation.h:44
armarx::navigation::algorithm::detail::ApproximationResult
Definition: ChainApproximation.h:42
Line
Eigen::ParametrizedLine< float, 2 > Line
Definition: LaserScannerSelfLocalisation.cpp:52