Apollo  v5.5.0
Open source self driving car software
convex_hull_2d.h
Go to the documentation of this file.
1 /******************************************************************************
2  * Copyright 2018 The Apollo Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *****************************************************************************/
16 #pragma once
17 
18 #include <algorithm>
19 #include <cfloat>
20 #include <vector>
21 
22 #include "Eigen/Dense"
23 
24 namespace apollo {
25 namespace perception {
26 namespace common {
27 
28 template <class CLOUD_IN_TYPE, class CLOUD_OUT_TYPE>
29 class ConvexHull2D {
30  public:
31  ConvexHull2D() : in_cloud_(nullptr) {
32  points_.reserve(1000.0);
33  polygon_indices_.reserve(1000.0);
34  }
35  ~ConvexHull2D() { in_cloud_ = nullptr; }
36  // main interface to get polygon from input point cloud
37  bool GetConvexHull(const CLOUD_IN_TYPE& in_cloud,
38  CLOUD_OUT_TYPE* out_polygon) {
39  SetPoints(in_cloud);
40  if (!GetConvexHullMonotoneChain(out_polygon)) {
41  return MockConvexHull(out_polygon);
42  }
43  return true;
44  }
45  // main interface to get polygon from input point cloud(without ground points)
46  bool GetConvexHullWithoutGround(const CLOUD_IN_TYPE& in_cloud,
47  const float& distance_above_ground_thres,
48  CLOUD_OUT_TYPE* out_polygon) {
49  CLOUD_IN_TYPE in_cloud_without_ground;
50  in_cloud_without_ground.reserve(in_cloud.size());
51  for (std::size_t id = 0; id < in_cloud.size(); ++id) {
52  // compute point_heigh, note FLT_MAX is the default value
53  if (in_cloud.points_height(id) >= distance_above_ground_thres) {
54  in_cloud_without_ground.push_back(in_cloud[id]);
55  }
56  }
57  if (in_cloud_without_ground.empty()) {
58  return GetConvexHull(in_cloud, out_polygon);
59  } else {
60  SetPoints(in_cloud_without_ground);
61  if (!GetConvexHullMonotoneChain(out_polygon)) {
62  return MockConvexHull(out_polygon);
63  }
64  }
65  return true;
66  }
67  // main interface to get polygon from input point cloud
68  // (without ground points and points above the head of self-driving car)
70  const CLOUD_IN_TYPE& in_cloud, const float& distance_above_ground_thres,
71  const float& distance_beneath_head_thres, CLOUD_OUT_TYPE* out_polygon) {
72  CLOUD_IN_TYPE in_cloud_without_ground_and_head;
73  in_cloud_without_ground_and_head.reserve(in_cloud.size());
74  for (std::size_t id = 0; id < in_cloud.size(); ++id) {
75  // compute point_heigh, note FLT_MAX is the default value
76  if (in_cloud.points_height(id) == FLT_MAX ||
77  (in_cloud.points_height(id) >= distance_above_ground_thres &&
78  in_cloud.points_height(id) <= distance_beneath_head_thres)) {
79  in_cloud_without_ground_and_head.push_back(in_cloud[id]);
80  }
81  }
82  if (in_cloud_without_ground_and_head.empty()) {
83  return GetConvexHull(in_cloud, out_polygon);
84  } else {
85  SetPoints(in_cloud_without_ground_and_head);
86  if (!GetConvexHullMonotoneChain(out_polygon)) {
87  return MockConvexHull(out_polygon);
88  }
89  }
90  return true;
91  }
92 
93  private:
94  // save points in local memory, and transform to double
95  void SetPoints(const CLOUD_IN_TYPE& in_cloud);
96  // mock a polygon for some degenerate cases
97  bool MockConvexHull(CLOUD_OUT_TYPE* out_polygon);
98  // compute convex hull using Andrew's monotone chain algorithm
99  bool GetConvexHullMonotoneChain(CLOUD_OUT_TYPE* out_polygon);
100  // given 3 ordered points, return true if in counter clock wise.
101  bool IsCounterClockWise(const Eigen::Vector2d& p1, const Eigen::Vector2d& p2,
102  const Eigen::Vector2d& p3, const double& eps) {
103  Eigen::Vector2d p12 = p2 - p1;
104  Eigen::Vector2d p13 = p3 - p1;
105  return (p12(0) * p13(1) - p12(1) * p13(0) > eps);
106  }
107 
108  private:
109  std::vector<Eigen::Vector2d> points_;
110  std::vector<std::size_t> polygon_indices_;
111  const CLOUD_IN_TYPE* in_cloud_;
112 };
113 
114 template <class CLOUD_IN_TYPE, class CLOUD_OUT_TYPE>
116  const CLOUD_IN_TYPE& in_cloud) {
117  points_.resize(in_cloud.size());
118  for (std::size_t i = 0; i < points_.size(); ++i) {
119  points_[i] << in_cloud[i].x, in_cloud[i].y;
120  }
121  in_cloud_ = &in_cloud;
122 }
123 
124 template <class CLOUD_IN_TYPE, class CLOUD_OUT_TYPE>
126  CLOUD_OUT_TYPE* out_polygon) {
127  if (in_cloud_->size() == 0) {
128  return false;
129  }
130  out_polygon->resize(4);
131  Eigen::Matrix<double, 3, 1> maxv;
132  Eigen::Matrix<double, 3, 1> minv;
133  maxv << in_cloud_->at(0).x, in_cloud_->at(0).y, in_cloud_->at(0).z;
134  minv = maxv;
135  for (std::size_t i = 1; i < in_cloud_->size(); ++i) {
136  maxv(0) = std::max<double>(maxv(0), in_cloud_->at(i).x);
137  maxv(1) = std::max<double>(maxv(1), in_cloud_->at(i).y);
138  maxv(2) = std::max<double>(maxv(2), in_cloud_->at(i).z);
139 
140  minv(0) = std::min<double>(minv(0), in_cloud_->at(i).x);
141  minv(1) = std::min<double>(minv(1), in_cloud_->at(i).y);
142  minv(2) = std::min<double>(minv(2), in_cloud_->at(i).z);
143  }
144 
145  static const double eps = 1e-3;
146  for (std::size_t i = 0; i < 3; ++i) {
147  if (maxv(i) - minv(i) < eps) {
148  maxv(i) += eps;
149  minv(i) -= eps;
150  }
151  }
152 
153  out_polygon->at(0).x = static_cast<float>(minv(0));
154  out_polygon->at(0).y = static_cast<float>(minv(1));
155  out_polygon->at(0).z = static_cast<float>(minv(2));
156 
157  out_polygon->at(1).x = static_cast<float>(maxv(0));
158  out_polygon->at(1).y = static_cast<float>(minv(1));
159  out_polygon->at(1).z = static_cast<float>(minv(2));
160 
161  out_polygon->at(2).x = static_cast<float>(maxv(0));
162  out_polygon->at(2).y = static_cast<float>(maxv(1));
163  out_polygon->at(2).z = static_cast<float>(minv(2));
164 
165  out_polygon->at(3).x = static_cast<float>(minv(0));
166  out_polygon->at(3).y = static_cast<float>(maxv(1));
167  out_polygon->at(3).z = static_cast<float>(minv(2));
168  return true;
169 }
170 
171 template <class CLOUD_IN_TYPE, class CLOUD_OUT_TYPE>
173  CLOUD_OUT_TYPE* out_polygon) {
174  if (points_.size() < 3) {
175  return false;
176  }
177 
178  std::vector<std::size_t> sorted_indices(points_.size());
179  std::iota(sorted_indices.begin(), sorted_indices.end(), 0);
180 
181  static const double eps = 1e-9;
182  std::sort(sorted_indices.begin(), sorted_indices.end(),
183  [&](const std::size_t& lhs, const std::size_t& rhs) {
184  double dx = points_[lhs](0) - points_[rhs](0);
185  if (std::abs(dx) > eps) {
186  return dx < 0.0;
187  }
188  return points_[lhs](1) < points_[rhs](1);
189  });
190  int count = 0;
191  int last_count = 1;
192  polygon_indices_.clear();
193  polygon_indices_.reserve(points_.size());
194 
195  std::size_t size2 = points_.size() * 2;
196  for (std::size_t i = 0; i < size2; ++i) {
197  if (i == points_.size()) {
198  last_count = count;
199  }
200  const std::size_t& idx =
201  sorted_indices[(i < points_.size()) ? i : (size2 - 1 - i)];
202  const auto& point = points_[idx];
203  while (count > last_count &&
204  !IsCounterClockWise(points_[polygon_indices_[count - 2]],
205  points_[polygon_indices_[count - 1]], point,
206  eps)) {
207  polygon_indices_.pop_back();
208  --count;
209  }
210  polygon_indices_.push_back(idx);
211  ++count;
212  }
213  --count;
214  polygon_indices_.pop_back();
215  if (count < 3) {
216  return false;
217  }
218  out_polygon->clear();
219  out_polygon->resize(polygon_indices_.size());
220  float min_z = static_cast<float>(in_cloud_->at(0).z);
221  for (std::size_t id = 0; id < in_cloud_->size(); ++id) {
222  min_z = std::min<float>(static_cast<float>(in_cloud_->at(id).z), min_z);
223  }
224  for (std::size_t i = 0; i < polygon_indices_.size(); ++i) {
225  out_polygon->at(i).x = static_cast<float>(points_[polygon_indices_[i]](0));
226  out_polygon->at(i).y = static_cast<float>(points_[polygon_indices_[i]](1));
227  out_polygon->at(i).z = min_z;
228  }
229  return true;
230 }
231 
232 } // namespace common
233 } // namespace perception
234 } // namespace apollo
Definition: blob.h:72
bool GetConvexHull(const CLOUD_IN_TYPE &in_cloud, CLOUD_OUT_TYPE *out_polygon)
Definition: convex_hull_2d.h:37
bool GetConvexHullWithoutGround(const CLOUD_IN_TYPE &in_cloud, const float &distance_above_ground_thres, CLOUD_OUT_TYPE *out_polygon)
Definition: convex_hull_2d.h:46
T abs(const T &x)
Definition: misc.h:48
Definition: convex_hull_2d.h:29
~ConvexHull2D()
Definition: convex_hull_2d.h:35
ConvexHull2D()
Definition: convex_hull_2d.h:31
bool GetConvexHullWithoutGroundAndHead(const CLOUD_IN_TYPE &in_cloud, const float &distance_above_ground_thres, const float &distance_beneath_head_thres, CLOUD_OUT_TYPE *out_polygon)
Definition: convex_hull_2d.h:69