Autoware.Auto
lfit.hpp
Go to the documentation of this file.
1 // Copyright 2017-2019 the Autoware Foundation
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // Co-developed by Tier IV, Inc. and Apex.AI, Inc.
16 
20 
21 #ifndef GEOMETRY__BOUNDING_BOX__LFIT_HPP_
22 #define GEOMETRY__BOUNDING_BOX__LFIT_HPP_
23 
25 #include <limits>
26 #include <utility>
27 
28 namespace autoware
29 {
30 namespace common
31 {
32 namespace geometry
33 {
34 namespace bounding_box
35 {
36 namespace details
37 {
38 
40 struct LFitWs
41 {
43  std::size_t p;
45  std::size_t q;
46  // assume matrix of form: [a b; c d]
55  // m22 is a symmetric matrix
62 }; // struct LFitWs
63 
70 template<typename IT>
71 void init_lfit_ws(const IT begin, const IT end, const std::size_t size, LFitWs & ws)
72 {
73  ws.p = 1UL;
74  ws.q = size - 1UL;
75  // init P terms (first partition)
76  using point_adapter::x_;
77  using point_adapter::y_;
78  const auto & pt = *begin;
79  const float32_t px = x_(pt);
80  const float32_t py = y_(pt);
81  // assume matrix of form: [a b; c d]
82  ws.m12a = px;
83  ws.m12b = py;
84  ws.m12c = 0.0F;
85  ws.m12d = 0.0F;
86  // m22 is a symmetric matrix
87  ws.m22a = px * px;
88  ws.m22b = px * py;
89  ws.m22d = py * py;
90  auto it = begin;
91  ++it;
92  for (; it != end; ++it) {
93  const auto & qt = *it;
94  const float32_t qx = x_(qt);
95  const float32_t qy = y_(qt);
96  ws.m12c += qy;
97  ws.m12d -= qx;
98  ws.m22a += qy * qy;
99  ws.m22b -= qx * qy;
100  ws.m22d += qx * qx;
101  }
102 }
103 
109 template<typename PointT>
110 float32_t solve_lfit(const LFitWs & ws, PointT & dir)
111 {
112  const float32_t pi = 1.0F / static_cast<float32_t>(ws.p);
113  const float32_t qi = 1.0F / static_cast<float32_t>(ws.q);
114  const Covariance2d M{ // matrix of form [x, z; z y]
115  ws.m22a - (((ws.m12a * ws.m12a) * pi) + ((ws.m12c * ws.m12c) * qi)),
116  ws.m22d - (((ws.m12b * ws.m12b) * pi) + ((ws.m12d * ws.m12d) * qi)),
117  ws.m22b - (((ws.m12a * ws.m12b) * pi) + ((ws.m12c * ws.m12d) * qi)),
118  0UL
119  };
120  PointT eig1;
121  const auto eigv = eig_2d(M, eig1, dir);
122  return eigv.second;
123 }
124 
129 template<typename PointT>
130 void increment_lfit_ws(const PointT & pt, LFitWs & ws)
131 {
132  const float32_t px = point_adapter::x_(pt);
133  const float32_t py = point_adapter::y_(pt);
134  ws.m12a += px;
135  ws.m12b += py;
136  ws.m12c -= py;
137  ws.m12d += px;
138  ws.m22b += 2.0F * px * py;
139  const float32_t px2y2 = (px - py) * (px + py);
140  ws.m22a += px2y2;
141  ws.m22d -= px2y2;
142 }
143 
145 template<typename PointT>
147 {
148 public:
152  explicit LFitCompare(const PointT & hint)
153  : m_nx(point_adapter::x_(hint)),
154  m_ny(point_adapter::y_(hint))
155  {
156  }
157 
163  bool8_t operator()(const PointT & p, const PointT & q) const
164  {
165  using point_adapter::x_;
166  using point_adapter::y_;
167  return ((x_(p) * m_nx) + (y_(p) * m_ny)) < ((x_(q) * m_nx) + (y_(q) * m_ny));
168  }
169 
170 private:
171  const float32_t m_nx;
172  const float32_t m_ny;
173 }; // class LFitCompare
174 
182 template<typename IT>
183 BoundingBox lfit_bounding_box_2d_impl(const IT begin, const IT end, const std::size_t size)
184 {
185  // initialize M
186  LFitWs ws{};
187  init_lfit_ws(begin, end, size, ws);
188  // solve initial problem
190  float32_t min_eig = solve_lfit(ws, best_normal);
191  // solve subsequent problems
192  auto it = begin;
193  ++it;
194  for (; it != end; ++it) {
195  // update M
196  ws.p += 1U;
197  ws.q -= 1U;
198  if (ws.q == 0U) { // checks for q = 0 case
199  break;
200  }
201  increment_lfit_ws(*it, ws);
202  // solve incremented problem
203  decltype(best_normal) dir;
204  const float32_t score = solve_lfit(ws, dir);
205  // update optima
206  if (score < min_eig) {
207  min_eig = score;
208  best_normal = dir;
209  }
210  }
211  // can recover best corner point, but don't care, need to cover all points
212  const auto inorm = 1.0F / norm_2d(best_normal);
213  if (!std::isnormal(inorm)) {
214  throw std::runtime_error{"LFit: Abnormal norm"};
215  }
216  best_normal = times_2d(best_normal, inorm);
217  auto best_tangent = get_normal(best_normal);
218  // find extreme points
219  Point4<IT> supports;
220  const bool8_t is_ccw = details::compute_supports(begin, end, best_normal, best_tangent, supports);
221  if (is_ccw) {
222  std::swap(best_normal, best_tangent);
223  }
224  BoundingBox bbox = details::compute_bounding_box(best_normal, best_tangent, supports);
225  bbox.value = min_eig;
226 
227  return bbox;
228 }
229 } // namespace details
230 
242 template<typename IT, typename PointT>
244  const IT begin,
245  const IT end,
246  const PointT hint,
247  const std::size_t size)
248 {
249  if (size <= 1U) {
250  throw std::domain_error("LFit requires >= 2 points!");
251  }
252  // NOTE: assumes points are in base_link/sensor frame!
253  // sort along tangent wrt sensor origin
254  //lint -e522 NOLINT Not a pure function: data structure iterators are pointing to is modified
255  std::partial_sort(begin, end, end, details::LFitCompare<PointT>{hint});
256 
257  return details::lfit_bounding_box_2d_impl(begin, end, size);
258 }
259 
267 template<typename IT>
268 BoundingBox lfit_bounding_box_2d(const IT begin, const IT end)
269 {
270  // use the principal component as the sorting direction
271  const auto cov = details::covariance_2d(begin, end);
273  PointT eig1;
274  PointT eig2;
275  (void)details::eig_2d(cov, eig1, eig2);
276  (void)eig2;
277  return lfit_bounding_box_2d(begin, end, eig1, cov.num_points);
278 }
279 } // namespace bounding_box
280 } // namespace geometry
281 } // namespace common
282 } // namespace autoware
283 
284 #endif // GEOMETRY__BOUNDING_BOX__LFIT_HPP_
BoundingBox lfit_bounding_box_2d(const IT begin, const IT end, const PointT hint, const std::size_t size)
Compute bounding box which best fits an L-shaped cluster. Uses the method proposed in "Efficient L-sh...
Definition: lfit.hpp:243
BoundingBox lfit_bounding_box_2d_impl(const IT begin, const IT end, const std::size_t size)
The main implementation of L-fitting a bounding box to a list of points. Assumes sufficiently valid...
Definition: lfit.hpp:183
This file implements 2D pca on a linked list of points to estimate an oriented bounding box...
autoware_auto_msgs::msg::BoundingBox BoundingBox
Definition: bounding_box_common.hpp:38
bool8_t compute_supports(const IT begin, const IT end, const PointT &eig1, const PointT &eig2, Point4< IT > &support)
Given eigenvectors, compute support (furthest) point in each direction.
Definition: eigenbox_2d.hpp:155
float float32_t
Definition: types.hpp:36
T get_normal(const T &pt)
compute q s.t. p T q, or p * q = 0 This is the equivalent of a 90 degree ccw rotation ...
Definition: common_2d.hpp:265
Simplified 2d covariance matrix.
Definition: eigenbox_2d.hpp:40
T times_2d(const T &p, const float32_t a)
The scalar multiplication operation, p * a.
Definition: common_2d.hpp:192
Covariance2d covariance_2d(const IT begin, const IT end)
Compute 2d covariance matrix of a list of points using Welford&#39;s online algorithm.
Definition: eigenbox_2d.hpp:58
float32_t solve_lfit(const LFitWs &ws, PointT &dir)
Solves the L fit problem for a given M matrix.
Definition: lfit.hpp:110
bool bool8_t
Definition: types.hpp:33
float32_t m22d
Sum_p y_2 + sum_x y_2.
Definition: lfit.hpp:61
void increment_lfit_ws(const PointT &pt, LFitWs &ws)
Increments L fit M matrix with information in the point.
Definition: lfit.hpp:130
std::pair< float32_t, float32_t > eig_2d(const Covariance2d &cov, PointT &eigvec1, PointT &eigvec2)
Compute eigenvectors and eigenvalues.
Definition: eigenbox_2d.hpp:101
typename std::remove_cv< typename std::remove_reference< T >::type >::type base_type
Templated alias for getting a type without CV or reference qualification.
Definition: bounding_box_common.hpp:72
float32_t m22a
Sum_p x_2 + sum_q y_2.
Definition: lfit.hpp:57
float32_t m12c
Sum of y values in second partition.
Definition: lfit.hpp:52
auto norm_2d(const T &pt)
get magnitude of x and y components:
Definition: common_2d.hpp:278
float32_t m12a
Sum of x values in first partition.
Definition: lfit.hpp:48
std::size_t q
Number of points in the second partition.
Definition: lfit.hpp:45
A representation of the M matrix for the L-fit algorithm.
Definition: lfit.hpp:40
float32_t m12b
Sum of y values in first partition.
Definition: lfit.hpp:50
void init_lfit_ws(const IT begin, const IT end, const std::size_t size, LFitWs &ws)
Initialize M matrix for L-fit algorithm.
Definition: lfit.hpp:71
auto x_(const PointT &pt)
Gets the x value for a point.
Definition: common_2d.hpp:47
float32_t m12d
Negative sum of x values in second partition.
Definition: lfit.hpp:54
LFitCompare(const PointT &hint)
Constructor, initializes normal direction.
Definition: lfit.hpp:152
auto y_(const PointT &pt)
Gets the y value for a point.
Definition: common_2d.hpp:56
end
Definition: scripts/get_open_port.py:23
BoundingBox compute_bounding_box(const PointT &ax1, const PointT &ax2, const Point4< IT > &supports)
Compute bounding box given a pair of basis directions.
Definition: eigenbox_2d.hpp:202
bool8_t operator()(const PointT &p, const PointT &q) const
Comparator operation, returns true if the projection of a the tangent line is smaller than the projec...
Definition: lfit.hpp:163
float32_t m22b
Sum_p x*y - sum_q x*y.
Definition: lfit.hpp:59
std::size_t p
Number of points in the first partition.
Definition: lfit.hpp:43
This file defines the lanelet2_map_provider_node class.
Definition: quick_sort.hpp:24
std::array< PointT, 4U > Point4
Templated alias for an array of 4 objects of type PointT.
Definition: bounding_box_common.hpp:76