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 by approximately 50% so that the sampled points approximately
# follow a blue noise distribution
# idx is an array of integer indices into v indicating which samples to keep
idx = pcu.downsample_point_cloud_poisson_disk(v, num_samples=int(0.5*v.shape[0]))

# 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 squared 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))

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.29.1.tar.gz (44.7 kB view details)

Uploaded Source

Built Distributions

point_cloud_utils-0.29.1-cp311-cp311-win_amd64.whl (4.6 MB view details)

Uploaded CPython 3.11 Windows x86-64

point_cloud_utils-0.29.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.6 MB view details)

Uploaded CPython 3.11 manylinux: glibc 2.17+ x86-64

point_cloud_utils-0.29.1-cp311-cp311-macosx_11_0_arm64.whl (6.5 MB view details)

Uploaded CPython 3.11 macOS 11.0+ ARM64

point_cloud_utils-0.29.1-cp311-cp311-macosx_10_9_x86_64.whl (6.5 MB view details)

Uploaded CPython 3.11 macOS 10.9+ x86-64

point_cloud_utils-0.29.1-cp311-cp311-macosx_10_9_universal2.whl (6.5 MB view details)

Uploaded CPython 3.11 macOS 10.9+ universal2 (ARM64, x86-64)

point_cloud_utils-0.29.1-cp310-cp310-win_amd64.whl (4.6 MB view details)

Uploaded CPython 3.10 Windows x86-64

point_cloud_utils-0.29.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.6 MB view details)

Uploaded CPython 3.10 manylinux: glibc 2.17+ x86-64

point_cloud_utils-0.29.1-cp310-cp310-macosx_11_0_arm64.whl (6.5 MB view details)

Uploaded CPython 3.10 macOS 11.0+ ARM64

point_cloud_utils-0.29.1-cp310-cp310-macosx_10_9_x86_64.whl (6.5 MB view details)

Uploaded CPython 3.10 macOS 10.9+ x86-64

point_cloud_utils-0.29.1-cp310-cp310-macosx_10_9_universal2.whl (6.5 MB view details)

Uploaded CPython 3.10 macOS 10.9+ universal2 (ARM64, x86-64)

point_cloud_utils-0.29.1-cp39-cp39-win_amd64.whl (4.6 MB view details)

Uploaded CPython 3.9 Windows x86-64

point_cloud_utils-0.29.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.6 MB view details)

Uploaded CPython 3.9 manylinux: glibc 2.17+ x86-64

point_cloud_utils-0.29.1-cp39-cp39-macosx_11_0_arm64.whl (6.5 MB view details)

Uploaded CPython 3.9 macOS 11.0+ ARM64

point_cloud_utils-0.29.1-cp39-cp39-macosx_10_9_x86_64.whl (6.5 MB view details)

Uploaded CPython 3.9 macOS 10.9+ x86-64

point_cloud_utils-0.29.1-cp39-cp39-macosx_10_9_universal2.whl (6.5 MB view details)

Uploaded CPython 3.9 macOS 10.9+ universal2 (ARM64, x86-64)

point_cloud_utils-0.29.1-cp38-cp38-win_amd64.whl (4.6 MB view details)

Uploaded CPython 3.8 Windows x86-64

point_cloud_utils-0.29.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.6 MB view details)

Uploaded CPython 3.8 manylinux: glibc 2.17+ x86-64

point_cloud_utils-0.29.1-cp38-cp38-macosx_11_0_arm64.whl (6.5 MB view details)

Uploaded CPython 3.8 macOS 11.0+ ARM64

point_cloud_utils-0.29.1-cp38-cp38-macosx_10_9_x86_64.whl (6.5 MB view details)

Uploaded CPython 3.8 macOS 10.9+ x86-64

point_cloud_utils-0.29.1-cp38-cp38-macosx_10_9_universal2.whl (6.5 MB view details)

Uploaded CPython 3.8 macOS 10.9+ universal2 (ARM64, x86-64)

point_cloud_utils-0.29.1-cp37-cp37m-win_amd64.whl (4.6 MB view details)

Uploaded CPython 3.7m Windows x86-64

point_cloud_utils-0.29.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.6 MB view details)

Uploaded CPython 3.7m manylinux: glibc 2.17+ x86-64

point_cloud_utils-0.29.1-cp37-cp37m-macosx_10_9_x86_64.whl (6.5 MB view details)

Uploaded CPython 3.7m macOS 10.9+ x86-64

point_cloud_utils-0.29.1-cp36-cp36m-win_amd64.whl (4.6 MB view details)

Uploaded CPython 3.6m Windows x86-64

point_cloud_utils-0.29.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.6 MB view details)

Uploaded CPython 3.6m manylinux: glibc 2.17+ x86-64

point_cloud_utils-0.29.1-cp36-cp36m-macosx_10_9_x86_64.whl (6.5 MB view details)

Uploaded CPython 3.6m macOS 10.9+ x86-64

File details

Details for the file point-cloud-utils-0.29.1.tar.gz.

File metadata

  • Download URL: point-cloud-utils-0.29.1.tar.gz
  • Upload date:
  • Size: 44.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.16

File hashes

Hashes for point-cloud-utils-0.29.1.tar.gz
Algorithm Hash digest
SHA256 71d968495e007143ef70e2b53464a3d806258638ebbc9c0b63d2fdd7226be919
MD5 f2abeb555d3f70bb00c9484e35406552
BLAKE2b-256 f21eced0c0e1ada8611fa080034dea8d803a7cd95019d1714c685b54c27ec393

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 6de5716d5d7c048c022687e7447ee9ebc8124dbf4adc1685e593c137f8db6782
MD5 4356414674540ab1f5073e14be90c38c
BLAKE2b-256 303c8d002bdfc26903bf60dd7d5033d3ffb753051258fe723fd761dbf37bac76

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 c1ad48b1f1316b93ad3cac7fb407e9632356980791f29aa9de2075d12822e0c2
MD5 b4590e68cafc771d40b8d2221ce6eb6e
BLAKE2b-256 07ede093a44b72f489a67fa99c270a8ec0d4e7838d158a263b5a0a84bca93183

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d5b2334a9a6e3b87668b047ea0a0f1ef8436d77d8b51f73b17bfb3090e4c316c
MD5 9e3a658ca255f869c39d80d17094ab54
BLAKE2b-256 11e6857f22145d578ff57f16efe7da0876b6cb3e360ddd4638264486324024d4

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp311-cp311-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 d30570c7e8e0a5326ee72d1803ffc22249d2a5666e086486652069483144c732
MD5 1588ec9b696f674d2ca57cfe81f3f171
BLAKE2b-256 11ef82e4ed847cd01c5955bbc9c2def2c0a7ff081ac8045d7a2ac57dfb29830f

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.29.1-cp311-cp311-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp311-cp311-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 40e8c246b56f134d3752af494396432f7d42df6824c10b2d1d6d0f7936c6e939
MD5 5a2779252b6436a0ea41037b60f867b8
BLAKE2b-256 f4bda45ef696d72b6b77f5adda8b23aed2c87e3e99afc84d62be1c4e6fcb55ea

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 4f86d2fa6d03180e39211ac2b539feb53842d3ba4edd4ea17ce69582209e05b6
MD5 bbdff8e8b293b2e0953759f13b7e50f9
BLAKE2b-256 e269c0d1f1eae1d7e425a6df310b37dd865dfd5ec97b3917fb8565102599cde4

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 9306711ea7425dd10c39662aa0378c4e32ba8416248eae038d88e1d131e63e9f
MD5 1144e69a510406a4aec185ebe80a0cd1
BLAKE2b-256 0861627dbba1ea766af10481ca41aa24136c32cd69257f90014791caad0864d7

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 40ab80b18123e569c88064daaf3258a9d847e534162eb0505f347a331a22f4e0
MD5 03e7f4c03907a964a749c6f88fc28f2d
BLAKE2b-256 91605c1a42b52bcc257b1a229ed1a8096fd0c7cf2b6da0c9486caa19c5db9b43

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp310-cp310-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 f33d1c60305e44adf7fba72b8ed3024fd9eb7c175720f5ddbc00a94ec7def478
MD5 7b28b47a8468ef2fefb6bebb39e4403d
BLAKE2b-256 d5fde6e54b251e3f9a6d0e274d42225fbe00f8059663870bdcb2958ae7f121e3

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.29.1-cp310-cp310-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp310-cp310-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 bb99df28eacaca2b7aaeeb7b395d7e016ee2578bc031fe945a9895ff2bf6a469
MD5 9281c20dfd18a7183743f61e5fe18dd5
BLAKE2b-256 4d9a2af1f51015b95479a140c3f37c1743bd040b7a448044f8623c1c0614ce8f

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 47853d6e107b6f1942875d40f764d8bc2291810de735bcadeabb230f42edd3ea
MD5 a35924400d06f4434f63cba8076bec46
BLAKE2b-256 ec5569fe5d8b71cfdc2ff0742e674365c033042fb6442a4cf31fb4669696281f

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5c9c1213456b72787c220c1246fd1150bdff354b6303e17225d3817af001f6c6
MD5 33f7bffcfd6a86ed56d88958d3497cf0
BLAKE2b-256 219bad45ae9ae8e1479c0ca49c4612f1a2da2bbe262c5ff43d035d99bd2add0e

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp39-cp39-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 edf384d70b96d4fe2ca0ad2d1ea634f8b38359d87ee9475040a04bbcb44ad88a
MD5 4a79f26b15158ea49e115dfca8b5bca9
BLAKE2b-256 9c4f4fa27da05a13575af6c3fa4c5fabf8e1cf6022c98422606a3582d90db562

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp39-cp39-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 6cdd8a6cce291328afabf878f8208b807afd1d0120b66abde4a81f5676f45a46
MD5 3340bc6bd83c368152783ab965d9820f
BLAKE2b-256 78715b7ad00e2cebb585b1bd36be736e99a502bc2668a4812fe60516019d6e8a

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.29.1-cp39-cp39-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp39-cp39-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 f941dfc7f3767c8a6e742924495e9afd7183b0c07b49a5497496d86d29149332
MD5 c3b2e6487c8986e1d419ae6b2b8efea5
BLAKE2b-256 d621b85f228a7a38df1209ab778a3a1cd7d345d669d6491a4ac232c244bf366d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 dda2904b5fa956e3ee4d2179f6f8cf3b9cfad91e7471701a128ad30043681dfc
MD5 0e27479733f5268f35cd996be979263a
BLAKE2b-256 c3cbd3f6365d28ac30873a268fada71b830a630846b9d391b299064bfeea2c86

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 cbfac0de705b8d4675e3142e3a191692e9af4cfe0861b937578952a6fc91fdd8
MD5 d348b9479d02682f41b1c57a4ce67073
BLAKE2b-256 3c00131ad767929121c122e56b179b49143fffa7ff267083dc863274b8d50eb2

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp38-cp38-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7b983ccf0e7fd63cae3d5a4c4b3137d41dd73dca5bc91096c4260d22b154a03f
MD5 3e2295848a1f464b900be4f0811f39ec
BLAKE2b-256 9c598241f5ea9811427bfcae465874e81e0bf0f70a08c27920644b92053052be

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp38-cp38-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 3aed46f5dc739a4337821572c31b8c99776da1cc65b7cab864f159508a2df727
MD5 dddcec3d4116c6de22b8d0fe28c3d02c
BLAKE2b-256 15bf3b9480837eec113530ecfbfff7eab2cdaee8d8e70e4659e428eda937702d

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.29.1-cp38-cp38-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp38-cp38-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 fd2836c6498d803ae6ca03090d1974d1a155d777deaee6c4d3fb6467e6eeb0b5
MD5 d6d6ad62baa0088b9ce8b55addffe7d0
BLAKE2b-256 5fa0be2762729b05a10735fb71bb78178b19bafef48ac5c7e28916a45a15a89e

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.29.1-cp37-cp37m-win_amd64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp37-cp37m-win_amd64.whl
Algorithm Hash digest
SHA256 12429b245c0aa93e360cfdbf4d7610717e0269c7c9376c18b45590577af0169b
MD5 82f2d4e4832e11506ff7fa19c16a9776
BLAKE2b-256 e4018ececa7202c3aa9fdffd3d28f14eb19d98216753f45c891d8d825fcb9fc8

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.29.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 79f23539212133fbc37e68f5e04171c69e32f9f043f9b07a6590177a7e0e5f41
MD5 ba1ee9f257f5a89c98736ffdaac00106
BLAKE2b-256 dcc616f5e98b93183081844234f902bd0ebc475d5e985c617d5283e40c278fcb

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.29.1-cp37-cp37m-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp37-cp37m-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 578c590386c95691da6aff013195e8b579309ac8cc164bc1589a5bea17d9edf2
MD5 b2763be97fd328e3231c38e0358b480d
BLAKE2b-256 0556dab49d553f62a920c189c659696527302cf6c87c9c15dd3a82d16a07f670

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.29.1-cp36-cp36m-win_amd64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp36-cp36m-win_amd64.whl
Algorithm Hash digest
SHA256 c49a77c12cec2846100e607a1f205113a07eaaaebe68fef02ae49d6766edb2f7
MD5 2e56e75bce3a0d21c7feb42c18759477
BLAKE2b-256 f2e9a54816895a7cfa5fc88fb79dc56d9ab7178b9e11e35adbde2e01e27ec553

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.29.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 9f244ca9b29c7797ca65c35674003e62d16ac89ae63b05872d874dfaa6938165
MD5 8495936ebdd97a68723ab5143dd092d4
BLAKE2b-256 e5a06a9e9934b0d811bffdd062bd136ac22f081dfd1f1ff7116a6ad24a69895c

See more details on using hashes here.

File details

Details for the file point_cloud_utils-0.29.1-cp36-cp36m-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for point_cloud_utils-0.29.1-cp36-cp36m-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 0198aab24e78698a85cd1146c5d6439b86e10fce5c5eb68f208da87ee75b66a4
MD5 42b37164c34af2b1b7f4f3855725ad66
BLAKE2b-256 fa3d7b16351ae6ab3b430108fd01947e2de15b75f3c19856a41ae8f0b5afc40b

See more details on using hashes here.

Supported by

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