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