parent
952f59d499
commit
d3473fca4a
|
@ -86,8 +86,8 @@ VoxelKeyType GetVoxelCellIndex(const Eigen::Vector3f& point,
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T, class PointFunction>
|
template <class T, class PointFunction>
|
||||||
std::vector<T> RandomizedVoxelFilter(const std::vector<T>& point_cloud,
|
std::vector<bool> RandomizedVoxelFilterIndices(
|
||||||
const float resolution,
|
const std::vector<T>& point_cloud, const float resolution,
|
||||||
PointFunction&& point_function) {
|
PointFunction&& point_function) {
|
||||||
// According to https://en.wikipedia.org/wiki/Reservoir_sampling
|
// According to https://en.wikipedia.org/wiki/Reservoir_sampling
|
||||||
std::minstd_rand0 generator;
|
std::minstd_rand0 generator;
|
||||||
|
@ -110,6 +110,15 @@ std::vector<T> RandomizedVoxelFilter(const std::vector<T>& point_cloud,
|
||||||
for (const auto& voxel_and_index : voxel_count_and_point_index) {
|
for (const auto& voxel_and_index : voxel_count_and_point_index) {
|
||||||
points_used[voxel_and_index.second.second] = true;
|
points_used[voxel_and_index.second.second] = true;
|
||||||
}
|
}
|
||||||
|
return points_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class PointFunction>
|
||||||
|
std::vector<T> RandomizedVoxelFilter(const std::vector<T>& point_cloud,
|
||||||
|
const float resolution,
|
||||||
|
PointFunction&& point_function) {
|
||||||
|
const std::vector<bool> points_used =
|
||||||
|
RandomizedVoxelFilterIndices(point_cloud, resolution, point_function);
|
||||||
|
|
||||||
std::vector<T> results;
|
std::vector<T> results;
|
||||||
for (size_t i = 0; i < point_cloud.size(); i++) {
|
for (size_t i = 0; i < point_cloud.size(); i++) {
|
||||||
|
@ -130,7 +139,25 @@ std::vector<RangefinderPoint> VoxelFilter(
|
||||||
}
|
}
|
||||||
|
|
||||||
PointCloud VoxelFilter(const PointCloud& point_cloud, const float resolution) {
|
PointCloud VoxelFilter(const PointCloud& point_cloud, const float resolution) {
|
||||||
return PointCloud(VoxelFilter(point_cloud.points(), resolution));
|
const std::vector<bool> points_used = RandomizedVoxelFilterIndices(
|
||||||
|
point_cloud.points(), resolution,
|
||||||
|
[](const RangefinderPoint& point) { return point.position; });
|
||||||
|
|
||||||
|
std::vector<RangefinderPoint> filtered_points;
|
||||||
|
for (size_t i = 0; i < point_cloud.size(); i++) {
|
||||||
|
if (points_used[i]) {
|
||||||
|
filtered_points.push_back(point_cloud[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<float> filtered_intensities;
|
||||||
|
CHECK_LE(point_cloud.intensities().size(), point_cloud.points().size());
|
||||||
|
for (size_t i = 0; i < point_cloud.intensities().size(); i++) {
|
||||||
|
if (points_used[i]) {
|
||||||
|
filtered_intensities.push_back(point_cloud.intensities()[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PointCloud(std::move(filtered_points),
|
||||||
|
std::move(filtered_intensities));
|
||||||
}
|
}
|
||||||
|
|
||||||
TimedPointCloud VoxelFilter(const TimedPointCloud& timed_point_cloud,
|
TimedPointCloud VoxelFilter(const TimedPointCloud& timed_point_cloud,
|
||||||
|
|
|
@ -25,6 +25,7 @@ namespace sensor {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using ::testing::Contains;
|
using ::testing::Contains;
|
||||||
|
using ::testing::IsEmpty;
|
||||||
|
|
||||||
TEST(VoxelFilterTest, ReturnsOnePointInEachVoxel) {
|
TEST(VoxelFilterTest, ReturnsOnePointInEachVoxel) {
|
||||||
const PointCloud point_cloud({{{0.f, 0.f, 0.f}},
|
const PointCloud point_cloud({{{0.f, 0.f, 0.f}},
|
||||||
|
@ -33,11 +34,32 @@ TEST(VoxelFilterTest, ReturnsOnePointInEachVoxel) {
|
||||||
{{0.f, 0.f, 0.1f}}});
|
{{0.f, 0.f, 0.1f}}});
|
||||||
const PointCloud result = VoxelFilter(point_cloud, 0.3f);
|
const PointCloud result = VoxelFilter(point_cloud, 0.3f);
|
||||||
ASSERT_EQ(result.size(), 2);
|
ASSERT_EQ(result.size(), 2);
|
||||||
|
EXPECT_THAT(result.intensities(), IsEmpty());
|
||||||
EXPECT_THAT(point_cloud.points(), Contains(result[0]));
|
EXPECT_THAT(point_cloud.points(), Contains(result[0]));
|
||||||
EXPECT_THAT(point_cloud.points(), Contains(result[1]));
|
EXPECT_THAT(point_cloud.points(), Contains(result[1]));
|
||||||
EXPECT_THAT(result.points(), Contains(point_cloud[2]));
|
EXPECT_THAT(result.points(), Contains(point_cloud[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(VoxelFilterTest, CorrectIntensities) {
|
||||||
|
std::vector<RangefinderPoint> points;
|
||||||
|
std::vector<float> intensities;
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
const float value = 0.1f * i;
|
||||||
|
// We add points with intensity equal to the z coordinate, so we can later
|
||||||
|
// verify that the resulting intensities are corresponding to the filtered
|
||||||
|
// points.
|
||||||
|
points.push_back({{-100.f, 0.3f, value}});
|
||||||
|
intensities.push_back(value);
|
||||||
|
}
|
||||||
|
const PointCloud point_cloud(points, intensities);
|
||||||
|
const PointCloud result = VoxelFilter(point_cloud, 0.3f);
|
||||||
|
|
||||||
|
ASSERT_EQ(result.intensities().size(), result.points().size());
|
||||||
|
for (size_t i = 0; i < result.size(); ++i) {
|
||||||
|
ASSERT_NEAR(result[i].position.z(), result.intensities()[i], 1e-6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST(VoxelFilterTest, HandlesLargeCoordinates) {
|
TEST(VoxelFilterTest, HandlesLargeCoordinates) {
|
||||||
const PointCloud point_cloud({{{100000.f, 0.f, 0.f}},
|
const PointCloud point_cloud({{{100000.f, 0.f, 0.f}},
|
||||||
{{100000.001f, -0.0001f, 0.0001f}},
|
{{100000.001f, -0.0001f, 0.0001f}},
|
||||||
|
@ -45,6 +67,7 @@ TEST(VoxelFilterTest, HandlesLargeCoordinates) {
|
||||||
{{-200000.f, 0.f, 0.f}}});
|
{{-200000.f, 0.f, 0.f}}});
|
||||||
const PointCloud result = VoxelFilter(point_cloud, 0.01f);
|
const PointCloud result = VoxelFilter(point_cloud, 0.01f);
|
||||||
EXPECT_EQ(result.size(), 2);
|
EXPECT_EQ(result.size(), 2);
|
||||||
|
EXPECT_THAT(result.intensities(), IsEmpty());
|
||||||
EXPECT_THAT(result.points(), Contains(point_cloud[3]));
|
EXPECT_THAT(result.points(), Contains(point_cloud[3]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue