FeatureMerger.cpp
Go to the documentation of this file.
1#include "FeatureMerger.h"
2
3#include <cstddef>
4#include <optional>
5#include <utility>
6#include <vector>
7
8#include <range/v3/view/enumerate.hpp>
9#include <range/v3/view/sliding.hpp>
10
14
18
20{
21
22
23 FeatureMerger::FeatureMerger(std::vector<Features>&& features,
24 const ChainApproximation::Params& chainApproximationParams) :
25 inputFeatures(std::move(features)), chainApproximationParams(chainApproximationParams)
26 {
27 }
28
29 FeatureMerger::FeatureMerger(const std::vector<Features>& features,
30 const ChainApproximation::Params& chainApproximationParams) :
31 inputFeatures(features), chainApproximationParams(chainApproximationParams)
32 {
33 }
34
35 std::vector<Features>
37 {
38 ARMARX_DEBUG << "Starting merge of " << inputFeatures.size() << " features";
39 UnionFind uf{inputFeatures.size()};
40
41 for (const auto [i, f] : ranges::views::enumerate(inputFeatures))
42 {
43 std::optional<std::size_t> lastFeature = std::nullopt;
44 for (const auto& p : f.points)
45 {
46 if (auto res = checkPoint(p, i, lastFeature); res)
47 {
48 uf.unite(i, res.value());
49 lastFeature = res.value();
50 }
51 }
52 }
53
54 for (const auto [i, f] : ranges::views::enumerate(inputFeatures))
55 {
56 std::size_t representative = uf.find(i);
57 if (i != representative)
58 {
59 ARMARX_DEBUG << "add feature " << i << " to feature " << representative;
60 addToFeature(inputFeatures[representative], f);
61 }
62 }
63
64 std::vector<Features> merged;
65 for (const auto [i, f] : ranges::views::enumerate(inputFeatures))
66 {
67 if (i == uf.find(i))
68 {
69 ARMARX_DEBUG << "feature " << i << " is representative";
70 merged.push_back(f);
71 }
72 }
73 // recalculate convex hull etc. for all merged features
74 recalculateFeatures(merged);
75
76 ARMARX_DEBUG << "Merged " << inputFeatures.size() << " features into " << merged.size();
77 return merged;
78 }
79
80 bool
81 FeatureMerger::insideConvexPoly(const Eigen::Vector2f& p,
82 const std::vector<Eigen::Vector2f>& vertices)
83 {
84 // algorithm to check whether a point is inside a convex polygon
85 // see https://stackoverflow.com/questions/1119627/how-to-test-if-a-point-is-inside-of-a-convex-polygon-in-2d-integer-coordinates
86
87 const auto signum = [](float x)
88 {
89 if (x == 0)
90 return 0;
91 return x > 0 ? 1 : -1;
92 };
93
94 const auto checkSide = [&](const Eigen::Vector2f& a, const Eigen::Vector2f& b)
95 { return signum((p.x() - a.x()) * (b.y() - a.y()) - (p.y() - a.y()) * (b.x() - a.x())); };
96
97 float previousSide = 0;
98 for (const auto edge : vertices | ranges::views::sliding(2))
99 {
100 int d = checkSide(edge[0], edge[1]);
101 if (d == 0)
102 return true;
103
104 if (previousSide != 0 && d != previousSide)
105 {
106 return false;
107 }
108 previousSide = d;
109 }
110 int d = checkSide(vertices.back(), vertices.front());
111 if (d == 0)
112 return true;
113 return d == previousSide;
114 }
115
116 std::optional<std::size_t>
117 FeatureMerger::checkPoint(const Eigen::Vector2f p,
118 std::size_t ignore,
119 const std::optional<std::size_t> checkFirst)
120 {
122 // when try first is given check this feature before all others
123 if (checkFirst)
124 {
125
127 ARMARX_CHECK_LESS(checkFirst.value(), inputFeatures.size());
128 const auto convexHullOpt = inputFeatures[checkFirst.value()].convexHull;
129
130 if (convexHullOpt.has_value() and insideConvexPoly(p, convexHullOpt->vertices))
131 {
132 return checkFirst.value();
133 }
134 }
135
136 for (const auto [i, f] : ranges::views::enumerate(inputFeatures))
137 {
139 if (i == ignore || (checkFirst && i == checkFirst.value()))
140 {
141 continue;
142 }
143
144 const auto convexHullOpt = f.convexHull;
145
146 if (convexHullOpt.has_value() and insideConvexPoly(p, convexHullOpt->vertices))
147 {
148 return i;
149 }
150 }
151 return std::nullopt;
152 }
153
154 void
155 FeatureMerger::addToFeature(Features& f, const Features& toAdd)
156 {
157 f.points.insert(f.points.end(), toAdd.points.begin(), toAdd.points.end());
158
159 f.convexHull = std::nullopt;
160 f.circle = std::nullopt;
161 f.ellipsoid = std::nullopt;
162 f.chain = std::nullopt;
163 }
164
165 void
166 FeatureMerger::recalculateFeatures(std::vector<Features>& features)
167 {
169 for (auto& f : features)
170 {
171 if (!f.convexHull)
172 {
173 f.convexHull = FeatureExtractor::convexHull(f.points);
174 }
175
176 if (!f.circle)
177 {
178 f.circle = FeatureExtractor::circle(f.points);
179 }
180
181 // if (!f.ellipsoid)
182 // {
183 // f.ellipsoid = FeatureExtractor::ellipsoid(f.convexHull);
184 // }
185
186 if (!f.chain)
187 {
189 f.points, f.convexHull, chainApproximationParams);
190 }
191 }
192 }
193
194
195} // namespace armarx::navigation::components::laser_scanner_feature_extraction
detail::ChainApproximationParams Params
static std::optional< VirtualRobot::MathTools::ConvexHull2D > convexHull(const Points &points)
static std::optional< Points > chainApproximation(const Points &points, const std::optional< VirtualRobot::MathTools::ConvexHull2D > &convexHull, const ChainApproximation::Params &params)
FeatureMerger(std::vector< Features > &&features, const ChainApproximation::Params &chainApproximationParams)
static bool insideConvexPoly(const Eigen::Vector2f &p, const std::vector< Eigen::Vector2f > &vertices)
#define ARMARX_CHECK_LESS(lhs, rhs)
This macro evaluates whether lhs is less (<) than rhs and if it turns out to be false it will throw a...
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
This file offers overloads of toIce() and fromIce() functions for STL container types.
#define ARMARX_TRACE
Definition trace.h:77