Table of Contents

Overview
#

In point cloud processing, normal vectors represent the surface orientation at each point. They are perpendicular to the local surface and essential for many 3D processing tasks.

What are Normal Vectors?
#

A normal vector is perpendicular to a surface at a specific point:

        Normal (n)
    ───────●───────  Surface

Properties
#

  • Direction: Perpendicular to surface
  • Magnitude: Usually normalized (length 1)
  • Indicates: Surface slope and orientation

Why Point Clouds Need Normals
#

Point clouds contain only position data (x, y, z). No inherent surface information exists:

●  ●  ●  ●  ●
●  ●  ●  ●  ●   Raw point cloud
●  ●  ●  ●  ●   (no surface info)

Normals must be estimated from local point relationships.

Applications
#

1. Surface Reconstruction
#

Normals provide slope and direction for accurate mesh generation.

2. Object Recognition
#

Surface features help identify shapes and objects.

3. Segmentation
#

Detect boundaries where surface orientation changes significantly.

4. Collision Avoidance
#

Robotic path planning uses surface orientation.

5. Rendering
#

Normals determine how light reflects for realistic visualization.

Calculation Method: PCA
#

Principal Component Analysis (PCA) finds the normal vector from neighboring points.

Step 1: Find Neighbors
#

For point \(p\), find all points within radius \(r\):

$$ \mathcal{N}(p) = \\{p_i : \|p_i - p\| < r\\} $$

Or use k-nearest neighbors.

Step 2: Compute Centroid
#

$$ \bar{p} = \frac{1}{N}\sum_{i=1}^{N} p_i $$

Step 3: Build Covariance Matrix
#

$$ \Sigma = \frac{1}{N}\sum_{i=1}^{N} (p_i - \bar{p})(p_i - \bar{p})^T $$

This is a 3×3 symmetric matrix.

Step 4: Eigenvalue Decomposition
#

$$ \Sigma = Q\Lambda Q^T $$

Where:

  • \(Q\): Eigenvector matrix
  • \(\Lambda\): Eigenvalue matrix (diagonal)

Step 5: Extract Normal
#

The eigenvector corresponding to the smallest eigenvalue is the normal:

$$ \mathbf{n} = \mathbf{q}_{min} $$

Why Smallest Eigenvalue?
#

Eigenvalues represent variance along each principal axis:

λ₁ (largest):  Most spread → along surface
λ₂ (middle):   Medium spread → along surface
λ₃ (smallest): Least spread → perpendicular to surface
                              (this is the normal!)

Implementation
#

Python with Open3D
#

import open3d as o3d

# Load point cloud
pcd = o3d.io.read_point_cloud("cloud.ply")

# Estimate normals
pcd.estimate_normals(
    search_param=o3d.geometry.KDTreeSearchParamHybrid(
        radius=0.1,
        max_nn=30
    )
)

# Orient normals consistently
pcd.orient_normals_consistent_tangent_plane(k=10)

# Visualize
o3d.visualization.draw_geometries([pcd])

Parameters
#

ParameterEffect
RadiusLarger = smoother, less detail
k neighborsMore = stable, slower
OrientationConsistent facing direction

Normal Orientation Ambiguity
#

PCA gives direction, not sense (could point inward or outward):

     ↑ n         or        ↓ -n
     │                     │
─────●─────           ─────●─────

Solutions
#

  1. View-dependent: Point toward sensor
  2. Minimum spanning tree: Propagate consistent orientation
  3. Signed distance: Use reference surface

Quality Considerations
#

Dense vs Sparse
#

Point DensityNormal Quality
HighAccurate
MediumGood
LowNoisy

Noise Sensitivity
#

Noisy points → inaccurate normals

Solutions:

  • Larger neighborhood radius
  • Statistical outlier removal
  • Smoothing filter

Curvature Estimation
#

Eigenvalue ratios indicate surface curvature:

$$ \text{curvature} = \frac{\lambda_{min}}{\lambda_1 + \lambda_2 + \lambda_3} $$

Low curvature → flat surface High curvature → sharp edge or corner

Visualization
#

Normals typically shown as arrows:

    ↗  ↑  ↖
    ●  ●  ●
   ╱ ╲╱ ╲╱ ╲   Surface with normals
──●──●──●──●──