Point Cloud Library (PCL) 1.15.1-dev
Loading...
Searching...
No Matches
voxel_grid_covariance.hpp
1/*
2 * Software License Agreement (BSD License)
3 *
4 * Point Cloud Library (PCL) - www.pointclouds.org
5 * Copyright (c) 2010-2011, Willow Garage, Inc.
6 *
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * * Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
19 * * Neither the name of the copyright holder(s) nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
38#ifndef PCL_VOXEL_GRID_COVARIANCE_IMPL_H_
39#define PCL_VOXEL_GRID_COVARIANCE_IMPL_H_
40
41#include <pcl/common/common.h>
42#include <pcl/common/point_tests.h> // for isXYZFinite
43#include <pcl/filters/voxel_grid_covariance.h>
44#include <Eigen/Cholesky>
45#include <Eigen/Eigenvalues> // for SelfAdjointEigenSolver
46#include <boost/mpl/size.hpp> // for size
47#include <boost/random/mersenne_twister.hpp> // for mt19937
48#include <boost/random/normal_distribution.hpp> // for normal_distribution
49#include <boost/random/variate_generator.hpp> // for variate_generator
50
51//////////////////////////////////////////////////////////////////////////////////////////
52template<typename PointT> void
54{
55 voxel_centroids_leaf_indices_.clear ();
56 leaves_.clear ();
57
58 // Has the input dataset been set already?
59 if (!input_)
60 {
61 PCL_WARN ("[pcl::%s::applyFilter] No input dataset given!\n", getClassName ().c_str ());
62 output.width = output.height = 0;
63 output.clear ();
64 return;
65 }
66
67 // Copy the header (and thus the frame_id) + allocate enough space for points
68 output.height = 1; // downsampling breaks the organized structure
69 output.is_dense = true; // we filter out invalid points
70 output.clear ();
71
72 Eigen::Vector4f min_p, max_p;
73 // Get the minimum and maximum dimensions
74 if (!filter_field_name_.empty ()) // If we don't want to process the entire cloud...
75 getMinMax3D<PointT> (input_, filter_field_name_, static_cast<float> (filter_limit_min_), static_cast<float> (filter_limit_max_), min_p, max_p, filter_limit_negative_);
76 else
77 getMinMax3D<PointT> (*input_, min_p, max_p);
78
79 // Check that the leaf size is not too small, given the size of the data
80 std::int64_t dx = static_cast<std::int64_t>((max_p[0] - min_p[0]) * inverse_leaf_size_[0])+1;
81 std::int64_t dy = static_cast<std::int64_t>((max_p[1] - min_p[1]) * inverse_leaf_size_[1])+1;
82 std::int64_t dz = static_cast<std::int64_t>((max_p[2] - min_p[2]) * inverse_leaf_size_[2])+1;
83
84 // The number of voxels can be small even though the voxel indices are large, if the data is far from the origin.
85 // Check that the indices of the first and last voxel along each axis fit into an int. The maximum int converts to 2^31 as a float, so the upper bound is exclusive.
86 const float int_min_f = static_cast<float> (std::numeric_limits<int>::lowest());
87 const float int_max_f = static_cast<float> (std::numeric_limits<int>::max());
88 bool indices_fit = true;
89 for (int i = 0; i < 3; ++i)
90 {
91 // min_p > max_p if there is no valid point, so there are no indices to check
92 if (min_p[i] > max_p[i])
93 continue;
94 const float min_index = std::floor (min_p[i] * inverse_leaf_size_[i]);
95 const float max_index = std::floor (max_p[i] * inverse_leaf_size_[i]);
96 indices_fit = indices_fit && min_index >= int_min_f && min_index < int_max_f &&
97 max_index >= int_min_f && max_index < int_max_f;
98 }
99
100 if((dx*dy*dz) > std::numeric_limits<std::int32_t>::max() || !indices_fit)
101 {
102 PCL_WARN("[pcl::%s::applyFilter] Leaf size is too small for the input dataset. Integer indices would overflow.\n", getClassName().c_str());
103 output.clear();
104 return;
105 }
106
107 // Compute the minimum and maximum bounding box values
108 min_b_[0] = static_cast<int> (std::floor (min_p[0] * inverse_leaf_size_[0]));
109 max_b_[0] = static_cast<int> (std::floor (max_p[0] * inverse_leaf_size_[0]));
110 min_b_[1] = static_cast<int> (std::floor (min_p[1] * inverse_leaf_size_[1]));
111 max_b_[1] = static_cast<int> (std::floor (max_p[1] * inverse_leaf_size_[1]));
112 min_b_[2] = static_cast<int> (std::floor (min_p[2] * inverse_leaf_size_[2]));
113 max_b_[2] = static_cast<int> (std::floor (max_p[2] * inverse_leaf_size_[2]));
114
115 // Compute the number of divisions needed along all axis
116 div_b_ = max_b_ - min_b_ + Eigen::Vector4i::Ones ();
117 div_b_[3] = 0;
118
119 // Set up the division multiplier
120 divb_mul_ = Eigen::Vector4i (1, div_b_[0], div_b_[0] * div_b_[1], 0);
121
122 int centroid_size = 4;
123
124 if (downsample_all_data_)
125 centroid_size = boost::mpl::size<FieldList>::value;
126
127 // ---[ RGB special case
128 std::vector<pcl::PCLPointField> fields;
129 int rgba_index = -1;
130 rgba_index = pcl::getFieldIndex<PointT> ("rgb", fields);
131 if (rgba_index == -1)
132 rgba_index = pcl::getFieldIndex<PointT> ("rgba", fields);
133 if (rgba_index >= 0)
134 {
135 rgba_index = fields[rgba_index].offset;
136 centroid_size += 4;
137 }
138
139 // If we don't want to process the entire cloud, but rather filter points far away from the viewpoint first...
140 if (!filter_field_name_.empty ())
141 {
142 // Get the distance field index
143 std::vector<pcl::PCLPointField> fields;
144 int distance_idx = pcl::getFieldIndex<PointT> (filter_field_name_, fields);
145 if (distance_idx == -1)
146 PCL_WARN ("[pcl::%s::applyFilter] Invalid filter field name. Index is %d.\n", getClassName ().c_str (), distance_idx);
147
148 // First pass: go over all points and insert them into the right leaf
149 for (const auto& point: *input_)
150 {
151 if (!input_->is_dense)
152 // Check if the point is invalid
153 if (!isXYZFinite (point))
154 continue;
155
156 // Get the distance value
157 const auto* pt_data = reinterpret_cast<const std::uint8_t*> (&point);
158 float distance_value = 0;
159 memcpy (&distance_value, pt_data + fields[distance_idx].offset, sizeof (float));
160
161 if (filter_limit_negative_)
162 {
163 // Use a threshold for cutting out points which inside the interval
164 if ((distance_value < filter_limit_max_) && (distance_value > filter_limit_min_))
165 continue;
166 }
167 else
168 {
169 // Use a threshold for cutting out points which are too close/far away
170 if ((distance_value > filter_limit_max_) || (distance_value < filter_limit_min_))
171 continue;
172 }
173
174 // Compute the centroid leaf index
175 const Eigen::Vector4i ijk =
176 Eigen::floor(point.getArray4fMap() * inverse_leaf_size_.array())
177 .template cast<int>();
178 // divb_mul_[3] = 0 by assignment
179 int idx = (ijk - min_b_).dot(divb_mul_);
180
181 Leaf& leaf = leaves_[idx];
182 if (leaf.nr_points == 0)
183 {
184 leaf.centroid.resize (centroid_size);
185 leaf.centroid.setZero ();
186 }
187
188 Eigen::Vector3d pt3d = point.getVector3fMap().template cast<double>();
189 // Accumulate point sum for centroid calculation
190 leaf.mean_ += pt3d;
191 // Accumulate x*xT for single pass covariance calculation
192 leaf.cov_.noalias() += pt3d * pt3d.transpose ();
193
194 // Do we need to process all the fields?
195 if (!downsample_all_data_)
196 {
197 leaf.centroid.template head<3> () += point.getVector3fMap();
198 }
199 else
200 {
201 // Copy all the fields
202 Eigen::VectorXf centroid = Eigen::VectorXf::Zero (centroid_size);
203 pcl::for_each_type<FieldList> (NdCopyPointEigenFunctor<PointT> (point, centroid));
204 // ---[ RGB special case
205 if (rgba_index >= 0)
206 {
207 // Fill r/g/b data, assuming that the order is BGRA
208 const pcl::RGB& rgb = *reinterpret_cast<const RGB*> (reinterpret_cast<const char*> (&point) + rgba_index);
209 centroid[centroid_size - 4] = rgb.a;
210 centroid[centroid_size - 3] = rgb.r;
211 centroid[centroid_size - 2] = rgb.g;
212 centroid[centroid_size - 1] = rgb.b;
213 }
214 leaf.centroid += centroid;
215 }
216 ++leaf.nr_points;
217 }
218 }
219 // No distance filtering, process all data
220 else
221 {
222 // First pass: go over all points and insert them into the right leaf
223 for (const auto& point: *input_)
224 {
225 if (!input_->is_dense)
226 // Check if the point is invalid
227 if (!isXYZFinite (point))
228 continue;
229
230 // Compute the centroid leaf index
231 const Eigen::Vector4i ijk =
232 Eigen::floor(point.getArray4fMap() * inverse_leaf_size_.array())
233 .template cast<int>();
234 // divb_mul_[3] = 0 by assignment
235 int idx = (ijk - min_b_).dot(divb_mul_);
236
237 Leaf& leaf = leaves_[idx];
238 if (leaf.nr_points == 0)
239 {
240 leaf.centroid.resize (centroid_size);
241 leaf.centroid.setZero ();
242 }
243
244 Eigen::Vector3d pt3d = point.getVector3fMap().template cast<double>();
245 // Accumulate point sum for centroid calculation
246 leaf.mean_ += pt3d;
247 // Accumulate x*xT for single pass covariance calculation
248 leaf.cov_.noalias() += pt3d * pt3d.transpose ();
249
250 // Do we need to process all the fields?
251 if (!downsample_all_data_)
252 {
253 leaf.centroid.template head<3> () += point.getVector3fMap();
254 }
255 else
256 {
257 // Copy all the fields
258 Eigen::VectorXf centroid = Eigen::VectorXf::Zero (centroid_size);
259 pcl::for_each_type<FieldList> (NdCopyPointEigenFunctor<PointT> (point, centroid));
260 // ---[ RGB special case
261 if (rgba_index >= 0)
262 {
263 // Fill r/g/b data, assuming that the order is BGRA
264 const pcl::RGB& rgb = *reinterpret_cast<const RGB*> (reinterpret_cast<const char*> (&point) + rgba_index);
265 centroid[centroid_size - 4] = rgb.a;
266 centroid[centroid_size - 3] = rgb.r;
267 centroid[centroid_size - 2] = rgb.g;
268 centroid[centroid_size - 1] = rgb.b;
269 }
270 leaf.centroid += centroid;
271 }
272 ++leaf.nr_points;
273 }
274 }
275
276 // Second pass: go over all leaves and compute centroids and covariance matrices
277 output.reserve (leaves_.size ());
278 if (searchable_)
279 voxel_centroids_leaf_indices_.reserve (leaves_.size ());
280 int cp = 0;
281 if (save_leaf_layout_)
282 leaf_layout_.resize (div_b_[0] * div_b_[1] * div_b_[2], -1);
283
284 // Eigen values and vectors calculated to prevent near singular matrices
285 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigensolver;
286 Eigen::Matrix3d eigen_val;
287 Eigen::Vector3d pt_sum;
288
289 // Eigen values less than a threshold of max eigen value are inflated to a set fraction of the max eigen value.
290 double min_covar_eigvalue;
291
292 for (auto it = leaves_.begin (); it != leaves_.end (); ++it)
293 {
294
295 // Normalize the centroid
296 Leaf& leaf = it->second;
297
298 // Normalize the centroid
299 leaf.centroid /= static_cast<float> (leaf.nr_points);
300 // Point sum used for single pass covariance calculation
301 pt_sum = leaf.mean_;
302 // Normalize mean
303 leaf.mean_ /= leaf.nr_points;
304
305 // If the voxel contains sufficient points, its covariance is calculated and is added to the voxel centroids and output clouds.
306 // Points with less than the minimum points will have a can not be accurately approximated using a normal distribution.
307 if (leaf.nr_points >= min_points_per_voxel_)
308 {
309 if (save_leaf_layout_)
310 leaf_layout_[it->first] = cp++;
311
312 output.push_back (PointT ());
313
314 // Do we need to process all the fields?
315 if (!downsample_all_data_)
316 {
317 output.back ().x = leaf.centroid[0];
318 output.back ().y = leaf.centroid[1];
319 output.back ().z = leaf.centroid[2];
320 }
321 else
322 {
323 pcl::for_each_type<FieldList> (pcl::NdCopyEigenPointFunctor<PointT> (leaf.centroid, output.back ()));
324 // ---[ RGB special case
325 if (rgba_index >= 0)
326 {
327 pcl::RGB& rgb = *reinterpret_cast<RGB*> (reinterpret_cast<char*> (&output.back ()) + rgba_index);
328 rgb.a = leaf.centroid[centroid_size - 4];
329 rgb.r = leaf.centroid[centroid_size - 3];
330 rgb.g = leaf.centroid[centroid_size - 2];
331 rgb.b = leaf.centroid[centroid_size - 1];
332 }
333 }
334
335 // Stores the voxel indice for fast access searching
336 if (searchable_)
337 voxel_centroids_leaf_indices_.push_back (static_cast<int> (it->first));
338
339 // Single pass covariance calculation
340 leaf.cov_ = (leaf.cov_ - pt_sum * leaf.mean_.transpose()) / (leaf.nr_points - 1.0);
341
342 //Normalize Eigen Val such that max no more than 100x min.
343 eigensolver.compute (leaf.cov_);
344 eigen_val = eigensolver.eigenvalues ().asDiagonal ();
345 leaf.evecs_ = eigensolver.eigenvectors ();
346
347 if (eigen_val (0, 0) < -Eigen::NumTraits<double>::dummy_precision () || eigen_val (1, 1) < -Eigen::NumTraits<double>::dummy_precision () || eigen_val (2, 2) <= 0)
348 {
349 PCL_WARN ("[VoxelGridCovariance::applyFilter] Invalid eigen value! (%g, %g, %g)\n", eigen_val (0, 0), eigen_val (1, 1), eigen_val (2, 2));
350 leaf.nr_points = -1;
351 continue;
352 }
353
354 // Avoids matrices near singularities (eq 6.11)[Magnusson 2009]
355
356 min_covar_eigvalue = min_covar_eigvalue_mult_ * eigen_val (2, 2);
357 if (eigen_val (0, 0) < min_covar_eigvalue)
358 {
359 eigen_val (0, 0) = min_covar_eigvalue;
360
361 if (eigen_val (1, 1) < min_covar_eigvalue)
362 {
363 eigen_val (1, 1) = min_covar_eigvalue;
364 }
365
366 leaf.cov_.noalias() = leaf.evecs_ * eigen_val * leaf.evecs_.inverse ();
367 }
368 leaf.evals_ = eigen_val.diagonal ();
369
370 leaf.icov_ = leaf.cov_.inverse ();
371 if (leaf.icov_.maxCoeff () == std::numeric_limits<float>::infinity ( )
372 || leaf.icov_.minCoeff () == -std::numeric_limits<float>::infinity ( ) )
373 {
374 leaf.nr_points = -1;
375 }
376
378 }
379
380 output.width = output.size ();
381}
382
383//////////////////////////////////////////////////////////////////////////////////////////
384template<typename PointT> int
385pcl::VoxelGridCovariance<PointT>::getNeighborhoodAtPoint (const Eigen::Matrix<int, 3, Eigen::Dynamic>& relative_coordinates, const PointT& reference_point, std::vector<LeafConstPtr> &neighbors) const
387 neighbors.clear ();
388
389 // Find displacement coordinates
390 Eigen::Vector4i ijk = Eigen::floor(reference_point.getArray4fMap() * inverse_leaf_size_).template cast<int>();
391 ijk[3] = 0;
392 const Eigen::Array4i diff2min = min_b_ - ijk;
393 const Eigen::Array4i diff2max = max_b_ - ijk;
394 neighbors.reserve (relative_coordinates.cols ());
396 // Check each neighbor to see if it is occupied and contains sufficient points
397 for (Eigen::Index ni = 0; ni < relative_coordinates.cols (); ni++)
398 {
399 const Eigen::Vector4i displacement = (Eigen::Vector4i () << relative_coordinates.col (ni), 0).finished ();
400 // Checking if the specified cell is in the grid
401 if ((diff2min <= displacement.array ()).all () && (diff2max >= displacement.array ()).all ())
402 {
403 const auto leaf_iter = leaves_.find (((ijk + displacement - min_b_).dot (divb_mul_)));
404 if (leaf_iter != leaves_.end () && leaf_iter->second.nr_points >= min_points_per_voxel_)
405 {
406 LeafConstPtr leaf = &(leaf_iter->second);
407 neighbors.push_back (leaf);
408 }
409 }
410 }
411
412 return static_cast<int> (neighbors.size());
414
415//////////////////////////////////////////////////////////////////////////////////////////
416template<typename PointT> int
417pcl::VoxelGridCovariance<PointT>::getNeighborhoodAtPoint (const PointT& reference_point, std::vector<LeafConstPtr> &neighbors) const
418{
419 Eigen::MatrixXi relative_coordinates = pcl::getAllNeighborCellIndices();
420 return getNeighborhoodAtPoint(relative_coordinates, reference_point, neighbors);
421}
422
423//////////////////////////////////////////////////////////////////////////////////////////
424template<typename PointT> int
425pcl::VoxelGridCovariance<PointT>::getVoxelAtPoint(const PointT& reference_point, std::vector<LeafConstPtr> &neighbors) const
426{
427 return getNeighborhoodAtPoint(Eigen::Matrix<int, 3, Eigen::Dynamic>::Zero(3,1), reference_point, neighbors);
428}
429
430//////////////////////////////////////////////////////////////////////////////////////////
431template<typename PointT> int
432pcl::VoxelGridCovariance<PointT>::getFaceNeighborsAtPoint(const PointT& reference_point, std::vector<LeafConstPtr> &neighbors) const
433{
434 Eigen::Matrix<int, 3, Eigen::Dynamic> relative_coordinates(3, 7);
435 relative_coordinates.setZero();
436 relative_coordinates(0, 1) = 1;
437 relative_coordinates(0, 2) = -1;
438 relative_coordinates(1, 3) = 1;
439 relative_coordinates(1, 4) = -1;
440 relative_coordinates(2, 5) = 1;
441 relative_coordinates(2, 6) = -1;
442
443 return getNeighborhoodAtPoint(relative_coordinates, reference_point, neighbors);
444}
445
446//////////////////////////////////////////////////////////////////////////////////////////
447template<typename PointT> int
448pcl::VoxelGridCovariance<PointT>::getAllNeighborsAtPoint(const PointT& reference_point, std::vector<LeafConstPtr> &neighbors) const
449{
450 Eigen::Matrix<int, 3, Eigen::Dynamic> relative_coordinates(3, 27);
451 relative_coordinates.col(0).setZero();
452 relative_coordinates.rightCols(26) = pcl::getAllNeighborCellIndices();
453
454 return getNeighborhoodAtPoint(relative_coordinates, reference_point, neighbors);
455}
456
457//////////////////////////////////////////////////////////////////////////////////////////
458template<typename PointT> void
460{
461 cell_cloud.clear ();
462
463 // for now, we use random generator and normal distribution from boost instead of std because switching to std would make this function up to 2.8 times slower
464 boost::mt19937 rng;
465 boost::normal_distribution<> nd (0.0, 1.0);
466 boost::variate_generator<boost::mt19937&, boost::normal_distribution<> > var_nor (rng, nd);
467
468 Eigen::LLT<Eigen::Matrix3d> llt_of_cov;
469 Eigen::Matrix3d cholesky_decomp;
470 Eigen::Vector3d cell_mean;
471 Eigen::Vector3d rand_point;
472 Eigen::Vector3d dist_point;
473
474 cell_cloud.reserve (pnt_per_cell * std::count_if (leaves_.begin (), leaves_.end (),
475 [this] (auto& l) { return (l.second.nr_points >= min_points_per_voxel_); }));
476
477 // Generate points for each occupied voxel with sufficient points.
478 for (auto it = leaves_.begin (); it != leaves_.end (); ++it)
479 {
480 const Leaf& leaf = it->second;
481
482 if (leaf.nr_points >= min_points_per_voxel_)
483 {
484 cell_mean = leaf.mean_;
485 llt_of_cov.compute (leaf.cov_);
486 cholesky_decomp = llt_of_cov.matrixL ();
487
488 // Random points generated by sampling the normal distribution given by voxel mean and covariance matrix
489 for (int i = 0; i < pnt_per_cell; i++)
490 {
491 rand_point = Eigen::Vector3d (var_nor (), var_nor (), var_nor ());
492 dist_point.noalias() = cell_mean + cholesky_decomp * rand_point;
493 cell_cloud.push_back (PointXYZ (static_cast<float> (dist_point (0)), static_cast<float> (dist_point (1)), static_cast<float> (dist_point (2))));
494 }
495 }
496 }
497}
498
499#define PCL_INSTANTIATE_VoxelGridCovariance(T) template class PCL_EXPORTS pcl::VoxelGridCovariance<T>;
500
501#endif // PCL_VOXEL_GRID_COVARIANCE_IMPL_H_
PointCloud represents the base class in PCL for storing collections of 3D points.
void push_back(const PointT &pt)
Insert a new point in the cloud, at the end of the container.
void clear()
Removes all points in a cloud and sets the width and height to 0.
void reserve(std::size_t n)
void applyFilter(PointCloud &output) override
Filter cloud and initializes voxel structure.
typename Filter< PointT >::PointCloud PointCloud
void getDisplayCloud(pcl::PointCloud< PointXYZ > &cell_cloud, int pnt_per_cell=1000) const
Get a cloud to visualize each voxels normal distribution.
int getAllNeighborsAtPoint(const PointT &reference_point, std::vector< LeafConstPtr > &neighbors) const
Get all 3x3x3 neighbor voxels of p (up to 27 voxels).
int getVoxelAtPoint(const PointT &reference_point, std::vector< LeafConstPtr > &neighbors) const
Get the voxel at p.
const Leaf * LeafConstPtr
Const pointer to VoxelGridCovariance leaf structure.
int getNeighborhoodAtPoint(const Eigen::Matrix< int, 3, Eigen::Dynamic > &relative_coordinates, const PointT &reference_point, std::vector< LeafConstPtr > &neighbors) const
Get the voxels surrounding point p designated by relative_coordinates.
int getFaceNeighborsAtPoint(const PointT &reference_point, std::vector< LeafConstPtr > &neighbors) const
Get the voxel at p and its facing voxels (up to 7 voxels).
Define standard C methods and C++ classes that are common to all methods.
Eigen::MatrixXi getAllNeighborCellIndices()
Get the relative cell indices of all the 26 neighbors.
Definition voxel_grid.h:89
constexpr bool isXYZFinite(const PointT &) noexcept
Helper functor structure for copying data between an Eigen type and a PointT.
Definition point_cloud.h:78
Helper functor structure for copying data between an Eigen type and a PointT.
A point structure representing Euclidean xyz coordinates.
A point structure representing Euclidean xyz coordinates, and the RGB color.
A structure representing RGB color information.
Simple structure to hold a centroid, covarince and the number of points in a leaf.
int nr_points
Number of points contained by voxel.
Eigen::Vector3d mean_
3D voxel centroid
Eigen::Matrix3d icov_
Inverse of voxel covariance matrix.
Eigen::VectorXf centroid
Nd voxel centroid.
Eigen::Vector3d evals_
Eigen values of voxel covariance matrix.
Eigen::Matrix3d cov_
Voxel covariance matrix.
Eigen::Matrix3d evecs_
Eigen vectors of voxel covariance matrix.