Skip to main content

A Python library for common tasks on 3D point clouds and meshes

Project description

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.

Documentation


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)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

point_cloud_utils-0.34.0.tar.gz (46.9 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

point_cloud_utils-0.34.0-cp313-cp313-win_amd64.whl (5.4 MB view details)

Uploaded CPython 3.13Windows x86-64

point_cloud_utils-0.34.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (9.8 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

point_cloud_utils-0.34.0-cp313-cp313-macosx_11_0_arm64.whl (6.9 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

point_cloud_utils-0.34.0-cp313-cp313-macosx_10_13_x86_64.whl (8.4 MB view details)

Uploaded CPython 3.13macOS 10.13+ x86-64

point_cloud_utils-0.34.0-cp312-cp312-win_amd64.whl (5.4 MB view details)

Uploaded CPython 3.12Windows x86-64

point_cloud_utils-0.34.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (9.8 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

point_cloud_utils-0.34.0-cp312-cp312-macosx_11_0_arm64.whl (6.9 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

point_cloud_utils-0.34.0-cp312-cp312-macosx_10_13_x86_64.whl (8.4 MB view details)

Uploaded CPython 3.12macOS 10.13+ x86-64

point_cloud_utils-0.34.0-cp311-cp311-win_amd64.whl (5.4 MB view details)

Uploaded CPython 3.11Windows x86-64

point_cloud_utils-0.34.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (9.8 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

point_cloud_utils-0.34.0-cp311-cp311-macosx_11_0_arm64.whl (6.9 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

point_cloud_utils-0.34.0-cp311-cp311-macosx_10_9_x86_64.whl (8.4 MB view details)

Uploaded CPython 3.11macOS 10.9+ x86-64

point_cloud_utils-0.34.0-cp310-cp310-win_amd64.whl (5.4 MB view details)

Uploaded CPython 3.10Windows x86-64

point_cloud_utils-0.34.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (9.8 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

point_cloud_utils-0.34.0-cp310-cp310-macosx_11_0_arm64.whl (6.9 MB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

point_cloud_utils-0.34.0-cp310-cp310-macosx_10_9_x86_64.whl (8.4 MB view details)

Uploaded CPython 3.10macOS 10.9+ x86-64

point_cloud_utils-0.34.0-cp39-cp39-win_amd64.whl (5.4 MB view details)

Uploaded CPython 3.9Windows x86-64

point_cloud_utils-0.34.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (9.8 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

point_cloud_utils-0.34.0-cp39-cp39-macosx_11_0_arm64.whl (6.9 MB view details)

Uploaded CPython 3.9macOS 11.0+ ARM64

point_cloud_utils-0.34.0-cp39-cp39-macosx_10_9_x86_64.whl (8.4 MB view details)

Uploaded CPython 3.9macOS 10.9+ x86-64

point_cloud_utils-0.34.0-cp38-cp38-win_amd64.whl (5.4 MB view details)

Uploaded CPython 3.8Windows x86-64

point_cloud_utils-0.34.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (9.8 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

point_cloud_utils-0.34.0-cp38-cp38-macosx_11_0_arm64.whl (8.4 MB view details)

Uploaded CPython 3.8macOS 11.0+ ARM64

point_cloud_utils-0.34.0-cp38-cp38-macosx_10_9_x86_64.whl (8.4 MB view details)

Uploaded CPython 3.8macOS 10.9+ x86-64

File details

Details for the file point_cloud_utils-0.34.0.tar.gz.

File metadata

  • Download URL: point_cloud_utils-0.34.0.tar.gz
  • Upload date:
  • Size: 46.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.9.22

File hashes

Hashes for point_cloud_utils-0.34.0.tar.gz
Algorithm Hash digest
SHA256 922f326f4c3058cb0b48b32105fb979edb048cb4b8e4f371e92c040365b130c5
MD5 477c2f46999c4de4025b26600016a0d2
BLAKE2b-256 117a0b72860fa195a2f506698201c7e0f11a7f74bc4e46d42257706af342c749

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp313-cp313-win_amd64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 f1793250cb61a6f016ab28980e0fe4ab05378897fec8de6f25b1d489dad5d57a
MD5 2cf17546d2b2b6b18afb6ca3c368f674
BLAKE2b-256 b0c978e3ba5559114cd3802f01f42f778e064d10153189c0d03d0fd1a54d15cc

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 b4e5edd7dfc3aba98527b7bc1229d92b0e4d8bb0e6794cc8eec1774ef5d1b785
MD5 7dfd4e27bd30789a120108e3117a8cbe
BLAKE2b-256 1ebd9c0ef0ff44c84270d0cf6eb0843720a39199cb197568e455219232870433

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 8b87b21a5add75f14e23809dc4e3517b23955990bf2d9b170fa6e9972ea2f156
MD5 c6c91f0f97477e94795aebd34aaf4ee4
BLAKE2b-256 e463836346a1e444cd5ec8ee4374645ba7d5a0ee5fda3180434cc55ecb6ed856

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp313-cp313-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp313-cp313-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 f953b6fe4c23715ba47029ff9d04d6b10d9cd323c8562b587fc22b875dc6680f
MD5 feaa7128a363cdb0013fadf9b178e3c2
BLAKE2b-256 a9ee4a4bf66edc7a1ea59572d48b255496b4c5ec9feef80b056c2e2227256b43

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 d49f1cb1f0dcb009888d464a9e96489ba655751a01d84f0cf3861b30ac140ab4
MD5 75f230257f37ef608fcc0f1dec2e54ff
BLAKE2b-256 54b9e822f343389bcb20c2dba7266294291cf17be1d9b99bf9984aff3a6d1d3b

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 58aca1db983397822046ec69c596d1d6315794af862d20320313857a192a9913
MD5 677fb0e0959af470906db61c516e6f83
BLAKE2b-256 26bc4869442f54ed3e99cc6fc10d8b1d677f41fc53a8802f7857d9005740b330

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 5dc33c1620f36f66b15dd713a796fe03c4252d15e67b611a4dc3c149bce2e9c1
MD5 e257f105a1b2755bb950de7458d354ec
BLAKE2b-256 2c575ec1265fdee6bc245231d5fabecb06b943a6147ecf3119bcf77e1c58e521

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp312-cp312-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp312-cp312-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 18ee54149bb08f127c41a933ddc4492fa486d6fa38d59a9344237ffdc835ccf6
MD5 2be92139804e38118364163767254cc9
BLAKE2b-256 81c1bb21f02c872f4becd76eea4544bf3fd4657c2bcf63b73bc08b3cee2b17e7

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 40469bc373765586e4f18940ccff17f9f90db53e9f5c4d238f2c6932d3ff2544
MD5 a69f632d1dfd70347fc9a4fd3622c7b9
BLAKE2b-256 17dab12b2c7b9d6b9611b33c6c9c2f93d74d1b225bcc88c55a7b8494a8162c69

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 a1ecef3ef1ce43a332d20da73b34d079a50ae0fd1b51f39b7eef509740f69cf2
MD5 e882242693018a2c6421a7fc154a6870
BLAKE2b-256 ad42f46fe08797d3f8cc6e4e91659d3ab83fe531b3eb4eecc9e44cd8756bb0fb

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 59cd4591183fd38cc032d43213f4be4fe85f305fa45aea0454db3bf9548df08d
MD5 de3fa711d20f212ca084fcbacb0345ec
BLAKE2b-256 43015e9b22c025bc556f16cc5ff7d6979e3ed8d04b2784abc19a3c84dd04227b

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp311-cp311-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp311-cp311-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 fea1590956fb9a89eadc1f0a924a583b61551ce067b943db1880f89d673d42a0
MD5 167c72da28df31dc7a1051e5c56bda51
BLAKE2b-256 f24e31147d926276c63c7bb1fef81ac92bff4a248c08c464716bcd2600de5dfe

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 bb1810ea1f6d2f1b0daa6cb09c3861fced5a2e65b52cbb429a123e1f825d95ed
MD5 baad3bcdaa32a2090d0aea920fd1ee43
BLAKE2b-256 e2e493306793cf1470633df27d2ff6747a1c7666e3894a62b05ae2915bea9395

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f33528708d1e48a9b74646019d45b63a5c9ac46497191d26c222f5654a3c68fa
MD5 f3e9045c3733635ec155033c0e289308
BLAKE2b-256 cb52f1da257f937c3b25e6323c02f5c238b74994697bf2260340ce2034dbc468

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b57b956831a52c3fafb2afe4da5168416f87b17e756f0f66b65dec726b1a33e3
MD5 129ec0adb810a1cf3d17afa70285dd51
BLAKE2b-256 b12c6a05e9fd9687bcdf01366e49981dae89780038a9da8db7a92a7a36a8ed8b

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp310-cp310-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp310-cp310-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 5fb14aef6a0e18b095ddf06df1a5de6cc4397ae27855b223fe03fea20e8d9358
MD5 cff89cbdbb983d833c914b944cd29062
BLAKE2b-256 42d8bb7a955d42e3f3bdb8f6b6953cf1b917821d2e70a75f9b9f561496a99d70

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp39-cp39-win_amd64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 d4b80f71e4a86315b8a482ab37e547959f73dc389490d964ca1b61f1eff17b88
MD5 512a29287fc48928450e3c70aded6848
BLAKE2b-256 599b94e5721f360e10993da0141befe28cdfaa00e75213f6030fb8316e897de8

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e3bf7feebcfcb75f053cde87fe89d205ad8b459729b7f2be3c3276ea5d193b00
MD5 de17b95dc8a79f2d56b22f1e8a804026
BLAKE2b-256 fd56e96bffe869b2c133c2edfa20d14facb3dc1454a5248cc079b987ee8484e4

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp39-cp39-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp39-cp39-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 86b8ccbc8238193bfa1fb3d01cee6f30a0263d41cc7fad6a30ecb2a035d81776
MD5 44fe3ee3f22eb344e02af46e8034030c
BLAKE2b-256 0f7d0b6a9344ac7d9194ab95d005a5362ce43dd210b5d3f4872e6e62015a37b5

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp39-cp39-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp39-cp39-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 e0763275d581afa2c0ea84820caa295d4098c36769fff587ecdbf3a35f43fb30
MD5 6b1c4e0249003892726ce14134b3b5de
BLAKE2b-256 6d2e2b974bfe8291aa036676684993f2db7386a4d4463999a4e3ea5e0396b1d2

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp38-cp38-win_amd64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 29d7444fdc2161d97589e5e30959392417da135ea2327e29ef4b8bba10f4cc5f
MD5 0bb9b7bff8c415c0316f43a1400e5c13
BLAKE2b-256 474b94ac4455694995260a84a0475b0cfc980d3e7bf7c95e93bd7eb175ebd783

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 ca824fdeee9e20b44add67e25ade9739e7afc60b8ae60679ee203a9fc8202818
MD5 3c1129631fffc9f7722154557043e69c
BLAKE2b-256 e0d1806b243123f01b67f0a143ca1d3bd796fad1c9cefee989ea2546910e1fdb

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp38-cp38-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp38-cp38-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 db53003d6e801f72a02e87a599d991db11758bb06f356621865cff4e6df26152
MD5 bb3f5fe23bab4751969650e55b163046
BLAKE2b-256 0dfb670545f0ca9e75c18e636a25e72fc7d30a8b7f207fd5979973df01483a5d

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.34.0-cp38-cp38-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.34.0-cp38-cp38-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 9c327e52dc0abdbaa5d92ad09e7faba9f4f9899a6b2d03d8a9711bd7bea826b8
MD5 061c32bc316368ccecb83601e52d3bbb
BLAKE2b-256 feff0b0b3055bdc53c5f513949fcafaade1563ac39c7fbacdfb62fef555621bf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page