Post

Point Cloud Data Processing: Complete Guide

Point Cloud Data Processing: Complete Guide

Project

Table of Contents

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

This post is licensed under CC BY 4.0 by the author.