point_cloud_sampler_with_normal.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020, the mcl_3dl authors
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #ifndef MCL_3DL_POINT_CLOUD_RANDOM_SAMPLERS_POINT_CLOUD_SAMPLER_WITH_NORMAL_H
31 #define MCL_3DL_POINT_CLOUD_RANDOM_SAMPLERS_POINT_CLOUD_SAMPLER_WITH_NORMAL_H
32 
33 #include <memory>
34 #include <random>
35 #include <unordered_set>
36 #include <vector>
37 
38 #include <Eigen/Core>
39 #include <Eigen/Eigenvalues>
40 
41 #include <pcl/features/normal_3d.h>
42 #include <pcl/kdtree/kdtree_flann.h>
43 #include <pcl/point_cloud.h>
44 #include <pcl/point_types.h>
45 #include <ros/ros.h>
46 
48 #include <mcl_3dl/state_6dof.h>
49 
50 namespace mcl_3dl
51 {
52 template <class POINT_TYPE>
54 {
55 private:
56  using Matrix = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>;
57  using Vector = Eigen::Matrix<double, Eigen::Dynamic, 1>;
58 
59  std::shared_ptr<std::default_random_engine> engine_;
65  double max_weight_;
67 
68 public:
69  explicit PointCloudSamplerWithNormal(const unsigned int random_seed = std::random_device()())
70  : engine_(std::make_shared<std::default_random_engine>(random_seed))
71  , perform_weighting_ratio_(2.0)
72  , max_weight_ratio_(5.0)
73  , max_weight_(10.0)
74  , normal_search_range_(0.4)
75  {
76  }
77 
78  void loadConfig(const ros::NodeHandle& nh)
79  {
80  ros::NodeHandle pnh(nh, "random_sampler_with_normal");
81  pnh.param("perform_weighting_ratio", perform_weighting_ratio_, 2.0);
82  pnh.param("max_weight_ratio", max_weight_ratio_, 5.0);
83  pnh.param("max_weight", max_weight_, 5.0);
84  pnh.param("normal_search_range", normal_search_range_, 0.4);
85  }
86 
87  void setParameters(const double perform_weighting_ratio, const double max_weight_ratio,
88  const double max_weight, const double normal_search_range)
89  {
90  perform_weighting_ratio_ = perform_weighting_ratio;
91  max_weight_ratio_ = max_weight_ratio;
92  max_weight_ = max_weight;
93  normal_search_range_ = normal_search_range;
94  }
95 
96  void setParticleStatistics(const State6DOF& mean, const std::vector<State6DOF>& covariances)
97  {
98  mean_ = mean;
99  Matrix pos_cov(3, 3);
100  for (size_t i = 0; i < 3; ++i)
101  {
102  for (size_t j = 0; j < 3; ++j)
103  {
104  pos_cov(i, j) = std::abs(covariances[i][j]);
105  }
106  }
107  const Eigen::SelfAdjointEigenSolver<Matrix> eigen_solver(pos_cov);
108  eigen_vectors_ = eigen_solver.eigenvectors();
109  eigen_values_ = eigen_solver.eigenvalues();
110  }
111 
112  typename pcl::PointCloud<POINT_TYPE>::Ptr sample(
113  const typename pcl::PointCloud<POINT_TYPE>::ConstPtr& pc,
114  const size_t num) const final
115  {
116  const ros::WallTime start_timestamp = ros::WallTime::now();
117 
118  typename pcl::PointCloud<POINT_TYPE>::Ptr output(new pcl::PointCloud<POINT_TYPE>);
119  output->header = pc->header;
120 
121  if ((pc->points.size() == 0) || (num == 0))
122  {
123  return output;
124  }
125  if (pc->size() <= num)
126  {
127  *output = *pc;
128  return output;
129  }
130 
131  const double eigen_value_ratio = std::sqrt(eigen_values_[2] / eigen_values_[1]);
132 
133  double max_weight = 1.0;
134  if (eigen_value_ratio < perform_weighting_ratio_)
135  {
136  max_weight = 1.0;
137  }
138  else if (eigen_value_ratio > max_weight_ratio_)
139  {
140  max_weight = max_weight_;
141  }
142  else
143  {
144  const double weight_ratio =
145  (eigen_value_ratio - perform_weighting_ratio_) / (max_weight_ratio_ - perform_weighting_ratio_);
146  max_weight = 1.0 + (max_weight_ - 1.0) * weight_ratio;
147  }
148  const mcl_3dl::Vec3 fpc_global(eigen_vectors_(0, 2), eigen_vectors_(1, 2), eigen_vectors_(2, 2));
149  const mcl_3dl::Vec3 fpc_local = mean_.rot_.inv() * fpc_global;
150  pcl::NormalEstimation<POINT_TYPE, pcl::Normal> ne;
151  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);
152  typename pcl::search::KdTree<POINT_TYPE>::Ptr tree(new pcl::search::KdTree<POINT_TYPE>());
153  ne.setInputCloud(pc);
154  ne.setSearchMethod(tree);
155  ne.setRadiusSearch(normal_search_range_);
156  ne.compute(*cloud_normals);
157 
158  const ros::WallTime compute_normal_timestamp = ros::WallTime::now();
159  std::vector<double> cumulative_weight(cloud_normals->points.size(), 0.0);
160  for (size_t i = 0; i < cloud_normals->points.size(); i++)
161  {
162  double weight = 1.0;
163  const auto& normal = cloud_normals->points[i];
164  if (!std::isnan(normal.normal_x) && !std::isnan(normal.normal_y) && !std::isnan(normal.normal_z))
165  {
166  double acos_angle = std::abs(normal.normal_x * fpc_local.x_ +
167  normal.normal_y * fpc_local.y_ +
168  normal.normal_z * fpc_local.z_);
169  // Avoid that std::acos() returns nan because of calculation errors
170  if (acos_angle > 1.0)
171  {
172  acos_angle = 1.0;
173  }
174  const double angle = std::acos(acos_angle);
175  weight = 1.0 + (max_weight - 1.0) * ((M_PI / 2 - angle) / (M_PI / 2));
176  }
177  cumulative_weight[i] = weight + ((i == 0) ? 0.0 : cumulative_weight[i - 1]);
178  }
179  std::uniform_real_distribution<double> ud(0, cumulative_weight.back());
180  // Use unordered_set to avoid duplication
181  std::unordered_set<size_t> selected_ids;
182  while (true)
183  {
184  const double random_value = ud(*engine_);
185  auto it = std::lower_bound(cumulative_weight.begin(), cumulative_weight.end(), random_value);
186  const size_t n = it - cumulative_weight.begin();
187  selected_ids.insert(n);
188  if (selected_ids.size() >= num)
189  {
190  break;
191  }
192  }
193  output->points.reserve(num);
194  for (const auto& index : selected_ids)
195  {
196  output->push_back(pc->points[index]);
197  }
198 
199  const ros::WallTime final_timestamp = ros::WallTime::now();
200  ROS_DEBUG("PointCloudSamplerWithNormal::sample() computation time: %f[s] (Normal calculation: %f[s])",
201  (final_timestamp - start_timestamp).toSec(), (compute_normal_timestamp - start_timestamp).toSec());
202  ROS_DEBUG("Chosen eigen vector: (%f, %f, %f), max weight: %f",
203  eigen_vectors_(0, 2), eigen_vectors_(1, 2), eigen_vectors_(2, 2), max_weight);
204  return output;
205  }
206 };
207 
208 } // namespace mcl_3dl
209 
210 #endif // MCL_3DL_POINT_CLOUD_RANDOM_SAMPLERS_POINT_CLOUD_SAMPLER_WITH_NORMAL_H
float z_
Definition: vec3.h:42
void setParameters(const double perform_weighting_ratio, const double max_weight_ratio, const double max_weight, const double normal_search_range)
std::shared_ptr< std::default_random_engine > engine_
PointCloudSamplerWithNormal(const unsigned int random_seed=std::random_device()())
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
float y_
Definition: vec3.h:41
constexpr Quat inv() const
Definition: quat.h:187
mcl_3dl::Quat rot_
Definition: state_6dof.h:53
float x_
Definition: vec3.h:40
static WallTime now()
void setParticleStatistics(const State6DOF &mean, const std::vector< State6DOF > &covariances)
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > Matrix
pcl::PointCloud< POINT_TYPE >::Ptr sample(const typename pcl::PointCloud< POINT_TYPE >::ConstPtr &pc, const size_t num) const final
Eigen::Matrix< double, Eigen::Dynamic, 1 > Vector
#define ROS_DEBUG(...)


mcl_3dl
Author(s): Atsushi Watanabe
autogenerated on Wed May 12 2021 02:16:29