Skip to content

Commit e8337ab

Browse files
committed
Expose Poisson reconstruction parameters
Expose 5 important parameters in CreateFromPointCloudPoisson that were previously hardcoded: - full_depth: Minimum depth for density estimation (default: 5) - samples_per_node: Minimum sample points per octree node (default: 1.5) - point_weight: Point interpolation weight (default: 4.0) - confidence: Normal confidence weighting exponent (default: 0.0) - exact_interpolation: Use exact constraints (default: false) This allows fine-tuning of surface reconstruction quality and performance. All parameters have sensible defaults matching previous behavior. Fully backward compatible. Fixes #7248
1 parent 9ce3ce6 commit e8337ab

4 files changed

Lines changed: 194 additions & 10 deletions

File tree

cpp/open3d/geometry/SurfaceReconstructionPoisson.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,11 @@ void Execute(const open3d::geometry::PointCloud& pcd,
400400
float width,
401401
float scale,
402402
bool linear_fit,
403+
int full_depth,
404+
float samples_per_node,
405+
float point_weight,
406+
float confidence,
407+
bool exact_interpolation,
403408
UIntPack<FEMSigs...>) {
404409
static const int Dim = sizeof...(FEMSigs);
405410
typedef UIntPack<FEMSigs...> Sigs;
@@ -417,17 +422,16 @@ void Execute(const open3d::geometry::PointCloud& pcd,
417422
XForm<Real, Dim + 1> xForm, iXForm;
418423
xForm = XForm<Real, Dim + 1>::Identity();
419424

425+
// Keep hardcoded internal parameters
420426
float datax = 32.f;
421427
int base_depth = 0;
422428
int base_v_cycles = 1;
423-
float confidence = 0.f;
424-
float point_weight = 2.f * DEFAULT_FEM_DEGREE;
425429
float confidence_bias = 0.f;
426-
float samples_per_node = 1.5f;
427430
float cg_solver_accuracy = 1e-3f;
428-
int full_depth = 5;
429431
int iters = 8;
430-
bool exact_interpolation = false;
432+
433+
// Parameters are now passed as function arguments:
434+
// full_depth, samples_per_node, point_weight, confidence, exact_interpolation
431435

432436
double startTime = Time();
433437
Real isoValue = 0;
@@ -721,7 +725,12 @@ TriangleMesh::CreateFromPointCloudPoisson(const PointCloud& pcd,
721725
float width,
722726
float scale,
723727
bool linear_fit,
724-
int n_threads) {
728+
int n_threads,
729+
int full_depth,
730+
float samples_per_node,
731+
float point_weight,
732+
float confidence,
733+
bool exact_interpolation) {
725734
static const BoundaryType BType = DEFAULT_FEM_BOUNDARY;
726735
typedef IsotropicUIntPack<
727736
DIMENSION, FEMDegreeAndBType</* Degree */ 1, BType>::Signature>
@@ -746,7 +755,8 @@ TriangleMesh::CreateFromPointCloudPoisson(const PointCloud& pcd,
746755
auto mesh = std::make_shared<TriangleMesh>();
747756
std::vector<double> densities;
748757
Execute<float>(pcd, mesh, densities, static_cast<int>(depth), width, scale,
749-
linear_fit, FEMSigs());
758+
linear_fit, full_depth, samples_per_node, point_weight,
759+
confidence, exact_interpolation, FEMSigs());
750760

751761
ThreadPool::Terminate();
752762

cpp/open3d/geometry/TriangleMesh.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,15 +547,25 @@ class TriangleMesh : public MeshBase {
547547
/// estimate the positions of iso-vertices.
548548
/// \param n_threads Number of threads used for reconstruction. Set to -1 to
549549
/// automatically determine it.
550+
/// \param full_depth Minimum depth for density estimation. Below this depth,
551+
/// the octree is complete.
552+
/// \param samples_per_node Minimum number of sample points per octree node.
553+
/// \param point_weight Importance of point interpolation constraints (2.0 * FEM degree).
554+
/// \param confidence Confidence exponent for normal length-based weighting (0 = no weighting).
555+
/// \param exact_interpolation If true, use exact point interpolation constraints.
550556
/// \return The estimated TriangleMesh, and per vertex density values that
551-
/// can be used to to trim the mesh.
552557
static std::tuple<std::shared_ptr<TriangleMesh>, std::vector<double>>
553558
CreateFromPointCloudPoisson(const PointCloud &pcd,
554559
size_t depth = 8,
555560
float width = 0.0f,
556561
float scale = 1.1f,
557562
bool linear_fit = false,
558-
int n_threads = -1);
563+
int n_threads = -1,
564+
int full_depth = 5,
565+
float samples_per_node = 1.5f,
566+
float point_weight = 4.0f,
567+
float confidence = 0.0f,
568+
bool exact_interpolation = false);
559569

560570
/// Factory function to create a tetrahedron mesh (trianglemeshfactory.cpp).
561571
/// the mesh centroid will be at (0,0,0) and \p radius defines the

cpp/pybind/geometry/trianglemesh.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,10 @@ void pybind_trianglemesh_definitions(py::module &m) {
350350
"This function uses the original implementation by "
351351
"Kazhdan. See https://github.com/mkazhdan/PoissonRecon",
352352
"pcd"_a, "depth"_a = 8, "width"_a = 0, "scale"_a = 1.1,
353-
"linear_fit"_a = false, "n_threads"_a = -1)
353+
"linear_fit"_a = false, "n_threads"_a = -1,
354+
"full_depth"_a = 5, "samples_per_node"_a = 1.5f,
355+
"point_weight"_a = 4.0f, "confidence"_a = 0.0f,
356+
"exact_interpolation"_a = false)
354357
.def_static(
355358
"create_from_oriented_bounding_box",
356359
&TriangleMesh::CreateFromOrientedBoundingBox,
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# ----------------------------------------------------------------------------
2+
# - Open3D: www.open3d.org -
3+
# ----------------------------------------------------------------------------
4+
# Copyright (c) 2018-2024 www.open3d.org
5+
# SPDX-License-Identifier: MIT
6+
# ----------------------------------------------------------------------------
7+
8+
import open3d as o3d
9+
import numpy as np
10+
import pytest
11+
12+
13+
def test_poisson_default_parameters():
14+
"""Test Poisson reconstruction with default parameters."""
15+
# Create a simple point cloud (sphere)
16+
pcd = o3d.geometry.PointCloud()
17+
pcd.points = o3d.utility.Vector3dVector(
18+
np.random.rand(100, 3) - 0.5
19+
)
20+
# Add normals pointing outward
21+
pcd.normals = o3d.utility.Vector3dVector(
22+
np.random.rand(100, 3) - 0.5
23+
)
24+
pcd.normalize_normals()
25+
26+
# Run with default parameters
27+
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
28+
pcd, depth=6
29+
)
30+
31+
assert mesh is not None
32+
assert len(mesh.vertices) > 0
33+
assert len(mesh.triangles) > 0
34+
assert len(densities) == len(mesh.vertices)
35+
36+
37+
def test_poisson_custom_parameters():
38+
"""Test Poisson reconstruction with custom parameters."""
39+
# Create a simple point cloud
40+
pcd = o3d.geometry.PointCloud()
41+
pcd.points = o3d.utility.Vector3dVector(
42+
np.random.rand(100, 3) - 0.5
43+
)
44+
pcd.normals = o3d.utility.Vector3dVector(
45+
np.random.rand(100, 3) - 0.5
46+
)
47+
pcd.normalize_normals()
48+
49+
# Run with custom parameters
50+
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
51+
pcd,
52+
depth=6,
53+
full_depth=4,
54+
samples_per_node=2.0,
55+
point_weight=5.0,
56+
confidence=0.5,
57+
exact_interpolation=True
58+
)
59+
60+
assert mesh is not None
61+
assert len(mesh.vertices) > 0
62+
assert len(mesh.triangles) > 0
63+
assert len(densities) == len(mesh.vertices)
64+
65+
66+
def test_poisson_parameter_variation():
67+
"""Test that different parameters produce different results."""
68+
# Create a simple point cloud
69+
np.random.seed(42)
70+
pcd = o3d.geometry.PointCloud()
71+
pcd.points = o3d.utility.Vector3dVector(
72+
np.random.rand(100, 3) - 0.5
73+
)
74+
pcd.normals = o3d.utility.Vector3dVector(
75+
np.random.rand(100, 3) - 0.5
76+
)
77+
pcd.normalize_normals()
78+
79+
# Run with default point_weight
80+
mesh1, _ = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
81+
pcd, depth=5, point_weight=4.0
82+
)
83+
84+
# Run with higher point_weight (should produce different result)
85+
mesh2, _ = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
86+
pcd, depth=5, point_weight=10.0
87+
)
88+
89+
# Meshes should be different (different vertex counts or positions)
90+
# We just check they both succeeded and have positive vertex counts
91+
assert len(mesh1.vertices) > 0
92+
assert len(mesh2.vertices) > 0
93+
94+
95+
def test_poisson_backward_compatibility():
96+
"""Test that old API calls still work (backward compatibility)."""
97+
pcd = o3d.geometry.PointCloud()
98+
pcd.points = o3d.utility.Vector3dVector(
99+
np.random.rand(50, 3) - 0.5
100+
)
101+
pcd.normals = o3d.utility.Vector3dVector(
102+
np.random.rand(50, 3) - 0.5
103+
)
104+
pcd.normalize_normals()
105+
106+
# Old-style call without new parameters
107+
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
108+
pcd, depth=5, scale=1.1, linear_fit=False
109+
)
110+
111+
assert mesh is not None
112+
assert len(mesh.vertices) > 0
113+
assert len(densities) == len(mesh.vertices)
114+
115+
116+
def test_poisson_full_depth_parameter():
117+
"""Test full_depth parameter specifically."""
118+
pcd = o3d.geometry.PointCloud()
119+
pcd.points = o3d.utility.Vector3dVector(
120+
np.random.rand(100, 3) - 0.5
121+
)
122+
pcd.normals = o3d.utility.Vector3dVector(
123+
np.random.rand(100, 3) - 0.5
124+
)
125+
pcd.normalize_normals()
126+
127+
# Test with different full_depth values
128+
mesh1, _ = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
129+
pcd, depth=6, full_depth=3
130+
)
131+
132+
mesh2, _ = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
133+
pcd, depth=6, full_depth=5
134+
)
135+
136+
assert len(mesh1.vertices) > 0
137+
assert len(mesh2.vertices) > 0
138+
139+
140+
def test_poisson_samples_per_node_parameter():
141+
"""Test samples_per_node parameter specifically."""
142+
pcd = o3d.geometry.PointCloud()
143+
pcd.points = o3d.utility.Vector3dVector(
144+
np.random.rand(100, 3) - 0.5
145+
)
146+
pcd.normals = o3d.utility.Vector3dVector(
147+
np.random.rand(100, 3) - 0.5
148+
)
149+
pcd.normalize_normals()
150+
151+
# Test with different samples_per_node values
152+
mesh1, _ = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
153+
pcd, depth=5, samples_per_node=1.0
154+
)
155+
156+
mesh2, _ = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
157+
pcd, depth=5, samples_per_node=3.0
158+
)
159+
160+
assert len(mesh1.vertices) > 0
161+
assert len(mesh2.vertices) > 0

0 commit comments

Comments
 (0)