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 option to retrieve origin data from point cloud generation #6564

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions cpp/open3d/geometry/PointCloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ PointCloud &PointCloud::Clear() {
normals_.clear();
colors_.clear();
covariances_.clear();
origindata_.clear();
return *this;
}

Expand Down Expand Up @@ -112,6 +113,13 @@ PointCloud &PointCloud::operator+=(const PointCloud &cloud) {
} else {
covariances_.clear();
}
if ((!HasPoints() || HasOriginData()) && cloud.HasOriginData()) {
origindata_.resize(new_vert_num);
for (size_t i = 0; i < add_vert_num; i++)
origindata_[old_vert_num + i] = cloud.origindata_[i];
} else {
origindata_.clear();
}
points_.resize(new_vert_num);
for (size_t i = 0; i < add_vert_num; i++)
points_[old_vert_num + i] = cloud.points_[i];
Expand Down
21 changes: 21 additions & 0 deletions cpp/open3d/geometry/PointCloud.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ class PointCloud : public Geometry3D {
return !points_.empty() && covariances_.size() == points_.size();
}

/// Returns 'true' if the point cloud contains per-point origin data.
bool HasOriginData() const {
return !points_.empty() && origindata_.size() == points_.size();
}

/// Normalize point normals to length 1.
PointCloud &NormalizeNormals() {
for (size_t i = 0; i < normals_.size(); i++) {
Expand Down Expand Up @@ -458,6 +463,20 @@ class PointCloud : public Geometry3D {
static std::shared_ptr<PointCloud> CreateFromVoxelGrid(
const VoxelGrid &voxel_grid);

/// \brief Structure corresponding to a single point and describing its
/// origin depending on the PointCloud creation type.
///
/// Optional information about the origin of the point relative to the data
/// used to generate the PointCloud. Currently supports adding geometrical
/// information for PointClouds sampled from a triangular mesh. Can be
/// expanded as required to include e.g. voxel ID and relative coordinates,
/// index of a point in original PointCloud etc.
struct OriginData {
size_t tri_id;
Eigen::Vector2d tri_uv;
// add other data such as voxel ID etc as required
};

public:
/// Points coordinates.
std::vector<Eigen::Vector3d> points_;
Expand All @@ -467,6 +486,8 @@ class PointCloud : public Geometry3D {
std::vector<Eigen::Vector3d> colors_;
/// Covariance Matrix for each point
std::vector<Eigen::Matrix3d> covariances_;
/// Origin Data for each point
std::vector<OriginData> origindata_;
};

} // namespace geometry
Expand Down
30 changes: 25 additions & 5 deletions cpp/open3d/geometry/TriangleMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,8 @@ std::shared_ptr<PointCloud> TriangleMesh::SamplePointsUniformlyImpl(
size_t number_of_points,
std::vector<double> &triangle_areas,
double surface_area,
bool use_triangle_normal) {
bool use_triangle_normal,
bool record_origin) {
if (surface_area <= 0) {
utility::LogError("Invalid surface area {}, it must be > 0.",
surface_area);
Expand Down Expand Up @@ -463,6 +464,9 @@ std::shared_ptr<PointCloud> TriangleMesh::SamplePointsUniformlyImpl(
if (has_vert_color) {
pcd->colors_.resize(number_of_points);
}
if (record_origin) {
pcd->origindata_.resize(number_of_points);
}
size_t point_idx = 0;
for (size_t tidx = 0; tidx < triangles_.size(); ++tidx) {
size_t n = size_t(std::round(triangle_areas[tidx] * number_of_points));
Expand Down Expand Up @@ -490,6 +494,10 @@ std::shared_ptr<PointCloud> TriangleMesh::SamplePointsUniformlyImpl(
b * vertex_colors_[triangle(1)] +
c * vertex_colors_[triangle(2)];
}
if (record_origin) {
pcd->origindata_[point_idx].tri_id = tidx;
pcd->origindata_[point_idx].tri_uv = Eigen::Vector2d(a, b);
}

point_idx++;
}
Expand All @@ -499,7 +507,9 @@ std::shared_ptr<PointCloud> TriangleMesh::SamplePointsUniformlyImpl(
}

std::shared_ptr<PointCloud> TriangleMesh::SamplePointsUniformly(
size_t number_of_points, bool use_triangle_normal /* = false */) {
size_t number_of_points,
bool use_triangle_normal /* = false */,
bool record_origin /* = false */) {
if (number_of_points <= 0) {
utility::LogError("number_of_points <= 0");
}
Expand All @@ -512,14 +522,16 @@ std::shared_ptr<PointCloud> TriangleMesh::SamplePointsUniformly(
double surface_area = GetSurfaceArea(triangle_areas);

return SamplePointsUniformlyImpl(number_of_points, triangle_areas,
surface_area, use_triangle_normal);
surface_area, use_triangle_normal,
record_origin);
}

std::shared_ptr<PointCloud> TriangleMesh::SamplePointsPoissonDisk(
size_t number_of_points,
double init_factor /* = 5 */,
const std::shared_ptr<PointCloud> pcl_init /* = nullptr */,
bool use_triangle_normal /* = false */) {
bool use_triangle_normal /* = false */,
bool record_origin /* = false */) {
if (number_of_points <= 0) {
utility::LogError("number_of_points <= 0");
}
Expand All @@ -546,12 +558,13 @@ std::shared_ptr<PointCloud> TriangleMesh::SamplePointsPoissonDisk(
if (pcl_init == nullptr) {
pcl = SamplePointsUniformlyImpl(size_t(init_factor * number_of_points),
triangle_areas, surface_area,
use_triangle_normal);
use_triangle_normal, record_origin);
} else {
pcl = std::make_shared<PointCloud>();
pcl->points_ = pcl_init->points_;
pcl->normals_ = pcl_init->normals_;
pcl->colors_ = pcl_init->colors_;
pcl->origindata_ = pcl_init->origindata_;
}

// Set-up sample elimination
Expand Down Expand Up @@ -635,6 +648,7 @@ std::shared_ptr<PointCloud> TriangleMesh::SamplePointsPoissonDisk(
// update pcl
bool has_vert_normal = pcl->HasNormals();
bool has_vert_color = pcl->HasColors();
bool has_origin_data = pcl->HasOriginData();
int next_free = 0;
for (size_t idx = 0; idx < pcl->points_.size(); ++idx) {
if (!deleted[idx]) {
Expand All @@ -645,6 +659,9 @@ std::shared_ptr<PointCloud> TriangleMesh::SamplePointsPoissonDisk(
if (has_vert_color) {
pcl->colors_[next_free] = pcl->colors_[idx];
}
if (has_origin_data) {
pcl->origindata_[next_free] = pcl->origindata_[idx];
}
next_free++;
}
}
Expand All @@ -655,6 +672,9 @@ std::shared_ptr<PointCloud> TriangleMesh::SamplePointsPoissonDisk(
if (has_vert_color) {
pcl->colors_.resize(next_free);
}
if (has_origin_data) {
pcl->origindata_.resize(next_free);
}

return pcl;
}
Expand Down
14 changes: 11 additions & 3 deletions cpp/open3d/geometry/TriangleMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,16 +339,21 @@ class TriangleMesh : public MeshBase {
size_t number_of_points,
std::vector<double> &triangle_areas,
double surface_area,
bool use_triangle_normal);
bool use_triangle_normal,
bool record_origin);

/// Function to sample points uniformly from the mesh.
///
/// \param number_of_points points uniformly from the mesh.
/// \param use_triangle_normal Set to true to assign the triangle normals to
/// the returned points instead of the interpolated vertex normals. The
/// triangle normals will be computed and added to the mesh if necessary.
/// \param record_origin Set to true to assign origin triangle ID and
/// UV coordinates to the returned points
std::shared_ptr<PointCloud> SamplePointsUniformly(
size_t number_of_points, bool use_triangle_normal = false);
size_t number_of_points,
bool use_triangle_normal = false,
bool record_origin = false);

/// Function to sample points from the mesh with Possion disk, based on the
/// method presented in Yuksel, "Sample Elimination for Generating Poisson
Expand All @@ -362,11 +367,14 @@ class TriangleMesh : public MeshBase {
/// \param use_triangle_normal If True assigns the triangle normals instead
/// of the interpolated vertex normals to the returned points. The triangle
/// normals will be computed and added to the mesh if necessary.
/// \param record_origin Set to true to assign origin triangle ID and
/// UV coordinates to the returned points
std::shared_ptr<PointCloud> SamplePointsPoissonDisk(
size_t number_of_points,
double init_factor = 5,
const std::shared_ptr<PointCloud> pcl_init = nullptr,
bool use_triangle_normal = false);
bool use_triangle_normal = false,
bool record_origin = false);

/// Function to subdivide triangle mesh using the simple midpoint algorithm.
/// Each triangle is subdivided into four triangles per iteration and the
Expand Down
21 changes: 20 additions & 1 deletion cpp/pybind/geometry/pointcloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ void pybind_pointcloud(py::module &m) {
"Returns ``True`` if the point cloud contains point colors.")
.def("has_covariances", &PointCloud::HasCovariances,
"Returns ``True`` if the point cloud contains covariances.")
.def("has_origin_data", &PointCloud::HasOriginData,
"Returns ``True`` if the point cloud contains origin data.")
.def("normalize_normals", &PointCloud::NormalizeNormals,
"Normalize point normals to length 1.")
.def("paint_uniform_color", &PointCloud::PaintUniformColor,
Expand Down Expand Up @@ -256,10 +258,27 @@ camera. Given depth value d at (u, v) image coordinate, the corresponding 3d poi
.def_readwrite("covariances", &PointCloud::covariances_,
"``float64`` array of shape ``(num_points, 3, 3)``, "
"use ``numpy.asarray()`` to access data: Points "
"covariances.");
"covariances.")
.def(
"origin_data",
[](const std::shared_ptr<PointCloud> pcl) {
std::vector<size_t> tri_id;
std::vector<Eigen::Vector2d> tri_uv;
for (size_t i = 0; i < pcl->origindata_.size(); ++i) {
tri_id.push_back(pcl->origindata_[i].tri_id);
tri_uv.push_back(pcl->origindata_[i].tri_uv);
};
return py::dict("tri_id"_a = tri_id,
"tri_uv"_a = tri_uv);
},
"dict with entries of shape ``(num_points, ...)``, "
"each corresponding to different origin data information. "
"Use ``numpy.asarray()`` to access matrix data (e.g., "
"tri_uv).");
docstring::ClassMethodDocInject(m, "PointCloud", "has_colors");
docstring::ClassMethodDocInject(m, "PointCloud", "has_normals");
docstring::ClassMethodDocInject(m, "PointCloud", "has_points");
docstring::ClassMethodDocInject(m, "PointCloud", "has_origin_data");
docstring::ClassMethodDocInject(m, "PointCloud", "normalize_normals");
docstring::ClassMethodDocInject(
m, "PointCloud", "paint_uniform_color",
Expand Down
15 changes: 11 additions & 4 deletions cpp/pybind/geometry/trianglemesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ void pybind_trianglemesh(py::module &m) {
.def("sample_points_uniformly",
&TriangleMesh::SamplePointsUniformly,
"Function to uniformly sample points from the mesh.",
"number_of_points"_a = 100, "use_triangle_normal"_a = false)
"number_of_points"_a = 100, "use_triangle_normal"_a = false,
"record_origin"_a = false)
.def("sample_points_poisson_disk",
&TriangleMesh::SamplePointsPoissonDisk,
"Function to sample points from the mesh, where each point "
Expand All @@ -231,7 +232,7 @@ void pybind_trianglemesh(py::module &m) {
"noise). Method is based on Yuksel, \"Sample Elimination for "
"Generating Poisson Disk Sample Sets\", EUROGRAPHICS, 2015.",
"number_of_points"_a, "init_factor"_a = 5, "pcl"_a = nullptr,
"use_triangle_normal"_a = false)
"use_triangle_normal"_a = false, "record_origin"_a = false)
.def("subdivide_midpoint", &TriangleMesh::SubdivideMidpoint,
"Function subdivide mesh using midpoint algorithm.",
"number_of_iterations"_a = 1)
Expand Down Expand Up @@ -554,7 +555,10 @@ void pybind_trianglemesh(py::module &m) {
"If True assigns the triangle normals instead of the "
"interpolated vertex normals to the returned points. The "
"triangle normals will be computed and added to the mesh if "
"necessary."}});
"necessary."},
{"record_origin",
"If True assigns origin triangle ID and UV coordinates to the "
"returned points."}});
docstring::ClassMethodDocInject(
m, "TriangleMesh", "sample_points_poisson_disk",
{{"number_of_points", "Number of points that should be sampled."},
Expand All @@ -568,7 +572,10 @@ void pybind_trianglemesh(py::module &m) {
"If True assigns the triangle normals instead of the "
"interpolated vertex normals to the returned points. The "
"triangle normals will be computed and added to the mesh if "
"necessary."}});
"necessary."},
{"record_origin",
"If True assigns origin triangle ID and UV coordinates to the "
"returned points."}});
docstring::ClassMethodDocInject(
m, "TriangleMesh", "subdivide_midpoint",
{{"number_of_iterations",
Expand Down