Skip to content

Filter Point Cloud Using Plane Splitting

SUMMARY

Filter Point Cloud Using Plane Splitting keeps only the points on one side of a defined plane, based on the plane’s normal direction.

This Skill is useful in industrial, mobile, and humanoid robotics pipelines for segregating objects or regions in 3D space. For example, it can isolate items on one side of a conveyor belt in industrial settings, remove floor points for mobile robot perception, or separate reachable objects for humanoid manipulation. By splitting the point cloud along a plane, robots can focus on relevant regions and simplify downstream tasks like clustering, segmentation, and pose estimation.

Use this Skill when you want to retain only points on a specific side of a plane to support perception, alignment, or manipulation operations.

The Skill

python
from telekinesis import vitreous

filtered_point_cloud = vitreous.filter_point_cloud_using_plane_splitting(
    point_cloud=point_cloud,
    plane_coefficients=[0.0, 0.0, 1.0, -0.5],
    keep_positive_side=True,
)

API Reference

Performance Note

Current Data Limits: The system currently supports up to 1 million points per request (approximately 16MB of data). We're actively optimizing data transfer performance as part of our beta program, with improvements rolling out regularly to enhance processing speed.

Example

Raw Sensor Input

Unprocessed point cloud captured directly from the sensor. Shows full resolution, natural noise, and uneven sampling density.

Splitted Points

Filtered points on the negative side of the red plane.

Raw Sensor Input with Splitting Plane

Splitting Plane shown in red.

The Code

python
from telekinesis import vitreous
from datatypes import datatypes, io
import pathlib

# Optional for logging
from loguru import logger

DATA_DIR = pathlib.Path("path/to/telekinesis-data")

# Load point cloud
filepath = str(DATA_DIR / "point_clouds" / "mounts_3_raw.ply")
point_cloud = io.load_point_cloud(filepath=filepath)
logger.success(f"Loaded point cloud with {len(point_cloud.positions)} points")

# Execute operation
filtered_point_cloud = vitreous.filter_point_cloud_using_plane_splitting(
  point_cloud=point_cloud,
  plane_coefficients=[0, 0, 1, -547],
  keep_positive_side=True,
)
logger.success("Filtered points using plane splitting")

Running the Example

Runnable examples are available in the Telekinesis examples repository. Follow the README in that repository to set up the environment. Once set up, you can run this specific example with:

bash
cd telekinesis-examples
python examples/vitreous_examples.py --example filter_point_cloud_using_plane_splitting

The Explanation of the Code

The code begins by importing necessary modules for point cloud processing, data handling, and optional logging. Key modules here include vitreous, datatypes, io, pathlib, and loguru.

python
from telekinesis import vitreous
from datatypes import datatypes, io
import pathlib

# Optional for logging
from loguru import logger

A point cloud is then loaded from a .ply file, and logging is used to verify the number of points, ensuring the dataset is ready for processing.

python
DATA_DIR = pathlib.Path("path/to/telekinesis-data")

# Load point cloud
filepath = str(DATA_DIR / "point_clouds" / "mounts_3_raw.ply")
point_cloud = io.load_point_cloud(filepath=filepath)
logger.success(f"Loaded point cloud with {len(point_cloud.positions)} points")

The main operation uses the filter_point_cloud_using_plane_splitting Skill. A plane is defined using general plane coefficients, and the Skill retains only the points on the specified side of the plane. This is particularly useful in robotics pipelines for segmenting a workspace, isolating parts of interest, or removing background points from sensor data before downstream perception or manipulation tasks.

python
# Execute operation
filtered_point_cloud = vitreous.filter_point_cloud_using_plane_splitting(
  point_cloud=point_cloud,
  plane_coefficients=[0, 0, 1, -547],
  keep_positive_side=True,
)
logger.success("Filtered points using plane splitting")

How to Tune the Parameters

The filter_point_cloud_using_plane_splitting Skill has two parameters that control the splitting:

plane_coefficients (required):

  • The plane equation coefficients as [a, b, c, d] where ax + by + cz + d = 0
  • The vector [a, b, c] is the plane normal (should be normalized)
  • d is the distance from origin
  • The sign of (ax + by + cz + d) determines which side a point is on
  • Can be obtained from plane fitting algorithms (e.g., segment_point_cloud_using_plane)
  • Examples:
    • For a horizontal plane at z=0.5: [0, 0, 1, -0.5]
    • For a plane through origin with normal [1,0,0]: [1, 0, 0, 0]

keep_positive_side (required):

  • Whether to keep points on the positive side of the plane
  • True: Keeps points where (ax + by + cz + d) > 0 (points in the direction of the normal vector)
  • False: Keeps points where (ax + by + cz + d) < 0 (points opposite to the normal)
  • The choice depends on which half-space you want to retain

TIP

Best practice: Use segment_point_cloud_using_plane to detect planes and get plane coefficients. The sign of the normal vector determines which side is "positive", so test both True and False if unsure which side you want.

Where to Use the Skill in a Pipeline

Plane splitting is commonly used in the following pipelines:

  • Half-space extraction
  • Ground plane removal
  • Workspace segmentation
  • Object separation

A typical pipeline for ground plane removal looks as follows:

python
# Example pipeline using plane splitting (parameters omitted).

from telekinesis import vitreous

# 1. Load point cloud
point_cloud = vitreous.load_point_cloud(...)

# 2. Preprocess: remove outliers and downsample
filtered_cloud = vitreous.filter_point_cloud_using_statistical_outlier_removal(...)
downsampled_cloud = vitreous.filter_point_cloud_using_voxel_downsampling(...)

# 3. Segment ground plane to get plane coefficients
ground_plane = vitreous.segment_point_cloud_using_plane(
    point_cloud=downsampled_cloud,
    distance_threshold=0.01,
    keep_outliers=False,
)

# 4. Get plane coefficients (from plane segmentation result)
# plane_coefficients = [a, b, c, d]  # Extract from plane segmentation

# 5. Remove ground plane (keep points above the plane)
objects_above_ground = vitreous.filter_point_cloud_using_plane_splitting(
    point_cloud=downsampled_cloud,
    plane_coefficients=[0.0, 0.0, 1.0, -0.5],
    keep_positive_side=True,  # Keep points above the plane
)

# 6. Process the filtered point cloud
clusters = vitreous.cluster_point_cloud_using_dbscan(...)

Related skills to build such a pipeline:

  • segment_point_cloud_using_plane: detect planes and get plane coefficients
  • calculate_plane_normal: extract normal vector from plane coefficients
  • filter_point_cloud_using_statistical_outlier_removal: clean input before filtering
  • filter_point_cloud_using_plane_proximity: alternative for keeping points within a distance threshold
  • cluster_point_cloud_using_dbscan: process filtered point clouds

Alternative Skills

Skillvs. Filter Point Cloud Using Plane Splitting
filter_point_cloud_using_plane_proximityUse plane proximity when you want to keep points within a distance threshold on both sides. Use plane splitting when you want to keep only one side of the plane.
filter_point_cloud_using_plane_defined_by_point_normal_proximityUse plane proximity (point+normal) when you have a point and normal. Use plane splitting when you have plane coefficients and want to split by side.

When Not to Use the Skill

Do not use filter point cloud using plane splitting when:

  • You want to keep points within a distance threshold on both sides (use filter_point_cloud_using_plane_proximity instead)
  • The plane coefficients are invalid (ensure the normal vector [a, b, c] is normalized)
  • You need to detect planes (use segment_point_cloud_using_plane first to find planes)
  • You have a point and normal vector (use filter_point_cloud_using_plane_defined_by_point_normal_proximity instead, though it doesn't split by side)
  • You need to filter based on 3D box regions (use bounding box or passthrough filter instead)