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