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

Enable attributes in vtk wrapper functions #5347

Merged
merged 4 commits into from
Jul 27, 2022
Merged
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
3 changes: 3 additions & 0 deletions cpp/open3d/t/geometry/PointCloud.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ class PointCloud : public Geometry, public DrawableGeometry {
/// Getter for point_attr_ TensorMap. Used in Pybind.
const TensorMap &GetPointAttr() const { return point_attr_; }

/// Getter for point_attr_ TensorMap.
TensorMap &GetPointAttr() { return point_attr_; }

/// Get attributes. Throws exception if the attribute does not exist.
///
/// \param key Attribute name.
Expand Down
10 changes: 10 additions & 0 deletions cpp/open3d/t/geometry/TensorMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include <string>
#include <unordered_map>
#include <unordered_set>

#include "open3d/core/Tensor.h"

Expand Down Expand Up @@ -114,6 +115,15 @@ class TensorMap : public std::unordered_map<std::string, core::Tensor> {
/// Returns the primary key of the TensorMap.
std::string GetPrimaryKey() const { return primary_key_; }

/// Returns a set with all keys.
std::unordered_set<std::string> GetKeySet() const {
std::unordered_set<std::string> keys;
for (const auto& item : *this) {
keys.insert(item.first);
}
return keys;
}

/// Returns true if all tensors in the map have the same size.
bool IsSizeSynchronized() const;

Expand Down
20 changes: 15 additions & 5 deletions cpp/open3d/t/geometry/TriangleMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,9 @@ TriangleMesh TriangleMesh::ClipPlane(const core::Tensor &point,
auto point_ = point.To(core::Device(), core::Float64).Contiguous();
auto normal_ = normal.To(core::Device(), core::Float64).Contiguous();

auto polydata = CreateVtkPolyDataFromGeometry(*this);
auto polydata = CreateVtkPolyDataFromGeometry(
*this, GetVertexAttr().GetKeySet(), GetTriangleAttr().GetKeySet(),
{}, {}, false);

vtkNew<vtkPlane> clipPlane;
clipPlane->SetNormal(normal_.GetDataPtr<double>());
Expand All @@ -324,7 +326,8 @@ TriangleMesh TriangleMesh::SimplifyQuadricDecimation(
target_reduction);
}

auto polydata = CreateVtkPolyDataFromGeometry(*this);
// exclude attributes because they will not be preserved
auto polydata = CreateVtkPolyDataFromGeometry(*this, {}, {}, {}, {}, false);

vtkNew<vtkQuadricDecimation> decimate;
decimate->SetInputData(polydata);
Expand All @@ -342,12 +345,16 @@ TriangleMesh BooleanOperation(const TriangleMesh &mesh_A,
double tolerance,
int op) {
using namespace vtkutils;
// exclude triangle attributes because they will not be preserved
auto polydata_A = CreateVtkPolyDataFromGeometry(
mesh_A, mesh_A.GetVertexAttr().GetKeySet(), {}, {}, {}, false);
auto polydata_B = CreateVtkPolyDataFromGeometry(
mesh_B, mesh_B.GetVertexAttr().GetKeySet(), {}, {}, {}, false);

// clean meshes before passing them to the boolean operation
auto polydata_A = CreateVtkPolyDataFromGeometry(mesh_A);
vtkNew<vtkCleanPolyData> cleaner_A;
cleaner_A->SetInputData(polydata_A);

auto polydata_B = CreateVtkPolyDataFromGeometry(mesh_B);
vtkNew<vtkCleanPolyData> cleaner_B;
cleaner_B->SetInputData(polydata_B);

Expand Down Expand Up @@ -384,7 +391,10 @@ TriangleMesh TriangleMesh::BooleanDifference(const TriangleMesh &mesh,

TriangleMesh TriangleMesh::FillHoles(double hole_size) const {
using namespace vtkutils;
auto polydata = CreateVtkPolyDataFromGeometry(*this);
// do not include triangle attributes because they will not be preserved by
// the hole filling algorithm
auto polydata = CreateVtkPolyDataFromGeometry(
*this, GetVertexAttr().GetKeySet(), {}, {}, {}, false);
vtkNew<vtkFillHolesFilter> fill_holes;
fill_holes->SetInputData(polydata);
fill_holes->SetHoleSize(hole_size);
Expand Down
6 changes: 6 additions & 0 deletions cpp/open3d/t/geometry/TriangleMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ class TriangleMesh : public Geometry, public DrawableGeometry {
/// Getter for vertex_attr_ TensorMap. Used in Pybind.
const TensorMap &GetVertexAttr() const { return vertex_attr_; }

/// Getter for vertex_attr_ TensorMap.
TensorMap &GetVertexAttr() { return vertex_attr_; }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@yxlao I added another getter without const to simplify some conversion code. Is there a reason for just having the const version? Same for TriangleAttr and in PointCloud.h


/// Get vertex attributes in vertex_attr_. Throws exception if the attribute
/// does not exist.
///
Expand All @@ -163,6 +166,9 @@ class TriangleMesh : public Geometry, public DrawableGeometry {
/// Getter for triangle_attr_ TensorMap. Used in Pybind.
const TensorMap &GetTriangleAttr() const { return triangle_attr_; }

/// Getter for triangle_attr_ TensorMap.
TensorMap &GetTriangleAttr() { return triangle_attr_; }

/// Get triangle attributes in triangle_attr_. Throws exception if the
/// attribute does not exist.
///
Expand Down
109 changes: 88 additions & 21 deletions cpp/open3d/t/geometry/VtkUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@

#include <vtkArrayDispatch.h>
#include <vtkCellArray.h>
#include <vtkCellData.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkPointData.h>
#include <vtkPoints.h>

namespace open3d {
Expand Down Expand Up @@ -281,8 +283,70 @@ static core::Tensor CreateTensorFromVtkCellArray(vtkCellArray* cells,
return result.Reshape({num_cells, cell_size});
}

/// Adds point or cell attribute arrays to a TensorMap.
/// \param tmap The destination TensorMap.
/// \param field_data The source vtkFieldData.
/// \param copy If true always create a copy for attribute arrays.
static void AddVtkFieldDataToTensorMap(TensorMap& tmap,
vtkFieldData* field_data,
bool copy) {
for (int i = 0; i < field_data->GetNumberOfArrays(); ++i) {
auto array = field_data->GetArray(i);
std::string array_name = array->GetName();
tmap[array_name] = CreateTensorFromVtkDataArray(array, copy);
}
}

/// Adds attribute tensors to vtkFieldData.
/// Primary key tensors will be ignored by this function.
/// \param field_data The destination vtkFieldData.
/// \param tmap The source TensorMap.
/// \param copy If true always create a copy for attribute arrays.
/// \param include A set of keys to select which attributes should be added.
/// \param exclude A set of keys for which attributes will not be added to the
/// vtkFieldData. The exclusion set has precedence over the included keys.
static void AddTensorMapToVtkFieldData(
vtkFieldData* field_data,
TensorMap& tmap,
bool copy,
std::unordered_set<std::string> include,
std::unordered_set<std::string> exclude = {}) {
for (auto key_tensor : tmap) {
// we only want attributes and ignore the primary key here
if (key_tensor.first == tmap.GetPrimaryKey()) {
continue;
}
// we only support 2D tensors
if (key_tensor.second.NumDims() != 2) {
utility::LogWarning(
"Ignoring attribute '{}' for TensorMap with primary key "
"'{}' because of incompatible ndim={}",
key_tensor.first, tmap.GetPrimaryKey(),
key_tensor.second.NumDims());
continue;
}

if (include.count(key_tensor.first) &&
!exclude.count(key_tensor.first)) {
auto array = CreateVtkDataArrayFromTensor(key_tensor.second, copy);
array->SetName(key_tensor.first.c_str());
field_data->AddArray(array);
} else {
utility::LogWarning(
"Ignoring attribute '{}' for TensorMap with primary key "
"'{}'",
key_tensor.first, tmap.GetPrimaryKey());
}
}
}

vtkSmartPointer<vtkPolyData> CreateVtkPolyDataFromGeometry(
const Geometry& geometry, bool copy) {
const Geometry& geometry,
const std::unordered_set<std::string>& point_attr_include,
const std::unordered_set<std::string>& face_attr_include,
const std::unordered_set<std::string>& point_attr_exclude,
const std::unordered_set<std::string>& face_attr_exclude,
bool copy) {
vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();

if (geometry.GetGeometryType() == Geometry::GeometryType::PointCloud) {
Expand All @@ -299,14 +363,10 @@ vtkSmartPointer<vtkPolyData> CreateVtkPolyDataFromGeometry(
}

polydata->SetVerts(cells);
AddTensorMapToVtkFieldData(polydata->GetPointData(), pcd.GetPointAttr(),
copy, point_attr_include,
point_attr_exclude);

for (auto key_tensor : pcd.GetPointAttr()) {
if (key_tensor.first != pcd.GetPointAttr().GetPrimaryKey()) {
utility::LogWarning("Ignoring point attribute {}",
key_tensor.first);
}
}
// TODO convert other data like normals, colors, ...
} else if (geometry.GetGeometryType() ==
Geometry::GeometryType::TriangleMesh) {
auto mesh = static_cast<const TriangleMesh&>(geometry);
Expand All @@ -315,19 +375,12 @@ vtkSmartPointer<vtkPolyData> CreateVtkPolyDataFromGeometry(
polydata->SetPolys(
CreateVtkCellArrayFromTensor(mesh.GetTriangleIndices(), copy));

for (auto key_tensor : mesh.GetVertexAttr()) {
if (key_tensor.first != mesh.GetVertexAttr().GetPrimaryKey()) {
utility::LogWarning("Ignoring vertex attribute {}",
key_tensor.first);
}
}
for (auto key_tensor : mesh.GetTriangleAttr()) {
if (key_tensor.first != mesh.GetTriangleAttr().GetPrimaryKey()) {
utility::LogWarning("Ignoring triangle attribute {}",
key_tensor.first);
}
}
// TODO convert other data like normals, colors, ...
AddTensorMapToVtkFieldData(polydata->GetPointData(),
mesh.GetVertexAttr(), copy,
point_attr_include, point_attr_exclude);
AddTensorMapToVtkFieldData(polydata->GetCellData(),
mesh.GetTriangleAttr(), copy,
face_attr_include, face_attr_exclude);
} else {
utility::LogError("Unsupported geometry type {}",
geometry.GetGeometryType());
Expand All @@ -338,11 +391,25 @@ vtkSmartPointer<vtkPolyData> CreateVtkPolyDataFromGeometry(

TriangleMesh CreateTriangleMeshFromVtkPolyData(vtkPolyData* polydata,
bool copy) {
if (!polydata->GetPoints()) {
return TriangleMesh();
}
core::Tensor vertices = CreateTensorFromVtkDataArray(
polydata->GetPoints()->GetData(), copy);

core::Tensor triangles =
CreateTensorFromVtkCellArray(polydata->GetPolys(), copy);
// Some algorithms return an empty tensor with shape (0,0).
// Fix the last dim here.
if (triangles.GetShape() == core::SizeVector{0, 0}) {
triangles = triangles.Reshape({0, 3});
}
TriangleMesh mesh(vertices, triangles);

AddVtkFieldDataToTensorMap(mesh.GetVertexAttr(), polydata->GetPointData(),
copy);
AddVtkFieldDataToTensorMap(mesh.GetTriangleAttr(), polydata->GetCellData(),
copy);
return mesh;
}

Expand Down
20 changes: 19 additions & 1 deletion cpp/open3d/t/geometry/VtkUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,26 @@ int DtypeToVtkType(const core::Dtype& dtype);
/// kept alive until the returned vtkPolyData object is deleted.
/// \param geometry Open3D geometry object, e.g., a TriangleMesh.
/// \param copy If true always create a copy of the data.
/// \param point_attr_include A set of keys to select which point/vertex
/// attributes should be added. Note that the primary key may be included and
/// will silently be ignored.
/// \param face_attr_include A set of keys to select
/// which face attributes should be added. Note that the primary key may be
/// included and will silently be ignored.
/// \param point_attr_exclude A set of keys for which point/vertex attributes
/// will not be added to the vtkPolyData. The exclusion set has precedence over
/// the included keys.
/// \param face_attr_exclude A set of keys for which face attributes will not be
/// added
/// to the vtkPolyData. The exclusion set has precedence over the included
/// keys.
vtkSmartPointer<vtkPolyData> CreateVtkPolyDataFromGeometry(
const Geometry& geometry, bool copy = false);
const Geometry& geometry,
const std::unordered_set<std::string>& point_attr_include,
const std::unordered_set<std::string>& face_attr_include,
const std::unordered_set<std::string>& point_attr_exclude = {},
const std::unordered_set<std::string>& face_attr_exclude = {},
bool copy = false);

/// Creates a triangle mesh from a vtkPolyData object.
/// The returned TriangleMesh may directly use the memory of the data arrays in
Expand Down
6 changes: 4 additions & 2 deletions cpp/tests/t/geometry/VtkUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ TEST_P(VtkUtilsTest, PointCloudToVtkPolyData) {

auto pcd = PointCloud(
core::Tensor::Init<float>({{0, 0, 0}, {1, 1, 1}, {2, 2, 2}}));
auto polydata = vtkutils::CreateVtkPolyDataFromGeometry(pcd, copy);
auto polydata =
vtkutils::CreateVtkPolyDataFromGeometry(pcd, {}, {}, {}, {}, copy);

auto tensor = pcd.GetPointPositions();
auto data_array = polydata->GetPoints()->GetData();
Expand All @@ -86,7 +87,8 @@ TEST_P(VtkUtilsTest, TriangleMeshToVtkPolyData) {

auto legacy_box = geometry::TriangleMesh::CreateBox();
auto box = TriangleMesh::FromLegacy(*legacy_box);
auto polydata = vtkutils::CreateVtkPolyDataFromGeometry(box, copy);
auto polydata =
vtkutils::CreateVtkPolyDataFromGeometry(box, {}, {}, {}, {}, copy);

auto tensor = box.GetVertexPositions();
auto data_array = polydata->GetPoints()->GetData();
Expand Down