Skip to content

Commit aa667c1

Browse files
VoxelGridOcclusionEstimation should always round down to go from coordinates to voxel indices. (#5942)
* VoxelGrid coordinate to index mappoing should always round down * occlusion estimation: Fix floating point inaccuracies placing entry voxel outside of cloud bounding box * add VoxelGridOcclusionEstimation test * Fix incorrect doc * Fix missing include * add epsilon to ray entry coordinate in second location as well
1 parent 511ea48 commit aa667c1

File tree

3 files changed

+46
-24
lines changed

3 files changed

+46
-24
lines changed

filters/include/pcl/filters/impl/voxel_grid_occlusion_estimation.hpp

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pcl::VoxelGridOcclusionEstimation<PointT>::initializeVoxelGrid ()
4747
{
4848
// initialization set to true
4949
initialized_ = true;
50-
50+
5151
// create the voxel grid and store the output cloud
5252
this->filter (filtered_cloud_);
5353

@@ -162,13 +162,13 @@ pcl::VoxelGridOcclusionEstimation<PointT>::occlusionEstimationAll (std::vector<E
162162
Eigen::Vector4f p = getCentroidCoordinate (ijk);
163163
Eigen::Vector4f direction = p - sensor_origin_;
164164
direction.normalize ();
165-
165+
166166
// estimate entry point into the voxel grid
167167
float tmin = rayBoxIntersection (sensor_origin_, direction);
168168

169169
// ray traversal
170170
int state = rayTraversal (ijk, sensor_origin_, direction, tmin);
171-
171+
172172
// if voxel is occluded
173173
if (state == 1)
174174
occluded_voxels.push_back (ijk);
@@ -179,7 +179,7 @@ pcl::VoxelGridOcclusionEstimation<PointT>::occlusionEstimationAll (std::vector<E
179179

180180
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
181181
template <typename PointT> float
182-
pcl::VoxelGridOcclusionEstimation<PointT>::rayBoxIntersection (const Eigen::Vector4f& origin,
182+
pcl::VoxelGridOcclusionEstimation<PointT>::rayBoxIntersection (const Eigen::Vector4f& origin,
183183
const Eigen::Vector4f& direction)
184184
{
185185
float tmin, tmax, tymin, tymax, tzmin, tzmax;
@@ -198,7 +198,7 @@ pcl::VoxelGridOcclusionEstimation<PointT>::rayBoxIntersection (const Eigen::Vect
198198
if (direction[1] >= 0)
199199
{
200200
tymin = (b_min_[1] - origin[1]) / direction[1];
201-
tymax = (b_max_[1] - origin[1]) / direction[1];
201+
tymax = (b_max_[1] - origin[1]) / direction[1];
202202
}
203203
else
204204
{
@@ -233,7 +233,7 @@ pcl::VoxelGridOcclusionEstimation<PointT>::rayBoxIntersection (const Eigen::Vect
233233
{
234234
PCL_ERROR ("no intersection with the bounding box \n");
235235
tmin = -1.0f;
236-
return tmin;
236+
return tmin;
237237
}
238238

239239
if (tzmin > tmin)
@@ -246,15 +246,16 @@ pcl::VoxelGridOcclusionEstimation<PointT>::rayBoxIntersection (const Eigen::Vect
246246
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
247247
template <typename PointT> int
248248
pcl::VoxelGridOcclusionEstimation<PointT>::rayTraversal (const Eigen::Vector3i& target_voxel,
249-
const Eigen::Vector4f& origin,
249+
const Eigen::Vector4f& origin,
250250
const Eigen::Vector4f& direction,
251251
const float t_min)
252252
{
253-
// coordinate of the boundary of the voxel grid
254-
Eigen::Vector4f start = origin + t_min * direction;
253+
// coordinate of the boundary of the voxel grid. To avoid numerical imprecision
254+
// causing the start voxel index to be off by one, we add the machine epsilon
255+
Eigen::Vector4f start = origin + (t_min > 0.0f ? (t_min + std::numeric_limits<float>::epsilon()) : t_min) * direction;
255256

256257
// i,j,k coordinate of the voxel were the ray enters the voxel grid
257-
Eigen::Vector3i ijk = getGridCoordinatesRound (start[0], start[1], start[2]);
258+
Eigen::Vector3i ijk = this->getGridCoordinates(start[0], start[1], start[2]);
258259

259260
// steps in which direction we have to travel in the voxel grid
260261
int step_x, step_y, step_z;
@@ -296,13 +297,13 @@ pcl::VoxelGridOcclusionEstimation<PointT>::rayTraversal (const Eigen::Vector3i&
296297
float t_max_x = t_min + (voxel_max[0] - start[0]) / direction[0];
297298
float t_max_y = t_min + (voxel_max[1] - start[1]) / direction[1];
298299
float t_max_z = t_min + (voxel_max[2] - start[2]) / direction[2];
299-
300+
300301
float t_delta_x = leaf_size_[0] / static_cast<float> (std::abs (direction[0]));
301302
float t_delta_y = leaf_size_[1] / static_cast<float> (std::abs (direction[1]));
302303
float t_delta_z = leaf_size_[2] / static_cast<float> (std::abs (direction[2]));
303304

304-
while ( (ijk[0] < max_b_[0]+1) && (ijk[0] >= min_b_[0]) &&
305-
(ijk[1] < max_b_[1]+1) && (ijk[1] >= min_b_[1]) &&
305+
while ( (ijk[0] < max_b_[0]+1) && (ijk[0] >= min_b_[0]) &&
306+
(ijk[1] < max_b_[1]+1) && (ijk[1] >= min_b_[1]) &&
306307
(ijk[2] < max_b_[2]+1) && (ijk[2] >= min_b_[2]) )
307308
{
308309
// check if we reached target voxel
@@ -339,20 +340,20 @@ pcl::VoxelGridOcclusionEstimation<PointT>::rayTraversal (const Eigen::Vector3i&
339340
template <typename PointT> int
340341
pcl::VoxelGridOcclusionEstimation<PointT>::rayTraversal (std::vector<Eigen::Vector3i, Eigen::aligned_allocator<Eigen::Vector3i> >& out_ray,
341342
const Eigen::Vector3i& target_voxel,
342-
const Eigen::Vector4f& origin,
343+
const Eigen::Vector4f& origin,
343344
const Eigen::Vector4f& direction,
344345
const float t_min)
345346
{
346347
// reserve space for the ray vector
347348
int reserve_size = div_b_.maxCoeff () * div_b_.maxCoeff ();
348349
out_ray.reserve (reserve_size);
349350

350-
// coordinate of the boundary of the voxel grid
351-
Eigen::Vector4f start = origin + t_min * direction;
351+
// coordinate of the boundary of the voxel grid. To avoid numerical imprecision
352+
// causing the start voxel index to be off by one, we add the machine epsilon
353+
Eigen::Vector4f start = origin + (t_min > 0.0f ? (t_min + std::numeric_limits<float>::epsilon()) : t_min) * direction;
352354

353355
// i,j,k coordinate of the voxel were the ray enters the voxel grid
354-
Eigen::Vector3i ijk = getGridCoordinatesRound (start[0], start[1], start[2]);
355-
//Eigen::Vector3i ijk = this->getGridCoordinates (start_x, start_y, start_z);
356+
Eigen::Vector3i ijk = this->getGridCoordinates (start[0], start[1], start[2]);
356357

357358
// steps in which direction we have to travel in the voxel grid
358359
int step_x, step_y, step_z;
@@ -394,16 +395,16 @@ pcl::VoxelGridOcclusionEstimation<PointT>::rayTraversal (std::vector<Eigen::Vect
394395
float t_max_x = t_min + (voxel_max[0] - start[0]) / direction[0];
395396
float t_max_y = t_min + (voxel_max[1] - start[1]) / direction[1];
396397
float t_max_z = t_min + (voxel_max[2] - start[2]) / direction[2];
397-
398+
398399
float t_delta_x = leaf_size_[0] / static_cast<float> (std::abs (direction[0]));
399400
float t_delta_y = leaf_size_[1] / static_cast<float> (std::abs (direction[1]));
400401
float t_delta_z = leaf_size_[2] / static_cast<float> (std::abs (direction[2]));
401402

402403
// the index of the cloud (-1 if empty)
403404
int result = 0;
404405

405-
while ( (ijk[0] < max_b_[0]+1) && (ijk[0] >= min_b_[0]) &&
406-
(ijk[1] < max_b_[1]+1) && (ijk[1] >= min_b_[1]) &&
406+
while ( (ijk[0] < max_b_[0]+1) && (ijk[0] >= min_b_[0]) &&
407+
(ijk[1] < max_b_[1]+1) && (ijk[1] >= min_b_[1]) &&
407408
(ijk[2] < max_b_[2]+1) && (ijk[2] >= min_b_[2]) )
408409
{
409410
// add voxel to ray

filters/include/pcl/filters/voxel_grid_occlusion_estimation.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ namespace pcl
216216
const Eigen::Vector4f& direction,
217217
const float t_min);
218218

219-
/** \brief Returns a rounded value.
219+
/** \brief Returns a value rounded to the nearest integer
220220
* \param[in] d
221221
* \return rounded value
222222
*/
@@ -226,8 +226,7 @@ namespace pcl
226226
return static_cast<float> (std::floor (d + 0.5f));
227227
}
228228

229-
// We use round here instead of std::floor due to some numerical issues.
230-
/** \brief Returns the corresponding (i,j,k) coordinates in the grid of point (x,y,z).
229+
/** \brief Returns the corresponding (i,j,k) coordinates in the grid of point (x,y,z).
231230
* \param[in] x the X point coordinate to get the (i, j, k) index at
232231
* \param[in] y the Y point coordinate to get the (i, j, k) index at
233232
* \param[in] z the Z point coordinate to get the (i, j, k) index at

test/filters/test_filters.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include <pcl/filters/frustum_culling.h>
4949
#include <pcl/filters/sampling_surface_normal.h>
5050
#include <pcl/filters/voxel_grid.h>
51+
#include <pcl/filters/voxel_grid_occlusion_estimation.h>
5152
#include <pcl/filters/voxel_grid_covariance.h>
5253
#include <pcl/filters/extract_indices.h>
5354
#include <pcl/filters/project_inliers.h>
@@ -2460,6 +2461,27 @@ TEST (NormalRefinement, Filters)
24602461
EXPECT_LT(err_refined_var, err_est_var);
24612462
}
24622463

2464+
TEST (VoxelGridOcclusionEstimation, Filters)
2465+
{
2466+
auto input_cloud = pcl::make_shared<pcl::PointCloud<pcl::PointXYZ>>();
2467+
input_cloud->emplace_back(0.0, 0.0, 0.0);
2468+
input_cloud->emplace_back(9.9, 9.9, 9.9); // we want a nice bounding box from (0, 0, 0) to (10, 10, 10)
2469+
input_cloud->sensor_origin_ << -0.1, 0.5, 0.5; // just outside the bounding box. Most rays will enter at voxel (0, 0, 0)
2470+
pcl::VoxelGridOcclusionEstimation<pcl::PointXYZ> vg;
2471+
vg.setInputCloud (input_cloud);
2472+
vg.setLeafSize (1.0, 1.0, 1.0);
2473+
vg.initializeVoxelGrid ();
2474+
std::vector<Eigen::Vector3i, Eigen::aligned_allocator<Eigen::Vector3i> > occluded_voxels;
2475+
vg.occlusionEstimationAll (occluded_voxels);
2476+
for(std::size_t y=0; y<10; ++y) {
2477+
for (std::size_t z=0; z<10; ++z) {
2478+
if(y==9 && z==9) continue; // voxel (9, 9, 9) is occupied by point (9.9, 9.9, 9.9), so it will not be counted as occluded
2479+
Eigen::Vector3i cell(9, y, z);
2480+
EXPECT_NE(std::find(occluded_voxels.begin(), occluded_voxels.end(), cell), occluded_voxels.end()); // not equal means it was found
2481+
}
2482+
}
2483+
}
2484+
24632485
/* ---[ */
24642486
int
24652487
main (int argc, char** argv)

0 commit comments

Comments
 (0)