FilterEdgesPlanes.cpp
Go to the documentation of this file.
1 /* -------------------------------------------------------------------------
2  * A repertory of multi primitive-to-primitive (MP2P) ICP algorithms in C++
3  * Copyright (C) 2018-2024 Jose Luis Blanco, University of Almeria
4  * See LICENSE for license information.
5  * ------------------------------------------------------------------------- */
14 #include <mrpt/containers/yaml.h>
15 #include <mrpt/maps/CSimplePointsMap.h>
16 #include <mrpt/math/ops_containers.h> // dotProduct
17 
20 
21 using namespace mp2p_icp_filters;
22 
24  const mrpt::containers::yaml& c)
25 {
26  MCP_LOAD_OPT(c, input_pointcloud_layer);
27 
28  MCP_LOAD_REQ(c, voxel_filter_resolution);
29  MCP_LOAD_REQ(c, voxel_filter_decimation);
30  MCP_LOAD_REQ(c, full_pointcloud_decimation);
31  MCP_LOAD_REQ(c, voxel_filter_max_e2_e0);
32  MCP_LOAD_REQ(c, voxel_filter_max_e1_e0);
33  MCP_LOAD_REQ(c, voxel_filter_min_e2_e0);
34  MCP_LOAD_REQ(c, voxel_filter_min_e1_e0);
35  MCP_LOAD_OPT(c, voxel_filter_min_e1);
36 }
37 
39 
40 void FilterEdgesPlanes::initialize(const mrpt::containers::yaml& c)
41 {
42  MRPT_START
43 
44  MRPT_LOG_DEBUG_STREAM("Loading these params:\n" << c);
46 
48 
49  MRPT_END
50 }
51 
53 {
54  MRPT_START
55 
56  // In:
57  const auto& pcPtr = inOut.point_layer(params_.input_pointcloud_layer);
58  ASSERTMSG_(
59  pcPtr, mrpt::format(
60  "Input point cloud layer '%s' was not found.",
62 
63  const auto& pc = *pcPtr;
64 
65  // Out:
66  auto pc_edges = mrpt::maps::CSimplePointsMap::Create();
67  auto pc_planes = mrpt::maps::CSimplePointsMap::Create();
68  auto pc_full_decim = mrpt::maps::CSimplePointsMap::Create();
69  auto pc_plane_centroids = mrpt::maps::CSimplePointsMap::Create();
70 
71  inOut.layers["edge_points"] = pc_edges;
72  inOut.layers["plane_points"] = pc_planes;
73  inOut.layers["full_decim"] = pc_full_decim;
74  inOut.layers["plane_centroids"] = pc_plane_centroids;
75 
76  pc_edges->reserve(pc.size() / 10);
77  pc_planes->reserve(pc.size() / 10);
78  pc_full_decim->reserve(pc.size() / 10);
79  pc_plane_centroids->reserve(pc.size() / 1000);
80 
83 
84  const auto& xs = pc.getPointsBufferRef_x();
85  const auto& ys = pc.getPointsBufferRef_y();
86  const auto& zs = pc.getPointsBufferRef_z();
87 
88  const float max_e20 = params_.voxel_filter_max_e2_e0;
89  const float max_e10 = params_.voxel_filter_max_e1_e0;
90  const float min_e20 = params_.voxel_filter_min_e2_e0;
91  const float min_e10 = params_.voxel_filter_min_e1_e0;
92  const float min_e1 = params_.voxel_filter_min_e1;
93 
94  std::size_t nEdgeVoxels = 0, nPlaneVoxels = 0, nTotalVoxels = 0;
95 
99  {
100  if (!vxl.indices.empty()) nTotalVoxels++;
101  if (vxl.indices.size() < 5) return;
102 
103  // Analyze the voxel contents:
104  mrpt::math::TPoint3Df mean{0, 0, 0};
105  const float inv_n = (1.0f / vxl.indices.size());
106  for (size_t i = 0; i < vxl.indices.size(); i++)
107  {
108  const auto pt_idx = vxl.indices[i];
109  mean.x += xs[pt_idx];
110  mean.y += ys[pt_idx];
111  mean.z += zs[pt_idx];
112  }
113  mean.x *= inv_n;
114  mean.y *= inv_n;
115  mean.z *= inv_n;
116 
117  mrpt::math::CMatrixFixed<double, 3, 3> mat_a;
118  mat_a.setZero();
119  for (size_t i = 0; i < vxl.indices.size(); i++)
120  {
121  const auto pt_idx = vxl.indices[i];
122  const mrpt::math::TPoint3Df a(
123  xs[pt_idx] - mean.x, ys[pt_idx] - mean.y,
124  zs[pt_idx] - mean.z);
125  mat_a(0, 0) += a.x * a.x;
126  mat_a(1, 0) += a.x * a.y;
127  mat_a(2, 0) += a.x * a.z;
128  mat_a(1, 1) += a.y * a.y;
129  mat_a(2, 1) += a.y * a.z;
130  mat_a(2, 2) += a.z * a.z;
131  }
132  mat_a *= inv_n;
133 
134  // Find eigenvalues & eigenvectors:
135  // This only looks at the lower-triangular part of the cov matrix.
136  mrpt::math::CMatrixFixed<double, 3, 3> eig_vectors;
137  std::vector<double> eig_vals;
138  mat_a.eig_symmetric(eig_vectors, eig_vals);
139 
140  const float e0 = eig_vals[0], e1 = eig_vals[1], e2 = eig_vals[2];
141 
142  mrpt::maps::CPointsMap* dest = nullptr;
143  if (e2 < max_e20 * e0 && e1 < max_e10 * e0)
144  {
145  // Classified as EDGE
146  // ------------------------
147  nEdgeVoxels++;
148  dest = pc_edges.get();
149  }
150  else if (e2 > min_e20 * e0 && e1 > min_e10 * e0 && e1 > min_e1)
151  {
152  // Classified as PLANE
153  // ------------------------
154  nPlaneVoxels++;
155 
156  // Define a plane from its centroid + a normal:
157  const auto pl_c = mrpt::math::TPoint3D(mean);
158 
159  // Normal = largest eigenvector:
160  const auto ev0 =
161  eig_vectors.extractColumn<mrpt::math::TVector3D>(0);
162  auto pl_n = mrpt::math::TVector3D(ev0.x, ev0.y, ev0.z);
163 
164  // Normal direction criterion: make it to face towards the
165  // vehicle. We can use the dot product to find it out, since
166  // pointclouds are given in vehicle-frame coordinates.
167  {
168  // Unit vector: vehicle -> plane centroid:
169  ASSERT_GT_(pl_c.norm(), 1e-3);
170  const auto u = pl_c * (1.0 / pl_c.norm());
171  const auto dot_prod =
172  mrpt::math::dotProduct<3, double>(u, pl_n);
173 
174  // It should be <0 if the normal is pointing to the vehicle.
175  // Otherwise, reverse the normal.
176  if (dot_prod > 0) pl_n = -pl_n;
177  }
178 
179  // Add plane & centroid:
180  const auto pl = mrpt::math::TPlane3D(pl_c, pl_n);
181  inOut.planes.emplace_back(pl, pl_c);
182 
183  // Also: add the centroid to this special layer:
184  pc_plane_centroids->insertPointFast(pl_c.x, pl_c.y, pl_c.z);
185 
186  // Filter out horizontal planes, since their uneven density
187  // makes ICP fail to converge.
188  // A plane on the ground has its 0'th eigenvector like [0 0 1]
189  if (std::abs(ev0.z) < 0.9f) { dest = pc_planes.get(); }
190  }
191  if (dest != nullptr)
192  {
193  for (size_t i = 0; i < vxl.indices.size();
195  {
196  const auto pt_idx = vxl.indices[i];
197  dest->insertPointFast(xs[pt_idx], ys[pt_idx], zs[pt_idx]);
198  }
199  }
200  // full_pointcloud_decimation=0 means dont use this layer
202  {
203  for (size_t i = 0; i < vxl.indices.size();
205  {
206  const auto pt_idx = vxl.indices[i];
207  pc_full_decim->insertPointFast(
208  xs[pt_idx], ys[pt_idx], zs[pt_idx]);
209  }
210  }
211  });
212 
213  MRPT_LOG_DEBUG_STREAM(
214  "[VoxelGridFilter] Voxel counts: total=" << nTotalVoxels
215  << " edges=" << nEdgeVoxels
216  << " planes=" << nPlaneVoxels);
217 
218  MRPT_END
219 }
mp2p_icp_filters::FilterEdgesPlanes::filter_grid_
PointCloudToVoxelGrid filter_grid_
Definition: FilterEdgesPlanes.h:70
mp2p_icp_filters::FilterEdgesPlanes::Parameters::voxel_filter_min_e2_e0
float voxel_filter_min_e2_e0
Definition: FilterEdgesPlanes.h:61
mp2p_icp_filters::FilterEdgesPlanes::Parameters::voxel_filter_min_e1_e0
float voxel_filter_min_e1_e0
Definition: FilterEdgesPlanes.h:62
mp2p_icp_filters::FilterEdgesPlanes::Parameters::load_from_yaml
void load_from_yaml(const mrpt::containers::yaml &c)
Definition: FilterEdgesPlanes.cpp:23
mp2p_icp_filters::FilterEdgesPlanes::filter
void filter(mp2p_icp::metric_map_t &inOut) const override
Definition: FilterEdgesPlanes.cpp:52
mp2p_icp_filters::FilterEdgesPlanes::Parameters::voxel_filter_min_e1
float voxel_filter_min_e1
Definition: FilterEdgesPlanes.h:63
mp2p_icp_filters::FilterEdgesPlanes::Parameters::input_pointcloud_layer
std::string input_pointcloud_layer
Definition: FilterEdgesPlanes.h:50
mp2p_icp_filters::FilterEdgesPlanes::FilterEdgesPlanes
FilterEdgesPlanes()
mp2p_icp_filters::PointCloudToVoxelGrid::voxel_t::indices
std::vector< std::size_t > indices
Definition: PointCloudToVoxelGrid.h:59
mp2p_icp_filters::FilterEdgesPlanes::Parameters::voxel_filter_decimation
unsigned int voxel_filter_decimation
Definition: FilterEdgesPlanes.h:58
mp2p_icp_filters::FilterEdgesPlanes::Parameters::full_pointcloud_decimation
unsigned int full_pointcloud_decimation
Definition: FilterEdgesPlanes.h:53
mp2p_icp_filters::PointCloudToVoxelGrid::indices_t
Definition: PointCloudToVoxelGrid.h:62
mp2p_icp::metric_map_t::planes
std::vector< plane_patch_t > planes
Definition: metricmap.h:82
mp2p_icp_filters::FilterBase
Definition: FilterBase.h:46
mp2p_icp_filters::FilterEdgesPlanes::params_
Parameters params_
Definition: FilterEdgesPlanes.h:67
mp2p_icp::metric_map_t::point_layer
mrpt::maps::CPointsMap::Ptr point_layer(const layer_name_t &name) const
Definition: metricmap.cpp:629
mp2p_icp_filters::FilterEdgesPlanes::Parameters::voxel_filter_resolution
double voxel_filter_resolution
Definition: FilterEdgesPlanes.h:56
mp2p_icp_filters::FilterEdgesPlanes::initialize
void initialize(const mrpt::containers::yaml &c) override
Definition: FilterEdgesPlanes.cpp:40
mp2p_icp_filters::PointCloudToVoxelGrid::voxel_t
Definition: PointCloudToVoxelGrid.h:57
mp2p_icp_filters::PointCloudToVoxelGrid::clear
void clear()
Definition: PointCloudToVoxelGrid.cpp:77
mp2p_icp_filters::PointCloudToVoxelGrid::processPointCloud
void processPointCloud(const mrpt::maps::CPointsMap &p)
Definition: PointCloudToVoxelGrid.cpp:39
mp2p_icp_filters::PointCloudToVoxelGrid::setResolution
void setResolution(const float voxel_size)
Definition: PointCloudToVoxelGrid.cpp:29
mp2p_icp::metric_map_t
Generic container of pointcloud(s), extracted features and other maps.
Definition: metricmap.h:49
mp2p_icp_filters::PointCloudToVoxelGrid::visit_voxels
void visit_voxels(const std::function< void(const indices_t idx, const voxel_t &vxl)> &userCode) const
Definition: PointCloudToVoxelGrid.cpp:83
IMPLEMENTS_MRPT_OBJECT
IMPLEMENTS_MRPT_OBJECT(FilterEdgesPlanes, mp2p_icp_filters::FilterBase, mp2p_icp_filters) using namespace mp2p_icp_filters
mp2p_icp::metric_map_t::layers
std::map< layer_name_t, mrpt::maps::CMetricMap::Ptr > layers
Definition: metricmap.h:76
mp2p_icp_filters
Definition: FilterAdjustTimestamps.h:19
FilterEdgesPlanes.h
Classify pointcloud voxels into planes / "edges".
mp2p_icp_filters::FilterEdgesPlanes::Parameters::voxel_filter_max_e1_e0
float voxel_filter_max_e1_e0
Definition: FilterEdgesPlanes.h:60
mp2p_icp_filters::FilterEdgesPlanes::Parameters::voxel_filter_max_e2_e0
float voxel_filter_max_e2_e0
Definition: FilterEdgesPlanes.h:59


mp2p_icp
Author(s):
autogenerated on Thu Oct 17 2024 02:45:33