Apollo  6.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 <limits>
20 #include <numeric>
21 #include <vector>
22 
23 #include "Eigen/Dense"
24 
25 namespace apollo {
26 namespace perception {
27 namespace common {
28 
29 template <class CLOUD_IN_TYPE, class CLOUD_OUT_TYPE>
30 class ConvexHull2D {
31  public:
32  ConvexHull2D() : in_cloud_(nullptr) {
33  points_.reserve(1000.0);
34  polygon_indices_.reserve(1000.0);
35  }
36  ~ConvexHull2D() { in_cloud_ = nullptr; }
37  // main interface to get polygon from input point cloud
38  bool GetConvexHull(const CLOUD_IN_TYPE& in_cloud,
39  CLOUD_OUT_TYPE* out_polygon) {
40  SetPoints(in_cloud);
41  if (!GetConvexHullMonotoneChain(out_polygon)) {
42  return MockConvexHull(out_polygon);
43  }
44  return true;
45  }
46  // main interface to get polygon from input point cloud(without ground points)
47  bool GetConvexHullWithoutGround(const CLOUD_IN_TYPE& in_cloud,
48  const float& distance_above_ground_thres,
49  CLOUD_OUT_TYPE* out_polygon) {
50  CLOUD_IN_TYPE in_cloud_without_ground;
51  in_cloud_without_ground.reserve(in_cloud.size());
52  for (std::size_t id = 0; id < in_cloud.size(); ++id) {
53  // compute point_heigh, note std::numeric_limits<float>::max() is the
54  // default value
55  if (in_cloud.points_height(id) >= distance_above_ground_thres) {
56  in_cloud_without_ground.push_back(in_cloud[id]);
57  }
58  }
59  if (in_cloud_without_ground.empty()) {
60  return GetConvexHull(in_cloud, out_polygon);
61  } else {
62  SetPoints(in_cloud_without_ground);
63  if (!GetConvexHullMonotoneChain(out_polygon)) {
64  return MockConvexHull(out_polygon);
65  }
66  }
67  return true;
68  }
69  // main interface to get polygon from input point cloud
70  // (without ground points and points above the head of self-driving car)
72  const CLOUD_IN_TYPE& in_cloud, const float& distance_above_ground_thres,
73  const float& distance_beneath_head_thres, CLOUD_OUT_TYPE* out_polygon) {
74  CLOUD_IN_TYPE in_cloud_without_ground_and_head;
75  in_cloud_without_ground_and_head.reserve(in_cloud.size());
76  for (std::size_t id = 0; id < in_cloud.size(); ++id) {
77  // compute point_heigh, note std::numeric_limits<float>::max() is the
78  // default value
79  if (in_cloud.points_height(id) == std::numeric_limits<float>::max() ||
80  (in_cloud.points_height(id) >= distance_above_ground_thres &&
81  in_cloud.points_height(id) <= distance_beneath_head_thres)) {
82  in_cloud_without_ground_and_head.push_back(in_cloud[id]);
83  }
84  }
85  if (in_cloud_without_ground_and_head.empty()) {
86  return GetConvexHull(in_cloud, out_polygon);
87  } else {
88  SetPoints(in_cloud_without_ground_and_head);
89  if (!GetConvexHullMonotoneChain(out_polygon)) {
90  return MockConvexHull(out_polygon);
91  }
92  }
93  return true;
94  }
95 
96  private:
97  // save points in local memory, and transform to double
98  void SetPoints(const CLOUD_IN_TYPE& in_cloud);
99  // mock a polygon for some degenerate cases
100  bool MockConvexHull(CLOUD_OUT_TYPE* out_polygon);
101  // compute convex hull using Andrew's monotone chain algorithm
102  bool GetConvexHullMonotoneChain(CLOUD_OUT_TYPE* out_polygon);
103  // given 3 ordered points, return true if in counter clock wise.
104  bool IsCounterClockWise(const Eigen::Vector2d& p1, const Eigen::Vector2d& p2,
105  const Eigen::Vector2d& p3, const double& eps) {
106  Eigen::Vector2d p12 = p2 - p1;
107  Eigen::Vector2d p13 = p3 - p1;
108  return (p12(0) * p13(1) - p12(1) * p13(0) > eps);
109  }
110 
111  private:
112  std::vector<Eigen::Vector2d> points_;
113  std::vector<std::size_t> polygon_indices_;
114  const CLOUD_IN_TYPE* in_cloud_;
115 };
116 
117 template <class CLOUD_IN_TYPE, class CLOUD_OUT_TYPE>
119  const CLOUD_IN_TYPE& in_cloud) {
120  points_.resize(in_cloud.size());
121  for (std::size_t i = 0; i < points_.size(); ++i) {
122  points_[i] << in_cloud[i].x, in_cloud[i].y;
123  }
124  in_cloud_ = &in_cloud;
125 }
126 
127 template <class CLOUD_IN_TYPE, class CLOUD_OUT_TYPE>
129  CLOUD_OUT_TYPE* out_polygon) {
130  if (in_cloud_->size() == 0) {
131  return false;
132  }
133  out_polygon->resize(4);
134  Eigen::Matrix<double, 3, 1> maxv;
135  Eigen::Matrix<double, 3, 1> minv;
136  maxv << in_cloud_->at(0).x, in_cloud_->at(0).y, in_cloud_->at(0).z;
137  minv = maxv;
138  for (std::size_t i = 1; i < in_cloud_->size(); ++i) {
139  maxv(0) = std::max<double>(maxv(0), in_cloud_->at(i).x);
140  maxv(1) = std::max<double>(maxv(1), in_cloud_->at(i).y);
141  maxv(2) = std::max<double>(maxv(2), in_cloud_->at(i).z);
142 
143  minv(0) = std::min<double>(minv(0), in_cloud_->at(i).x);
144  minv(1) = std::min<double>(minv(1), in_cloud_->at(i).y);
145  minv(2) = std::min<double>(minv(2), in_cloud_->at(i).z);
146  }
147 
148  static const double eps = 1e-3;
149  for (std::size_t i = 0; i < 3; ++i) {
150  if (maxv(i) - minv(i) < eps) {
151  maxv(i) += eps;
152  minv(i) -= eps;
153  }
154  }
155 
156  out_polygon->at(0).x = static_cast<float>(minv(0));
157  out_polygon->at(0).y = static_cast<float>(minv(1));
158  out_polygon->at(0).z = static_cast<float>(minv(2));
159 
160  out_polygon->at(1).x = static_cast<float>(maxv(0));
161  out_polygon->at(1).y = static_cast<float>(minv(1));
162  out_polygon->at(1).z = static_cast<float>(minv(2));
163 
164  out_polygon->at(2).x = static_cast<float>(maxv(0));
165  out_polygon->at(2).y = static_cast<float>(maxv(1));
166  out_polygon->at(2).z = static_cast<float>(minv(2));
167 
168  out_polygon->at(3).x = static_cast<float>(minv(0));
169  out_polygon->at(3).y = static_cast<float>(maxv(1));
170  out_polygon->at(3).z = static_cast<float>(minv(2));
171  return true;
172 }
173 
174 template <class CLOUD_IN_TYPE, class CLOUD_OUT_TYPE>
176  CLOUD_OUT_TYPE* out_polygon) {
177  if (points_.size() < 3) {
178  return false;
179  }
180 
181  std::vector<std::size_t> sorted_indices(points_.size());
182  std::iota(sorted_indices.begin(), sorted_indices.end(), 0);
183 
184  static const double eps = 1e-9;
185  std::sort(sorted_indices.begin(), sorted_indices.end(),
186  [&](const std::size_t& lhs, const std::size_t& rhs) {
187  double dx = points_[lhs](0) - points_[rhs](0);
188  if (std::abs(dx) > eps) {
189  return dx < 0.0;
190  }
191  return points_[lhs](1) < points_[rhs](1);
192  });
193  int count = 0;
194  int last_count = 1;
195  polygon_indices_.clear();
196  polygon_indices_.reserve(points_.size());
197 
198  std::size_t size2 = points_.size() * 2;
199  for (std::size_t i = 0; i < size2; ++i) {
200  if (i == points_.size()) {
201  last_count = count;
202  }
203  const std::size_t& idx =
204  sorted_indices[(i < points_.size()) ? i : (size2 - 1 - i)];
205  const auto& point = points_[idx];
206  while (count > last_count &&
207  !IsCounterClockWise(points_[polygon_indices_[count - 2]],
208  points_[polygon_indices_[count - 1]], point,
209  eps)) {
210  polygon_indices_.pop_back();
211  --count;
212  }
213  polygon_indices_.push_back(idx);
214  ++count;
215  }
216  --count;
217  polygon_indices_.pop_back();
218  if (count < 3) {
219  return false;
220  }
221  out_polygon->clear();
222  out_polygon->resize(polygon_indices_.size());
223  float min_z = static_cast<float>(in_cloud_->at(0).z);
224  for (std::size_t id = 0; id < in_cloud_->size(); ++id) {
225  min_z = std::min<float>(static_cast<float>(in_cloud_->at(id).z), min_z);
226  }
227  for (std::size_t i = 0; i < polygon_indices_.size(); ++i) {
228  out_polygon->at(i).x = static_cast<float>(points_[polygon_indices_[i]](0));
229  out_polygon->at(i).y = static_cast<float>(points_[polygon_indices_[i]](1));
230  out_polygon->at(i).z = min_z;
231  }
232  return true;
233 }
234 
235 } // namespace common
236 } // namespace perception
237 } // namespace apollo
PlanningContext is the runtime context in planning. It is persistent across multiple frames...
Definition: atomic_hash_map.h:25
Eigen::Vector2d Vector2d
Definition: base_map_fwd.h:36
bool GetConvexHull(const CLOUD_IN_TYPE &in_cloud, CLOUD_OUT_TYPE *out_polygon)
Definition: convex_hull_2d.h:38
bool GetConvexHullWithoutGround(const CLOUD_IN_TYPE &in_cloud, const float &distance_above_ground_thres, CLOUD_OUT_TYPE *out_polygon)
Definition: convex_hull_2d.h:47
T abs(const T &x)
Definition: misc.h:48
Definition: convex_hull_2d.h:30
~ConvexHull2D()
Definition: convex_hull_2d.h:36
ConvexHull2D()
Definition: convex_hull_2d.h:32
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:71