Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add uniform and random down sample methods in Tensor PointCloud #5202

Merged
merged 9 commits into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions cpp/benchmarks/t/geometry/PointCloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,31 @@ void VoxelDownSample(benchmark::State& state,
}
}

void LegacyUniformDownSample(benchmark::State& state, size_t k) {
auto pcd = open3d::io::CreatePointCloudFromFile(path);
for (auto _ : state) {
pcd->UniformDownSample(k);
}
}

void UniformDownSample(benchmark::State& state,
const core::Device& device,
size_t k) {
t::geometry::PointCloud pcd;
// t::io::CreatePointCloudFromFile lacks support of remove_inf_points and
// remove_nan_points
t::io::ReadPointCloud(path, pcd, {"auto", false, false, false});
pcd = pcd.To(device);

// Warp up
pcd.UniformDownSample(k);

for (auto _ : state) {
pcd.UniformDownSample(k);
core::cuda::Synchronize(device);
}
}

void LegacyTransform(benchmark::State& state, const int no_use) {
open3d::geometry::PointCloud pcd;
open3d::io::ReadPointCloud(path, pcd, {"auto", false, false, false});
Expand Down Expand Up @@ -302,6 +327,19 @@ BENCHMARK_CAPTURE(LegacyVoxelDownSample, Legacy_0_32, 0.32)
->Unit(benchmark::kMillisecond);
ENUM_VOXELDOWNSAMPLE_BACKEND()

BENCHMARK_CAPTURE(LegacyUniformDownSample, Legacy_2, 2)
->Unit(benchmark::kMillisecond);
BENCHMARK_CAPTURE(LegacyUniformDownSample, Legacy_5, 5)
->Unit(benchmark::kMillisecond);
BENCHMARK_CAPTURE(LegacyUniformDownSample, Legacy_10, 10)
->Unit(benchmark::kMillisecond);
BENCHMARK_CAPTURE(UniformDownSample, CPU_2, core::Device("CPU:0"), 2)
->Unit(benchmark::kMillisecond);
BENCHMARK_CAPTURE(UniformDownSample, CPU_5, core::Device("CPU:0"), 5)
->Unit(benchmark::kMillisecond);
BENCHMARK_CAPTURE(UniformDownSample, CPU_10, core::Device("CPU:0"), 10)
->Unit(benchmark::kMillisecond);

BENCHMARK_CAPTURE(Transform, CPU, core::Device("CPU:0"))
->Unit(benchmark::kMillisecond);

Expand All @@ -315,6 +353,12 @@ BENCHMARK_CAPTURE(SelectByIndex,
->Unit(benchmark::kMillisecond);

#ifdef BUILD_CUDA_MODULE
BENCHMARK_CAPTURE(UniformDownSample, CUDA_2, core::Device("CUDA:0"), 2)
->Unit(benchmark::kMillisecond);
BENCHMARK_CAPTURE(UniformDownSample, CUDA_5, core::Device("CUDA:0"), 5)
->Unit(benchmark::kMillisecond);
BENCHMARK_CAPTURE(UniformDownSample, CUDA_10, core::Device("CUDA:0"), 10)
->Unit(benchmark::kMillisecond);
BENCHMARK_CAPTURE(Transform, CUDA, core::Device("CUDA:0"))
->Unit(benchmark::kMillisecond);
BENCHMARK_CAPTURE(SelectByIndex, CUDA, false, core::Device("CUDA:0"))
Expand Down
46 changes: 46 additions & 0 deletions cpp/open3d/t/geometry/PointCloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "open3d/t/geometry/kernel/PointCloud.h"
#include "open3d/t/geometry/kernel/Transform.h"
#include "open3d/t/pipelines/registration/Registration.h"
#include "open3d/utility/Random.h"

namespace open3d {
namespace t {
Expand Down Expand Up @@ -314,6 +315,51 @@ PointCloud PointCloud::VoxelDownSample(
return pcd_down;
}

PointCloud PointCloud::UniformDownSample(size_t every_k_points) const {
if (every_k_points == 0) {
utility::LogError(
"Illegal sample rate, every_k_points must be larger than 0.");
}

const int64_t length = GetPointPositions().GetLength();

PointCloud pcd_down(GetDevice());
for (auto &kv : GetPointAttr()) {
pcd_down.SetPointAttr(
kv.first,
kv.second.Slice(0, 0, length, (int64_t)every_k_points));
}

return pcd_down;
}

PointCloud PointCloud::RandomDownSample(double sampling_ratio) const {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. For reproducible experiments, we need the ability to set the random seed for all RNG calls by Open3D. One way to achieve this is with a singleton RNG, with some care for concurrent access.
  2. CUDA has an RNG that will likely be faster for CUDA tensors.

if (sampling_ratio < 0 || sampling_ratio > 1) {
utility::LogError(
"Illegal sampling_ratio {}, sampling_ratio must be between 0 "
"and 1.");
}

if (IsCPU()) {
const int64_t length = GetPointPositions().GetLength();
std::vector<int64_t> indices(length);
std::iota(std::begin(indices), std::end(indices), 0);
{
std::lock_guard<std::mutex> lock(*utility::random::GetMutex());
std::shuffle(indices.begin(), indices.end(),
*utility::random::GetEngine());
}

const int sample_size = sampling_ratio * length;
indices.resize(sample_size);
return SelectByIndex(
core::Tensor(indices, {sample_size}, core::Int64, GetDevice()),
false, false);
} else {
utility::LogError("RandomDownSample is not supported on GPU.");
}
}

std::tuple<PointCloud, core::Tensor> PointCloud::RemoveRadiusOutliers(
size_t nb_points, double search_radius) const {
if (nb_points < 1 || search_radius <= 0) {
Expand Down
15 changes: 15 additions & 0 deletions cpp/open3d/t/geometry/PointCloud.h
Original file line number Diff line number Diff line change
Expand Up @@ -334,11 +334,26 @@ class PointCloud : public Geometry, public DrawableGeometry {
bool remove_duplicates = false) const;

/// \brief Downsamples a point cloud with a specified voxel size.
///
/// \param voxel_size Voxel size. A positive number.
PointCloud VoxelDownSample(double voxel_size,
const core::HashBackendType &backend =
core::HashBackendType::Default) const;

/// \brief Downsamples a point cloud by selecting every kth index point and
/// its attributes.
///
/// \param every_k_points Sample rate, the selected point indices are [0, k,
/// 2k, …].
PointCloud UniformDownSample(size_t every_k_points) const;

/// \brief Downsample a pointcloud by selecting random index point and its
/// attributes.
///
/// \param sampling_ratio Sampling ratio, the ratio of sample to total
/// number of points in the pointcloud.
PointCloud RandomDownSample(double sampling_ratio) const;

/// \brief Remove points that have less than \p nb_points neighbors in a
/// sphere of a given radius.
///
Expand Down
8 changes: 8 additions & 0 deletions cpp/pybind/t/geometry/pointcloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,14 @@ The attributes of the point cloud have different levels::
},
"Downsamples a point cloud with a specified voxel size.",
"voxel_size"_a);
pointcloud.def("uniform_down_sample", &PointCloud::UniformDownSample,
"Downsamples a point cloud by selecting every kth index "
"point and its attributes.",
"every_k_points"_a);
pointcloud.def("random_down_sample", &PointCloud::RandomDownSample,
"Downsample a pointcloud by selecting random index point "
"and its attributes.",
"sampling_ratio"_a);
pointcloud.def("remove_radius_outliers", &PointCloud::RemoveRadiusOutliers,
"nb_points"_a, "search_radius"_a,
"Remove points that have less than nb_points neighbors in a "
Expand Down
36 changes: 36 additions & 0 deletions cpp/tests/t/geometry/PointCloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,42 @@ TEST_P(PointCloudPermuteDevices, VoxelDownSample) {
core::Tensor::Init<float>({{0, 0, 0}}, device)));
}

TEST_P(PointCloudPermuteDevices, UniformDownSample) {
core::Device device = GetParam();

// Value test.
t::geometry::PointCloud pcd_small(core::Tensor::Init<float>({{0, 0, 0},
{1, 0, 0},
{2, 0, 0},
{3, 0, 0},
{4, 0, 0},
{5, 0, 0},
{6, 0, 0},
{7, 0, 0}},
device));
auto pcd_small_down = pcd_small.UniformDownSample(3);
EXPECT_TRUE(pcd_small_down.GetPointPositions().AllClose(
core::Tensor::Init<float>({{0, 0, 0}, {3, 0, 0}, {6, 0, 0}},
device)));
}

TEST_P(PointCloudPermuteDevices, RandomDownSample) {
core::Device device = GetParam();

// Value test.
t::geometry::PointCloud pcd_small(core::Tensor::Init<float>({{0, 0, 0},
{1, 0, 0},
{2, 0, 0},
{3, 0, 0},
{4, 0, 0},
{5, 0, 0},
{6, 0, 0},
{7, 0, 0}},
device));
auto pcd_small_down = pcd_small.RandomDownSample(0.5);
EXPECT_TRUE(pcd_small_down.GetPointPositions().GetLength() == 4);
}

TEST_P(PointCloudPermuteDevices, RemoveRadiusOutliers) {
core::Device device = GetParam();

Expand Down