Point Cloud Data Processing: Complete Guide
Project
Table of Contents
- Introduction
- Part 1: Point Cloud Fundamentals
- Part 2: Point Cloud Preprocessing
- Part 3: Complete Preprocessing Pipeline
- Troubleshooting
- Resources
Introduction
Point clouds are collections of 3D points (X, Y, Z coordinates) often with additional attributes like color, intensity, or normal vectors. They represent 3D structures captured by sensors like LiDAR, depth cameras, or photogrammetry. Point cloud processing is essential for robotics, autonomous vehicles, 3D reconstruction, medical imaging, and geospatial analysis.
What is a Point Cloud?
A point cloud is a set of data points in 3D space, where each point is defined by its X, Y, and Z coordinates. These points may also include additional attributes such as color, intensity, surface normals, or confidence scores. Point clouds are commonly used to represent the shape and appearance of real-world objects or environments, captured by sensors like LiDAR, depth cameras, or photogrammetry systems.
1
2
Point Cloud = { (x₁, y₁, z₁), (x₂, y₂, z₂), ..., (xₙ, yₙ, zₙ) }
with optional: color, intensity, normals, confidence
Applications of Point Clouds
Point clouds are used in a wide range of industries and research fields. Their ability to capture detailed 3D information makes them invaluable for tasks such as autonomous navigation, digital reconstruction, medical analysis, robotics, and mapping large-scale environments. The table below highlights some of the most common applications and the types of sensors used to generate point clouds.
| Application | Use Case | Sensor |
|---|---|---|
| Autonomous Vehicles | Object detection, road mapping | LiDAR |
| 3D Reconstruction | Building models from photos | Photogrammetry |
| Medical Imaging | CT/MRI surface modeling | Medical scanners |
| Robotics | SLAM, obstacle avoidance | RGB-D cameras |
| Geospatial | Terrain mapping, city models | Aerial LiDAR |
Part 1: Point Cloud Fundamentals
Point Cloud Formats
Point cloud data can be stored in a variety of file formats, each designed for different use cases and features. Choosing the right format is important for compatibility, efficiency, and preserving information such as color, intensity, or mesh structure. The table below summarizes the most common formats used in point cloud processing.
| Format | Extension | Use Case | Features |
|---|---|---|---|
| Point Cloud Data | .pcd |
Point cloud standard | Most flexible |
| Polygon File Format | .ply |
Mesh & point clouds | Widely supported |
| ASCII XYZ | .xyz |
Simple text-based | Easy to parse |
| LAS | .las |
LiDAR standard | Geospatial data |
| OBJ | .obj |
3D mesh format | Mesh support |
| E57 | .e57 |
3D imaging | Images + 3D |
Coordinate Systems
Coordinate systems define how the position of each point in a point cloud is described in 3D space. The choice of coordinate system depends on the sensor or application. For example, cameras and RGB-D sensors often use a forward-facing Z axis, LiDAR and vehicle systems typically use X as the forward direction, while geospatial applications align axes with geographic directions (e.g., North, East, Up). Understanding these differences is crucial for correctly interpreting, transforming, and integrating point cloud data from various sources.
Point clouds use different coordinate systems depending on the sensor:
1
2
3
4
5
6
Camera/RGB-D: LiDAR/Vehicle: Geospatial:
Z (forward) X (forward) North (Y)
| | |
+--X (right) +--Y (left) +--East (X)
/ / /
Y (down) Z (up) Up (Z)
Part 2: Point Cloud Preprocessing
Setup and Libraries
To process and analyze point cloud data efficiently, several Python libraries are commonly used. Open3D is a powerful library for 3D data processing and visualization. NumPy and pandas help with data manipulation, while Plotly enables interactive 3D visualizations. The following libraries are recommended for a typical point cloud workflow:
1
2
3
4
5
6
7
# Core libraries for point cloud processing
import numpy as np
import open3d as o3d
import pandas as pd
# Visualization
import plotly.graph_objects as go
1. Loading Point Clouds
This section demonstrates how to load point cloud data from different sources, such as files, NumPy arrays, or pandas DataFrames, using Open3D. It also shows how to inspect basic statistics of the loaded point cloud, which is an essential first step before processing or visualization.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Load from file
pcd = o3d.io.read_point_cloud("data.pcd")
# From numpy array
points = np.random.rand(10000, 3) # 10K random points
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points)
# From pandas DataFrame
df = pd.read_csv("points.csv")
points = df[['x', 'y', 'z']].values
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points)
# Get statistics
print(f"Points: {len(pcd.points)}")
print(f"Bounds: {pcd.get_axis_aligned_bounding_box()}")
print(f"Has color: {pcd.has_colors()}")
print(f"Has normals: {pcd.has_normals()}")
2. Visualization
This section shows how to visualize point cloud data using both Open3D and Plotly. Visualization is crucial for inspecting the quality, distribution, and attributes of your point cloud, and helps identify issues before further processing. The code demonstrates interactive 3D rendering and color mapping for enhanced analysis.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Open3D visualization
o3d.visualization.draw_geometries([pcd])
# Plotly 3D scatter plot
points = np.asarray(pcd.points)
fig = go.Figure(data=[go.Scatter3d(
x=points[:, 0], y=points[:, 1], z=points[:, 2],
mode='markers',
marker=dict(size=2, color=points[:, 2], showscale=True)
)])
fig.show()
# With colors
if pcd.has_colors():
colors = np.asarray(pcd.colors) * 255
fig = go.Figure(data=[go.Scatter3d(
x=points[:, 0], y=points[:, 1], z=points[:, 2],
mode='markers',
marker=dict(
size=2,
color=[f'rgb({c[0]},{c[1]},{c[2]})' for c in colors]
)
)])
fig.show()
3. Noise Removal
This section covers techniques for removing noise and outliers from point cloud data. Cleaning the data is essential for improving the accuracy of downstream processing. The code demonstrates two common methods: statistical outlier removal (based on neighbor distances) and radius-based outlier removal (based on local point density).
Statistical Outlier Removal:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Remove points that are statistical outliers
# Based on average distance to k nearest neighbors
pcd_clean, ind = pcd.remove_statistical_outlier(
nb_neighbors=20, # Number of neighbors
std_ratio=2.0 # Outlier threshold (std deviations)
)
print(f"Removed {len(pcd.points) - len(pcd_clean.points)} outliers")
# Visualize removed points
inliers = pcd.select_by_index(ind)
outliers = pcd.select_by_index(ind, invert=True)
outliers.paint_uniform_color([1, 0, 0]) # Red
inliers.paint_uniform_color([0, 0, 1]) # Blue
o3d.visualization.draw_geometries([inliers, outliers])
Radius-based Outlier Removal:
1
2
3
4
5
6
7
# Remove points with too few neighbors in radius
pcd_clean = pcd.remove_radius_outlier(
nb_points=10, # Minimum neighbors required
radius=0.05 # Search radius (meters)
)
print(f"Remaining points: {len(pcd_clean.points)}")
4. Downsampling
This section explains how to reduce the number of points in a point cloud while preserving its overall structure. Downsampling is important for speeding up processing and reducing memory usage. The code demonstrates three common methods: voxel downsampling (spatial averaging), uniform downsampling (regular subsampling), and random sampling.
Voxel Downsampling:
1
2
3
4
5
6
7
8
9
10
11
# Groups points into voxels and averages
pcd_down = pcd.voxel_down_sample(voxel_size=0.01) # 1cm voxels
print(f"Original: {len(pcd.points)} points")
print(f"Downsampled: {len(pcd_down.points)} points")
# Higher voxel size = more aggressive downsampling
sizes = [0.01, 0.02, 0.05, 0.1]
for size in sizes:
pcd_temp = pcd.voxel_down_sample(voxel_size=size)
print(f"Voxel size {size}: {len(pcd_temp.points)} points")
Uniform Downsampling:
1
2
3
4
# Keep every N-th point (deterministic)
pcd_down = pcd.uniform_down_sample(every_k_points=5)
# Useful for: Preserving regular patterns
Random Sampling:
1
2
3
4
5
6
7
# Randomly select N points
target_count = 50000
if len(pcd.points) > target_count:
indices = np.random.choice(len(pcd.points), target_count, replace=False)
pcd_sampled = pcd.select_by_index(indices)
else:
pcd_sampled = pcd
5. Point Cloud Registration (Alignment)
This section explains how to align two or more point clouds into a common coordinate system, a process known as registration. Registration is essential for combining scans from different viewpoints or sensors. The code demonstrates two main approaches: ICP (Iterative Closest Point) for fine alignment when clouds are already roughly aligned, and feature-based registration (FPFH) for handling larger misalignments.
Understanding Registration:
Registration finds optimal rotation (R) and translation (t):
\[P_{aligned} = R \cdot P_{source} + t\]This code demonstrates the mathematical foundation of point cloud registration. It shows how the registration process computes the optimal rotation (R) and translation (t) needed to align a source point cloud to a target. The transformation is applied so that the source points best match the target points in 3D space. The code also visualizes the result by coloring the target cloud and displaying both clouds together for inspection.
1
2
3
t = reg_p2p.transformation[:3, 3]
target.paint_uniform_color([0, 0.7, 1]) # Blue
o3d.visualization.draw_geometries([source, target])
ICP (Iterative Closest Point):
The ICP (Iterative Closest Point) algorithm is used to refine the alignment between two point clouds by minimizing the distance between corresponding points. The code below loads two point clouds, optionally applies a rough initial transformation, and then uses ICP to compute the optimal rotation and translation. The results include alignment quality metrics and a visualization of the registered clouds.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# Load source and target
source = o3d.io.read_point_cloud("source.pcd")
target = o3d.io.read_point_cloud("target.pcd")
# Optional: rough pre-alignment with known transformation
# 4x4 homogeneous transformation matrix [R|t; 0|1]
init_transform = np.array([
[1, 0, 0, 0.5], # Rotation (identity) + X translation (0.5m)
[0, 1, 0, 0.0],
[0, 0, 1, 0.3], # Z translation (0.3m)
[0, 0, 0, 1]
])
source.transform(init_transform)
# ICP registration
reg_p2p = o3d.pipelines.registration.registration_icp(
source, target,
max_correspondence_distance=0.02, # 2cm max correspondence
init=np.eye(4), # Identity as starting point
estimation_method=o3d.pipelines.registration.TransformationEstimationPointToPoint(),
criteria=o3d.pipelines.registration.ICPConvergenceCriteria(
relative_fitness=1e-6,
relative_rmse=1e-6,
max_iteration=2000
)
)
# Results
print(f"Fitness: {reg_p2p.fitness:.6f}") # 0-1, higher is better
print(f"RMSE: {reg_p2p.inlier_rmse:.6f}") # Lower is better
print(f"Transformation:\n{reg_p2p.transformation}")
# Extract R and t
R = reg_p2p.transformation[:3, :3]
t = reg_p2p.transformation[:3, 3]
print(f"Rotation:\n{R}")
print(f"Translation: {t}")
# Apply transformation
source.transform(reg_p2p.transformation)
# Visualize result
source.paint_uniform_color([1, 0.7, 0]) # Orange
target.paint_uniform_color([0, 0.7, 1]) # Blue
o3d.visualization.draw_geometries([source, target])
Feature-based Registration (FPFH):
Feature-based registration uses local geometric descriptors (FPFH: Fast Point Feature Histogram) to match and align point clouds that are not already roughly aligned. The code below prepares the point clouds by downsampling and computing FPFH features, then uses RANSAC for initial alignment and refines the result with ICP for higher accuracy.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def prepare_dataset(pcd, voxel_size):
"""Prepare cloud for feature-based registration"""
# Downsample
pcd_down = pcd.voxel_down_sample(voxel_size)
# Estimate normals (required for FPFH)
pcd_down.estimate_normals(
search_param=o3d.geometry.KDTreeSearchParamHybrid(
radius=voxel_size*15,
max_nn=30
)
)
# Compute FPFH features
# FPFH = Fast Point Feature Histogram
# Descriptor for local surface properties
pcd_fpfh = o3d.pipelines.registration.compute_fpfh_feature(
pcd_down,
o3d.geometry.KDTreeSearchParamHybrid(
radius=voxel_size*5,
max_nn=100
)
)
return pcd_down, pcd_fpfh
# Prepare both clouds
voxel_size = 0.05
source_down, source_fpfh = prepare_dataset(source, voxel_size)
target_down, target_fpfh = prepare_dataset(target, voxel_size)
# Feature-based registration with RANSAC
reg_ransac = o3d.pipelines.registration.registration_ransac_based_on_feature_matching(
source_down, target_down,
source_fpfh, target_fpfh,
mutual_filter=True,
max_correspondence_distance=voxel_size*1.5,
estimation_method=o3d.pipelines.registration.TransformationEstimationPointToPoint(False),
ransac_n=4,
checklist_size=4,
max_validation=500
)
print(f"RANSAC fitness: {reg_ransac.fitness:.6f}")
print(f"Transformation:\n{reg_ransac.transformation}")
# Refine with ICP
reg_p2p = o3d.pipelines.registration.registration_icp(
source_down, target_down,
max_correspondence_distance=voxel_size*0.4,
init=reg_ransac.transformation
)
print(f"ICP fitness: {reg_p2p.fitness:.6f}")
source_down.transform(reg_p2p.transformation)
6. Normal Estimation
This section demonstrates how to estimate surface normal vectors for each point in a point cloud. Surface normals are essential for understanding local geometry, enabling tasks like surface reconstruction, segmentation, and feature extraction. The code shows how to estimate normals using both k-nearest neighbors and radius-based searches, orient normals consistently, and visualize them as arrows for inspection.
Computes surface normal vectors (perpendicular to surface)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# Estimate normals with K-nearest neighbors
pcd.estimate_normals(
search_param=o3d.geometry.KDTreeSearchParamKNN(knn=10)
)
# Or with radius search
pcd.estimate_normals(
search_param=o3d.geometry.KDTreeSearchParamHybrid(
radius=0.1,
max_nn=30
)
)
# Orient normals consistently (towards camera)
pcd.orient_normals_towards_camera_location(
camera_loc=np.array([0., 0., 0.])
)
# Visualize normals
def visualize_normals(pcd, level=20):
"""Visualize normals with arrows"""
normals = np.asarray(pcd.normals)
points = np.asarray(pcd.points)
# Sample every level-th point
indices = np.arange(0, len(points), level)
lines = []
for idx in indices:
end_point = points[idx] + normals[idx] * 0.1
lines.append([points[idx], end_point])
line_set = o3d.geometry.LineSet(
points=o3d.utility.Vector3dVector(np.vstack(lines)),
lines=o3d.utility.Vector2iVector(np.arange(len(lines)*2).reshape(-1, 2))
)
pcd_colored = o3d.geometry.PointCloud(pcd)
pcd_colored.paint_uniform_color([0.5, 0.5, 0.5])
o3d.visualization.draw_geometries([pcd_colored, line_set])
visualize_normals(pcd)
7. Coordinate Normalization
This section describes how to normalize the coordinates of a point cloud for machine learning and analysis. The code recenters the cloud at the origin, scales it to fit within a unit sphere, and optionally aligns it with its principal axes using PCA. These steps standardize the data, making it easier to compare, visualize, and use in downstream algorithms.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# Get points as numpy
points = np.asarray(pcd.points)
# Step 1: Center at origin
center = points.mean(axis=0)
points_centered = points - center
# Step 2: Scale to unit sphere
distances = np.linalg.norm(points_centered, axis=1)
max_distance = distances.max()
points_normalized = points_centered / max_distance
print(f"Center: {center}")
print(f"Max distance: {max_distance}")
print(f"Normalized bounds: [{points_normalized.min():.3f}, {points_normalized.max():.3f}]")
# Step 3: PCA alignment (align with principal axes)
def align_with_pca(points):
"""Rotate so largest variance is along X"""
cov = np.cov(points.T)
eigenvalues, eigenvectors = np.linalg.eigh(cov)
# Sort by eigenvalue (largest first)
idx = np.argsort(eigenvalues)[::-1]
eigenvectors = eigenvectors[:, idx]
# Rotation matrix
points_aligned = points @ eigenvectors
return points_aligned, eigenvectors
points_aligned, R = align_with_pca(points_normalized)
# Update point cloud
pcd.points = o3d.utility.Vector3dVector(points_aligned)
8. Segmentation
This section demonstrates how to segment a point cloud into meaningful parts using two common methods: plane detection with RANSAC and density-based clustering (DBSCAN). Plane detection isolates dominant flat surfaces, while DBSCAN groups points into clusters based on spatial density. These techniques are essential for separating objects, extracting features, and preparing data for further analysis.
Plane Detection (RANSAC):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Find dominant plane
plane_model, inliers = pcd.segment_plane(
distance_threshold=0.01, # 1cm tolerance
ransac_n=3, # Points per iteration
num_iterations=1000
)
# plane_model = [a, b, c, d] for plane equation ax + by + cz + d = 0
print(f"Plane equation: {plane_model[0]:.3f}x + {plane_model[1]:.3f}y + {plane_model[2]:.3f}z + {plane_model[3]:.3f} = 0")
print(f"Points on plane: {len(inliers)}")
# Separate plane from rest
plane_cloud = pcd.select_by_index(inliers)
remaining_cloud = pcd.select_by_index(inliers, invert=True)
plane_cloud.paint_uniform_color([1, 0, 0]) # Red
remaining_cloud.paint_uniform_color([0, 0, 1]) # Blue
o3d.visualization.draw_geometries([plane_cloud, remaining_cloud])
DBSCAN Clustering:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Density-based clustering
labels = np.array(pcd.cluster_dbscan(
eps=0.05, # Neighborhood radius
min_points=10 # Minimum points per cluster
))
max_label = labels.max()
print(f"Clusters: {max_label + 1}")
print(f"Noise points: {(labels == -1).sum()}")
# Visualize clusters
colors = plt.cm.Spectral(labels / (max_label + 1))
pcd.colors = o3d.utility.Vector3dVector(colors[:, :3])
o3d.visualization.draw_geometries([pcd])
9. Feature Extraction
This section explains how to extract local geometric features from each point in a point cloud. The provided function computes descriptors such as linearity, planarity, sphericity, curvature, verticality, and density for every point using its neighborhood. These features are useful for tasks like segmentation, classification, and object recognition in 3D data.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def extract_geometric_features(pcd, radius=0.1):
"""Extract local geometric features for each point"""
tree = o3d.geometry.KDTreeFlann(pcd)
points = np.asarray(pcd.points)
normals = np.asarray(pcd.normals)
features = []
for i, point in enumerate(points):
# Find neighbors
k, idx, _ = tree.search_radius_vector_3d(point, radius)
if k < 4: # Need at least 4 points for PCA
features.append([0, 0, 0, 0, 0, 0])
continue
# Local point cloud
local_points = points[idx] - point
# PCA
cov = np.cov(local_points.T)
eigenvalues = np.linalg.eigvalsh(cov)
eigenvalues = np.sort(eigenvalues)[::-1]
# Geometric descriptors
e1, e2, e3 = eigenvalues
linearity = (e1 - e2) / (e1 + 1e-10) # Rod-like
planarity = (e2 - e3) / (e1 + 1e-10) # Plane-like
sphericity = e3 / (e1 + 1e-10) # Ball-like
# Curvature
curvature = e3 / (e1 + e2 + e3 + 1e-10)
# Verticality (angle with vertical Z-axis)
normal = normals[i]
verticality = np.abs(normal[2])
# Density
density = k / (4/3 * np.pi * radius**3)
features.append([
linearity, planarity, sphericity,
curvature, verticality, density
])
return np.array(features)
features = extract_geometric_features(pcd, radius=0.1)
print(f"Features shape: {features.shape}")
print(f"Sample features:\n{features[:5]}")
Part 3: Complete Preprocessing Pipeline
This section provides a complete, automated pipeline for preprocessing point cloud data. The code defines a function that loads a point cloud, removes outliers, downsamples the data, estimates normals, normalizes coordinates, orients normals, and saves the processed result. This workflow ensures your point cloud is clean, normalized, and ready for downstream analysis or machine learning tasks.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import open3d as o3d
import numpy as np
def preprocess_point_cloud_complete(
input_file,
output_file,
voxel_size=0.01,
outlier_nb_neighbors=20,
outlier_std_ratio=2.0,
radius_outlier_nb_points=10,
radius_outlier_radius=0.05
):
"""
Complete preprocessing pipeline for point clouds
Steps:
1. Load point cloud
2. Remove statistical outliers
3. Remove radius outliers
4. Downsample with voxelization
5. Estimate normals
6. Center and normalize
7. Orient normals
8. Save result
"""
print("Loading point cloud...")
pcd = o3d.io.read_point_cloud(input_file)
initial_count = len(pcd.points)
print(f"Initial points: {initial_count}")
# Step 1: Statistical outlier removal
print("\nRemoving statistical outliers...")
pcd, _ = pcd.remove_statistical_outlier(
nb_neighbors=outlier_nb_neighbors,
std_ratio=outlier_std_ratio
)
print(f"After statistical removal: {len(pcd.points)}")
# Step 2: Radius outlier removal
print("Removing radius outliers...")
pcd = pcd.remove_radius_outlier(
nb_points=radius_outlier_nb_points,
radius=radius_outlier_radius
)
print(f"After radius removal: {len(pcd.points)}")
# Step 3: Downsample
print("Downsampling with voxelization...")
pcd = pcd.voxel_down_sample(voxel_size=voxel_size)
print(f"After downsampling: {len(pcd.points)}")
# Step 4: Estimate normals
print("Estimating normals...")
pcd.estimate_normals(
search_param=o3d.geometry.KDTreeSearchParamHybrid(
radius=voxel_size*15,
max_nn=30
)
)
# Step 5: Normalize coordinates
print("Normalizing coordinates...")
points = np.asarray(pcd.points)
center = points.mean(axis=0)
points_centered = points - center
max_dist = np.linalg.norm(points_centered, axis=1).max()
points_normalized = points_centered / max_dist
pcd.points = o3d.utility.Vector3dVector(points_normalized)
# Step 6: Orient normals
print("Orienting normals...")
pcd.orient_normals_towards_camera_location(np.array([0., 0., 0.]))
# Save
print(f"\nSaving to {output_file}...")
o3d.io.write_point_cloud(output_file, pcd)
print(f"\nProcessing complete!")
print(f"Reduction: {initial_count} → {len(pcd.points)} points ({100*len(pcd.points)/initial_count:.1f}%)")
return pcd
# Usage
pcd_processed = preprocess_point_cloud_complete(
"input.pcd",
"output.pcd",
voxel_size=0.01
)
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| ICP not converging | Bad initial alignment | Use FPFH for initial alignment |
| Too many outliers removed | Threshold too strict | Increase std_ratio or radius |
| Slow processing | Too many points | Increase voxel size or downsample |
| Poor normals | Insufficient neighbors | Increase knn or radius |
| Memory errors | Large point cloud | Process in chunks or downsample |
Resources
3D Point Cloud Libraries & Documentation
Benchmark Datasets & Data Sources
- Semantic3D – Large-scale outdoor point cloud benchmark
- ScanNet – Indoor 3D scene dataset
- ShapeNet – Large-scale 3D shape dataset
Additional Resources
Project
Control Point Deformation Network (Computer Vision, Medical Project): Please find related post under the Computer Vision group
Project repository
GitHub Code: PPG_3Dpoints