FastSplatStyler / pointCloudToMesh.py
incrl's picture
Initial Upload (attempt 2)
5b557cf verified
import numpy as np
import open3d as o3d
from skimage import measure
from scipy.spatial import cKDTree
import splat_helpers as splt
# based on code from https://towardsdatascience.com/transform-point-clouds-into-3d-meshes-a-python-guide-8b0407a780e6
# credit Florent Poux
# Towards Data Science (2024)
def MarchingCubes_from_ply(dataset, voxel_size, iso_level_percentile):
pcd = o3d.io.read_point_cloud(dataset)
# Convert Open3D point cloud to numpy array
points = np.asarray(pcd.points)
# Compute the bounds of the point cloud
mins = np.min(points, axis=0)
maxs = np.max(points, axis=0)
# Create a 3D grid
x = np.arange(mins[0], maxs[0], voxel_size)
y = np.arange(mins[1], maxs[1], voxel_size)
z = np.arange(mins[2], maxs[2], voxel_size)
x, y, z = np.meshgrid(x, y, z, indexing='ij')
# Create a KD-tree for efficient nearest neighbor search
tree = cKDTree(points)
# Compute the scalar field (distance to nearest point)
grid_points = np.vstack([x.ravel(), y.ravel(), z.ravel()]).T
distances, _ = tree.query(grid_points)
scalar_field = distances.reshape(x.shape)
# Determine iso-level based on percentile of distances
iso_level = np.percentile(distances, iso_level_percentile)
# Apply Marching Cubes
verts, faces, _, _ = measure.marching_cubes(scalar_field, level=iso_level)
# Scale and translate vertices back to original coordinate system
verts = verts * voxel_size + mins
# Create mesh
mesh = o3d.geometry.TriangleMesh()
mesh.vertices = o3d.utility.Vector3dVector(verts)
mesh.triangles = o3d.utility.Vector3iVector(faces)
# Compute vertex normals
mesh.compute_vertex_normals()
# Visualize the result
o3d.visualization.draw_geometries([mesh], mesh_show_back_face=True)
def MarchingCubes_with_filtering(dataset, voxel_size, iso_level_percentile, threshold=99, out_file = 'out.obj'):
pos3D, _, _, _, _, _, _ = splt.splat_unpacker_threshold(25, dataset, threshold)
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(pos3D.numpy())
# Convert Open3D point cloud to numpy array
points = np.asarray(pcd.points)
# Compute the bounds of the point cloud
mins = np.min(points, axis=0)
maxs = np.max(points, axis=0)
# Create a 3D grid
x = np.arange(mins[0], maxs[0], voxel_size)
y = np.arange(mins[1], maxs[1], voxel_size)
z = np.arange(mins[2], maxs[2], voxel_size)
x, y, z = np.meshgrid(x, y, z, indexing='ij')
# Create a KD-tree for efficient nearest neighbor search
tree = cKDTree(points)
# Compute the scalar field (distance to nearest point)
grid_points = np.vstack([x.ravel(), y.ravel(), z.ravel()]).T
distances, _ = tree.query(grid_points)
scalar_field = distances.reshape(x.shape)
# Determine iso-level based on percentile of distances
iso_level = np.percentile(distances, iso_level_percentile)
# Apply Marching Cubes
verts, faces, _, _ = measure.marching_cubes(scalar_field, level=iso_level)
# Scale and translate vertices back to original coordinate system
verts = verts * voxel_size + mins
# Create mesh
mesh = o3d.geometry.TriangleMesh()
mesh.vertices = o3d.utility.Vector3dVector(verts)
mesh.triangles = o3d.utility.Vector3iVector(faces)
# Compute vertex normals
mesh.compute_vertex_normals()
# Save the result
o3d.io.write_triangle_mesh(out_file, mesh)
# Visualize the result
o3d.visualization.draw_geometries([mesh], mesh_show_back_face=True)
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(np.asarray(mesh.vertices))
pcd.estimate_normals()
o3d.visualization.draw_geometries([pcd])
def MarchingCubes_return_vertices(dataset, visualize = False):
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(dataset.numpy())
voxel_size_tensor = (abs(dataset.min()) + abs(dataset.max()))/100
voxel_size = voxel_size_tensor.item()
iso_level_percentile = 5
# Convert Open3D point cloud to numpy array
points = np.asarray(pcd.points)
# Compute the bounds of the point cloud
mins = np.min(points, axis=0)
maxs = np.max(points, axis=0)
# Create a 3D grid
x = np.arange(mins[0], maxs[0], voxel_size)
y = np.arange(mins[1], maxs[1], voxel_size)
z = np.arange(mins[2], maxs[2], voxel_size)
x, y, z = np.meshgrid(x, y, z, indexing='ij')
# Create a KD-tree for efficient nearest neighbor search
tree = cKDTree(points)
# Compute the scalar field (distance to nearest point)
grid_points = np.vstack([x.ravel(), y.ravel(), z.ravel()]).T
distances, _ = tree.query(grid_points)
scalar_field = distances.reshape(x.shape)
# Determine iso-level based on percentile of distances
iso_level = np.percentile(distances, iso_level_percentile)
# Apply Marching Cubes
verts, faces, _, _ = measure.marching_cubes(scalar_field, level=iso_level)
# Scale and translate vertices back to original coordinate system
verts = verts * voxel_size + mins
# Create mesh
mesh = o3d.geometry.TriangleMesh()
mesh.vertices = o3d.utility.Vector3dVector(verts)
mesh.triangles = o3d.utility.Vector3iVector(faces)
# Compute vertex normals
mesh.compute_vertex_normals()
# Save the result
#o3d.io.write_triangle_mesh(out_file, mesh)
pcd2 = o3d.geometry.PointCloud()
pcd2.points = o3d.utility.Vector3dVector(np.asarray(mesh.vertices))
pcd2.estimate_normals()
if visualize == True:
# Visualize the result
o3d.visualization.draw_geometries([mesh], mesh_show_back_face=True)
o3d.visualization.draw_geometries([pcd2])
vertices = np.asarray(pcd2.points, dtype= np.float32)
return vertices
def graph_Points(points, colors):
import matplotlib.pyplot as plt
x = points[:,0]
y = points[:,1]
z = points[:,2]
# 3D Plot
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
# Scatter plot with colors
ax.scatter(x, y, z, c=colors)
# Set labels for axes
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# Display the plot
plt.show()
return
def Estimate_Normals(points, n = 25, threshold=75):
#pos3D, _, _, _, _ = splt.splat_unpacker_threshold(n, dataset, threshold)
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points.numpy())
pcd.estimate_normals()
pcd.normalize_normals()
normals = np.asarray(pcd.normals)
#scaleSize = normals.max()
#normalsBad = np.random.rand(normals.shape[0],normals.shape[1])
#normalsBad = normalsBad * scaleSize
#return normalsBad
return normals