Code Monkey home page Code Monkey logo

point-cloud-utils's Introduction

Point Cloud Utils Logo Point Cloud Utils Teaser

Point Cloud Utils is an easy-to-use Python library for processing and manipulating 3D point clouds and meshes.


build workflow

Author: Francis Williams

If Point Cloud Utils contributes to an academic publication, cite it as:

@misc{point-cloud-utils,
  title = {Point Cloud Utils},
  author = {Francis Williams},
  note = {https://www.github.com/fwilliams/point-cloud-utils},
  year = {2022}
}

Point Cloud Utils (pcu) is a utility library providing the following functionality for 3D processing point clouds and triangle meshes. See the Examples section for documentation on how to use these:

  • Utility functions for reading and writing many common mesh formats (PLY, STL, OFF, OBJ, 3DS, VRML 2.0, X3D, COLLADA). If it can be imported into MeshLab, we can read it!
  • A series of algorithms for generating point samples on meshes:
  • Utilities for downsampling point clouds:
    • To satisfy a blue noise distribution
    • On a voxel grid
  • Closest points between a point cloud and a mesh
  • Normal estimation from point clouds and triangle meshes
  • Fast k-nearest-neighbor search between point clouds (based on nanoflann).
  • Hausdorff distances between point-clouds.
  • Chamfer distances between point-clouds.
  • Approximate Wasserstein distances between point-clouds using the Sinkhorn method.
  • Compute signed distances between a point cloud and a mesh using Fast Winding Numbers
  • Compute closest points on a mesh to a point cloud
  • Deduplicating point clouds and mesh vertices
  • Fast ray/mesh intersection using embree
  • Fast ray/surfel intersection using embree
  • Mesh smoothing
  • Mesh connected components
  • Mesh decimation
  • Removing duplicate/unreferenced vertices in point clouds and meshes
  • Making a mesh watertight (based on the Watertight Manifold algorithm)

Installation

pip install point-cloud-utils

Examples

List of examples

Loading meshes and point clouds

Point-Cloud-Utils supports reading many common mesh formats (PLY, STL, OFF, OBJ, 3DS, VRML 2.0, X3D, COLLADA). If it can be imported into MeshLab, we can read it! The type of file is inferred from its file extension.

If you only need a few attributes of a point cloud or mesh, the quickest way to load a mesh is using one of the read_mesh_* utility functions

import point_cloud_utils as pcu

# Load vertices and faces for a mesh
v, f = pcu.load_mesh_vf("path/to/mesh")

# Load vertices and per-vertex normals
v, n = pcu.load_mesh_vn("path/to/mesh")

# Load vertices, per-vertex normals, and per-vertex-colors
v, n, c = pcu.load_mesh_vnc("path/to/mesh")

# Load vertices, faces, and per-vertex normals
v, f, n = pcu.load_mesh_vfn("path/to/mesh")

# Load vertices, faces, per-vertex normals, and per-vertex colors
v, f, n, c = pcu.load_mesh_vfnc("path/to/mesh")

For meshes and point clouds with more complex attributes, use load_triangle_mesh which returns a TriangleMesh object.

import point_cloud_utils as pcu

# mesh is a lightweight TriangleMesh container object holding mesh vertices, faces, and their attributes.
# Any attributes which aren't loaded (because they aren't present in the file) are set to None.
# The data in TriangleMesh is layed out as follows (run help(pcu.TriangleMesh) for more details):
# TriangleMesh:
#   vertex_data:
#       positions: [V, 3]-shaped numpy array of per-vertex positions
#       normals: [V, 3]-shaped numpy array of per-vertex normals (or None)
#       texcoords: [V, 2]-shaped numpy array of per-vertex uv coordinates (or None)
#       tex_ids: [V,]-shaped numpy array of integer indices into TriangleMesh.textures indicating which texture to
#                use at this vertex (or None)
#       colors: [V, 4]-shaped numpy array of per-vertex RBGA colors in [0.0, 1.0] (or None)
#       radius: [V,]-shaped numpy array of per-vertex curvature radii (or None)
#       quality: [V,]-shaped numpy array of per-vertex quality measures (or None)
#       flags: [V,]-shaped numpy array of 32-bit integer flags per vertex (or None)
#   face_data:
#       vertex_ids: [F, 3]-shaped numpy array of integer face indices into TrianglMesh.vertex_data.positions
#       normals: [F, 3]-shaped numpy array of per-face normals (or None)
#       colors: [F, 4]-shaped numpy array of per-face RBGA colors in [0.0, 1.0] (or None)
#       quality: [F,]-shaped numpy array of per-face quality measures (or None)
#       flags: [F,]-shaped numpy array of 32-bit integer flags per face (or None)
#
#       wedge_colors: [F, 3, 4]-shaped numpy array of per-wedge RBGA colors in [0.0, 1.0] (or None)
#       wedge_normals: [F, 3, 3]-shaped numpy array of per-wedge normals (or None)
#       wedge_texcoords: [F, 3, 2]-shaped numpy array of per-wedge] uv coordinates (or None)
#       wedge_tex_ids: [F, 3]-shaped numpy array of integer indices into TriangleMesh.textures indicating which
#                      texture to use at this wedge (or None)
#   textures: A list of paths to texture image files for this mesh
#   normal_maps: A list of paths to texture image files for this mesh
mesh = pcu.load_triangle_mesh("path/to/mesh")

# You can also load a mesh directly using the TriangleMesh class
mesh = pcu.TriangleMesh("path/to/mesh")

For meshes and point clouds with more complex attributes, use save_triangle_mesh which accepts a whole host of named arguments which control the attributes to save.

import point_cloud_utils as pcu

# save_triangle_mesh accepts a path to save to (The type of mesh  saved is determined by the file extesion),
# an array of mesh vertices of shape [V, 3], and optional arguments specifying faces, per-mesh attributes,
# per-face attributes and per-wedge attributes:
#   filename    : Path to the mesh to save. The type of file will be determined from the file extension.
#   v           : [V, 3]-shaped numpy array of per-vertex positions
#   f           : [F, 3]-shaped numpy array of integer face indices into TrianglMesh.vertex_data.positions (or None)
#   vn          : [V, 3]-shaped numpy array of per-vertex normals (or None)
#   vt          : [V, 2]-shaped numpy array of per-vertex uv coordinates (or None)
#   vc          : [V, 4]-shaped numpy array of per-vertex RBGA colors in [0.0, 1.0] (or None)
#   vq          : [V,]-shaped numpy array of per-vertex quality measures (or None)
#   vr          : [V,]-shaped numpy array of per-vertex curvature radii (or None)
#   vti         : [V,]-shaped numpy array of integer indices into TriangleMesh.textures indicating which texture to
#                 use at this vertex (or None)
#   vflags      : [V,]-shaped numpy array of 32-bit integer flags per vertex (or None)
#   fn          : [F, 3]-shaped numpy array of per-face normals (or None)
#   fc          : [F, 4]-shaped numpy array of per-face RBGA colors in [0.0, 1.0] (or None)
#   fq          : [F,]-shaped numpy array of per-face quality measures (or None)
#   fflags      : [F,]-shaped numpy array of 32-bit integer flags per face (or None)
#   wc          : [F, 3, 4]-shaped numpy array of per-wedge RBGA colors in [0.0, 1.0] (or None)
#   wn          : [F, 3, 3]-shaped numpy array of per-wedge normals (or None)
#   wt          : [F, 3, 2]-shaped numpy array of per-wedge] uv coordinates (or None)
#   wti         : [F, 3]-shaped numpy array of integer indices into TriangleMesh.textures indicating which
#   textures    : A list of paths to texture image files for this mesh
#   normal_maps : A list of paths to texture image files for this mesh
pcu.save_triangle_mesh("path/to/mesh", v=v, f=f, vn=vertex_normals, vc=vertex_colors, fn=face_normals)

# You can also directly save a pcu.TrianglMesh object
mesh.save("path/to/mesh")

Saving meshes and point clouds

Point-Cloud-Utils supports writing many common mesh formats (PLY, STL, OFF, OBJ, 3DS, VRML 2.0, X3D, COLLADA). If it can be imported into MeshLab, we can read it! The type of file is inferred from its file extension.

If you only need to write few attributes of a point cloud or mesh, the quickest way to use the save_mesh_* functions

import point_cloud_utils as pcu

# Assume v, f, n, c are numpy arrays
# where
#   v are the mesh vertices of shape [V, 3]
#   f are the mesh face indices into v of shape [F, 3]
#   n are the mesh per-vertex normals of shape [V, 3]
#   c are the mesh per-vertex colors of shape [V, 4]
v, f, n, c = pcu.load_mesh_vfnc("input_mesh.ply")

# Save mesh vertices and faces
pcu.save_mesh_vf("path/to/mesh", v, f)

# Save mesh vertices and per-vertex normals
v, n = pcu.save_mesh_vn("path/to/mesh", v, n)

# Save mesh vertices, per-vertex normals, and per-vertex-colors
v, n, c = pcu.save_mesh_vnc("path/to/mesh", v, n, c)

# Save mesh vertices, faces, and per-vertex normals
v, f, n = pcu.save_mesh_vfn("path/to/mesh", v, f, n)

# Save vertices, faces, per-vertex normals, and per-vertex colors
v, f, n, c = pcu.save_mesh_vfnc("path/to/mesh", v, f, n, c)

Generating blue-noise samples on a mesh with Poisson-disk sampling

Generate 10000 samples on a mesh with poisson disk samples

import point_cloud_utils as pcu

# v is a nv by 3 NumPy array of vertices
# f is an nf by 3 NumPy array of face indexes into v
# n is a nv by 3 NumPy array of vertex normals
v, f, n = pcu.load_mesh_vfn("my_model.ply")

# Generate 10000 samples on a mesh with poisson disk samples
# f_i are the face indices of each sample and bc are barycentric coordinates of the sample within a face
f_i, bc = pcu.sample_mesh_poisson_disk(v, f, n, 10000)

# Use the face indices and barycentric coordinate to compute sample positions and normals
v_poisson = pcu.interpolate_barycentric_coords(f, f_i, bc, v)
n_poisson = pcu.interpolate_barycentric_coords(f, f_i, bc, n)

Generate blue noise samples on a mesh separated by approximately 0.01 times the bounding box diagonal

import point_cloud_utils as pcu
import numpy as np
# v is a nv by 3 NumPy array of vertices
# f is an nf by 3 NumPy array of face indexes into v
# n is a nv by 3 NumPy array of vertex normals
v, f, n = pcu.load_mesh_vfn("my_model.ply")


# Generate samples on a mesh with poisson disk samples seperated by approximately 0.01 times
# the length of the bounding box diagonal
bbox = np.max(v, axis=0) - np.min(v, axis=0)
bbox_diag = np.linalg.norm(bbox)

# f_i are the face indices of each sample and bc are barycentric coordinates of the sample within a face
f_i, bc = pcu.sample_mesh_poisson_disk(v, f, n, 10000)

# Use the face indices and barycentric coordinate to compute sample positions and normals
v_sampled = pcu.interpolate_barycentric_coords(f, f_i, bc, v)
n_sampled = pcu.interpolate_barycentric_coords(f, f_i, bc, n)

Generate random samples on a mesh

import point_cloud_utils as pcu
import numpy as np

# v is a nv by 3 NumPy array of vertices
# f is an nf by 3 NumPy array of face indexes into v
# n is a nv by 3 NumPy array of vertex normals
v, f, n = pcu.load_mesh_vfn("my_model.ply")

# Generate random samples on the mesh (v, f, n)
# f_i are the face indices of each sample and bc are barycentric coordinates of the sample within a face
f_i, bc = pcu.sample_mesh_random(v, f, num_samples=v.shape[0] * 40)

# Use the face indices and barycentric coordinate to compute sample positions and normals
v_sampled = pcu.interpolate_barycentric_coords(f, f_i, bc, v)
n_sampled = pcu.interpolate_barycentric_coords(f, f_i, bc, n)

Downsample a point cloud to have a blue noise distribution

import point_cloud_utils as pcu
import numpy as np

# v is a nv by 3 NumPy array of vertices
# n is a nv by 3 NumPy array of vertex normals
v, n = pcu.load_mesh_vn("my_model.ply")

# Downsample a point cloud so that all the points are separated by approximately a fixed value
# i.e. the downsampled points follow a blue noise distribution
# idx is an array of integer indices into v indicating which samples to keep
radius = 0.01  
idx = pcu.downsample_point_cloud_poisson_disk(v, radius)

# Use the indices to get the sample positions and normals
v_sampled = v[idx]
n_sampled = n[idx]

Downsample a point cloud on a voxel grid

Simple downsampling within the bounding box of a point cloud

import point_cloud_utils as pcu
import numpy as np

# v is a nv by 3 NumPy array of vertices
# n is a nv by 3 NumPy array of vertex normals
# c is a nv by 4 NumPy array of vertex colors
v, n, c = pcu.load_mesh_vnc("my_model.ply")

# We'll use a voxel grid with 128 voxels per axis
num_voxels_per_axis = 128

# Size of the axis aligned bounding box of the point cloud
bbox_size = v.max(0) - v.min(0)

# The size per-axis of a single voxel
sizeof_voxel = bbox_size / num_voxels_per_axis

# Downsample a point cloud on a voxel grid so there is at most one point per voxel.
# Any arguments after the points are treated as attribute arrays and get averaged within each voxel
v_sampled, n_sampled, c_sampled = pcu.downsample_point_cloud_on_voxel_grid(sizeof_voxel, v, n, c)

Specifying the location of the voxel grid in space (e.g. to only consider points wihtin a sub-region of the point cloud)

import point_cloud_utils as pcu
import numpy as np

# v is a nv by 3 NumPy array of vertices
# n is a nv by 3 NumPy array of vertex normals
# c is a nv by 4 NumPy array of vertex colors
v, n, c = pcu.load_mesh_vnc("my_model.ply")

# We'll use a voxel grid with 128 voxels per axis
num_voxels_per_axis = 128

# Size of the axis aligned bounding box of the point cloud
bbox_size = v.max(0) - v.min(0)

# Let's say we only want to consider points in the top right corner of the bounding box
domain_min = v.min(0) + bbox_size / 2.0
domain_max = v.min(0) + bbox_size

# The size per-axis of a single voxel
sizeof_voxel = bbox_size / num_voxels_per_axis

# Downsample a point cloud on a voxel grid so there is at most one point per voxel.
# Multiple points, normals, and colors within a voxel cell are averaged together.
# min_bound and max_bound specify a bounding box in which we will downsample points
v_sampled, n_sampled, c_sampled = pcu.downsample_point_cloud_voxel_grid(sizeof_voxel, v, n, c,
                                                                        min_bound=domain_min, max_bound=domain_max)

Discarding voxels with too few points

import point_cloud_utils as pcu
import numpy as np

# v is a nv by 3 NumPy array of vertices
# n is a nv by 3 NumPy array of vertex normals
# c is a nv by 4 NumPy array of vertex colors
v, n, c = pcu.load_mesh_vnc("my_model.ply")

# We'll use a voxel grid with 128 voxels per axis
num_voxels_per_axis = 128

# Size of the axis aligned bounding box of the point cloud
bbox_size = v.max(0) - v.min(0)

# The size per-axis of a single voxel
sizeof_voxel = bbox_size / num_voxels_per_axis

# We will throw away points within voxel cells containing fewer than 3 points
min_points_per_voxel = 3

# Downsample a point cloud on a voxel grid so there is at most one point per voxel.
# Multiple points, normals, and colors within a voxel cell are averaged together.
v_sampled, n_sampled, c_sampled = pcu.downsample_point_cloud_voxel_grid(sizeof_voxel, v, n, c,
                                                                        min_points_per_voxel=min_points_per_voxel)

Compute closest points on a mesh

import point_cloud_utils as pcu
import numpy as np

# v is a nv by 3 NumPy array of vertices
v, f = pcu.load_mesh_vf("my_model.ply")

# Generate 1000 random query points. We will find the closest point on the mesh for each of these
p = np.random.rand(1000, 3)

# For each query point, find the closest point on the mesh.
# Here:
#  - d is an array of closest distances for each query point with shape (1000,)
#  - fi is an array of closest face indices for each point with shape (1000,)
#  - bc is an array of barycentric coordinates within each face (shape (1000, 3)
#    of the closest point for each query point
d, fi, bc = pcu.closest_points_on_mesh(p, v, f)

# Convert barycentric coordinates to 3D positions
closest_points = pcu.interpolate_barycentric_coords(f, fi, bc, v)

Estimating normals from a point cloud

import point_cloud_utils as pcu

# v is a nv by 3 NumPy array of vertices
v = pcu.load_mesh_v("my_model.ply")

# Estimate a normal at each point (row of v) using its 16 nearest neighbors
n = pcu.estimate_point_cloud_normals_knn(v, 16)

# Estimate a normal at each point (row of v) using its neighbors within a 0.1-radius ball
n = pcu.estimate_point_cloud_normals_ball(v, 0.1)

Computing mesh normals per vertex

import point_cloud_utils as pcu

# v is a nv by 3 NumPy array of vertices
# f is an nf by 3 NumPy array of face indexes into v
v, f = pcu.load_mesh_vf("my_model.ply")

# Estimate per-vertex normal using the average of adjacent face normals
# n is a NumPy array of shape [nv, 3] where n[i] is the normal of vertex v[i]
n = pcu.estimate_mesh_vertex_normals(v, f)

Computing mesh normals per face

import point_cloud_utils as pcu

# v is a nv by 3 NumPy array of vertices
# f is an nf by 3 NumPy array of face indexes into v
v, f = pcu.load_mesh_vf("my_model.ply")

# Estimate per-face normal using the average of adjacent face normals
# n is a NumPy array of shape [nf, 3] where n[i] is the normal of face f[i]
n = pcu.estimate_mesh_face_normals(v, f)

Consistently orienting faces of a mesh

import point_cloud_utils as pcu

# v is a nv by 3 NumPy array of vertices
# f is an nf by 3 NumPy array of face indexes into v
v, f = pcu.load_mesh_vf("my_model.ply")

# Re-orient faces in a mesh so they are consistent within each connected component
# f_orient is a (nf, 3)-shaped array of re-oriented faces indexes into v
# f_comp_ids is a (nf,)-shaped array of component ids for each face
#    i.e. f_comp_ids[i] is the connected component id of face f[i] (and f_orient[i])
f_oriented, f_comp_ids = pcu.orient_mesh_faces(f)

Approximate Wasserstein (Sinkhorn) distance between two point clouds

import point_cloud_utils as pcu
import numpy as np

# a and b are arrays where each row contains a point
# Note that the point sets can have different sizes (e.g [100, 3], [111, 3])
a = np.random.rand(100, 3)
b = np.random.rand(100, 3)

# M is a 100x100 array where each entry  (i, j) is the L2 distance between point a[i, :] and b[j, :]
M = pcu.pairwise_distances(a, b)

# w_a and w_b are masses assigned to each point. In this case each point is weighted equally.
w_a = np.ones(a.shape[0])
w_b = np.ones(b.shape[0])

# P is the transport matrix between a and b, eps is a regularization parameter, smaller epsilons lead to
# better approximation of the true Wasserstein distance at the expense of slower convergence
P = pcu.sinkhorn(w_a, w_b, M, eps=1e-3)

# To get the distance as a number just compute the frobenius inner product <M, P>
sinkhorn_dist = (M*P).sum()

Chamfer distance between two point clouds

import point_cloud_utils as pcu
import numpy as np

# a and b are arrays where each row contains a point
# Note that the point sets can have different sizes (e.g [100, 3], [111, 3])
a = np.random.rand(100, 3)
b = np.random.rand(100, 3)

chamfer_dist = pcu.chamfer_distance(a, b)

Hausdorff distance between two point clouds

import point_cloud_utils as pcu
import numpy as np

# Generate two random point sets
a = np.random.rand(1000, 3)
b = np.random.rand(500, 3)

# Compute one-sided squared Hausdorff distances
hausdorff_a_to_b = pcu.one_sided_hausdorff_distance(a, b)
hausdorff_b_to_a = pcu.one_sided_hausdorff_distance(b, a)

# Take a max of the one sided squared  distances to get the two sided Hausdorff distance
hausdorff_dist = pcu.hausdorff_distance(a, b)

# Find the index pairs of the two points with maximum shortest distancce
hausdorff_b_to_a, idx_b, idx_a = pcu.one_sided_hausdorff_distance(b, a, return_index=True)
assert np.abs(np.sum((a[idx_a] - b[idx_b])**2) - hausdorff_b_to_a**2) < 1e-5, "These values should be almost equal"

# Find the index pairs of the two points with maximum shortest distancce
hausdorff_dist, idx_b, idx_a = pcu.hausdorff_distance(b, a, return_index=True)
assert np.abs(np.sum((a[idx_a] - b[idx_b])**2) - hausdorff_dist**2) < 1e-5, "These values should be almost equal"

K-nearest-neighbors between two point clouds

import point_cloud_utils as pcu
import numpy as np

# Generate two random point sets
pts_a = np.random.rand(1000, 3)
pts_b = np.random.rand(500, 3)

k = 10

# dists_a_to_b is of shape (pts_a.shape[0], k) and contains the (sorted) distances
# to the k nearest points in pts_b
# corrs_a_to_b is of shape (a.shape[0], k) and contains the index into pts_b of the
# k closest points for each point in pts_a
dists_a_to_b, corrs_a_to_b = pcu.k_nearest_neighbors(pts_a, pts_b, k)

Generating point samples in the square and cube with Lloyd relaxation

import point_cloud_utils as pcu

# v is a nv by 3 NumPy array of vertices
# f is an nf by 3 NumPy array of face indexes into v
v, f = pcu.load_mesh_vf("my_model.ply")

# Generate 1000 points on the mesh with Lloyd's algorithm
samples = pcu.sample_mesh_lloyd(v, f, 1000)

# Generate 100 points on the unit square with Lloyd's algorithm
samples_2d = pcu.lloyd_2d(100)

# Generate 100 points on the unit cube with Lloyd's algorithm
samples_3d = pcu.lloyd_3d(100)

Compute shortest signed distances to a triangle mesh with fast winding numbers

import point_cloud_utils as pcu
import numpy as np

# v is a nv by 3 NumPy array of vertices
# f is an nf by 3 NumPy array of face indexes into v
v, f = pcu.load_mesh_vf("my_model.ply")

# Generate 1000 points in the volume around the mesh. We'll compute the signed distance to the
# mesh at each of these points
pts = np.random.rand(1000, 3) * (v.max(0) - v.min(0)) + v.min(0)

# Compute the sdf, the index of the closest face in the mesh, and the barycentric coordinates of
# closest point on the mesh, for each point in pts
sdfs, face_ids, barycentric_coords = pcu.signed_distance_to_mesh(pts, v, f)

Deduplicating Point Clouds and Meshes

Point Clouds:

import point_cloud_utils as pcu

# p is a (n, 3)-shaped array of points (one per row)
# p is a (n, 3)-shaped array of normals at each point
p, n = pcu.load_mesh_vn("my_pcloud.ply")

# Treat any points closer than 1e-7 apart as the same point
# idx_i is an array of indices such that p_dedup = p[idx_i]
# idx_j is an array of indices such that p = p_dedup[idx_j]
p_dedup, idx_i, idx_j  = pcu.deduplicate_point_cloud(p, 1e-7)

# Use idx_i to deduplicate the normals
n_dedup = n[idx_i]

Meshes:

import point_cloud_utils as pcu
# v is a (nv, 3)-shaped NumPy array of vertices
# f is an (nf, 3)-shaped NumPy array of face indexes into v
# c is a (nv, 4)-shaped numpy array of per-vertex colors
v, f, c = pcu.load_mesh_vfc("my_model.ply")

# Treat any points closer than 1e-7 apart as the same point
# idx_i is an array of indices such that v_dedup = v[idx_i]
# idx_j is an array of indices such that v = v_dedup[idx_j]
v_dedup, f_dedup, idx_i, idx_j = pcu.deduplicate_mesh_vertices(v, f, 1e-7)

# Use idx_i to deduplicate the colors
c_dedup = c[idx_i]

Removing unreferenced mesh vertices

import point_cloud_utils as pcu
# v is a (nv, 3)-shaped NumPy array of vertices
# f is an (nf, 3)-shaped NumPy array of face indexes into v
# c is a (nv, 4)-shaped numpy array of per-vertex colors
v, f, c = pcu.load_mesh_vfc("my_model.ply")

# Treat any points closer than 1e-7 apart as the same point
# idx_v is an array of indices mapping each vertex in the output mesh to its index in the input
# idx_f is an array of indices mapping each face in the output mesh to its index in the input
v_clean, f_clean, idx_v, idx_f = pcu.remove_unreferenced_mesh_vertices(v, f)

c_clean = c[idx_v]

Calculating face areas of a mesh

import point_cloud_utils as pcu
# v is a (nv, 3)-shaped NumPy array of vertices
# f is an (nf, 3)-shaped NumPy array of face indexes into v
v, f = pcu.load_mesh_vf("my_model.ply")

# Compute areas of each face, face_areas[i] is the area of face f[i]
face_areas = pcu.mesh_face_areas

# Remove faces with small areas
f_new = f[face_areas < 1e-4]

Smoothing a Mesh

import point_cloud_utils as pcu

# v is a nv by 3 NumPy array of vertices
# f is an nf by 3 NumPy array of face indexes into v
v, f = pcu.load_mesh_vf("my_model.ply")

num_iters = 3  # Number of smoothing iterations
use_cotan_weights = True  # Whether to use cotangent weighted laplacian

# vsmooth contains the vertices of the smoothed mesh (the new mesh has the same face indices f)
vsmooth = pcu.laplacian_smooth_mesh(v, f, num_iters, use_cotan_weights=use_cotan_weights)

Computing connected components

import point_cloud_utils as pcu
import numpy as np

# v is a nv by 3 NumPy array of vertices
# f is an nf by 3 NumPy array of face indexes into v
v, f = pcu.load_mesh_vf("my_model.ply")

# cv is the index of the connected component of each vertex
# nv is the number of vertices per component
# cf is the index of the connected component of each face
# nf is the number of faces per connected component
cv, nv, cf, nf = pcu.connected_components(v, f)

# Extract mesh of connected component with most faces
comp_max = np.argmax(nf)
v_max, f_max, _, _ = pcu.remove_unreferenced_mesh_vertices(v, f[cf == comp_max])

Decimating a triangle mesh

import point_cloud_utils as pcu

v, f = pcu.load_mesh_vf("mymesh.ply")
target_num_faces = f.shape[0] // 10  # Downsample by a factor of 10

# v_decimate, f_decimate are the vertices/faces of the decimated mesh
# v_correspondence, f_correspondence are the vertices and faces in the dense mesh which generated each
# downsampled vertex/face
v_decimate, f_decimate, v_correspondence, f_correspondence = pcu.decimate_triangle_mesh(v, f, target_num_faces)
pcu.save_mesh_vf("decimated.ply", v_decimate, f_decimate)

Making a Mesh Watertight

import point_cloud_utils as pcu

# v is a nv by 3 NumPy array of vertices
# f is an nf by 3 NumPy array of face indexes into v
v, f = pcu.load_mesh_vf("my_model.ply")

# Optional resolution parameter (default is 20_000).
# See https://github.com/hjwdzh/Manifold for details
resolution = 20_000
v_watertight, f_watertight = pcu.make_mesh_watertight(v, f, resolution=resolution)

Ray/Mesh Intersection

import point_cloud_utils as pcu
import numpy as np

# v is a #v by 3 NumPy array of vertices
# f is an #f by 3 NumPy array of face indexes into v
# c is a #v by 4 array of vertex colors
v, f, c = pcu.load_mesh_vfc("my_model.ply")

# Generate rays on an image grid
uv = np.stack([a.ravel() for a in np.mgrid[-1:1:128j, -1.:1.:128j]], axis=-1)
ray_d = np.concatenate([uv, np.ones([uv.shape[0], 1])], axis=-1)
ray_d = ray_d / np.linalg.norm(ray_d, axis=-1, keepdims=True)
ray_o = np.array([[2.5, 0, -55.0] for _ in range(ray_d.shape[0])])

# Intersect rays with geometry
intersector = pcu.RayMeshIntersector(v, f)

# fid is the index of each face intersected (-1 for ray miss)
# bc are the barycentric coordinates of each intersected ray
# t are the distances from the ray origin to the intersection for each ray (inf for ray miss)
fid, bc, t = intersector.intersect_rays(ray_o, ray_d)

# Get intersection positions and colors by interpolating on the faces
hit_mask = np.isfinite(t)
hit_pos = pcu.interpolate_barycentric_coords(f, fid[hit_mask], bc[hit_mask], v)
hit_clr = pcu.interpolate_barycentric_coords(f, fid[hit_mask], bc[hit_mask], c)

Ray/Surfel Intersection

import point_cloud_utils as pcu
import numpy as np

# v is a #v by 3 NumPy array of vertices
# n is a #v by 3 NumPy array of vertex normals
v, n = pcu.load_mesh_vn("my_model.ply")

# Generate rays on an image grid
uv = np.stack([a.ravel() for a in np.mgrid[-1:1:128j, -1.:1.:128j]], axis=-1)
ray_d = np.concatenate([uv, np.ones([uv.shape[0], 1])], axis=-1)
ray_d = ray_d / np.linalg.norm(ray_d, axis=-1, keepdims=True)
ray_o = np.array([[2.5, 0, -55.0] for _ in range(ray_d.shape[0])])

# Intersect rays with surfels with fixed radius 0.55
intersector = pcu.RaySurfelIntersector(v, n, r=0.55)

# pid is the index of each point intersected by a ray
# t are the distances from the ray origin to the intersection for each ray (inf for ray miss)
pid, t = intersector.intersect_rays(ray_o, ray_d)

# Get points intersected by rays
hit_mask = pid >= 0
intersected_points = v[pid[hit_mask]]

Computing curvature on a mesh

import point_cloud_utils as pcu

# v is a #v by 3 NumPy array of vertices
# f is an #f by 3 NumPy array of face indexes into v
v, f = pcu.load_mesh_vfc("my_model.ply")

# Compute principal min/max curvature magnitudes (k1, k2) and directions (d1, d2)
# using the one ring of each vertex
k1, k2, d1, d2 = pcu.mesh_principal_curvatures(v, f)

# Compute principal min/max curvature magnitudes (k1, k2) and directions (d1, d2)
# using a radius. This method is much more robust but requires tuning the radius
k1, k2, d1, d2 = pcu.mesh_principal_curvatures(v, f, r=0.1)

# Compute Mean (kh) and Gaussian (kg) curvatures using the one ring of each vertex
kh, kg = pcu.mesh_mean_and_gaussian_curvatures(v, f)

# Compute Mean (kh) and Gaussian (kg) curvatures using using a radius.
# This method is much more robust but requires tuning the radius
kh, kg = pcu.mesh_mean_and_gaussian_curvatures(v, f, r=0.1)

Computing a consistent inside and outside for a triangle soup

import point_cloud_utils as pcu
import numpy as np

v, f = pcu.load_mesh_vf("my_model.ply")

# We're going to evaluate the inside/outside sign of 1000 points
p = np.random.rand(1000, 3)

# w has shape (1000,) where w[i] is the sign (positive for outside, negative for inside) of p[i]
w = pcu.triangle_soup_fast_winding_number(v, f, p.astype(v.dtype))

Voxelizing a triangle mesh

You can get a list of voxels which intersect a mesh as follows:

import point_cloud_utils as pcu

v, f = pcu.load_mesh_vf("mesh.ply")  # Load some mesh

voxel_size = 1.0 / 128  # size of each voxel
voxel_origin = [0., 0., 0.]  # Coordinate mapping to the bottom-left-back corner of the (0, 0, 0) voxel

# [num_vox, 3] array of integer coordinates for each voxel intersecting the mesh
ijk = pcu.voxelize_triangle_mesh(v, f, voxel_size, voxel_origin)

Flood filling a dense grid

If you have a 3D grid, you can flood fill it starting from a coordinate as follows:

import point_cloud_utils as pcu

# Grid of 0/1 values (but we also support floats/doubles/etc...)
grid = (np.random.rand([128, 128, 128]) > 0.5).astype(np.int32)

fill_value = 2  # Fill starting from [0, 0, 0] with the value 2

pcu.flood_fill_3d(grid, [0, 0, 0], fill_value)

Generating a mesh for a voxel grid

Suppose you an array ijk of integer voxel coordinates. You may wish to plot the associated voxel grid. You can do this via the voxel_grid_geometry function as follows

import point_cloud_utils as pcu

voxel_size = 1.0 / 200.0 # Size of each voxel
voxel_origin = [0.0, 0.0, 0.0]  # The position of the bottom-back-left corner of the (0, 0, 0) voxel

gap_fraction = 0.01  # Generate an optional small gap between voxels which can look nice -- this is a fraction of the voxel size

ijk = np.random.randint(-100, 100, size=(128, 3))  # Generate 128 random voxels in [-100, 100]^3

# vox_v, vox_f are vertices/faces of mesh for voxel grid
vox_v, vox_f = pcu.voxel_grid_geoemtry(ijk, v, voxel_size=voxel_size, voxel_origin=voxel_origin, gap_fraction=gap_fraction)

point-cloud-utils's People

Contributors

appledorem avatar clbarnes avatar davidcaron avatar eltociear avatar fwilliams avatar gattia avatar layer3 avatar maurock avatar mishasweetpie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

point-cloud-utils's Issues

Return barycentric coordinates and face indices in sample_mesh_lloyd

Dear developer,
Your poisson disk sampling and random sampling return f_idx and barycentric coordinate (bc), while the facial weight center coordinates could be further calculated to be v[f[f_i]] * bc[:, np.newaxis] [though np.newaxis doesn't work in the way I expected and I modified the part to be np.expand_dims(bc, axis=2)]. However, in your sample_mesh_lloyd method, the returned value is directly a matrix composed of the facial weight center coordinates.

I wonder if you could modify the sample_mesh_lloyd method a little bit so that it returns f_idx and bc too? That would add to much flexibility in identifying chosen faces and indexing corresponding normal vector coordinates.

Thanks!

ValueError: Invalid type (float64)

The following error is thrown from the sample_mesh_poisson_disk() method

image

This is without any modification to the example code, and occurs even with explicit typecasting of the arrays to either float64 or float32

error with pip install

Hi, thanks for your excellent work. But I have error when I use pip install, could you please help me ? The error is shown below:

[  2%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/LM7/libmeshb7.c.o
In file included from /tmp/pip-hhokn49i-build/external/numpyeigen/external/pybind11/include/pybind11/pytypes.h:12:0,
                 from /tmp/pip-hhokn49i-build/external/numpyeigen/external/pybind11/include/pybind11/cast.h:13,
                 from /tmp/pip-hhokn49i-build/external/numpyeigen/external/pybind11/include/pybind11/attr.h:13,
                 from /tmp/pip-hhokn49i-build/external/numpyeigen/external/pybind11/include/pybind11/pybind11.h:44,
                 from /tmp/pip-hhokn49i-build/external/numpyeigen/cmake/../src/npe_sparse_array.h:4,
                 from /tmp/pip-hhokn49i-build/external/numpyeigen/src/npe_typedefs.h:6,
                 from /tmp/pip-hhokn49i-build/external/numpyeigen/src/npe_typedefs.cpp:1:
/tmp/pip-hhokn49i-build/external/numpyeigen/external/pybind11/include/pybind11/detail/common.h:112:20: fatal error: Python.h: No such file or directory
compilation terminated.
CMakeFiles/npe.dir/build.make:62: recipe for target 'CMakeFiles/npe.dir/external/numpyeigen/src/npe_typedefs.cpp.o' failed
make[2]: *** [CMakeFiles/npe.dir/external/numpyeigen/src/npe_typedefs.cpp.o] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/npe.dir/all' failed
make[1]: *** [CMakeFiles/npe.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....


[ 20%] Built target geogram_third_party
Makefile:162: recipe for target 'all' failed
make: *** [all] Error 2
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/tmp/pip-hhokn49i-build/setup.py", line 113, in <module>
    main()
  File "/tmp/pip-hhokn49i-build/setup.py", line 108, in main
    test_suite="tests"
  File "/usr/lib/python3.5/distutils/core.py", line 148, in setup
    dist.run_commands()
  File "/usr/lib/python3.5/distutils/dist.py", line 955, in run_commands
    self.run_command(cmd)
  File "/usr/lib/python3.5/distutils/dist.py", line 974, in run_command
    cmd_obj.run()
  File "/usr/lib/python3/dist-packages/setuptools/command/install.py", line 61, in run
    return orig.install.run(self)
  File "/usr/lib/python3.5/distutils/command/install.py", line 583, in run
    self.run_command('build')
  File "/usr/lib/python3.5/distutils/cmd.py", line 313, in run_command
    self.distribution.run_command(command)
  File "/usr/lib/python3.5/distutils/dist.py", line 974, in run_command
    cmd_obj.run()
  File "/usr/lib/python3.5/distutils/command/build.py", line 135, in run
    self.run_command(cmd_name)
  File "/usr/lib/python3.5/distutils/cmd.py", line 313, in run_command
    self.distribution.run_command(command)
  File "/usr/lib/python3.5/distutils/dist.py", line 974, in run_command
    cmd_obj.run()
  File "/tmp/pip-hhokn49i-build/setup.py", line 39, in run
    self.build_extension(ext)
  File "/tmp/pip-hhokn49i-build/setup.py", line 68, in build_extension
    subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp)
  File "/usr/lib/python3.5/subprocess.py", line 581, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['cmake', '--build', '.', '--config', 'Release', '--', '-j2']' returned non-zero exit status 2

----------------------------------------

Command "/usr/bin/python3 -u -c "import setuptools, tokenize;file='/tmp/pip-hhokn49i-build/setup.py';exec(compile(getattr(tokenize, 'open', open)(file).read().replace('\r\n', '\n'), file, 'exec'))" install --record /tmp
/pip-sfteyg7z-record/install-record.txt --single-version-externally-managed --compile --user --prefix=" failed with error code 1 in /tmp/pip-hhokn49i-build/

Compatibility with macbook with m1

Hi,

I would like to know if this library is compatible with a MacBook pro with an M1 chip. I was trying to install it, and for now, I'm getting a lot of errors. Has anyone been able to install it on a MacBook?

Normalisation issues when extracting SDF values with ShapeNetCore V2

Hi, thank you for this awesome library! I have a question regarding the extraction of SDF values. From the example file you provide:

# Generate random points in the volume around the shape
# NOTE: ShapeNet shapes are normalized within [-0.5, 0.5]^3
p_vol = (np.random.rand(num_vol_pts, 3) - 0.5) * 1.1

I checked a few objects from the ShapeNetCore V2 dataset and noticed that they are not always bounded within [-0.5, 0.5] ^ 3. For example, the object 02942699/1ab3abb5c090d9b68e940c4e64a94e1e is within [0.115, 0.229, 0.56] and [-0.115, -0.171, -0.326]. Here's a picture of it:
image
It's hard to see, but the object slightly exceeds the upper face of the sampling volume. Also, it is not centred at 0.

Because papers that use SDF values for 3D reconstruction as DeepSDF suggest to normalise the object so that it fits a unit sphere, does it make sense to change the normalisation strategy to reflect that? Thank you.

Compilation error on windows

Compilation on windows stops with error

CMake Error at external/numpyeigen/cmake/numpyeigen.cmake:85 (warning):
    Unknown CMake command "warning".

read_obj: file not found

Thanks for your great work!

Warning: readOBJ() ignored non-comment line 155880:
  o mesh1.002_mesh1-geometry
Failed to cast FTC to matrix: min (0) != max (3)
Traceback (most recent call last):
  File "prepare_complete.py", line 49, in <module>
    pcd = sampling_from_mesh(mesh_path, pcd_path, num_points, sample_method)
  File "../sampling/sample.py", line 62, in sampling_from_mesh
    v, f, n = pcu.read_obj(mesh)
ValueError: File '/model.obj' not found.

but the model.obj does exists! what happened?

the principle of distance calculation of point cloud to mesh

I can't understand your algorithm that how to use the fast winding number to calculate the distance from the point cloud to the mesh, (I am not very familiar with the C++ , I can only understand the calculation process of calculating the winding number in Gavin's paper)

can you briefly describe the principle of distance calculation or recommand some paper to explain it.Thank you

TypeError: pairwise_distances() got an unexpected keyword argument 'squeeze'

After installing from Conda and copy-pasting the example for Chamfer distance, the following error is thrown.

TypeError: pairwise_distances() got an unexpected keyword argument 'squeeze'

Running on GPU enabled Windows 10 machine. Below is the full example from the main page:

import point_cloud_utils as pcu
import numpy as np

a = np.random.rand(100, 3)
b = np.random.rand(100, 3)

chamfer_dist = pcu.chamfer(a, b)

Voronoi Diagrams

We have almost all the internal API to do arbitrary dimensional voronoi diagrams as well as restricted voronoi diagrams. Need to expose a public API for this

Issues in example code 'Hausdorff distance between two point clouds'

Hello, thank you for your great work!
There is a typo in the README I think. In Hausdorff distance between two point clouds, we should **2 to hausdorf distance in all these assertions because we are calculating the square of the distance value:
assert np.abs(np.sum((a[idx_a] - b[idx_b])**2) - hausdorff_b_to_a**2) < 1e-5, "These values should be almost equal"

Thank you for your time.

pcu.write_ply() throws "Incompatible function arguments error"

I am trying to load a mesh in .obj format, perform Lloyd Relaxation and then save the point clouds to .ply format. I am writing the following code:

v,f,n = pcu.read_obj('./data/ChairNoArms9.obj')
samples = pcu.sample_mesh_lloyd(v, f, 15000)
pcu.write_ply(filename='sample.ply', v=samples, f=f, ascii=True)

Note: I have no normals in my mesh file

pairwise_distance returns the distance instead of the squared distance now?

Screenshot 2023-01-27 at 13 20 08
I used to work with pcu.pairwise_distance (v0.18.0, conda-forge) and it returned the squared distance as shown in the attached screenshot. Recently I download the package pcu 0.29.1 with pip, and it returns the distance directly. I didn't find any update docs regarding this change, and the demo is still showing the example code of the previous version.

shortest_distance_pairs problem

Dear sir,
I was trying to use pcu.shortest_distance_pairs to calcul the distance in Knn distance, but it shows me a problem that pcu has no attribute "shortest_distance_pairs"

Verison of pcu is 0.23.0 and it's used as python library.

Do you know how to solve it?

Thanks before

Succesful install, but functions not working properly

Hello,

I was able to succesfully install your library using the pip method on Ubuntu 18.04. I am able to import the module into python and I can run the test_examples.py script. However, when I look at the results, I see that the sampling functions do not work. Both pcu.sample_mesh_random() and pcu.sample_mesh_poisson_disk() just result in the original vertices and faces. There are no error or warning messages.

Screenshot from 2019-09-04 11-25-58

Invalid Mesh input error

what is reason for value error in python3.6 as
valueError: Invalid input mesh with zero elements: v and f must have shape (n, 3) and (m, 3) (n, m > 0). Got v.shape =(119844, 3), f.shape = (0, 0).

The PLY is opening good in other programs.

Fix Compilation on ARM

It seems like -m64 is an x86 tag. See (compilation error cc: error: unrecognized command line option ‘-m64' in #40 ). This appears to be breaking ARM builds.

compiling error on M1 Macbook pro

when pip install point_cloud_utils

got this error:
CMake Error: The source directory "/private/var/folders/_1/z9htdvys25dbgnl4nfxmvhf80000gn/T/pip-install-ihjh_3ei/point-cloud-utils_55486fa531a24a6886398db7d72c948d" does not appear to contain CMakeLists.txt.

Conda for osx-64?

Hey it would be nice to have conda module for osx-64.
When I tried to run pip, 2 errors were generated while building:
external/vcglib/eigenlib/Eigen/src/Householder/HouseholderSequence.h:409:31: error: no template named 'is_identity' in namespace 'Eigen::internal'; did you mean 'std::__identity'? applyThisOnTheLeft(res, internal::is_identity<OtherDerived>::value && res.rows()==res.cols()); ^~~~~~~~~~~~~~~~~~~~~ std::__identity /Library/Developer/CommandLineTools/usr/include/c++/v1/type_traits:403:8: note: 'std::__identity' declared here struct __identity { typedef _Tp type; };
/external/vcglib/eigenlib/Eigen/src/Geometry/Transform.h:915:6: error: use of undeclared identifier 'EIGEN_CONST_CONDITIONAL' if(EIGEN_CONST_CONDITIONAL(int(Mode)==int(Projective)))

Note that I have already have the the following dependencies which are required to install with pip:

  • A C++ compiler supporting C++14 or later
  • CMake 3.2 or later.
  • git

Would it be possible to make this code work natively for CUDA pytorch tensors?

I appreciate it might be difficult to program but I'm currently creating a loss function for my autoencoder by turning a pytorch tensor into numpy, calculating indicies of nearest neighbour pairs and then just indexing the tensor to get a differentiable loss function from it.

Would it be possible to make this algorithm natively compatible with pytorch cuda tensors to immediatly get a differentiable loss function between two point clouds?

How can i compare two ply files?

How to utilize your library to read two PLY files and compare the distance (distance matrix or similarity matrix). Thanks in advance.

Issues building on MacBook M1

I've tried pip install -vvv point-cloud-utils and building from source. Here's the error log:

[2022-05-20 14:06:51] ~/Documents/repos/point-cloud-utils $ cmake --version
cmake version 3.21.1

CMake suite maintained and supported by Kitware (kitware.com/cmake).

[2022-05-20 14:05:36] ~/Documents/repos/point-cloud-utils $ python setup.py build
running build
running build_py
creating build
creating build/lib.macosx-11.0-arm64-cpython-39
creating build/lib.macosx-11.0-arm64-cpython-39/point_cloud_utils
copying point_cloud_utils/_ray_mesh_intersector.py -> build/lib.macosx-11.0-arm64-cpython-39/point_cloud_utils
copying point_cloud_utils/_mesh_io.py -> build/lib.macosx-11.0-arm64-cpython-39/point_cloud_utils
copying point_cloud_utils/__init__.py -> build/lib.macosx-11.0-arm64-cpython-39/point_cloud_utils
copying point_cloud_utils/_octree.py -> build/lib.macosx-11.0-arm64-cpython-39/point_cloud_utils
copying point_cloud_utils/_pointcloud_normals.py -> build/lib.macosx-11.0-arm64-cpython-39/point_cloud_utils
copying point_cloud_utils/_ray_mesh.py -> build/lib.macosx-11.0-arm64-cpython-39/point_cloud_utils
copying point_cloud_utils/_sinkhorn.py -> build/lib.macosx-11.0-arm64-cpython-39/point_cloud_utils
running build_ext
-- The C compiler identification is AppleClang 12.0.5.12050022
-- The CXX compiler identification is AppleClang 12.0.5.12050022
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Library/Developer/CommandLineTools/usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Library/Developer/CommandLineTools/usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
CMake Deprecation Warning at CMakeLists.txt:4 (cmake_minimum_required):
  Compatibility with CMake < 2.8.12 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.


Cloning into 'numpyeigen'...
HEAD is now at 4916d92 update pybind11
-- Performing Test COMPILER_SUPPORT_OPENMP
-- Performing Test COMPILER_SUPPORT_OPENMP - Failed
CMake Warning (dev) at external/numpyeigen/cmake/numpyeigen.cmake:98 (set):
  Cannot set "NPE_ROOT_DIR": current scope has no parent.
Call Stack (most recent call first):
  CMakeLists.txt:21 (include)
This warning is for project developers.  Use -Wno-dev to suppress it.

CMake Deprecation Warning at CMakeLists.txt:4 (cmake_minimum_required):
  Compatibility with CMake < 2.8.12 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.


Cloning into 'eigen'...
HEAD is now at 21ae2afd4 bump to 3.3.7
CMake Deprecation Warning at CMakeLists.txt:4 (cmake_minimum_required):
  Compatibility with CMake < 2.8.12 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.


Cloning into 'pybind11'...
Switched to a new branch 'numpy_hacks_stable'
HEAD is now at aeda673 Hacks for numpyeigen
-- pybind11 v2.9.0 
-- Found PythonInterp: /opt/homebrew/Caskroom/miniforge/base/envs/rnb/bin/python (found version "3.9.12") 
-- Found PythonLibs: /opt/homebrew/Caskroom/miniforge/base/envs/rnb/lib/libpython3.9.dylib
-- Performing Test HAS_FLTO
-- Performing Test HAS_FLTO - Success
-- Performing Test HAS_FLTO_THIN
-- Performing Test HAS_FLTO_THIN - Success
CMake Deprecation Warning at CMakeLists.txt:4 (cmake_minimum_required):
  Compatibility with CMake < 2.8.12 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.


Cloning into 'manifold'...
HEAD is now at 81fd342 Update README.md
CMake Deprecation Warning at CMakeLists.txt:4 (cmake_minimum_required):
  Compatibility with CMake < 2.8.12 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.


Cloning into 'embree'...
HEAD is now at 69bd4c272 regenerated documentation
-- Found Git: /opt/homebrew/bin/git (found version "2.35.1") 
CMake Deprecation Warning at external/embree/CMakeLists.txt:64 (cmake_policy):
  The OLD behavior for policy CMP0072 will be removed from a future version
  of CMake.

  The cmake-policies(7) manual explains that the OLD behaviors of all
  policies are deprecated and that a policy should be set to OLD only under
  specific short-term circumstances.  Projects should be ported to the NEW
  behavior and not rely on setting a policy to OLD.


-- Detecting default ISA...
-- Detected default ISA: SSE2
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE  
CMake Deprecation Warning at external/geogram/CMakeLists.txt:9 (cmake_minimum_required):
  Compatibility with CMake < 2.8.12 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.


-- Using local options file: /Users/exing/Documents/repos/point-cloud-utils/external/geogram/CMakeOptions.txt
-- Configuring build for standalone Geogram (without Vorpaline)
-- Doxygen >= 1.7.0 not found, cannot generate documentation
CMake Deprecation Warning at external/geogram/doc/CMakeLists.txt:7 (cmake_minimum_required):
  Compatibility with CMake < 2.8.12 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.


-- Found OpenMP_C: -Xclang -fopenmp (found version "5.0") 
-- Found OpenMP_CXX: -Xclang -fopenmp (found version "5.0") 
-- Found OpenMP: TRUE (found version "5.0")  
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/exing/Documents/repos/point-cloud-utils/build/temp.macosx-11.0-arm64-cpython-39
[  1%] Building CXX object CMakeFiles/manifold.dir/external/manifold/src/BVH.cpp.o
[  1%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/LM7/libmeshb7.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
In file included from <built-in>:425:
<command line>:3:23: warning: missing terminating '"' character [-Winvalid-pp-token]
#define VERSION_INFO \"0.23.0\" -DWITH_OMP 
                      ^
<command line>:3:9: warning: 'VERSION_INFO' macro redefined [-Wmacro-redefined]
#define VERSION_INFO \"0.23.0\" -DWITH_OMP 
        ^
<command line>:1:9: note: previous definition is here
#define VERSION_INFO "0.23.0"
        ^
[  2%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/rply/rply.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
2 warnings generated.
[  2%] Building CXX object CMakeFiles/manifold.dir/external/manifold/src/Intersection.cpp.o
In file included from <built-in>:425:
<command line>:3:23: warning: missing terminating '"' character [-Winvalid-pp-token]
#define VERSION_INFO \"0.23.0\" -DWITH_OMP 
                      ^
<command line>:3:9: warning: 'VERSION_INFO' macro redefined [-Wmacro-redefined]
#define VERSION_INFO \"0.23.0\" -DWITH_OMP 
        ^
<command line>:1:9: note: previous definition is here
#define VERSION_INFO "0.23.0"
        ^
2 warnings generated.
[  2%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/adler32.c.o
[  3%] Building CXX object CMakeFiles/manifold.dir/external/manifold/src/main.cpp.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
In file included from <built-in>:425:
<command line>:3:23: warning: missing terminating '"' character [-Winvalid-pp-token]
#define VERSION_INFO \"0.23.0\" -DWITH_OMP 
                      ^
<command line>:3:9: warning: 'VERSION_INFO' macro redefined [-Wmacro-redefined]
#define VERSION_INFO \"0.23.0\" -DWITH_OMP 
        ^
<command line>:1:9: note: previous definition is here
#define VERSION_INFO "0.23.0"
        ^
[  3%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/compress.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
[  4%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/crc32.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
[  4%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/deflate.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
[  5%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/gzclose.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
[  5%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/gzlib.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
[  5%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/gzread.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
[  6%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/gzwrite.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
[  6%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/inffast.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
[  7%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/inflate.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
2 warnings generated.
[  7%] Building CXX object CMakeFiles/manifold.dir/external/manifold/src/Model_OBJ.cpp.o
In file included from <built-in>:425:
<command line>:3:23: warning: missing terminating '"' character [-Winvalid-pp-token]
#define VERSION_INFO \"0.23.0\" -DWITH_OMP 
                      ^
<command line>:3:9: warning: 'VERSION_INFO' macro redefined [-Wmacro-redefined]
#define VERSION_INFO \"0.23.0\" -DWITH_OMP 
        ^
<command line>:1:9: note: previous definition is here
#define VERSION_INFO "0.23.0"
        ^
[  7%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/inftrees.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
[  7%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/trees.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
[  8%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/uncompr.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
[  8%] Building C object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/zlib/zutil.c.o
clang: warning: argument unused during compilation: '-msse3' [-Wunused-command-line-argument]
[  8%] Building CXX object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/gzstream/gzstream.cpp.o
[  9%] Building CXX object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/PoissonRecon/Factor.cpp.o
[  9%] Building CXX object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/PoissonRecon/Geometry.cpp.o
[ 10%] Building CXX object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/PoissonRecon/MarchingCubes.cpp.o
[ 10%] Building CXX object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/PoissonRecon/poisson_geogram.cpp.o
2 warnings generated.
[ 10%] Linking CXX static library libmanifold.a
[ 10%] Built target manifold
[ 10%] Building CXX object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/HLBFGS/HLBFGS.cpp.o
[ 10%] Building CXX object CMakeFiles/npe.dir/external/numpyeigen/src/npe_typedefs.cpp.o
[ 11%] Linking CXX static library libnpe.a
[ 11%] Built target npe
[ 12%] Building CXX object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/HLBFGS/HLBFGS_BLAS.cpp.o
[ 12%] Building CXX object embree/common/sys/CMakeFiles/sys.dir/sysinfo.cpp.o
clang: warning: argument unused during compilation: '-msse2' [-Wunused-command-line-argument]
In file included from /Users/exing/Documents/repos/point-cloud-utils/external/embree/common/sys/sysinfo.cpp:4:
/Users/exing/Documents/repos/point-cloud-utils/external/embree/common/sys/sysinfo.h:63:2: error: Unknown ISA
#error Unknown ISA
 ^
In file included from /Users/exing/Documents/repos/point-cloud-utils/external/embree/common/sys/sysinfo.cpp:5:
In file included from /Users/exing/Documents/repos/point-cloud-utils/external/embree/common/sys/intrinsics.h:12:
In file included from /Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/immintrin.h:15:
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:33:5: error: use of undeclared identifier '__builtin_ia32_emms'; did you mean '__builtin_isless'?
    __builtin_ia32_emms();
    ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:33:5: note: '__builtin_isless' declared here
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:33:25: error: too few arguments to function call, expected 2, have 0
    __builtin_ia32_emms();
                        ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:50:19: error: use of undeclared identifier '__builtin_ia32_vec_init_v2si'
    return (__m64)__builtin_ia32_vec_init_v2si(__i, 0);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:67:12: error: use of undeclared identifier '__builtin_ia32_vec_ext_v2si'
    return __builtin_ia32_vec_ext_v2si((__v2si)__m, 0);
           ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:129:19: error: use of undeclared identifier '__builtin_ia32_packsswb'
    return (__m64)__builtin_ia32_packsswb((__v4hi)__m1, (__v4hi)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:159:19: error: use of undeclared identifier '__builtin_ia32_packssdw'
    return (__m64)__builtin_ia32_packssdw((__v2si)__m1, (__v2si)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:189:19: error: use of undeclared identifier '__builtin_ia32_packuswb'
    return (__m64)__builtin_ia32_packuswb((__v4hi)__m1, (__v4hi)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:216:19: error: use of undeclared identifier '__builtin_ia32_punpckhbw'
    return (__m64)__builtin_ia32_punpckhbw((__v8qi)__m1, (__v8qi)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:239:19: error: use of undeclared identifier '__builtin_ia32_punpckhwd'
    return (__m64)__builtin_ia32_punpckhwd((__v4hi)__m1, (__v4hi)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:260:19: error: use of undeclared identifier '__builtin_ia32_punpckhdq'
    return (__m64)__builtin_ia32_punpckhdq((__v2si)__m1, (__v2si)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:287:19: error: use of undeclared identifier '__builtin_ia32_punpcklbw'
    return (__m64)__builtin_ia32_punpcklbw((__v8qi)__m1, (__v8qi)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:310:19: error: use of undeclared identifier '__builtin_ia32_punpcklwd'
    return (__m64)__builtin_ia32_punpcklwd((__v4hi)__m1, (__v4hi)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:331:19: error: use of undeclared identifier '__builtin_ia32_punpckldq'
    return (__m64)__builtin_ia32_punpckldq((__v2si)__m1, (__v2si)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:352:19: error: use of undeclared identifier '__builtin_ia32_paddb'; did you mean '__builtin_arm_addg'?
    return (__m64)__builtin_ia32_paddb((__v8qi)__m1, (__v8qi)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:352:19: note: '__builtin_arm_addg' declared here
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:352:19: error: first argument of MTE builtin function must be a pointer ('__v8qi' (vector of 8 'char' values) invalid)
    return (__m64)__builtin_ia32_paddb((__v8qi)__m1, (__v8qi)__m2);
                  ^                    ~~~~~~~~~~~~
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:373:19: error: use of undeclared identifier '__builtin_ia32_paddw'; did you mean '__builtin_arm_addg'?
    return (__m64)__builtin_ia32_paddw((__v4hi)__m1, (__v4hi)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:352:19: note: '__builtin_arm_addg' declared here
    return (__m64)__builtin_ia32_paddb((__v8qi)__m1, (__v8qi)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:373:19: error: first argument of MTE builtin function must be a pointer ('__v4hi' (vector of 4 'short' values) invalid)
    return (__m64)__builtin_ia32_paddw((__v4hi)__m1, (__v4hi)__m2);
                  ^                    ~~~~~~~~~~~~
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:394:19: error: use of undeclared identifier '__builtin_ia32_paddd'; did you mean '__builtin_arm_addg'?
    return (__m64)__builtin_ia32_paddd((__v2si)__m1, (__v2si)__m2);
                  ^
/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include/mmintrin.h:352:19: note: '__builtin_arm_addg' declared here
    return (__m64)__builtin_ia32_paddb((__v8qi)__m1, (__v8qi)__m2);
                  ^
fatal error: too many errors emitted, stopping now [-ferror-limit=]
20 errors generated.
make[2]: *** [embree/common/sys/CMakeFiles/sys.dir/sysinfo.cpp.o] Error 1
make[1]: *** [embree/common/sys/CMakeFiles/sys.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
[ 12%] Building CXX object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/HLBFGS/ICFS.cpp.o
[ 12%] Building CXX object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/HLBFGS/LineSearch.cpp.o
[ 13%] Building CXX object geogram/src/lib/geogram/third_party/CMakeFiles/geogram_third_party.dir/HLBFGS/Lite_Sparse_Matrix.cpp.o
[ 13%] Linking CXX static library ../../../../../lib/libgeogram_third_party.a
[ 13%] Built target geogram_third_party
make: *** [all] Error 2
Traceback (most recent call last):
  File "/Users/exing/Documents/repos/point-cloud-utils/setup.py", line 116, in <module>
    main()
  File "/Users/exing/Documents/repos/point-cloud-utils/setup.py", line 89, in main
    setuptools.setup(
  File "/opt/homebrew/Caskroom/miniforge/base/envs/rnb/lib/python3.9/site-packages/setuptools/__init__.py", line 87, in setup
    return distutils.core.setup(**attrs)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/rnb/lib/python3.9/site-packages/setuptools/_distutils/core.py", line 148, in setup
    return run_commands(dist)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/rnb/lib/python3.9/site-packages/setuptools/_distutils/core.py", line 163, in run_commands
    dist.run_commands()
  File "/opt/homebrew/Caskroom/miniforge/base/envs/rnb/lib/python3.9/site-packages/setuptools/_distutils/dist.py", line 967, in run_commands
    self.run_command(cmd)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/rnb/lib/python3.9/site-packages/setuptools/dist.py", line 1229, in run_command
    super().run_command(command)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/rnb/lib/python3.9/site-packages/setuptools/_distutils/dist.py", line 986, in run_command
    cmd_obj.run()
  File "/opt/homebrew/Caskroom/miniforge/base/envs/rnb/lib/python3.9/site-packages/setuptools/_distutils/command/build.py", line 136, in run
    self.run_command(cmd_name)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/rnb/lib/python3.9/site-packages/setuptools/_distutils/cmd.py", line 313, in run_command
    self.distribution.run_command(command)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/rnb/lib/python3.9/site-packages/setuptools/dist.py", line 1229, in run_command
    super().run_command(command)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/rnb/lib/python3.9/site-packages/setuptools/_distutils/dist.py", line 986, in run_command
    cmd_obj.run()
  File "/Users/exing/Documents/repos/point-cloud-utils/setup.py", line 39, in run
    self.build_extension(ext)
  File "/Users/exing/Documents/repos/point-cloud-utils/setup.py", line 72, in build_extension
    subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/rnb/lib/python3.9/subprocess.py", line 373, in check_call
    raise CalledProcessError(retcode, cmd)

The wrong result of Ray/Mesh intersection

Hi,
I try to calculate the ray/mesh intersection using the sample code. The mesh I used for the sample is a Sphere. Here is the part code:
mesh = trimesh.primitives.Sphere()
ray_origins = np.array([[0, 0, -5],])
ray_directions = np.array([[0, 0, 1],])
intersector = pcu.RayMeshIntersector(np.array(mesh.vertices).astype(np.float32), np.array(mesh.faces))
fid, bc, t = intersector.intersect_rays(ray_origins.astype(np.float32), ray_directions.astype(np.float32))
The result should be [0, 0, -1] and [0, 0, 1], however I get the result as [ 5.9604645e-08 9.9999994e-01 -0.0000000e+00]. Could you give me some hints about the usage of the sample code?

Pip3 install point_cloud_utils Error

Thank you very much for your great work. However, I met some problems when I run pip3 install point_cloud_utils. We would be grateful if you could help us to find the problem!

Here is the output in the console:

Building wheel for point-cloud-utils (pyproject.toml) ... error
  ERROR: Command errored out with exit status 1:
   command: /usr/bin/python3 /home/orca/.local/lib/python3.6/site-packages/pip/_vendor/pep517/in_process/_in_process.py build_wheel /tmp/tmpqzzsiv3a
       cwd: /tmp/pip-install-9dj02x4j/point-cloud-utils_3880d41467d44289b8d9193e8f62e767
  Complete output (60 lines):
  running bdist_wheel
  running build
  running build_py
  creating build
  creating build/lib.linux-aarch64-3.6
  creating build/lib.linux-aarch64-3.6/point_cloud_utils
  copying point_cloud_utils/__init__.py -> build/lib.linux-aarch64-3.6/point_cloud_utils
  copying point_cloud_utils/_octree.py -> build/lib.linux-aarch64-3.6/point_cloud_utils
  copying point_cloud_utils/_mesh_io.py -> build/lib.linux-aarch64-3.6/point_cloud_utils
  copying point_cloud_utils/_ray_mesh_intersector.py -> build/lib.linux-aarch64-3.6/point_cloud_utils
  copying point_cloud_utils/_pointcloud_normals.py -> build/lib.linux-aarch64-3.6/point_cloud_utils
  copying point_cloud_utils/_sinkhorn.py -> build/lib.linux-aarch64-3.6/point_cloud_utils
  copying point_cloud_utils/_ray_mesh.py -> build/lib.linux-aarch64-3.6/point_cloud_utils
  running build_ext
  CMake Error: The source directory "/tmp/pip-install-9dj02x4j/point-cloud-utils_3880d41467d44289b8d9193e8f62e767" does not appear to contain CMakeLists.txt.
  Specify --help for usage, or press the help button on the CMake GUI.
  Traceback (most recent call last):
    File "/home/orca/.local/lib/python3.6/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 363, in <module>
      main()
    File "/home/orca/.local/lib/python3.6/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 345, in main
      json_out['return_val'] = hook(**hook_input['kwargs'])
    File "/home/orca/.local/lib/python3.6/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 262, in build_wheel
      metadata_directory)
    File "/usr/local/lib/python3.6/dist-packages/setuptools/build_meta.py", line 231, in build_wheel
      wheel_directory, config_settings)
    File "/usr/local/lib/python3.6/dist-packages/setuptools/build_meta.py", line 215, in _build_with_temp_dir
      self.run_setup()
    File "/usr/local/lib/python3.6/dist-packages/setuptools/build_meta.py", line 158, in run_setup
      exec(compile(code, __file__, 'exec'), locals())
    File "setup.py", line 116, in <module>
      main()
    File "setup.py", line 111, in main
      test_suite="tests"
    File "/usr/local/lib/python3.6/dist-packages/setuptools/__init__.py", line 153, in setup
      return distutils.core.setup(**attrs)
    File "/usr/lib/python3.6/distutils/core.py", line 148, in setup
      dist.run_commands()
    File "/usr/lib/python3.6/distutils/dist.py", line 955, in run_commands
      self.run_command(cmd)
    File "/usr/lib/python3.6/distutils/dist.py", line 974, in run_command
      cmd_obj.run()
    File "/tmp/pip-build-env-4fp4vm8t/overlay/lib/python3.6/site-packages/wheel/bdist_wheel.py", line 299, in run
      self.run_command('build')
    File "/usr/lib/python3.6/distutils/cmd.py", line 313, in run_command
      self.distribution.run_command(command)
    File "/usr/lib/python3.6/distutils/dist.py", line 974, in run_command
      cmd_obj.run()
    File "/usr/lib/python3.6/distutils/command/build.py", line 135, in run
      self.run_command(cmd_name)
    File "/usr/lib/python3.6/distutils/cmd.py", line 313, in run_command
      self.distribution.run_command(command)
    File "/usr/lib/python3.6/distutils/dist.py", line 974, in run_command
      cmd_obj.run()
    File "setup.py", line 39, in run
      self.build_extension(ext)
    File "setup.py", line 71, in build_extension
      subprocess.check_call(['cmake'] + cmake_args + [ext.sourcedir], cwd=self.build_temp, env=env)
    File "/usr/lib/python3.6/subprocess.py", line 311, in check_call
      raise CalledProcessError(retcode, cmd)
  subprocess.CalledProcessError: Command '['cmake', '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/tmp/pip-install-9dj02x4j/point-cloud-utils_3880d41467d44289b8d9193e8f62e767/build/lib.linux-aarch64-3.6/point_cloud_utils', '-DPYTHON_EXECUTABLE=/usr/bin/python3', '-DCMAKE_BUILD_TYPE=Release', '/tmp/pip-install-9dj02x4j/point-cloud-utils_3880d41467d44289b8d9193e8f62e767']' returned non-zero exit status 1.
  ----------------------------------------
  ERROR: Failed building wheel for point-cloud-utils
Failed to build point-cloud-utils
ERROR: Could not build wheels for point-cloud-utils, which is required to install pyproject.toml-based projects

Installation on M1 still failing on newer version

Hi Francis,

Thanks for your great work, love your library! I usually develop on Linux but wanted to try your lib on my M1 Macbook. To my surprise, it failed even on the latest release and a fresh virtual env. This is the error I am getting on a normal import:

Traceback (most recent call last):
  File "/tmp/test.py", line 2, in <module>
    import point_cloud_utils as pcu
  File "/tmp/demo/lib/python3.10/site-packages/point_cloud_utils/__init__.py", line 4, in <module>
    from ._pcu_internal import sample_mesh_poisson_disk, sample_mesh_random, \
ImportError: dlopen(/tmp/demo/lib/python3.10/site-packages/point_cloud_utils/_pcu_internal.cpython-310-darwin.so, 0x0002): tried: '/tmp/demo/lib/python3.10/site-packages/point_cloud_utils/_pcu_internal.cpython-310-darwin.so' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e')), '/usr/lib/_pcu_internal.cpython-310-darwin.so' (no such file)

For the record, a manual build of v0.29.2 works just fine, just not through pip. I've tried running pip install point-cloud-utils==0.29.2 but I get the same result.

Best,
Joey

New ply loader breaks save_mesh_*

After the introduction of the new ply loader, all pcu.save_mesh_*() and pcu.save_triangle_mesh() function fail with:

~/anaconda3/envs/kaolin/lib/python3.7/site-packages/point_cloud_utils/_mesh_io.py in save(self, filename, dtype)
    287                            {k: np.ascontiguousarray(v) if isinstance(v, np.ndarray) else v for (k, v) in self.vertex_data.custom_attributes.items()},
    288                            {k: np.ascontiguousarray(v) if isinstance(v, np.ndarray) else v for (k, v) in self.face_data.custom_attributes.items()},
--> 289                            self.textures, self.normal_maps, dtype, np.int32)
    290         self.vertex_data._set_empty_to_none()
    291         self.face_data._set_empty_to_none()

ValueError: Invalid  shape for argument 'w_texids' at dimension 1. Expected 1 but got 3.

To reproduce:
Version: 0.24.0
Installed using: pip install point-cloud-utils

import point_cloud_utils as pcu
import numpy as np
pcu.save_mesh_v("test.ply",v=np.random.rand(100,3))

Let me know if further information is needed,
Zan

Replace Mesh I/O functions with vcglib functions

These don't crash on bad inputs and can return many more fields.

Also the read API will change from returning many parameters to a single dict with whatever in your mesh file.

The write side API will not require passing empty arrays when they are not available.

Documentation for Monte-Carlo sampling

Dear developer,
From README I saw you listed Monte-Carlo sampling as one of the sampling methods provided, yet I can find no clue on how to use a related function, could you please enlighten me?
Thanks!

Is your library extendable for N-dimensional points?

It's actually a feature request!
I was trying to use distance metrics like Hausdorff or Chamfer on N-dimensional point sets and as I expected, an exception was thrown. Are these functions extendable for N-dimensional point sets? So that they can compute the distance between extracted features in deep networks like Pointnet?

Specify build-time dependencies in pyproject.toml

It looks like cmake, scipy, and numpy at least can be required by the PEP517 / PEP518 build system specification. This would allow someone installing a dependent library to install point-cloud-utils in a clean environment with one command (so long as they had a C++ compiler).

install successfully, but not find attributes

import point_cloud_utils as pcu
pcu.
pcu.class( pcu.eq( pcu.gt( pcu.loader pcu.package pcu.setattr(
pcu.delattr( pcu.file pcu.hash( pcu.lt( pcu.path pcu.sizeof(
pcu.dict pcu.format( pcu.init( pcu.name pcu.reduce( pcu.spec
pcu.dir( pcu.ge( pcu.init_subclass( pcu.ne( pcu.reduce_ex( pcu.str(
pcu.doc pcu.getattribute( pcu.le( pcu.new( pcu.repr( pcu.subclasshook(
v, f, n, _ = pcu.read_ply("model.ply")
Traceback (most recent call last):
File "", line 1, in
AttributeError: module 'point_cloud_utils' has no attribute 'read_ply'

I installed it by 'python3 setup.py install --user'.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.