ChainApproximation.cpp
Go to the documentation of this file.
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
15namespace armarx
16{
17
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 {
49 .iterations = iterations};
50 }
51
52 if (not approximateStep())
53 {
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
Eigen::ParametrizedLine< float, 2 > Line
std::string str(const T &t)
ChainApproximation(const Points &points, const Params &params)
ApproximationResult approximate()
std::vector< Point > Points
detail::ApproximationResult ApproximationResult
detail::ChainApproximationParams Params
#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...
std::ostream & operator<<(std::ostream &out, const LocationProvider::Location &l)
Definition trace.cpp:164
This file offers overloads of toIce() and fromIce() functions for STL container types.
constexpr auto n() noexcept
double distance(const Point &a, const Point &b)
Definition point.hpp:95