From b8651e9127fc88bcb45c8627c92b33d0f18d1031 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey Date: Sat, 16 Nov 2024 00:31:52 -0800 Subject: [PATCH] bind_vector initializer_list -> vector TODO: Check F-Score values are correct --- cpp/open3d/t/geometry/PointCloud.cpp | 7 +++---- cpp/open3d/t/geometry/PointCloud.h | 2 +- cpp/open3d/t/geometry/TriangleMesh.cpp | 2 +- cpp/open3d/t/geometry/TriangleMesh.h | 2 +- cpp/open3d/t/geometry/kernel/Metrics.cpp | 15 ++++++++------- cpp/open3d/t/geometry/kernel/Metrics.h | 2 +- cpp/pybind/data/dataset.cpp | 2 +- cpp/pybind/t/geometry/geometry.cpp | 13 ++++++++++--- cpp/pybind/t/geometry/trianglemesh.cpp | 9 ++++----- cpp/pybind/utility/eigen.cpp | 1 + python/test/t/geometry/test_pointcloud.py | 17 +++++++++++++++++ python/test/t/geometry/test_trianglemesh.py | 14 +++++++++----- 12 files changed, 57 insertions(+), 29 deletions(-) diff --git a/cpp/open3d/t/geometry/PointCloud.cpp b/cpp/open3d/t/geometry/PointCloud.cpp index a1422f538e9..1f08314fd2f 100644 --- a/cpp/open3d/t/geometry/PointCloud.cpp +++ b/cpp/open3d/t/geometry/PointCloud.cpp @@ -1332,10 +1332,9 @@ int PointCloud::PCAPartition(int max_points) { return num_partitions; } -std::vector PointCloud::ComputeDistance( - const PointCloud &pcd2, - std::initializer_list metrics, - MetricParameters params) const { +std::vector PointCloud::ComputeDistance(const PointCloud &pcd2, + std::vector metrics, + MetricParameters params) const { if (!IsCPU() || !pcd2.IsCPU()) { utility::LogWarning( "ComputeDistance is implemented only on CPU. Computing on " diff --git a/cpp/open3d/t/geometry/PointCloud.h b/cpp/open3d/t/geometry/PointCloud.h index 2e1d4844f49..69b72f8f2c2 100644 --- a/cpp/open3d/t/geometry/PointCloud.h +++ b/cpp/open3d/t/geometry/PointCloud.h @@ -707,7 +707,7 @@ class PointCloud : public Geometry, public DrawableGeometry { /// different metrics. std::vector ComputeDistance( const PointCloud &pcd2, - std::initializer_list metrics = {Metric::ChamferDistance}, + std::vector metrics = {Metric::ChamferDistance}, MetricParameters params = MetricParameters()) const; protected: diff --git a/cpp/open3d/t/geometry/TriangleMesh.cpp b/cpp/open3d/t/geometry/TriangleMesh.cpp index e728d15da89..db7df02588a 100644 --- a/cpp/open3d/t/geometry/TriangleMesh.cpp +++ b/cpp/open3d/t/geometry/TriangleMesh.cpp @@ -1593,7 +1593,7 @@ PointCloud TriangleMesh::SamplePointsUniformly( std::vector TriangleMesh::ComputeDistance( const TriangleMesh &mesh2, - std::initializer_list metrics, + std::vector metrics, MetricParameters params) const { if (!IsCPU() || !mesh2.IsCPU()) { utility::LogWarning( diff --git a/cpp/open3d/t/geometry/TriangleMesh.h b/cpp/open3d/t/geometry/TriangleMesh.h index 01ba9e700fc..9afe6c825d5 100644 --- a/cpp/open3d/t/geometry/TriangleMesh.h +++ b/cpp/open3d/t/geometry/TriangleMesh.h @@ -1030,7 +1030,7 @@ class TriangleMesh : public Geometry, public DrawableGeometry { /// different metrics. std::vector ComputeDistance( const TriangleMesh &mesh2, - std::initializer_list metrics = {Metric::ChamferDistance}, + std::vector metrics = {Metric::ChamferDistance}, MetricParameters params = MetricParameters()) const; protected: diff --git a/cpp/open3d/t/geometry/kernel/Metrics.cpp b/cpp/open3d/t/geometry/kernel/Metrics.cpp index 06e500d89be..9e30482729f 100644 --- a/cpp/open3d/t/geometry/kernel/Metrics.cpp +++ b/cpp/open3d/t/geometry/kernel/Metrics.cpp @@ -16,7 +16,7 @@ namespace geometry { std::vector OPEN3D_LOCAL ComputeDistanceCommon(core::Tensor distance12, core::Tensor distance21, - std::initializer_list metrics, + std::vector metrics, MetricParameters params) { std::vector metric_values; float metric_val; @@ -24,8 +24,9 @@ ComputeDistanceCommon(core::Tensor distance12, for (Metric metric : metrics) { switch (metric) { case Metric::ChamferDistance: - metric_val = 0.5 * (distance21.Mean({-1}).Item() + - distance12.Mean({-1}).Item()); + metric_val = 0.5 * + (distance21.Reshape({-1}).Mean({0}).Item() + + distance12.Reshape({-1}).Mean({0}).Item()); metric_values.push_back(metric_val); break; case Metric::FScore: @@ -35,13 +36,13 @@ ComputeDistanceCommon(core::Tensor distance12, // Workaround since we don't have Tensor::CountNonZeros float precision = 0., recall = 0.; for (size_t i = 0; - i < static_cast(distance12.GetLength()); ++i) + i < static_cast(distance12.NumElements()); ++i) precision += p_distance12[i] < radius; - precision *= 100. / distance12.GetLength(); + precision *= 100. / distance12.NumElements(); for (size_t i = 0; - i < static_cast(distance21.GetLength()); ++i) + i < static_cast(distance21.NumElements()); ++i) recall += p_distance21[i] < radius; - recall *= 100. / distance21.GetLength(); + recall *= 100. / distance21.NumElements(); float fscore = 0.0; if (precision + recall > 0) { fscore = 2 * precision * recall / (precision + recall); diff --git a/cpp/open3d/t/geometry/kernel/Metrics.h b/cpp/open3d/t/geometry/kernel/Metrics.h index d1257c7c8dd..a65c2ca6547 100644 --- a/cpp/open3d/t/geometry/kernel/Metrics.h +++ b/cpp/open3d/t/geometry/kernel/Metrics.h @@ -15,7 +15,7 @@ namespace geometry { std::vector ComputeDistanceCommon(core::Tensor distance12, core::Tensor distance21, - std::initializer_list metrics, + std::vector metrics, MetricParameters params); } } // namespace t diff --git a/cpp/pybind/data/dataset.cpp b/cpp/pybind/data/dataset.cpp index bbd331d1010..ce2b21ee273 100644 --- a/cpp/pybind/data/dataset.cpp +++ b/cpp/pybind/data/dataset.cpp @@ -35,7 +35,7 @@ void pybind_data_classes(py::module& m) { m, "DataDescriptor", "DataDescriptor is a class that describes a data file. It contains " "the URL mirrors to download the file, the MD5 hash of the file, " - "and wether to extract the file."); + "and whether to extract the file."); data_descriptor .def(py::init([](const std::vector& urls, const std::string& md5, diff --git a/cpp/pybind/t/geometry/geometry.cpp b/cpp/pybind/t/geometry/geometry.cpp index d6129b71411..84fa5d15da4 100644 --- a/cpp/pybind/t/geometry/geometry.cpp +++ b/cpp/pybind/t/geometry/geometry.cpp @@ -7,9 +7,12 @@ #include "open3d/t/geometry/Geometry.h" +#include + #include #include "pybind/docstring.h" +#include "pybind/open3d_pybind.h" #include "pybind/t/geometry/geometry.h" namespace open3d { @@ -50,7 +53,8 @@ void pybind_geometry_declarations(py::module& m) { "Chamfer Distance") .value("FScore", Metric::FScore, "F-Score") .export_values(); - py::class_( + py::bind_vector>(m_geometry, "VectorMetric"); + py::class_ metric_parameters( m_geometry, "MetricParameters", "Holder for various parameters required by metrics."); @@ -69,9 +73,12 @@ void pybind_geometry_declarations(py::module& m) { void pybind_geometry_definitions(py::module& m) { auto m_geometry = static_cast(m.attr("geometry")); - auto metric_params = static_cast>( + auto metric_parameters = static_cast>( m_geometry.attr("MetricParameters")); - metric_params.def(py::init&, size_t>()) + metric_parameters + .def(py::init&, size_t>(), + "fscore_radius"_a = std::vector{0.01}, + "n_sampled_points"_a = 1000) .def_readwrite("fscore_radius", &MetricParameters::fscore_radius, "Radius for computing the F-Score. A match between " "a point and its nearest neighbor is sucessful if " diff --git a/cpp/pybind/t/geometry/trianglemesh.cpp b/cpp/pybind/t/geometry/trianglemesh.cpp index 277fb04c3a8..e54baac26b5 100644 --- a/cpp/pybind/t/geometry/trianglemesh.cpp +++ b/cpp/pybind/t/geometry/trianglemesh.cpp @@ -1086,11 +1086,10 @@ adjacent triangles to the edge is `<= 2`. Example:: - dataset = o3d.data.AvocadoModel() - model = o3d.io.read_triangle_model(dataset.path) - meshes = o3d.t.geometry.TriangleMesh.from_triangle_mesh_model(model) - pcd = meshes[0].sample_points_uniformly(1000) - o3d.visualization.draw([model, pcd]) + mesh = o3d.t.geometry.TriangleMesh.create_box() + mesh.vertex.colors = mesh.vertex.positions.clone() + pcd = mesh.sample_points_uniformly(100000) + o3d.visualization.draw([mesh, pcd], point_size=5, show_ui=True, show_skybox=False) )"); diff --git a/cpp/pybind/utility/eigen.cpp b/cpp/pybind/utility/eigen.cpp index 1eb621772eb..232433642a6 100644 --- a/cpp/pybind/utility/eigen.cpp +++ b/cpp/pybind/utility/eigen.cpp @@ -308,6 +308,7 @@ namespace utility { void pybind_eigen_declarations(py::module &m) { auto intvector = pybind_eigen_vector_of_scalar(m, "IntVector"); + auto floatvector = pybind_eigen_vector_of_scalar(m, "FloatVector"); auto doublevector = pybind_eigen_vector_of_scalar(m, "DoubleVector"); auto vector3dvector = pybind_eigen_vector_of_vector( diff --git a/python/test/t/geometry/test_pointcloud.py b/python/test/t/geometry/test_pointcloud.py index f277bcabfb2..1aab8f8c76d 100644 --- a/python/test/t/geometry/test_pointcloud.py +++ b/python/test/t/geometry/test_pointcloud.py @@ -193,3 +193,20 @@ def test_pickle(device): assert pcd_load.point.positions.device == device and pcd_load.point.positions.dtype == o3c.float32 np.testing.assert_equal(pcd.point.positions.cpu().numpy(), pcd_load.point.positions.cpu().numpy()) + + +def test_metrics(): + + from open3d.t.geometry import Metric, MetricParameters + pos = o3d.t.geometry.TriangleMesh.create_box().vertex.positions + pcd1 = o3d.t.geometry.PointCloud(pos.clone()) + pcd2 = o3d.t.geometry.PointCloud(pos * 1.2) + + metric_params = MetricParameters( + fscore_radius=o3d.utility.FloatVector((0.05, 0.15))) + metrics = pcd1.compute_distance(pcd2, + (Metric.ChamferDistance, Metric.FScore), + metric_params) + + print(metrics) + np.testing.assert_allclose(metrics, (0.03, 200. / 3, 100), rtol=1e-6) diff --git a/python/test/t/geometry/test_trianglemesh.py b/python/test/t/geometry/test_trianglemesh.py index 7fdbda44cb8..0d737e9d694 100644 --- a/python/test/t/geometry/test_trianglemesh.py +++ b/python/test/t/geometry/test_trianglemesh.py @@ -1399,10 +1399,14 @@ def test_metrics(): from open3d.t.geometry import Metric, MetricParameters box1 = o3d.t.geometry.TriangleMesh.create_box() box2 = o3d.t.geometry.TriangleMesh.create_box() - box2.vertex.positions += 0.1 + box2.vertex.positions *= 1.2 - metrics = box1.compute_distance( - box2, (Metric.ChamferDistance, Metric.FScore), - MetricParameters(fscore_radius=(0.05, 0.15), n_sampled_points=10)) + metric_params = MetricParameters(fscore_radius=o3d.utility.FloatVector( + (0.05, 0.15)), + n_sampled_points=100000) + metrics = box1.compute_distance(box2, + (Metric.ChamferDistance, Metric.FScore), + metric_params) - assert metrics == (0.1, 0, 1) + print(metrics) + np.testing.assert_allclose(metrics, (0.1, 45, 53.2), rtol=0.05)