Skip to content

Cluster Point Cloud Based on Density Jump

SUMMARY

Cluster Point Cloud Based on Density Jump splits a point cloud into exactly two regions at the strongest density discontinuity along a chosen axis.

It projects points onto projection_axis, estimates local point density along that projection, and cuts the cloud where density changes abruptly — often where an object's volume or thickness changes rapidly (e.g. a part's head vs. its shaft, or a filled region vs. empty space). Unlike cluster_point_cloud_using_dbscan, which can discover any number of dense clusters, this Skill always returns exactly two regions, split at one density jump.

Use this Skill when you want to split a point cloud in two at a clear density transition, for example separating stacked or partially-touching objects where distance-based clustering can't tell them apart.

The Skill

python
from telekinesis import vitreous

regions = vitreous.cluster_point_cloud_based_on_density_jump(
    point_cloud=point_cloud,
    projection_axis=[0.0, 0.0, 1.0],
    num_nearest_neighbors=12,
    neighborhood_radius=0.001,
    is_point_cloud_linear=False,
)
API Reference
Full parameter and return type documentation for cluster_point_cloud_based_on_density_jump.
View Reference →

Data Transfer Notice

There is no longer a fixed limit of 1 million points per request. However, very large datasets may result in slower data transfer and processing times. We are continuously optimizing performance as part of our beta program, with ongoing improvements to enhance speed and reliability.

Example

Raw Point Cloud Input

Calculated Clusters

Clusters found based on density change.
Parameters: projection_axis = np.array([0, 0, 1.0]), num_nearest_neighbors=5, neighborhood_radius=0.05.

The Code

python
"""
Demonstrates splitting a point cloud into regions based on density discontinuities.
"""

from loguru import logger
import rerun as rr

from telekinesis import vitreous, datatypes


def cluster_point_cloud_based_on_density_jump_example():
    """
    Splits a point cloud into regions based on density discontinuities.

    Detects and splits point clouds at locations where point density changes
    dramatically.
    """
    # ===================== Load Data ==========================================
    point_cloud_url = (
        "https://assets.telekinesis.ai/examples/v1/point_clouds/mug_preprocessed.ply"
    )
    point_cloud = datatypes.PointCloud.from_url(url=point_cloud_url, use_cache=True)

    # ===================== Run Skill ==========================================
    clusters = vitreous.cluster_point_cloud_based_on_density_jump(
        point_cloud=point_cloud,
        num_nearest_neighbors=5,
        neighborhood_radius=0.05,
        is_point_cloud_linear=False,
        projection_axis=[0.0, 0.0, 1.0],
    )

    # ===================== Log ================================================
    logger.success(f"Split {point_cloud} into density-based clusters")
    logger.success(f"Results: {clusters}")
    logger.info(f"Number of density-based clusters: {len(clusters)}")
    logger.info(f"Points per cluster: {[len(p) for p in clusters.positions]}")
    logger.info(f"First cluster is a PointCloud with {len(clusters[0])} points")

    # ===================== Visualization  (Optional) ===========================
    rr.init("cluster_point_cloud_based_on_density_jump_example", spawn=True)
    datatypes.visualize(point_cloud, entity_path="/1-input_point_cloud")
    datatypes.visualize(clusters, entity_path="/2-density_jump_clusters")


if __name__ == "__main__":
    cluster_point_cloud_based_on_density_jump_example()

Runnable examples are available in the Telekinesis examples repository.

Follow the README in that repository to set up the environment, run this specific example with:

bash
cd telekinesis-examples
python examples/point_cloud/cluster_point_cloud_based_on_density_jump.py

Parameter Configuration

ParameterTypeDefaultDescription
point_clouddatatypes.PointCloudrequiredThe point cloud to split. Must contain at least 2 points
projection_axisdatatypes.Vector3D | np.ndarray | list[float]requiredThe direction [x, y, z] to project points onto before analyzing density. Set to the point cloud's principal axis (see estimate_principal_axes) for the clearest density signal
num_nearest_neighborsdatatypes.Int | int12The number of nearest neighbors used to estimate density at each point. Must be > 0
neighborhood_radiusdatatypes.Float | float | int0.001The radius, in the point cloud's coordinate units, of the spherical neighborhood used for density estimation. Must be > 0
is_point_cloud_lineardatatypes.Bool | boolFalseWhether point_cloud is approximately one-dimensional (e.g. a rod, wire, or cable) rather than a 2D surface or 3D volume. Currently, setting this to True causes the request to fail server-side — only the default False is actually supported

Returns

TypeDescription
datatypes.PointCloudBatchA batch with exactly 2 entries, one per side of the density jump; either entry can be empty. Use len(...) (always 2), .positions for the list of each region's (N_i, 3) position array, or index ([0]/[1]) to get a single region as a datatypes.PointCloud.

Raises

ExceptionCondition
TypeErrorA parameter's value does not match its expected type (see the Parameter Configuration table above), or (for a list projection_axis) it contains a non-numeric element
ValueErrorprojection_axis does not have exactly 3 elements, num_nearest_neighbors is not > 0, or neighborhood_radius is not > 0
ConfigurationErrorThe TELEKINESIS_API_KEY environment variable is not set
SerializationErrorThe request input failed to serialize, the response was not returned as an Arrow stream, or the response failed to deserialize
RequestTimeoutErrorThe request to the Vitreous service timed out
TransportErrorA network failure occurred before a response was received
ClientErrorThe Vitreous service rejected the request due to invalid or malformed input (HTTP 400/422), an unrecognized endpoint (HTTP 404), or another unexpected 4xx response
AuthenticationErrorThe API key was rejected as invalid or expired (HTTP 401)
AuthenticationServiceErrorThe authentication service returned an invalid response, was temporarily unavailable, or timed out (HTTP 502/503/504)
ServerErrorThe Vitreous service returned a 5xx or otherwise unexpected error response

How to Tune the Parameters

projection_axis

  • Controls: The direction points are projected onto before density is analyzed.
  • Units: Unitless direction vector [x, y, z]
  • Default: none — required
  • Set to the point cloud's principal axis (see estimate_principal_axes) for the clearest density signal
  • Typical values: [0, 0, 1] for a vertical axis, [1, 0, 0] for horizontal along X

num_nearest_neighbors

  • Controls: How many nearest neighbors are used to estimate density at each point.
  • Units: Points (count)
  • Default: 12
  • Must be > 0
  • Increase → more stable, smoother density estimate, but blurs out small/sharp density changes and is slower
  • Decrease → more sensitive to local variation, but noisier
  • Typical range: 6–30

neighborhood_radius

  • Controls: The radius of the spherical neighborhood used for density estimation.
  • Units: The point cloud's coordinate units
  • Default: 0.001
  • Must be > 0
  • Increase → considers a wider area (more robust, less locally sensitive)
  • Decrease → more locally sensitive, but noisier
  • Scale to your point cloud
  • Typical range: 0.0001–0.01 for small objects, 0.01–0.1 for larger scenes

is_point_cloud_linear

  • Controls: Which density-estimation method is used — one suited to linear structures (e.g. a rod, wire, or cable) vs. the default suited to 2D surfaces or 3D volumes.
  • Units: Boolean flag
  • Default: False
  • Set to True only when the point cloud is approximately one-dimensional

Known Limitation

is_point_cloud_linear=True currently causes the request to fail server-side. Only the default False is actually supported right now — leave this parameter at its default until this is fixed upstream.

TIP

Best practice: use estimate_principal_axes to find projection_axis rather than guessing it, then adjust neighborhood_radius to your point cloud's scale (in its own coordinate units, not necessarily meters) before fine-tuning num_nearest_neighbors.

Where to Use the Skill

Common pipelines include:

  • Stacked object separation – splitting a head from its shaft, or a top item from the one beneath it
  • Layered structure analysis – separating regions of a scene that differ in thickness or fill along one axis
  • Assembly part identification – isolating a component whose density profile changes sharply from its neighbor
  • Conveyor belt item isolation – separating touching items where DBSCAN's distance-based grouping would merge them

Alternative Skills

Skillvs. Cluster Point Cloud Based on Density Jump
cluster_point_cloud_using_dbscanDiscovers an arbitrary number of dense clusters directly from the data, instead of always splitting into exactly two regions at one density jump. Use DBSCAN when objects are spatially separated; use density jump clustering when objects are closely packed or touching.
estimate_principal_axesComputes the point cloud's principal directions, useful for choosing a meaningful projection_axis instead of guessing it.

When Not to Use the Skill

Do not use Cluster Point Cloud Based on Density Jump when:

  • Objects are already spatially separated - use cluster_point_cloud_using_dbscan instead
  • There's no clear density discontinuity along any axis - the algorithm may not find a meaningful split
  • You need more than 2 regions - this Skill always returns exactly 2
  • The point cloud has roughly uniform density - there's no density jump to detect
  • You don't yet know the principal axis - run estimate_principal_axes first to choose projection_axis

WARNING

This Skill always returns exactly 2 regions, split at the single strongest density discontinuity found. If you need more than 2 groups, or the objects aren't aligned along one dominant axis, use cluster_point_cloud_using_dbscan instead.