FeatureExtractor.cpp
Go to the documentation of this file.
1 #include "FeatureExtractor.h"
2 
3 #include <algorithm>
4 #include <cmath>
5 #include <exception>
6 #include <iterator>
7 #include <optional>
8 #include <vector>
9 
10 #include <Eigen/Core>
11 #include <Eigen/Geometry>
12 
13 #include <opencv2/core/types.hpp>
14 #include <opencv2/imgproc.hpp>
15 
16 #include <VirtualRobot/MathTools.h>
17 #include <VirtualRobot/VirtualRobot.h>
18 #include <VirtualRobot/math/Helpers.h>
19 
21 
22 #include <RobotAPI/interface/units/LaserScannerUnit.h>
24 
29 
30 #include "Path.h"
31 #include "conversions/opencv.h"
33 
35 {
36 
37  // Features
38 
39  std::vector<Ellipsoid>
40  Features::linesAsEllipsoids(const float axisLength) const
41  {
42  if (not chain)
43  {
44  return {};
45  }
46 
47  const auto asEllipsoid = [&](const Path::Segment& segment) -> Ellipsoid
48  {
49  const Eigen::Vector2f center = (segment.first + segment.second) / 2;
50  const Eigen::Vector2f v = segment.second - center;
51  const float angle = math::Helpers::Angle(v);
52  const float r = v.norm();
53 
54  Eigen::Isometry3f globalPose = Eigen::Isometry3f::Identity();
55  globalPose.translation().head<2>() = center;
56  globalPose.linear() =
57  Eigen::AngleAxisf(angle, Eigen::Vector3f::UnitZ()).toRotationMatrix();
58 
59  return Ellipsoid{.pose = globalPose, .radii = Eigen::Vector2f{r, axisLength}};
60  };
61 
62  const auto segments = Path{.points = *chain}.segments();
63 
64  std::vector<Ellipsoid> ellipsoids;
66  segments.begin(), segments.end(), std::back_inserter(ellipsoids), asEllipsoid);
67 
68  return ellipsoids;
69  }
70 
71  // FeatureExtractor
72 
74  const ChainApproximation::Params& chainApproximationParams) :
75  scanClusteringParams(scanClusteringParams),
76  chainApproximationParams(chainApproximationParams)
77  {
78  }
79 
80  std::vector<Features>
82  {
83  ARMARX_DEBUG << "on data";
84  const auto clustersWithFeatures = features(data.data);
85 
86  ARMARX_DEBUG << "callback";
87  return clustersWithFeatures;
88  }
89 
90  std::vector<Features>
91  FeatureExtractor::features(const LaserScan& scan) const
92  {
93  const auto clusters = detectClusters(scan);
94 
95  std::vector<Features> fs;
96  std::transform(clusters.begin(),
97  clusters.end(),
98  std::back_inserter(fs),
99  [&](const LaserScan& cluster)
100  {
101  const auto points = toCartesian<Eigen::Vector2f>(cluster);
102  const auto hull = convexHull(points);
103 
104  return Features{
105  .convexHull = hull,
106  .circle = circle(points),
107  .ellipsoid = std::nullopt, //ellipsoid(hull),
108  .chain = chainApproximation(points, hull, chainApproximationParams),
109  .points = points};
110  });
111 
112  return fs;
113  }
114 
115  std::vector<LaserScan>
116  FeatureExtractor::detectClusters(const LaserScan& scan) const
117  {
118  ScanClustering clustering(scanClusteringParams);
119  return clustering.detectClusters(scan);
120  }
121 
122  std::optional<VirtualRobot::MathTools::ConvexHull2D>
124  {
125 
126  if (points.size() < 3) // no meaningful area
127  {
128  return std::nullopt;
129  }
130 
131  try
132  {
133  return *VirtualRobot::MathTools::createConvexHull2D(points);
134  }
135  catch (std::exception& e)
136  {
137  ARMARX_WARNING << "Couldn't create convex hull: " << e.what();
138  }
139  return std::nullopt;
140  }
141 
142  // Legacy OpenCV implementation. Didn't work as expected.
143  // Also, tries to fit an ellipsoid which is NOT the enclosing ellipsoid!
144 
145  // std::optional<Ellipsoid> FeatureExtractor::ellipsoid(const PointCloud &pointCloud) const
146  // {
147  // // OpenCV::fitEllipse requirement
148  // // "There should be at least 5 points to fit the ellipse"
149  // // => Too few points will cause algorithmic issues
150  // if (pointCloud.size() < 100)
151  // {
152  // return std::nullopt;
153  // }
154 
155  // const auto points2f = conversions::pcl2cv(pointCloud);
156  // const auto points2i = conversions::cast<cv::Point2i>(points2f);
157 
158  // cv::RotatedRect rect = cv::fitEllipse(points2i);
159 
160  // Eigen::Affine2f pose;
161  // pose.translation() = conversions::cv2eigen(rect.center);
162  // pose.linear() =
163  // Eigen::Rotation2Df(VirtualRobot::MathTools::deg2rad(rect.angle)).toRotationMatrix();
164 
165  // return Ellipsoid{.axisLengths = conversions::cv2eigen(rect.size), .pose = pose};
166  // }
167 
168  std::optional<Ellipsoid>
169  FeatureExtractor::ellipsoid(const std::optional<VirtualRobot::MathTools::ConvexHull2D>& hull)
170  {
171 
172  if (not hull)
173  {
174  return std::nullopt;
175  }
176 
177  // TODO(fabian.reister): it might result in multiple ellipsoids if hull->segments is considered
178 
179  // If there are not enough points, don't even try to fit an ellipse ...
180  if (hull->vertices.size() < 5)
181  {
182  return std::nullopt;
183  }
184 
185  EnclosingEllipsoid mvee(hull->vertices);
186  return mvee;
187  }
188 
189  std::optional<Circle>
190  FeatureExtractor::circle(const Points& points)
191  {
192  // Too few points will cause algorithmic issues
193  if (points.size() < 5)
194  {
195  return std::nullopt;
196  }
197 
198  const auto points2f = conversions::eigen2cv(points);
199  const auto points2i = conversions::cast<cv::Point2i>(points2f);
200 
201  cv::Point2f center;
202  float radius = NAN;
203  cv::minEnclosingCircle(points2i, center, radius);
204 
205  return Circle{.center = conversions::cv2eigen(center), .radius = radius};
206  }
207 
208  std::optional<FeatureExtractor::Points>
209  FeatureExtractor::chainApproximation(
210  const Points& points,
211  const std::optional<VirtualRobot::MathTools::ConvexHull2D>& convexHull,
212  const ChainApproximation::Params& params)
213  {
214  if (not convexHull)
215  {
216  return std::nullopt;
217  }
218 
219  ChainApproximation chApprx(points, params);
220  const auto result = chApprx.approximate();
221 
222  if (result.condition !=
224  {
225  return std::nullopt;
226  }
227 
228  return chApprx.approximatedChain();
229  }
230 
231 } // namespace armarx::navigation::components::laser_scanner_feature_extraction
armarx::navigation::components::laser_scanner_feature_extraction::FeatureExtractor::onData
std::vector< Features > onData(const armem::laser_scans::LaserScanStamped &data)
Definition: FeatureExtractor.cpp:81
armarx::navigation::components::laser_scanner_feature_extraction::detail::ScanClusteringParams
Definition: ScanClustering.h:32
armarx::armem::laser_scans::LaserScanStamped
Definition: types.h:40
armarx::detail::ChainApproximationParams
Definition: ChainApproximation.h:34
opencv.h
armarx::navigation::components::laser_scanner_feature_extraction::EnclosingEllipsoid
Minimum volume enclosing ellipsoid (MVEE) for a set of points.
Definition: EnclosingEllipsoid.h:52
armarx::ChainApproximation::approximatedChain
Points approximatedChain() const
Definition: ChainApproximation.cpp:177
Path.h
Convex::convexHull
ConvexHull< Point >::type convexHull(const std::vector< Point > &points)
Definition: convexHull.hpp:501
types.h
armarx::ChainApproximation
Definition: ChainApproximation.h:58
armarx::navigation::memory::Ellipsoid::pose
Eigen::Isometry3f pose
Definition: types.h:35
armarx::navigation::components::laser_scanner_feature_extraction::Path
Definition: Path.h:10
armarx::navigation::components::laser_scanner_feature_extraction::Features::chain
std::optional< Chain > chain
Definition: FeatureExtractor.h:52
EnclosingEllipsoid.h
armarx::navigation::components::laser_scanner_feature_extraction::Path::points
std::vector< Eigen::Vector2f > points
Definition: Path.h:12
FeatureExtractor.h
GfxTL::Identity
void Identity(MatrixXX< N, N, T > *a)
Definition: MatrixXX.h:570
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:184
armarx::navigation::components::laser_scanner_feature_extraction::Path::Segment
std::pair< Eigen::Vector2f, Eigen::Vector2f > Segment
Definition: Path.h:14
armarx::navigation::memory::Circle::center
Eigen::Vector2f center
Definition: types.h:42
armarx::navigation::memory::Ellipsoid
Definition: types.h:33
ScanClustering.h
armarx::conversions::cv2eigen
Eigen::Vector2f cv2eigen(const cv::Point2f &pt)
Definition: opencv_eigen.h:54
armarx::ChainApproximation::approximate
ApproximationResult approximate()
Definition: ChainApproximation.cpp:27
armarx::navigation::memory::Circle
Definition: types.h:40
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
armarx::ctrlutil::v
double v(double t, double v0, double a0, double j)
Definition: CtrlUtil.h:39
opencv_eigen.h
ChainApproximation.h
armarx::navigation::components::laser_scanner_feature_extraction::FeatureExtractor::FeatureExtractor
FeatureExtractor(const ScanClustering::Params &scanClusteringParams, const ChainApproximation::Params &chainApproximationParams)
Definition: FeatureExtractor.cpp:73
angle
double angle(const Point &a, const Point &b, const Point &c)
Definition: point.hpp:109
armarx::navigation::components::laser_scanner_feature_extraction::FeatureExtractor::Points
std::vector< Eigen::Vector2f > Points
Definition: FeatureExtractor.h:61
Logging.h
armarx::navigation::components::laser_scanner_feature_extraction::Features::linesAsEllipsoids
std::vector< Ellipsoid > linesAsEllipsoids(float axisLength) const
Definition: FeatureExtractor.cpp:40
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:193
laser_scanner_conversion.h
armarx::detail::ApproximationResult::TerminationCondition::Converged
@ Converged
armarx::navigation::components::laser_scanner_feature_extraction
Definition: ArVizDrawer.cpp:28
armarx::conversions::eigen2cv
cv::Point2f eigen2cv(const Eigen::Vector2f &pt)
Definition: opencv_eigen.h:33